Skip to content

Commit a2229df

Browse files
jirhikerclaude
andauthored
Cleanup Tier 2.3-2.7: replace magic strings/values with constants (#104)
- 2.3: config.py `parameter == "ph"` → `PH` constant. - 2.4: record_type string compares (`"analytes"`/`"waterlevels"`) → `ANALYTES`/`WATERLEVELS` constants in transformer.py + record.py; added `ANALYTES = "analytes"` to constants.py. - 2.5: api/app.py bucket `"die_cache"` (×3) and queue `"die-queue"` → module constants `_CACHE_BUCKET` / `_TASK_QUEUE`. - 2.7: definitions.py default cron `"0 6 * * *"` and timezone `"America/Denver"` → `_DEFAULT_CRON` / `_SCHEDULE_TIMEZONE` constants. 2.6 (router_parameters from PARAMETER_SOURCE_MAP) deferred: it would force the lean API service to import all of backend.config (every connector + shapely) for a display list, and changes the endpoint's response shape. Documented in docs/cleanup-todo.md. No behavior change. Full suite (311) + dg check defs clean. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 13df428 commit a2229df

7 files changed

Lines changed: 34 additions & 19 deletions

File tree

backend/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ def make_output_path(self):
587587

588588
def _update_output_units(self):
589589
parameter = self.parameter.lower()
590-
if parameter == "ph":
590+
if parameter == PH:
591591
self.analyte_output_units = ""
592592
elif parameter in [CONDUCTIVITY, SPECIFIC_CONDUCTANCE]:
593593
self.analyte_output_units = MICROSIEMENS_PER_CENTIMETER

backend/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919

2020
WATERLEVELS = "waterlevels"
21+
# Record-type / parameter-group label for the non-waterlevel parameters.
22+
ANALYTES = "analytes"
2123
ARSENIC = "arsenic"
2224
BICARBONATE = "bicarbonate"
2325
CALCIUM = "calcium"

backend/record.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
CONVERSION_FACTOR,
2323
SOURCE_DATASTREAM_LINK,
2424
FEET,
25+
WATERLEVELS,
2526
)
2627

2728

@@ -64,7 +65,7 @@ def _get_sigfig_formatted_value(self, attr):
6465

6566
# both analyte and water level tables have the same fields, but the
6667
# rounding should only occur for water level tables
67-
if self._payload.get("record_type") == "waterlevels":
68+
if self._payload.get("record_type") == WATERLEVELS:
6869
field_sigfigs.append((PARAMETER_VALUE, 2))
6970

7071
for field, sigfigs in field_sigfigs:

backend/transformer.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
DTW,
2727
EARLIEST,
2828
LATEST,
29+
WATERLEVELS,
30+
ANALYTES,
2931
)
3032
from backend.geo_utils import datum_transform, ALLOWED_DATUMS
3133
from backend.logger import make_logger
@@ -209,7 +211,7 @@ def do_transform(
209211
return None
210212
klassed_record = self._apply_elevation_transform(klassed_record)
211213
klassed_record = self._apply_well_depth_transform(klassed_record)
212-
elif klassed_record.record_type in ("analytes", "waterlevels"):
214+
elif klassed_record.record_type in (ANALYTES, WATERLEVELS):
213215
klassed_record = self._apply_unit_conversion(klassed_record)
214216
return klassed_record
215217

@@ -269,7 +271,7 @@ def _apply_well_depth_transform(self, klassed_record):
269271
def _apply_unit_conversion(self, klassed_record):
270272
output_units = (
271273
self.config.analyte_output_units
272-
if klassed_record.record_type == "analytes"
274+
if klassed_record.record_type == ANALYTES
273275
else self.config.waterlevel_output_units
274276
)
275277
source_result = klassed_record.parameter_value
@@ -538,7 +540,7 @@ def _get_record_klass(self) -> type[ParameterRecord] | type[SummaryRecord]:
538540
return SummaryRecord if self.config.output_summary else ParameterRecord
539541

540542
def _get_record_type(self) -> str:
541-
return "waterlevels"
543+
return WATERLEVELS
542544

543545
def _get_parameter_name_and_units(self) -> tuple:
544546
"""
@@ -557,7 +559,7 @@ def _get_record_klass(self) -> type[ParameterRecord] | type[SummaryRecord]:
557559
return SummaryRecord if self.config.output_summary else ParameterRecord
558560

559561
def _get_record_type(self) -> str:
560-
return "analytes"
562+
return ANALYTES
561563

562564
def _get_parameter_name_and_units(self) -> tuple:
563565
"""

docs/cleanup-todo.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DIE Cleanup TODO
22

3-
> **Status:** Tier 1 (all), plus 2.1 and 2.2, are **DONE** (this PR). Remaining:
4-
> Tier 2 items 2.3–2.7, all of Tier 3, all of Tier 4.
3+
> **Status:** Tier 1 (all) and **all of Tier 2 except 2.6** are **DONE**. 2.6 is
4+
> **deferred** (see note below). Remaining: 2.6, all of Tier 3, all of Tier 4.
55
66
Prioritized cleanup backlog from a code-analysis sweep (backend + frontend +
77
orchestration). Each item: location, effort (S/M/L), risk, and whether it
@@ -36,11 +36,11 @@ registry). Tiers are ordered by safety — Tier 1 is batchable into one no-risk
3636
|---|----------|--------|--------|----------|---|
3737
| **2.1 (requested) ✅** | `backend/config.py:79-97` + reads at `config.py`, `orchestration/assets/products.py:169`, 2 tests | **Flatten `PARAMETER_SOURCE_MAP`**: `param: {"agencies":[...]}``param: [...]`. `"agencies"` is the only key ever present; every read is `["agencies"]`. | S | none ||
3838
| 2.2 ✅ | `backend/unifier.py` | `type(site_records) == list``isinstance(...)` | S | none ||
39-
| 2.3 | `backend/config.py` (`parameter == "ph"`) | Use `PH` constant from `constants.py` | S | none | |
40-
| 2.4 | `backend/transformer.py:272`, `backend/record.py:70` | `record_type == "analytes"/"waterlevels"` string compares → constants | S | none | |
41-
| 2.5 | `frontend/api/app.py` | Hoist magic strings to constants: bucket `"die_cache"` (×3), queue `"die-queue"`, header casing | S | none | |
42-
| 2.6 | `frontend/api/app.py` `router_parameters()` | Derive parameter list from `PARAMETER_SOURCE_MAP` instead of hardcoded 2-item list | M | none | |
43-
| 2.7 | `orchestration/definitions.py:155,204` | Hoist default cron `"0 6 * * *"` + timezone `"America/Denver"` to constants | S | none | |
39+
| 2.3 | `backend/config.py` (`parameter == "ph"`) | Use `PH` constant from `constants.py` | S | none | |
40+
| 2.4 | `backend/transformer.py`, `backend/record.py` | `record_type == "analytes"/"waterlevels"` `ANALYTES`/`WATERLEVELS` constants (added `ANALYTES` to `constants.py`) | S | none | |
41+
| 2.5 | `frontend/api/app.py` | Bucket `"die_cache"` (×3) + queue `"die-queue"``_CACHE_BUCKET` / `_TASK_QUEUE` module constants | S | none | |
42+
| 2.6 ⏸ DEFERRED | `frontend/api/app.py` `router_parameters()` | Derive parameter list from `PARAMETER_SOURCE_MAP`. **Deferred**: would force the lean API service to import the whole `backend.config` (all connectors + shapely) just for a display list, and changes the endpoint's response shape (`dtw`/`tds` → param keys). Needs a lightweight parameter registry or coordination with the frontend that consumes `/parameters`. | M | yes | |
43+
| 2.7 | `orchestration/definitions.py` | Default cron `"0 6 * * *"` + timezone `"America/Denver"` `_DEFAULT_CRON` / `_SCHEDULE_TIMEZONE` constants | S | none | |
4444

