aeolus.schema, aeolus.qa, aeolus.network_registry¶
The v0.5.0 contract: the public column set, the QA enums, and the registry that maps each network's own quality tokens onto them.
Schema¶
The public wire format, and the one step that produces it.
Adapters emit types.ADAPTER_DATA_COLUMNS. finalise_data_frame runs where
every download converges and adds the network identity and the QA model. It is
the only code that knows both schemas — do not add these columns in adapters.
DATA_COLUMNS = ['site_code', 'network', 'date_time', 'measurand', 'value', 'units', 'qa_code', 'qa_tier', 'ratification_stage', 'backend', 'source_network', 'ratification', 'created_at']
module-attribute
¶
METADATA_COLUMNS = ['site_code', 'site_name', 'latitude', 'longitude', 'network', 'country', 'instrument_class', 'provider', 'backend', 'measurands', 'source_network']
module-attribute
¶
public_data_columns()
¶
The column list in force: with or without the legacy mirrors.
finalise_data_frame(df, source)
¶
Turn an adapter frame for source into the public schema. Idempotent.
Source code in src/aeolus/schema.py
finalise_metadata_frame(df, source)
¶
Add network identity to an adapter's site metadata. Extra columns are kept.
Source code in src/aeolus/schema.py
QA enums and derivation¶
The v0.5.0 data-quality model: frozen enums and pure derivation functions.
The enum values are permanent public keys (renaming one is a major breaking
change), shared with Argus. qa_code is whatever the upstream wrote;
qa_tier and ratification_stage are derived from it through the
network's vocabulary (see aeolus.network_registry).
QA_TIERS = ('reference_full_qc', 'reference_provisional', 'lcs_calibrated', 'lcs_factory_only', 'flagged', 'unknown')
module-attribute
¶
RATIFICATION_STAGES = ('unratified', 'ratified', 'supplied', 'not_applicable')
module-attribute
¶
QA_MODELS = ('regulatory_temporal_ratification', 'staged_calibration', 'point_in_time_validation', 'none', 'mixed', 'unknown')
module-attribute
¶
INSTRUMENT_CLASSES = ('reference', 'equivalent', 'indicative', 'LCS', 'mixed', 'unknown')
module-attribute
¶
derive_qa(qa_codes, vocabulary, default_stage)
¶
Return (qa_tier, ratification_stage) for a series of upstream codes.
A missing code means the upstream said nothing: tier unknown and the
network's default_stage. A code the vocabulary does not list is also
unknown, with a null stage — an unrecognised token must not be
silently promoted to the network default.
Source code in src/aeolus/qa.py
legacy_ratification(qa_tier, stage)
¶
The pre-0.5.0 ratification string, derived from the new columns (spec §4.3).
Source code in src/aeolus/qa.py
Network registry¶
Each network's identity, QA vocabulary, when its data gets ratified, and its licence live in src/aeolus/data/qa_vocabularies/<CODE>.yaml and are read through this module.
Network registry: who each network is and how to read its QA codes.
A network is who produced the data (AURN, LAQN, ...). A source is a way
aeolus fetches it (AURN via RData, AURN-SOS via the SOS API). Several
sources can serve one network; SOURCE_ROUTES records which. The routing
engine (choosing a backend) is v0.6.0 — this is only the lookup table.
SOURCE_ROUTES = {**{n: (n, 'RDATA') for n in ('AURN', 'SAQN', 'WAQN', 'NI', 'AQE', 'LMAM', 'LAQN')}, 'SAQD': ('SAQN', 'RDATA'), **{f'{n}-SOS': (n, 'SOS') for n in ('AURN', 'SAQN', 'WAQN', 'NI', 'AQE')}, 'LAQN-ERG': ('LAQN', 'ERG_REST'), **{n: (n, n) for n in ('BREATHE_LONDON', 'AIRQO', 'AIRNOW', 'EEA', 'SONITUS', 'SENSOR_COMMUNITY', 'OPENAQ', 'PURPLEAIR')}}
module-attribute
¶
NetworkSpec
dataclass
¶
Source code in src/aeolus/network_registry.py
code
instance-attribute
¶
name
instance-attribute
¶
country
instance-attribute
¶
regulatory
instance-attribute
¶
instrument_class
instance-attribute
¶
qa_model
instance-attribute
¶
default_ratification_stage
instance-attribute
¶
qa_code_vocabulary = field(default_factory=dict)
class-attribute
instance-attribute
¶
operators = ()
class-attribute
instance-attribute
¶
ratification_cadence_months = None
class-attribute
instance-attribute
¶
first_ratification_latency_months = None
class-attribute
instance-attribute
¶
full_year_ratified_by = None
class-attribute
instance-attribute
¶
ratification_overrides = ''
class-attribute
instance-attribute
¶
data_licence = ''
class-attribute
instance-attribute
¶
homepage_url = ''
class-attribute
instance-attribute
¶
notes = ''
class-attribute
instance-attribute
¶
__init__(code, name, country, regulatory, instrument_class, qa_model, default_ratification_stage, qa_code_vocabulary=dict(), operators=(), ratification_cadence_months=None, first_ratification_latency_months=None, full_year_ratified_by=None, ratification_overrides='', data_licence='', homepage_url='', notes='')
¶
get_network_spec(code)
¶
list_network_specs()
¶
route_for(source)
¶
spec_for_source(source)
¶
(network, backend, spec) for a source — including one aeolus has never
heard of.
A source registered with register_source but absent from
SOURCE_ROUTES (a user's own adapter) is treated as its own network with
nothing known about it: qa_tier is unknown and no stage is assumed.
Source code in src/aeolus/network_registry.py
Units and cache¶
One spelling per unit.
Upstreams write the same unit many ways (ug.m-3, µg/m³, UG/M3). Every
adapter and every metrics path canonicalises through :func:canonical_unit, so
there is a single list to maintain rather than one per module.
canonical_unit(unit)
¶
Return the canonical spelling of unit (ug/m3, mg/m3, ng/m3, ppb, ppm).
Units that are not concentration units (C, %, hPa, AQI) and
missing values are returned unchanged.
Source code in src/aeolus/units.py
canonical_units(units)
¶
Vectorised :func:canonical_unit, mapping each distinct value once.
Local file cache for downloaded air quality data.
Caches data as Parquet files, keyed by source, site, and date range. This avoids redundant API calls when re-running notebooks or analyses.
Cache location defaults to ~/.cache/aeolus/ and can be overridden
by setting the AEOLUS_CACHE_DIR environment variable.
Complete results for an explicit date range never expire. Two kinds of entry
are volatile and are re-fetched once older than AEOLUS_CACHE_VOLATILE_TTL_S
seconds (default 3600): rolling last= windows, which are keyed on the
shorthand so a re-run hits the cache, and results missing a requested site,
which may reflect a transient failure. A last= window no longer than the
TTL is always fetched live.
Usage::
import aeolus
from aeolus.cache import enable_cache, disable_cache, clear_cache
# Enable caching (all subsequent downloads are cached)
enable_cache()
# Downloads hit the API on first call, then use cache
data = aeolus.download("AURN", ["MY1"], start, end)
data = aeolus.download("AURN", ["MY1"], start, end) # instant
# Clear everything
clear_cache()
# Disable caching
disable_cache()
enable_cache(cache_dir=None)
¶
Enable local file caching for downloads.
Subsequent calls to aeolus.download() will check the cache before
hitting the network. Cached data is stored as Parquet files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cache_dir
|
str | Path | None
|
Override the cache directory. Defaults to
|
None
|
Example::
>>> import aeolus
>>> from aeolus.cache import enable_cache
>>> enable_cache()
>>> data = aeolus.download("AURN", ["MY1"], start, end) # fetches
>>> data = aeolus.download("AURN", ["MY1"], start, end) # cached
Source code in src/aeolus/cache.py
disable_cache()
¶
Disable local file caching.
Downloads will always go to the network. Existing cache files
are preserved (use clear_cache() to remove them).
Source code in src/aeolus/cache.py
clear_cache(source=None)
¶
Remove cached files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str | None
|
If given, only clear cache for this source. Otherwise clears the entire cache. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
Number of files removed. |
Example::
>>> from aeolus.cache import clear_cache
>>> clear_cache("AURN") # clear AURN cache only
>>> clear_cache() # clear everything
Source code in src/aeolus/cache.py
cache_info()
¶
Return information about the current cache state.
Returns:
| Type | Description |
|---|---|
dict
|
dict with keys: enabled, directory, sources, total_files, total_size_mb |
Example::
>>> from aeolus.cache import cache_info
>>> info = cache_info()
>>> print(f"Cache: {info['total_files']} files, {info['total_size_mb']:.1f} MB")