Skip to content

Commit 39c8183

Browse files
committed
feat: add legacy data transferers and models for AssociatedData, SurfaceWaterPhotos, WeatherPhotos, and SoilRockResults
1 parent 9e942ab commit 39c8183

15 files changed

Lines changed: 1082 additions & 0 deletions
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Create legacy NMA_AssociatedData table.
2+
3+
Revision ID: c2f4a9d0b1e2
4+
Revises: a7b8c9d0e1f2
5+
Create Date: 2026-03-05 00:00:00.000000
6+
"""
7+
8+
from typing import Sequence, Union
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
from sqlalchemy import inspect
13+
from sqlalchemy.dialects import postgresql
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "c2f4a9d0b1e2"
17+
down_revision: Union[str, Sequence[str], None] = "a7b8c9d0e1f2"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
"""Create the legacy associated data table."""
24+
bind = op.get_bind()
25+
inspector = inspect(bind)
26+
if not inspector.has_table("NMA_AssociatedData"):
27+
op.create_table(
28+
"NMA_AssociatedData",
29+
sa.Column("LocationId", postgresql.UUID(as_uuid=True), nullable=True),
30+
sa.Column("PointID", sa.String(length=10), nullable=True),
31+
sa.Column(
32+
"AssocID",
33+
postgresql.UUID(as_uuid=True),
34+
nullable=False,
35+
primary_key=True,
36+
),
37+
sa.Column("Notes", sa.String(length=255), nullable=True),
38+
sa.Column("Formation", sa.String(length=15), nullable=True),
39+
sa.Column("OBJECTID", sa.Integer(), nullable=True, unique=True),
40+
sa.UniqueConstraint("LocationId", name="AssociatedData$LocationId"),
41+
)
42+
op.create_index("AssociatedData$PointID", "NMA_AssociatedData", ["PointID"])
43+
44+
45+
def downgrade() -> None:
46+
"""Drop the legacy associated data table."""
47+
bind = op.get_bind()
48+
inspector = inspect(bind)
49+
if inspector.has_table("NMA_AssociatedData"):
50+
op.drop_table("NMA_AssociatedData")
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""Create legacy NMA_SurfaceWaterPhotos table.
2+
3+
Revision ID: d3a4b5c6d7e8
4+
Revises: c2f4a9d0b1e2
5+
Create Date: 2026-03-05 00:00:00.000000
6+
"""
7+
8+
from typing import Sequence, Union
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
from sqlalchemy import inspect
13+
from sqlalchemy.dialects import postgresql
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "d3a4b5c6d7e8"
17+
down_revision: Union[str, Sequence[str], None] = "c2f4a9d0b1e2"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
"""Create the legacy surface water photos table."""
24+
bind = op.get_bind()
25+
inspector = inspect(bind)
26+
if not inspector.has_table("NMA_SurfaceWaterPhotos"):
27+
op.create_table(
28+
"NMA_SurfaceWaterPhotos",
29+
sa.Column("SurfaceID", postgresql.UUID(as_uuid=True), nullable=True),
30+
sa.Column("PointID", sa.String(length=50), nullable=False),
31+
sa.Column("OLEPath", sa.String(length=50), nullable=True),
32+
sa.Column("OBJECTID", sa.Integer(), nullable=True, unique=True),
33+
sa.Column(
34+
"GlobalID",
35+
postgresql.UUID(as_uuid=True),
36+
nullable=False,
37+
primary_key=True,
38+
),
39+
)
40+
op.create_index(
41+
"SurfaceWaterPhotos$PointID", "NMA_SurfaceWaterPhotos", ["PointID"]
42+
)
43+
op.create_index(
44+
"SurfaceWaterPhotos$SurfaceID", "NMA_SurfaceWaterPhotos", ["SurfaceID"]
45+
)
46+
47+
48+
def downgrade() -> None:
49+
"""Drop the legacy surface water photos table."""
50+
bind = op.get_bind()
51+
inspector = inspect(bind)
52+
if inspector.has_table("NMA_SurfaceWaterPhotos"):
53+
op.drop_table("NMA_SurfaceWaterPhotos")
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Create legacy NMA_WeatherPhotos table.
2+
3+
Revision ID: e4b5c6d7e8f9
4+
Revises: d3a4b5c6d7e8
5+
Create Date: 2026-03-05 00:00:00.000000
6+
"""
7+
8+
from typing import Sequence, Union
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
from sqlalchemy import inspect
13+
from sqlalchemy.dialects import postgresql
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "e4b5c6d7e8f9"
17+
down_revision: Union[str, Sequence[str], None] = "d3a4b5c6d7e8"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
"""Create the legacy weather photos table."""
24+
bind = op.get_bind()
25+
inspector = inspect(bind)
26+
if not inspector.has_table("NMA_WeatherPhotos"):
27+
op.create_table(
28+
"NMA_WeatherPhotos",
29+
sa.Column("WeatherID", postgresql.UUID(as_uuid=True), nullable=True),
30+
sa.Column("PointID", sa.String(length=50), nullable=False),
31+
sa.Column("OLEPath", sa.String(length=50), nullable=True),
32+
sa.Column("OBJECTID", sa.Integer(), nullable=True, unique=True),
33+
sa.Column(
34+
"GlobalID",
35+
postgresql.UUID(as_uuid=True),
36+
nullable=False,
37+
primary_key=True,
38+
),
39+
)
40+
op.create_index("WeatherPhotos$PointID", "NMA_WeatherPhotos", ["PointID"])
41+
op.create_index("WeatherPhotos$WeatherID", "NMA_WeatherPhotos", ["WeatherID"])
42+
43+
44+
def downgrade() -> None:
45+
"""Drop the legacy weather photos table."""
46+
bind = op.get_bind()
47+
inspector = inspect(bind)
48+
if inspector.has_table("NMA_WeatherPhotos"):
49+
op.drop_table("NMA_WeatherPhotos")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Create legacy NMA_Soil_Rock_Results table.
2+
3+
Revision ID: f5a6b7c8d9e0
4+
Revises: e4b5c6d7e8f9
5+
Create Date: 2026-03-05 00:00:00.000000
6+
"""
7+
8+
from typing import Sequence, Union
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
from sqlalchemy import inspect
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = "f5a6b7c8d9e0"
16+
down_revision: Union[str, Sequence[str], None] = "e4b5c6d7e8f9"
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
"""Create the legacy soil/rock results table."""
23+
bind = op.get_bind()
24+
inspector = inspect(bind)
25+
if not inspector.has_table("NMA_Soil_Rock_Results"):
26+
op.create_table(
27+
"NMA_Soil_Rock_Results",
28+
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
29+
sa.Column("Point_ID", sa.String(length=255), nullable=True),
30+
sa.Column("Sample Type", sa.String(length=255), nullable=True),
31+
sa.Column("Date Sampled", sa.String(length=255), nullable=True),
32+
sa.Column("d13C", sa.Float(), nullable=True),
33+
sa.Column("d18O", sa.Float(), nullable=True),
34+
sa.Column("Sampled by", sa.String(length=255), nullable=True),
35+
)
36+
op.create_index(
37+
"Soil_Rock_Results$Point_ID", "NMA_Soil_Rock_Results", ["Point_ID"]
38+
)
39+
40+
41+
def downgrade() -> None:
42+
"""Drop the legacy soil/rock results table."""
43+
bind = op.get_bind()
44+
inspector = inspect(bind)
45+
if inspector.has_table("NMA_Soil_Rock_Results"):
46+
op.drop_table("NMA_Soil_Rock_Results")