4545
---
4646

frontend/api/app.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636
allow_headers=["*"],
3737
)
3838

39+
# GCS bucket holding cached API outputs; Cloud Tasks queue the trigger endpoints
40+
# enqueue worker jobs onto.
41+
_CACHE_BUCKET = "die_cache"
42+
_TASK_QUEUE = "die-queue"
43+
3944

4045
class BboxModel(BaseModel):
4146
minLat: float
@@ -66,10 +71,10 @@ def router_unify_waterlevels(item: ConfigModel):
6671
if not item.force:
6772
storage_client = storage.Client()
6873
if item.output_summary:
69-
bucket = storage_client.bucket("die_cache")
74+
bucket = storage_client.bucket(_CACHE_BUCKET)
7075
exists = bucket.blob(f"{itemhash}.csv").exists()
7176
else:
72-
bucket = storage_client.bucket("die_cache")
77+
bucket = storage_client.bucket(_CACHE_BUCKET)
7378
exists = bucket.blob(f"{itemhash}.zip").exists()
7479

7580
response = None
@@ -78,7 +83,7 @@ def router_unify_waterlevels(item: ConfigModel):
7883
project = os.getenv("PROJECT_ID")
7984
location = os.getenv("LOCATION")
8085
url = os.getenv("WORKER_URL")
81-
queue = "die-queue"
86+
queue = _TASK_QUEUE
8287

8388
cfgobj["output_name"] = itemhash
8489
task = tasks_v2.Task(
@@ -132,7 +137,7 @@ def router_status(task_id: str):
132137
def router_download_unified_waterlevels(downloadhash: str, output_summary: bool):
133138

134139
storage_client = storage.Client()
135-
bucket = storage_client.bucket("die_cache")
140+
bucket = storage_client.bucket(_CACHE_BUCKET)
136141

137142
if output_summary:
138143
blob = bucket.blob(f"{downloadhash}.csv")

orchestration/definitions.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ def load_input(self, context: dg.InputContext):
7171
"ogc_waterlevel_change",
7272
}
7373

74+
# Cohort scheduling defaults: cron used when a product has no `schedule`, and the
75+
# timezone all cohort schedules run in.
76+
_DEFAULT_CRON = "0 6 * * *"
77+
_SCHEDULE_TIMEZONE = "America/Denver"
78+
7479

7580
def _load_products() -> dict:
7681
return yaml.safe_load(_PRODUCTS_PATH.read_text())
@@ -152,7 +157,7 @@ def _build_cohorts(products_config: dict, specs_by_pid: dict) -> dict:
152157
name = _cohort_name(_cohort_key(specs))
153158
cohort = cohorts.setdefault(name, {"members": [], "cron": None})
154159
cohort["members"].append(pid)
155-
cron = product.get("schedule", "0 6 * * *")
160+
cron = product.get("schedule", _DEFAULT_CRON)
156161
if cohort["cron"] is None or _cron_sort_key(cron) < _cron_sort_key(cohort["cron"]):
157162
cohort["cron"] = cron
158163
return cohorts
@@ -201,7 +206,7 @@ def _build_schedules(
201206
name=f"schedule_{name}",
202207
job=cohort_jobs[name],
203208
cron_schedule=cohort["cron"],
204-
execution_timezone="America/Denver",
209+
execution_timezone=_SCHEDULE_TIMEZONE,
205210
)
206211
for name, cohort in cohorts.items()
207212
]

0 commit comments

Comments
 (0)