|
| 1 | +"""fix water elevation units to feet |
| 2 | +
|
| 3 | +Revision ID: n7a8b9c0d1e2 |
| 4 | +Revises: m6f7a8b9c0d1 |
| 5 | +Create Date: 2026-03-10 11:10:00.000000 |
| 6 | +""" |
| 7 | + |
| 8 | +from typing import Sequence, Union |
| 9 | + |
| 10 | +from alembic import op |
| 11 | +from sqlalchemy import inspect, text |
| 12 | + |
| 13 | +# revision identifiers, used by Alembic. |
| 14 | +revision: str = "n7a8b9c0d1e2" |
| 15 | +down_revision: Union[str, Sequence[str], None] = "m6f7a8b9c0d1" |
| 16 | +branch_labels: Union[str, Sequence[str], None] = None |
| 17 | +depends_on: Union[str, Sequence[str], None] = None |
| 18 | + |
| 19 | +METERS_TO_FEET = 3.28084 |
| 20 | + |
| 21 | +LATEST_LOCATION_CTE = """ |
| 22 | +SELECT DISTINCT ON (lta.thing_id) |
| 23 | + lta.thing_id, |
| 24 | + lta.location_id, |
| 25 | + lta.effective_start |
| 26 | +FROM location_thing_association AS lta |
| 27 | +WHERE lta.effective_end IS NULL |
| 28 | +ORDER BY lta.thing_id, lta.effective_start DESC |
| 29 | +""".strip() |
| 30 | + |
| 31 | + |
| 32 | +def _create_water_elevation_view() -> str: |
| 33 | + return f""" |
| 34 | + CREATE MATERIALIZED VIEW ogc_water_elevation_wells AS |
| 35 | + WITH latest_location AS ( |
| 36 | +{LATEST_LOCATION_CTE} |
| 37 | + ), |
| 38 | + ranked_obs AS ( |
| 39 | + SELECT |
| 40 | + fe.thing_id, |
| 41 | + o.id AS observation_id, |
| 42 | + o.observation_datetime, |
| 43 | + (o.value - COALESCE(o.measuring_point_height, 0)) |
| 44 | + AS depth_to_water_below_ground_surface |
| 45 | + FROM observation AS o |
| 46 | + JOIN sample AS s ON s.id = o.sample_id |
| 47 | + JOIN field_activity AS fa ON fa.id = s.field_activity_id |
| 48 | + JOIN field_event AS fe ON fe.id = fa.field_event_id |
| 49 | + JOIN thing AS t ON t.id = fe.thing_id |
| 50 | + WHERE |
| 51 | + t.thing_type = 'water well' |
| 52 | + AND fa.activity_type = 'groundwater level' |
| 53 | + AND o.value IS NOT NULL |
| 54 | + AND o.observation_datetime IS NOT NULL |
| 55 | + ), |
| 56 | + latest_obs AS ( |
| 57 | + SELECT |
| 58 | + ro.*, |
| 59 | + ROW_NUMBER() OVER ( |
| 60 | + PARTITION BY ro.thing_id |
| 61 | + ORDER BY ro.observation_datetime DESC, ro.observation_id DESC |
| 62 | + ) AS rn |
| 63 | + FROM ranked_obs AS ro |
| 64 | + ) |
| 65 | + SELECT |
| 66 | + t.id AS id, |
| 67 | + t.name, |
| 68 | + t.thing_type, |
| 69 | + lo.observation_id, |
| 70 | + lo.observation_datetime, |
| 71 | + l.elevation, |
| 72 | + lo.depth_to_water_below_ground_surface, |
| 73 | + ((l.elevation * {METERS_TO_FEET}) - lo.depth_to_water_below_ground_surface) |
| 74 | + AS water_elevation, |
| 75 | + l.point |
| 76 | + FROM latest_obs AS lo |
| 77 | + JOIN thing AS t ON t.id = lo.thing_id |
| 78 | + JOIN latest_location AS ll ON ll.thing_id = t.id |
| 79 | + JOIN location AS l ON l.id = ll.location_id |
| 80 | + WHERE lo.rn = 1 |
| 81 | + """ |
| 82 | + |
| 83 | + |
| 84 | +def upgrade() -> None: |
| 85 | + bind = op.get_bind() |
| 86 | + inspector = inspect(bind) |
| 87 | + existing_tables = set(inspector.get_table_names(schema="public")) |
| 88 | + required_tables = { |
| 89 | + "thing", |
| 90 | + "location", |
| 91 | + "location_thing_association", |
| 92 | + "observation", |
| 93 | + "sample", |
| 94 | + "field_activity", |
| 95 | + "field_event", |
| 96 | + } |
| 97 | + |
| 98 | + if not required_tables.issubset(existing_tables): |
| 99 | + missing = sorted(t for t in required_tables if t not in existing_tables) |
| 100 | + raise RuntimeError( |
| 101 | + "Cannot create ogc_water_elevation_wells. Missing required tables: " |
| 102 | + + ", ".join(missing) |
| 103 | + ) |
| 104 | + |
| 105 | + op.execute(text("DROP MATERIALIZED VIEW IF EXISTS ogc_water_elevation_wells")) |
| 106 | + op.execute(text(_create_water_elevation_view())) |
| 107 | + op.execute( |
| 108 | + text( |
| 109 | + "COMMENT ON MATERIALIZED VIEW ogc_water_elevation_wells IS " |
| 110 | + "'Latest water elevation per well in feet; computed as (elevation_m * 3.28084) - depth_to_water_below_ground_surface_ft.'" |
| 111 | + ) |
| 112 | + ) |
| 113 | + op.execute( |
| 114 | + text( |
| 115 | + "CREATE UNIQUE INDEX ux_ogc_water_elevation_wells_id " |
| 116 | + "ON ogc_water_elevation_wells (id)" |
| 117 | + ) |
| 118 | + ) |
| 119 | + |
| 120 | + |
| 121 | +def downgrade() -> None: |
| 122 | + op.execute(text("DROP MATERIALIZED VIEW IF EXISTS ogc_water_elevation_wells")) |
0 commit comments