recipe#
Functions to handle recipes for data ingestion and harmonization
Read and validate recipes, find recipes, build derivatives, get output paths etc.
Classes#
One dependency edge: a recipe consuming another recipe's output. |
Functions#
|
Load recipe (.yaml, .csv or .xlsx) |
|
Read a recipe .yaml file as a dictionary, cast it to schema |
|
Shortcut to get recipe_id by its parts |
|
Return the canonical recipe ID of a loaded recipe. |
|
Resolve the retention class of a recipe's output. |
|
Merge a primary recipe with an additional_layers spec. |
|
Return the merged recipe for a secondary layer identified by entity. |
|
Find a recipe ID by admin_id and entity/dataset identifier. |
|
Return the |
|
Provenance suffix -> source key, auto-generated from existing recipes. |
|
Split a trailing provenance suffix off name; return (base, source). |
|
Resolve a possibly provenance-suffixed column to its registry attribute. |
|
Extract the source id from a recipe id. |
|
Find the ID of an administrative data ingestion recipe |
|
Find the most suitable entity recipe. |
|
Extract upstream recipe references from a recipe. |
|
Return the layer names available for a recipe's 'additional_layers'. |
|
Return the path where recipe output is written. |
|
Return the admin level at which output files are split. |
|
Return the admin level at which data is chunked for processing. |
|
Return the admin level at which downloads are partitioned. |
|
Return the list of valid partition ID strings for a recipe. |
Module Contents#
- openplaces.recipe.get_recipe(*args, **kwargs)#
Load recipe (.yaml, .csv or .xlsx)
- Parameters:
args (tuple) – Arguments for openplaces.path.recipe_path
kwargs (dict) – Keywords arguments. Those in openplaces.path.OpenPlacesReference and openplaces.path.recipe_path will be used to find the path, the remainder is passed to the reading functions: - yaml.safe_load() - pd.read_csv() - pd.read_excel()
- openplaces.recipe.get_recipe_dict(filepath, *args, **kwargs)#
Read a recipe .yaml file as a dictionary, cast it to schema
- Parameters:
filepath (pathlib.Path) – Filepath to .yaml file
args (list) – Passed on from get_recipe
kwargs (dict) – Passed on from get_recipe
- openplaces.recipe.get_recipe_by_id(recipe_id, **kwargs)#
Shortcut to get recipe_id by its parts
Assumes syntax: {admin_id}_{entity}_{filename}.{extension}
admin_id or filename can be missing
(Datasets for non-entities aren’t yet supported)
- Parameters:
recipe_id (str) – Identifier or a recipe
kwargs (dict) – Keyword arguments will be passed on to get_recipe()
- openplaces.recipe.get_recipe_id(recipe: str | dict) str#
Return the canonical recipe ID of a loaded recipe.
The ID is the recipe file’s stem, recorded by get_recipe_dict at load time. For recipe dicts constructed without a file (e.g. in tests), the ID is rebuilt from admin_id and entity/dataset; filename suffixes cannot be recovered in that case.
- Parameters:
recipe (str or dict) – Recipe ID string (returned unchanged, minus a .yaml extension) or a loaded recipe dictionary.
- openplaces.recipe.get_recipe_retention(recipe: str | dict) str#
Resolve the retention class of a recipe’s output.
Combines the output bucket’s default (STANDARD_DIRS), configuration overrides, and the recipe’s own save_to.retention via
retention_for().- Parameters:
recipe (str or dict) – Recipe ID or loaded recipe dictionary.
- openplaces.recipe.build_table_recipe(primary_recipe: dict, layer_spec: dict) dict#
Merge a primary recipe with an additional_layers spec.
Per-table keys (entity, layer, columns, index config, etc.) are taken from layer_spec when present, otherwise removed so that primary-only values do not bleed into the secondary table. process_by is inherited from the primary unless layer_spec sets it explicitly (use ‘process_by: null’ in the YAML to disable chunking for a specific additional table).
- Parameters:
primary_recipe (dict) – Loaded primary recipe dictionary.
layer_spec (dict) – One entry from the primary recipe’s ‘additional_layers’ list.
- Returns:
Merged recipe dict for the layer.
- Return type:
dict
- openplaces.recipe.get_table_recipe(recipe: str | dict, layer: str) dict#
Return the merged recipe for a secondary layer identified by entity.
- Parameters:
recipe (str or dict) – Primary recipe (ID string or loaded dict).
layer (str) – Entity type (e.g. ‘property’) or full entity string (e.g. ‘property-massgis-2025’) of the additional layer.
- Returns:
Merged recipe dict for the requested layer.
- Return type:
dict
- Raises:
KeyError – If no additional_layers entry matching layer is found.
- openplaces.recipe.find_recipe_id(admin_id, entity_or_dataset, filename=None, silent=False)#
Find a recipe ID by admin_id and entity/dataset identifier.
- Parameters:
admin_id (str) – Administrative unit identifier.
entity_or_dataset (str) – Entity or dataset identifier string, may contain glob wildcards (e.g. ‘parcel--’, ‘admin-census-2021’).
filename (str, optional) – Filename stem to match within the recipe directory. When None (default), matches any .yaml file in the entity directory. A .yaml extension is appended automatically if absent.
silent (bool) – If True, suppress the message printed when multiple recipes are found.
- openplaces.recipe.iter_entity_sources() frozenset#
Return the
(entity_type, source_id)pairs across all entity recipes.Scans the bundled recipes directory once (cached) and parses each recipe filename for its entity token. Files whose entity token does not parse are skipped. A bare entity token (e.g.
footprint) followed by a{theme}-{source}-{version}remainder — an entity+dataset enrich recipe such asUS_footprint_built-n-stories-brails-2026— falls back to the dataset’s own source id. Used to auto-generate the provenance suffix vocabulary so adding a new source needs no hardcoded list edits.
- openplaces.recipe.provenance_suffixes() tuple[tuple[str, str], Ellipsis]#
Provenance suffix -> source key, auto-generated from existing recipes.
For every
(entity_type, source)pair known to the recipes, generate the column suffixes the harmonizer can produce:_{entity}_{source}and the bare_{source}fallback (e.g._building_nsiand_nsi;_footprint_femaand_fema). Parcels are interchangeable, so they map by the entity-only_parcel. Returned longest-first so a specific suffix wins over its bare fallback. No hardcoded list — adding a source recipe extends this automatically.
- openplaces.recipe.split_provenance_suffix(name: str) tuple[str, str | None]#
Split a trailing provenance suffix off name; return (base, source).
- openplaces.recipe.resolve_attribute_name(column: str) str#
Resolve a possibly provenance-suffixed column to its registry attribute.
An exact registry entry always wins, so genuinely distinct attributes whose names merely end in a source-like token (
n_footprints_per_parcel,priority_on_parcel,parcel_id_local) resolve to themselves. Only unregistered names fall back to stripping a provenance suffix (improvement_value_parcel->improvement_value); a name that is neither registered nor suffixed is returned unchanged.
- openplaces.recipe.source_id_from_recipe_id(recipe_id: str) str#
Extract the source id from a recipe id.
A recipe id is
{admin_id}_{entity_or_theme}-{source}-{version}[...]; takes the last_-delimited token, then the second--delimited field within it (e.g.'US_building-nsi-2022'->'nsi'). Falls back to the whole token when it has no-(an un-versioned or otherwise irregular recipe id).
- openplaces.recipe.find_admin_recipe_id(admin_id, admin_level, silent=False)#
Find the ID of an administrative data ingestion recipe
- Parameters:
admin_id (str) – Administrative unit identifier
admin_level (int) – Administrative level for which a recipe is sought.
silent (bool) – If True, suppress the message printed when multiple recipes are found.
- openplaces.recipe.find_entity_recipe_id(admin_id, entity_type, stage: str | None = None, source_id: str | None = None, filename: str | None = None, silent: bool = False)#
Find the most suitable entity recipe.
Recipes follow the pipeline order ingest, harmonize, enrich, curate unless stage is specified. Within a stage, prefer the requested source, the most specific applicable administrative scope, and the latest version.
- class openplaces.recipe.DepEdge#
Bases:
NamedTupleOne dependency edge: a recipe consuming another recipe’s output.
- recipe_id#
ID of the consuming recipe the edge was extracted from.
- Type:
str
- upstream_recipe_id#
ID of the consumed recipe; None when auto-discovery could not resolve a concrete recipe (see resolved).
- Type:
str or None
- kind#
Reference style: the recipe key the edge came from (‘entity_recipe’, ‘image_recipe’, ‘recipe_id’, ‘admin_recipe_id’, ‘tile_recipe_id’, ‘footprint_recipe_id’, …) or ‘auto_discover’.
- Type:
str
- step#
Pipeline step name (or top-level recipe section) where the reference was found.
- Type:
str or None
- resolved#
False when an auto-discovered reference could not be resolved to a concrete recipe. Consumers of the dependency graph must treat unresolved edges as “may consume anything” (fail safe).
- Type:
bool
- openplaces.recipe.get_recipe_dependencies(recipe, admin_id=None) list[DepEdge]#
Extract upstream recipe references from a recipe.
Edge sources (all present in committed recipes today):
top-level ‘entity_recipe’ (curate/enrich -> harmonized spine) and ‘image_recipe’ (enrich -> image ingest); enrich recipes without an explicit ‘entity_recipe’ resolve their spine dynamically, mirroring the enricher
any key matching the suffix ‘recipe_id’ anywhere in the recipe (‘recipe_id’ in pipeline sources and steps, ‘admin_recipe_id’, ‘download_by.tile_recipe_id’, ‘footprint_recipe_id’, merge_enrichments ‘recipes’ entries, …); keys under a ‘*crosswalk’ block and ‘remap_id’ are excluded (value crosswalks, not data dependencies)
pipeline steps or source entries with ‘auto_discover’ or a bare ‘entity_type’, resolved per admin unit the same way the pipeline resolves them at run time
- Parameters:
recipe (str or dict) – Recipe ID or loaded recipe dictionary.
admin_id (str or AdminId, optional) – Admin unit to resolve auto-discovered references for. When None, auto-discovered references are returned as unresolved edges.
- Returns:
Unresolved auto-discovery is returned as an edge with upstream_recipe_id=None and resolved=False (fail safe: the caller must assume such a recipe may consume anything it protects).
- Return type:
list of DepEdge
- openplaces.recipe.get_layers(recipe: str | dict) list[str]#
Return the layer names available for a recipe’s ‘additional_layers’.
These are the values accepted by the layer argument of ‘get_entities’ and ‘get_output_path’.
- Parameters:
recipe (str or dict) – Recipe dict or recipe ID string.
- Returns:
Entity type strings (e.g. ‘property’, ‘transaction’) for each entry in ‘additional_layers’.
- Return type:
list of str
- openplaces.recipe.get_output_path(recipe, admin_id=None, partition_id=None, geo=False, layer=None, entity_recipe_id=None)#
Return the path where recipe output is written.
Mirrors Ingester._get_output_path without instantiating an Ingester. The output root is determined by ‘save_to’: ‘data_dir’ in the recipe (default: ‘cache’), which must name a directory registered in STANDARD_DIRS.
- Parameters:
recipe (str or dict) – Recipe identifier (as accepted by get_recipe_by_id) or a pre-loaded recipe dict.
admin_id (str or AdminId, optional) – Administrative unit for which to resolve the output path. Pass None for recipes not split by admin unit.
partition_id (str, optional) – Partition value appended to the filename stem, e.g. ‘US-NC-BS_footprint-obm-2025_032012.parquet’ for a tile partition with id ‘032012’. Pass None (default) to obtain the final, merged output path.
geo (bool, optional) – If True, return the path to the companion ‘_geo.parquet’ file instead of the attribute parquet file.
layer (str, optional) – Entity type (e.g. ‘property’) or full entity string (e.g. ‘property-massgis-2025’) of a secondary layer defined in additional_layers. If given, the path for that layer is returned instead of the primary entity’s path.
entity_recipe_id (str or dict, optional) – Concrete entity recipe to enrich when recipe has stage ‘enrich’.
- Returns:
Resolved output path for the recipe data file.
- Return type:
pathlib.Path
- openplaces.recipe.get_save_admin_level(recipe, operation_keys=('download_by', 'process_by', 'save_to'))#
Return the admin level at which output files are split.
When save_to: admin_level is explicitly set it defines the output granularity directly — process_by or download_by may be finer (aggregation) or coarser than this level. When save_to: admin_level is absent the level is the maximum found across the given operation keys, falling back to the recipe’s own admin ID depth.
- Parameters:
recipe (dict) – Loaded recipe dictionary.
operation_keys (tuple of str) – Recipe section keys to inspect for ‘admin_level’. ‘save_to’ is included by default since save_to: admin_level controls output granularity. Override when calling from other recipe runners.
- Returns:
Admin level for output files (0 = no admin split).
- Return type:
int
- openplaces.recipe.get_process_admin_level(recipe)#
Return the admin level at which data is chunked for processing.
- openplaces.recipe.get_download_admin_level(recipe)#
Return the admin level at which downloads are partitioned.
- openplaces.recipe.get_partition_ids(recipe)#
Return the list of valid partition ID strings for a recipe.
Returns [None] for recipes without a ‘download_by’: ‘partition’ key.
- Parameters:
recipe (dict) – Loaded recipe dictionary.
- Return type:
list of str or list of None
- Raises:
ValueError – If ‘download_by’: ‘partition’ is ‘year’ or ‘year_month’ but ‘first’/’last’ are not defined.
NotImplementedError – If ‘download_by’: ‘partition’ names an unrecognised partition type.