Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions db/measuring_point_history.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this model only pertains to the thing model I don't think that it needs to go into its own file. It can go into db/thing.py

@ksmuczynski ksmuczynski Nov 12, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to do this. Just curious, how are we deciding which models get their own files? When I'm looking for info about a table/model, I'm expecting it to be listed in the db directory.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we have a standard, but when making a new model I put it into an existing file if it only pertains to other models in that file (e.g. WellPurpose and WellCasingMaterial). [Now that I think of it, I don't know if I've created a standalone model, but it makes sense for me to have the polymorphic models be in their own files for ease of understanding.]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what @jirhiker thoughts are.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jirhiker I merged this so I can use develop off of it - where the table is defined in the codebase won't affect how it's used in the schemas/tests. If you think it should be in the db/thing.py file I can move it there easily.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""
SQLAlchemy model for the MeasuringPointHistory table.

This table stores the authoritative MP height of a Thing from
construction or modification events. It provides a complete, auditable
history of the official, surveyed measuring point (MP) descriptions
and heights for a Thing.

This table is not for storing routine field checks of the
MP height (which are stored on the `Observation` table). This table should
only be updated when a well is first installed, physically modified
(e.g., a new wellhead is installed), or officially re-surveyed.
"""

from typing import TYPE_CHECKING

from sqlalchemy import Integer, ForeignKey, Date, Text, Numeric
from sqlalchemy.orm import relationship, Mapped, mapped_column

from db.base import Base, AutoBaseMixin, ReleaseMixin

if TYPE_CHECKING:
from db.thing import Thing


class MeasuringPointHistory(Base, AutoBaseMixin, ReleaseMixin):
"""
Represents a single, authoritative, time-stamped record of a
Thing's measuring point description and height.
"""

# --- Foreign Keys ---
thing_id: Mapped[int] = mapped_column(
Integer, ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
)

# --- Columns ---
measuring_point_height: Mapped[float] = mapped_column(
Numeric,
nullable=False,
comment="The official, surveyed height of the measuring point relative to ground surface (in feet).",
)
measuring_point_description: Mapped[str] = mapped_column(
Text,
nullable=True,
comment="A clear description of the measuring point (e.g., 'North side of casing, top of PVC', 'Top of new steel collar').",
)
start_date: Mapped[Date] = mapped_column(
Date,
nullable=False,
comment="The date this measuring point configuration became effective.",
)
end_date: Mapped[Date] = mapped_column(
Date,
nullable=True,
comment="The date this measuring point configuration was superseded. A `NULL` value indicates this is the current, active, and authoritative record for the `Thing`.",
)

reason: Mapped[str] = mapped_column(
Text,
nullable=True,
comment="Describes the reason for the new or updated measuring point (e.g., 'A new wellhead was installed').",
)

# --- Relationships ---
# Many-To-One: A description history record belongs to one Thing. Many history records may belong to a single Thing.
thing: Mapped["Thing"] = relationship("Thing", back_populates="measuring_points")
9 changes: 9 additions & 0 deletions db/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
StatusHistoryMixin,
PermissionMixin,
)
from db.measuring_point_history import MeasuringPointHistory

if TYPE_CHECKING:
from db.location import Location
Expand Down Expand Up @@ -230,6 +231,14 @@ class Thing(Base, AutoBaseMixin, ReleaseMixin, StatusHistoryMixin, PermissionMix
passive_deletes=True,
)

# One-To-Many: A Thing (well) can have multiple measuring points over time.
measuring_points: Mapped[List["MeasuringPointHistory"]] = relationship(
"MeasuringPointHistory",
back_populates="thing",
cascade="all, delete-orphan",
passive_deletes=True,
)

# --- Association Proxies ---
assets: AssociationProxy[list["Asset"]] = association_proxy(
"asset_associations", "asset"
Expand Down
Loading