-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsample.py
More file actions
117 lines (102 loc) · 4.49 KB
/
Copy pathsample.py
File metadata and controls
117 lines (102 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# ===============================================================================
# Copyright 2025 ross
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from sqlalchemy import DateTime, ForeignKey
from sqlalchemy.orm import mapped_column, relationship, Mapped
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
# import models from classes that are defined in separate files
from db.base import Base, AutoBaseMixin, ReleaseMixin, lexicon_term
import datetime
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from db.field import FieldEvent, FieldActivity, FieldEventParticipant
from db.thing import Thing
from db.contact import Contact
from db.observation import Observation
class Sample(Base, AutoBaseMixin, ReleaseMixin):
"""
This table serves as an inventory of all the individual items collected or
generated by a FieldActivity. Each record represents a single, discrete
sample, which can be either a physical object (like a bottle of water or a
jar of soil) or a "virtual" object (representing the act of taking a
measurement, like a water level).
Its purpose is to store the specific properties of each discrete sample,
such as its unique sample name (the label on the bottle), its matrix, and
any relevant physical measurements.
"""
# __table_name__ is inherited from AutoBaseMixin.
# --- Foreign Key Definitions ---
field_activity_id: Mapped[int] = mapped_column(
ForeignKey("field_activity.id", ondelete="CASCADE"), nullable=False
)
field_event_participant_id: Mapped[str] = mapped_column(
ForeignKey("field_event_participant.id"), nullable=True
)
# --- Columns ---
sample_date: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
comment="Date and time of sample collection.",
)
sample_name: Mapped[str] = mapped_column(
nullable=False,
unique=True,
comment="The unique identifier physically written on the sample container or used in field logs. Use for tracking/auditing purposes.",
)
sample_matrix: Mapped[str] = lexicon_term(
nullable=False, comment="The material of the sample (e.g., 'gw', 'soil')."
)
sample_method: Mapped[str] = lexicon_term(
comment="Method used to collect the sample.", nullable=False
)
qc_type: Mapped[str] = mapped_column(
default="Normal",
nullable=False,
comment="Quality control sample type (e.g., 'Normal', 'Split', 'Field duplicate').",
)
depth_top: Mapped[float] = mapped_column(
nullable=True, comment="Top depth of a discrete sample interval in ft."
)
depth_bottom: Mapped[float] = mapped_column(
nullable=True, comment="Bottom depth of a discrete sample interval in ft."
)
notes: Mapped[str] = mapped_column(nullable=True)
# --- Auditing Fields from NM_Aquifer ---
nma_pk_waterlevels: Mapped[str] = mapped_column(
nullable=True,
comment="NM_Aquifer primary key for waterlevels - to be used for transfer audits",
)
# --- Relationship Definitions ---
field_activity: Mapped["FieldActivity"] = relationship(back_populates="samples")
field_event_participant: Mapped["FieldEventParticipant"] = relationship(
back_populates="samples"
)
# association proxies to help keep code DRY
field_event: AssociationProxy["FieldEvent"] = association_proxy(
"field_activity", "field_event"
)
thing: AssociationProxy["Thing"] = association_proxy(
"field_activity", "field_event.thing"
)
contact: AssociationProxy["Contact"] = association_proxy(
"field_event_participant", "participant"
)
observations: Mapped[list["Observation"]] = relationship(
"Observation",
back_populates="sample",
cascade="all, delete-orphan",
passive_deletes=True,
)
# ============= EOF =============================================