Skip to content

Products and API

dpc_radar.products

The DPC radar products, and the requests and answers of the open radar API.

Pure: nothing here does I/O. A client sends the requests built here and hands the answers back to the parsers.

The API (radar-api.protezionecivile.it) has two calls that matter:

  • GET /findLastProductByType?type=<product> gives the time of the latest frame and its period;
  • POST /downloadProduct with {"productType", "productDate"} gives a short-lived S3 link to the GeoTIFF of one frame.

A frame the DPC no longer keeps (about five months back) does not answer 404: it answers 500 with S3's 403 Forbidden in the body. :func:is_expired tells the two failures apart, because an expired frame must not be retried.

Product dataclass

One DPC radar product.

period property

period: timedelta

How often a new frame comes out: 30 minutes for the «Merging», 5 for radar only.

The same as the period the API returns (PT30M, PT5M): the frame before one issued at t is the one issued at t - period.

LastProduct dataclass

What findLastProductByType says about the latest frame.

product

product(name: str | Product) -> Product

The :class:Product called name.

last_product_url

last_product_url(name: str | Product, *, base: str = API_BASE) -> tuple[str, dict[str, str]]

URL and query parameters of the latest-frame request.

download_url

download_url(base: str = API_BASE) -> str

URL of the download-link request (a POST).

download_body

download_body(name: str | Product, at: datetime) -> dict[str, Any]

JSON body of the download-link request for the frame issued at.

to_epoch_ms

to_epoch_ms(at: datetime) -> int

at as the API's milliseconds since the epoch; at must be aware.

from_epoch_ms

from_epoch_ms(ms: int | float | str) -> datetime

The API's milliseconds since the epoch as an aware UTC datetime.

parse_period

parse_period(text: str) -> timedelta

An ISO 8601 period of hours and minutes (PT5M, PT30M, PT1H).

parse_last_product

parse_last_product(payload: dict[str, Any]) -> LastProduct | None

The latest frame, or None when the API lists none.

parse_download

parse_download(payload: dict[str, Any]) -> str

The S3 link of a frame, from the download-link answer.

is_expired

is_expired(status: int, body: str) -> bool

True when the API says the frame is no longer kept.

That is a 500 carrying S3's 403 in the body, not a 404. Any other failure is worth retrying; this one never is.