Architecture
The layer model
The codebase has three layers with a strict one-way dependency:
ui → core, data_access → config
ui/may import fromcore/anddata_access/. No reverse imports.core/has zero dependencies on Dash, ondata_access/, or on the database. It is pure functions over plain types (floats, dicts, pandas DataFrames).data_access/may import fromconfig/only.config/reads environment variables. No hardcoded paths or credentials anywhere else in the codebase.
The point of the separation: core/ is unit-testable without a database
and reusable from a future CLI or ArcGIS Pro toolbox without changes.
Package layout
The tool is packaged as src/gwdrawdown/:
src/gwdrawdown/
├── app.py Dash entry point (multi-page)
├── config.py Single source of runtime configuration
├── analysis.py Pipeline orchestration
├── usage_logger.py Per-run usage logging
├── core/ Pure math — no Dash, no DB
│ ├── units.py Unit conversions
│ ├── crs_utils.py WGS84 ↔ BC Albers transforms
│ ├── drawdown.py Cooper-Jacob distance-drawdown
│ ├── aquifer_lookup.py Default T/S by aquifer subtype
│ ├── well_classification.py Reassigned aquifer material rule
│ ├── sad.py Safe Available Drawdown
│ └── flagging.py OK / AT_RISK / INSUFFICIENT_DATA / …
├── data_access/ BCGW Oracle access
│ ├── db.py Lazy oracledb connection pool
│ └── queries.py Parameterised SQL templates
└── ui/ Dash only
├── pages/ login, setup, results
└── components/ charts, tables, map, exports, footer
Key modules
-
config.py— the single source of truth for runtime configuration. The BCGW connection string is a hardcoded constant (bcgw.bcgov:1521/idwprod1.bcgov); it is not secret and never changes. Optional overrides are read from environment variables (and a.envfile if present), but the tool runs with no.env. -
core/drawdown.py— the Cooper-Jacob (1946) distance-drawdown calculation. It accepts a list of pumping sources and sums their contributions linearly, so multi-well superposition is available in the math even though the v1 UI exposes a single well. -
core/sad.py— Safe Available Drawdown: 70% of available drawdown. Confined and bedrock wells are flagged for manual review and accept a per-welltop_of_fracture_or_aquifer_or_screen_moverride. -
data_access/db.py— theoracledbthin-mode connection pool. The pool is created lazily by the login handler once a user has entered valid BCGW credentials, not at app startup. -
analysis.py— orchestrates the pipeline: queries → SI conversion → Cooper-Jacob → SAD → classification → flagging._compute_well_resultis a pure function, unit-tested without a database.
Working agreements
- No Dash imports anywhere in
core/ordata_access/. - No SQL strings outside
data_access/queries.py. - No hardcoded paths — anything filesystem-related goes through
config. - Use
logging, neverprint. Usepathlib.Path, never raw strings. - Type hints on all public signatures;
from __future__ import annotationsat the top of every module. - Tests for every function in
core/, mirroring the package structure.
The full specification — including the Cooper-Jacob equation, the SAD
formula, and the at-risk rules — is in spec/PROJECT_PLAN.md; see
Reference documents.