gedih3.logger#
Attributes#
Classes#
Functions#
|
|
|
|
|
|
|
Return the GEDI data version a local SOC tree was populated with, or None. |
|
The source exclusions a build log keeps ( |
Module Contents#
- gedih3.logger.PRODUCT_STATUS_INDEXED = 'INDEXED'#
- gedih3.logger.PRODUCT_STATUS_PARTIAL_NAN = 'PARTIAL_NAN'#
- gedih3.logger.PRODUCT_STATUS_MISSING_COLUMN = 'MISSING_COLUMN'#
- gedih3.logger.PRODUCT_STATUS_MISSING_SOURCE = 'MISSING_SOURCE'#
- gedih3.logger.PRODUCT_STATUS_FAILED = 'FAILED'#
- gedih3.logger.PRODUCT_STATUS_PENDING = 'PENDING'#
- gedih3.logger.GRANULE_STATUS_NO_DATA = 'NO_DATA'#
- gedih3.logger.resolve_soc_version(soc_dir)[source]#
Return the GEDI data version a local SOC tree was populated with, or None.
A-priori lookup — no HDF5 open, no recursive walk:
the download log’s
gedi_version(gedih3_download_log.jsonat the SOC root), written by every gh3 producer;else the
_V00Nfield of the firstGEDI*.h5found by a boundedyear/doyscandir (externally populated trees: manual rsync, NASA delivery).
Used by
gh3_build/gh3_downloadto pin a fresh build or resumed download to the release already on disk when the user did not pass--gedi-version; the package default (GEDI_DEFAULT_VERSION) only applies when nothing on disk answers the question.
- class gedih3.logger.SOCDownloadLogger(product_vars, spatial=None, temporal=None, version=None, dir=None)[source]#
- gedi_version = None#
- s3_access = False#
- log_file#
- log_data#
- updating = False#
- new_spatial = None#
- new_temporal = None#
- new_product_vars = None#
- register_pending_granules(granule_list)[source]#
Register granules as PENDING. Called before download starts.
- Parameters:
- granule_listlist of dict
Each dict must have ‘orbit’, ‘granule’, ‘track’ keys.
- update_granule_status(gran_key, status)[source]#
Update status of a single granule (does NOT auto-save).
Caller controls save frequency via explicit save_log() calls.
- Parameters:
- gran_keydict
Dict with ‘orbit’, ‘granule’, ‘track’ keys.
- statusstr
‘DOWNLOADED’ or ‘FAILED’.
- set_post_download_info()[source]#
Scan SOC directory and record downloaded granules.
Refreshes
_soc_manifest.txtfirst so subsequentsoc_file_treecalls (here and on the next resume) skip the recursive glob over the SOC tree. This is the same pattern the H3 database uses viaMANIFEST_FILENAME.
- gedih3.logger.stored_exclude(log_data) list[source]#
The source exclusions a build log keeps (
gh3_build --exclude).A single pattern written by hand as a string is one pattern, never its characters (
'*'alone would exclude every file).- Raises:
- GediValidationError
If
excludeis neither a string nor a list of strings.
- class gedih3.logger.H3BuildLogger(product_vars, spatial=None, temporal=None, res: int = 12, part: int = 3, version: int = None, dir=None, source_mode=None)[source]#
- default_products#
- preset_products#
- preset_extra_vars#
- log_file#
- log_data#
- updating = False#
- update_ran = False#
- source_mode = None#
- build_start_time#
- previous_status#
- allow_missing_products#
- exclude#
- new_spatial = None#
- new_temporal = None#
- new_product_vars = None#
- requested_expansion#
- register_pending_granules(granule_list)[source]#
Register granules as PENDING. Called before build starts.
- Parameters:
- granule_listlist of dict
Each dict must have ‘orbit’, ‘granule’, ‘track’ keys, and may have
releases:{product: tag}(gedi_release_tag()) of the source files the build lists for the granule. A newer release within the GEDI version is detected against the recorded tags (#47), so a tag is never replaced here: a granule not indexed yet gains tags only for products it has none for (rows a killed run left on disk may come from the file recorded first, and an older tag is reported later rather than a newer one hiding stale rows). AnINDEXEDorNO_DATAentry, and one registered before tags were recorded (noreleaseskey, so rows on disk may come from any file), are left as they are. No tag means the source is unknown: an older log, a product added by a variable update, or an S3-sourced build (which registers no granules).
- update_granule_status(gran_key, status)[source]#
Update status of a single granule (does NOT auto-save).
Caller controls save frequency via explicit save_log() calls.
- Parameters:
- gran_keydict
Dict with ‘orbit’, ‘granule’, ‘track’ keys.
- statusstr
‘INDEXED’ or ‘PENDING’.
- get_finished_granules()[source]#
Return skip list when resuming with same filters.
Only returns successfully completed granules, with the status and products fields stripped for backward compatibility with
_filter_granules()dict comparison (which expects bare{'orbit', 'granule', 'track'}dicts). Legacy logs without status fields are treated as successful.NO_DATAgranules (read in full, zero rows) are skipped under exactly the conditionsINDEXEDones are. They never stay skipped past a scope change that could make them non-empty:__init__demotes everyNO_DATAgranule toPENDINGas soon as the requested region or dates widen the stored ones, by any area, before this is consulted.
- reads_added_area_only()[source]#
True when Stage 1 reads only the region this update adds (a spatial-only expansion).
- stage1_only_granules()[source]#
Granules Stage 1 may read in this run, or None when it may read any.
A spatial-only expansion hands Stage 1 only the added region (
get_spatial()). That is exactly what a granule finished under the stored region still needs. Any other granule (new to the database,PENDING,FAILED, aNO_DATAreopened by the widening) would have its rows outside the added region dropped, while its rows inside made itINDEXEDand kept later builds from ever reading the rest (#42). Such granules are left as they are for the next build, which reads them over the whole region.- Returns:
- list of dict or None
{'orbit', 'granule', 'track'}dicts, as forget_finished_granules().
- is_up_to_date()[source]#
Check if database already matches requested parameters.
Returns True when all of these hold: - Log exists (updating=True) - No new products, spatial, or temporal changes detected - No pending variable update from a previous crash - All tracked granules are INDEXED (or NO_DATA: read, nothing to index)
- set_post_build_info(verify_observed=False)[source]#
Fold partition metadata into the log: granule status, columns, dates.
Also records
self._observed_granule_keys: the(orbit, granule, track)of every granule the partition metadata lists, orNonewhen that listing cannot be trusted to be complete (no partitions, an unreadable scan). Withverify_observed=Truethe scan additionally checks each partition’s sidecars against its parquet files and leavesNoneon any mismatch;mark_no_data()requires a set.
- stage1_full_scope()[source]#
True when Stage 1 reads under the whole database scope.
get_spatial/get_temporalhand Stage 1 only the ADDED area (or dates) on a spatial-only (temporal-only) expansion, so a granule empty under that diff may hold rows in the old scope. Zero rows then proves nothing, andmark_no_data()refuses. Full scope: a fresh build, a plain resume or update (the log already holds the union), and a mixed update’s Phase 1. Mirrors the branches of the two getters.
- mark_no_data(listed_keys, incomplete_keys, empty_keys)[source]#
Record granules Stage 1 read in full that hold no rows as
NO_DATA.Call after the final
set_post_build_info()of a build whose Stage 1 drained fully;incomplete_keysis that run’s set of granules with any task not proven complete,empty_keysthose whose every beam task is proven complete AND empty. Nothing is marked without a trustworthy observed set (set_post_build_infocould not verify the partition metadata) or when Stage 1 did not run under the full database scope (stage1_full_scope()). In doubt a granule stays PENDING: that costs a re-read, a wrongNO_DATAcosts rows.PENDING->NO_DATAwhen listed, inempty_keysand absent from every partition’s metadata (the last guard matters for tasks emptied by the skip check, whose rows are already in the database);NO_DATA->PENDINGwhen listed and incomplete this run;MERGE_FAILEDis never touched.
NO_DATAdrops theproductsmap andfill_attempts: there is nothing to fill. Does not save. Returns{'marked', 'reopened'}.- Parameters:
- listed_keysiterable of tuple
Granules this run listed for Stage 1.
- incomplete_keysiterable of tuple
Granules with any task not proven complete this run.
- empty_keysiterable of tuple
Granules whose every beam task is proven complete and empty.
- get_granule_product_status(gran_key, product)[source]#
Return per-product status for a granule, or None if unknown.
- Parameters:
- gran_keydict or tuple
Either
{'orbit', 'granule', 'track'}or(orbit, granule, track).- productstr
Product code (e.g. ‘L4A’).
- get_product_gaps(active_products=None, gap_statuses=None)[source]#
List granules where any active product is not INDEXED.
Returns a list of
(gran_key_dict, [missing_products])pairs. Used by gh3_doctor as the fast-path audit (log-only, no parquet scan).- Parameters:
- active_productsiterable of str, optional
Products to check. Defaults to
self.product_vars.keys().- gap_statusesiterable of str, optional
Per-product statuses considered as gaps. Defaults to everything except
INDEXED(i.e. PARTIAL_NAN, MISSING_COLUMN, MISSING_SOURCE, FAILED, PENDING).
- mark_granule_product(gran_key, product, status)[source]#
Set per-product status for a granule (does not auto-save).
Caller is responsible for invoking
save_log()to persist. Used by gh3_doctor after a fix or verification step.
- set_product_statuses(updates)[source]#
Bulk per-product status update (does not auto-save).
updatesmaps(orbit, granule, track)to{product: status}. One pass overgranule_infowhatever the number of updates, unlike repeatedmark_granule_product()calls. Returns the count applied.
- superseded_releases(soc_entries)[source]#
Indexed granules whose listed source differs from the release read.
Compares each
INDEXEDorNO_DATAgranule’s recordedreleaseswith the file a listing would read now. Log-only, no I/O. A product whose rows name several releases (mixed_releases, seeadopt_row_releases()) compares its oldest, so a listing of the newer one reports it until it is re-indexed whole. A product with no recorded tag is unknown and never reported. A log with no GEDI version reports nothing: tags carry no version, so a file of another release would read as a re-release.- Parameters:
- soc_entriesiterable of dict
{product: path}dicts: per granule, assoc_file_tree(..., to_list=True)returns them, or one per file. Where a granule product is listed more than once, its latest release (gedi_release_rank) is the one compared, the file a build reads. Only granules with a recorded tag are ranked.
- Returns:
- dict
{'newer': {key: {product: (recorded, listed)}}, 'older': {...}}keyed by(orbit, granule, track).newer: a re-release within the GEDI version that the rows do not come from (#47).older: only an older release than the one read is listed; it is never applied, but variables added and products filled later would read it.
- pending_product_fills(statuses=(PRODUCT_STATUS_MISSING_SOURCE,))[source]#
Indexed granules still waiting for products published after them.
{(orbit, granule, track): [product, ...]}for everyINDEXEDgranule with a product in one ofstatuses. The default,MISSING_SOURCE, is whatgh3_buildbackfills automatically;gh3_doctoralso passesFAILED(fills given up on, seerecord_fill_results()) to retry them on demand. Log-only, no I/O.
- record_releases(releases)[source]#
Record the source releases a write is about to read (does not save).
- Parameters:
- releasesdict
{(orbit, granule, track): {product: tag}}(tags fromgedi_release_tag), e.g. from_build_fill_products’releases_callbackbefore it writes. As inregister_pending_granules(), a product that has a tag keeps it (rows already written may come from that release), and an entry recorded before tags existed (noreleaseskey) stays unknown: its other products’ rows may come from any file.
- Returns:
- int
Number of tags added.
- static compared_releases(granule, newest=False)[source]#
{product: tag}a granule’s rows are compared by: its recordedreleases, plus the oldest (newest=True: the newest) of the releases a product’s rows name when they name several (mixed_releases). Log-only.
- row_release_targets()[source]#
Indexed granules whose releases were never recorded nor read from their rows (#67).
INDEXEDgranules with noreleaseskey: a database built before gedih3 0.20.10 recorded none, and an entry registered by such a version before it was indexed keeps none (its rows may come from any file). Their rows name their source files (root_file_<prod>), soadopt_row_releases()takes them from there. Empty when the database has no source column. Log-only.- Returns:
- set of tuple
(orbit, granule, track)keys.
- adopt_row_releases(releases, keys, mixed=None)[source]#
Record the releases the database’s own rows were read from (does not save).
- Parameters:
- releasesdict
{(orbit, granule, track): {product: tag}}from the rows’root_file_<prod>columns (gh3builder.releases_from_rows), for products whose rows name a single release. Unlikerecord_releases()(the files a write is about to read), these are what the rows hold.- keysiterable of tuple
The granules whose rows were all read (
row_release_targets()less those an unreadable file may hold). Each gets areleasesentry, empty when its rows named none, so it is not read again; a product notINDEXEDfor it stays out.- mixeddict, optional
{key: {product: [tags]}}for products whose rows name several releases (a replacement left half done). Kept apart as the granule’smixed_releases, never as its release: reported bygh3_doctor --check superseded_granules, compared by its oldest (superseded_releases()), and dropped when it is re-indexed.
- Returns:
- int
Number of tags added, mixed ones included.
- mixed_release_granules()[source]#
{(orbit, granule, track): {product: [tags]}}: granules whose rows name several releases of a product (mixed_releases). Log-only.
- record_fill_results(result, max_attempts=3, count_failures=True)[source]#
Apply a
_build_fill_productsresult to per-product statuses.filled→INDEXED(attempt count cleared);unlocated(the product file exists but no database file lists the granule, so it can never fill) →FAILEDat once;failed(read or merge error, or no row matched) → retried by the next runs; aftermax_attemptsconsecutive failures →FAILED, so one bad file does not make every later build rescan.FAILEDis not backfilled automatically;gh3_doctor --fix backfillretries it.count_failures=Falseleaves failures untouched.
Does not save the log. Returns
{key: [products]}newlyFAILED.