db/nma_legacy.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,25 @@ class ChemistrySampleInfo(Base):
278278
passive_deletes=True,
279279
)
280280

281+
282+
class AssociatedData(Base):
283+
"""
284+
Legacy AssociatedData table from NM_Aquifer.
285+
"""
286+
287+
__tablename__ = "NMA_AssociatedData"
288+
289+
location_id: Mapped[Optional[uuid.UUID]] = mapped_column(
290+
"LocationId", UUID(as_uuid=True), unique=True
291+
)
292+
point_id: Mapped[Optional[str]] = mapped_column("PointID", String(10))
293+
assoc_id: Mapped[uuid.UUID] = mapped_column(
294+
"AssocID", UUID(as_uuid=True), primary_key=True
295+
)
296+
notes: Mapped[Optional[str]] = mapped_column("Notes", String(255))
297+
formation: Mapped[Optional[str]] = mapped_column("Formation", String(15))
298+
object_id: Mapped[Optional[int]] = mapped_column("OBJECTID", Integer, unique=True)
299+
281300
major_chemistries: Mapped[List["NMAMajorChemistry"]] = relationship(
282301
"NMAMajorChemistry",
283302
back_populates="chemistry_sample_info",
@@ -328,6 +347,24 @@ class SurfaceWaterData(Base):
328347
data_source: Mapped[Optional[str]] = mapped_column("DataSource", String(255))
329348

330349

350+
class SurfaceWaterPhotos(Base):
351+
"""
352+
Legacy SurfaceWaterPhotos table from NM_Aquifer.
353+
"""
354+
355+
__tablename__ = "NMA_SurfaceWaterPhotos"
356+
357+
surface_id: Mapped[Optional[uuid.UUID]] = mapped_column(
358+
"SurfaceID", UUID(as_uuid=True)
359+
)
360+
point_id: Mapped[str] = mapped_column("PointID", String(50), nullable=False)
361+
ole_path: Mapped[Optional[str]] = mapped_column("OLEPath", String(50))
362+
object_id: Mapped[Optional[int]] = mapped_column("OBJECTID", Integer, unique=True)
363+
global_id: Mapped[uuid.UUID] = mapped_column(
364+
"GlobalID", UUID(as_uuid=True), primary_key=True
365+
)
366+
367+
331368
class WeatherData(Base):
332369
"""
333370
Legacy WeatherData table from AMPAPI.
@@ -345,6 +382,40 @@ class WeatherData(Base):
345382
object_id: Mapped[int] = mapped_column("OBJECTID", Integer, primary_key=True)
346383

347384

385+
class WeatherPhotos(Base):
386+
"""
387+
Legacy WeatherPhotos table from NM_Aquifer.
388+
"""
389+
390+
__tablename__ = "NMA_WeatherPhotos"
391+
392+
weather_id: Mapped[Optional[uuid.UUID]] = mapped_column(
393+
"WeatherID", UUID(as_uuid=True)
394+
)
395+
point_id: Mapped[str] = mapped_column("PointID", String(50), nullable=False)
396+
ole_path: Mapped[Optional[str]] = mapped_column("OLEPath", String(50))
397+
object_id: Mapped[Optional[int]] = mapped_column("OBJECTID", Integer, unique=True)
398+
global_id: Mapped[uuid.UUID] = mapped_column(
399+
"GlobalID", UUID(as_uuid=True), primary_key=True
400+
)
401+
402+
403+
class SoilRockResults(Base):
404+
"""
405+
Legacy Soil_Rock_Results table from NM_Aquifer.
406+
"""
407+
408+
__tablename__ = "NMA_Soil_Rock_Results"
409+
410+
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
411+
point_id: Mapped[Optional[str]] = mapped_column("Point_ID", String(255))
412+
sample_type: Mapped[Optional[str]] = mapped_column("Sample Type", String(255))
413+
date_sampled: Mapped[Optional[str]] = mapped_column("Date Sampled", String(255))
414+
d13c: Mapped[Optional[float]] = mapped_column("d13C", Float)
415+
d18o: Mapped[Optional[float]] = mapped_column("d18O", Float)
416+
sampled_by: Mapped[Optional[str]] = mapped_column("Sampled by", String(255))
417+
418+
348419
class NMAMinorTraceChemistry(Base):
349420
"""
350421
Legacy MinorandTraceChemistry table from AMPAPI.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# ==============================================================================
2+
# Copyright 2026 ross
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
# ==============================================================================
16+
"""
17+
Unit tests for AssociatedData legacy model.
18+
19+
These tests verify the migration of columns from the legacy AssociatedData table.
20+
Migrated columns:
21+
- LocationId -> location_id
22+
- PointID -> point_id
23+
- AssocID -> assoc_id
24+
- Notes -> notes
25+
- Formation -> formation
26+
- OBJECTID -> object_id
27+
"""
28+
29+
from uuid import uuid4
30+
31+
from db.engine import session_ctx
32+
from db.nma_legacy import AssociatedData
33+
34+
35+
def test_create_associated_data_all_fields():
36+
"""Test creating an associated data record with all fields."""
37+
with session_ctx() as session:
38+
record = AssociatedData(
39+
location_id=uuid4(),
40+
point_id="AA-0001",
41+
assoc_id=uuid4(),
42+
notes="Legacy notes",
43+
formation="TEST",
44+
object_id=42,
45+
)
46+
session.add(record)
47+
session.commit()
48+
session.refresh(record)
49+
50+
assert record.assoc_id is not None
51+
assert record.location_id is not None
52+
assert record.point_id == "AA-0001"
53+
assert record.notes == "Legacy notes"
54+
assert record.formation == "TEST"
55+
assert record.object_id == 42
56+
57+
session.delete(record)
58+
session.commit()
59+
60+
61+
def test_create_associated_data_minimal():
62+
"""Test creating an associated data record with required fields only."""
63+
with session_ctx() as session:
64+
record = AssociatedData(assoc_id=uuid4())
65+
session.add(record)
66+
session.commit()
67+
session.refresh(record)
68+
69+
assert record.assoc_id is not None
70+
assert record.location_id is None
71+
assert record.point_id is None
72+
assert record.notes is None
73+
assert record.formation is None
74+
assert record.object_id is None
75+
76+
session.delete(record)
77+
session.commit()
78+
79+
80+
# ============= EOF =============================================

0 commit comments

Comments
 (0)