From 3a9f8cb98496244453b1e3c6125c8e85e1a155aa Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 29 Oct 2025 12:13:37 -0600 Subject: [PATCH 1/5] fix: define DT_FMT in schemas for common use everywhere --- schemas/__init__.py | 2 ++ tests/__init__.py | 2 -- tests/test_asset.py | 2 +- tests/test_contact.py | 2 +- tests/test_group.py | 2 +- tests/test_lexicon.py | 2 +- tests/test_location.py | 2 +- tests/test_observation.py | 2 +- tests/test_sample.py | 2 +- tests/test_sensor.py | 2 +- tests/test_thing.py | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/schemas/__init__.py b/schemas/__init__.py index a16900404..1e45f0f6c 100644 --- a/schemas/__init__.py +++ b/schemas/__init__.py @@ -27,6 +27,8 @@ from core.enums import ReleaseStatus +DT_FMT = "%Y-%m-%dT%H:%M:%S.%fZ" + class ResourceNotFoundResponse(BaseModel): detail: str diff --git a/tests/__init__.py b/tests/__init__.py index c6814a907..1dbba0836 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -74,8 +74,6 @@ groundwater_level_parameter_id = parameter_map[("groundwater level", "Field Parameter")] pH_parameter_id = parameter_map[("pH", "Field Parameter")] -DT_FMT = "%Y-%m-%dT%H:%M:%S.%fZ" - def override_authentication(default=True): """ diff --git a/tests/test_asset.py b/tests/test_asset.py index eca9fbf60..6b8f5fbde 100644 --- a/tests/test_asset.py +++ b/tests/test_asset.py @@ -22,12 +22,12 @@ from core.app import app from core.dependencies import viewer_function, admin_function, editor_function from db import Asset +from schemas import DT_FMT from tests import ( client, cleanup_post_test, override_authentication, cleanup_patch_test, - DT_FMT, ) diff --git a/tests/test_contact.py b/tests/test_contact.py index 24c676499..4c25f4473 100644 --- a/tests/test_contact.py +++ b/tests/test_contact.py @@ -11,13 +11,13 @@ ) from db import Contact, Address, Email, Phone from main import app +from schemas import DT_FMT from schemas.contact import ValidateEmail, ValidatePhone, ValidateContact from tests import ( client, cleanup_post_test, cleanup_patch_test, override_authentication, - DT_FMT, ) diff --git a/tests/test_group.py b/tests/test_group.py index d73c1ec01..d703b0bd5 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -7,13 +7,13 @@ from core.dependencies import admin_function, viewer_function, editor_function from db import Group from main import app +from schemas import DT_FMT from schemas.group import ValidateGroup from tests import ( client, override_authentication, cleanup_post_test, cleanup_patch_test, - DT_FMT, ) diff --git a/tests/test_lexicon.py b/tests/test_lexicon.py index 7edba7334..4e9db24b3 100644 --- a/tests/test_lexicon.py +++ b/tests/test_lexicon.py @@ -24,12 +24,12 @@ ) from db import LexiconTerm, LexiconCategory, LexiconTriple from main import app +from schemas import DT_FMT from tests import ( client, override_authentication, cleanup_post_test, cleanup_patch_test, - DT_FMT, ) diff --git a/tests/test_location.py b/tests/test_location.py index 613cc5525..6ad1350e9 100644 --- a/tests/test_location.py +++ b/tests/test_location.py @@ -21,12 +21,12 @@ from core.dependencies import admin_function, editor_function, viewer_function from db import Location from main import app +from schemas import DT_FMT from tests import ( client, override_authentication, cleanup_post_test, cleanup_patch_test, - DT_FMT, ) diff --git a/tests/test_observation.py b/tests/test_observation.py index 6df2b45d1..50307b5ca 100644 --- a/tests/test_observation.py +++ b/tests/test_observation.py @@ -27,6 +27,7 @@ ) from db import Observation from main import app +from schemas import DT_FMT from tests import ( client, cleanup_post_test, @@ -34,7 +35,6 @@ cleanup_patch_test, groundwater_level_parameter_id, pH_parameter_id, - DT_FMT, ) diff --git a/tests/test_sample.py b/tests/test_sample.py index ccbf00f94..341bf6a64 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -21,13 +21,13 @@ from core.dependencies import admin_function, editor_function, viewer_function from db.sample import Sample from main import app +from schemas import DT_FMT from schemas.sample import ValidateSample from tests import ( client, cleanup_post_test, cleanup_patch_test, override_authentication, - DT_FMT, ) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index efc9dbc48..05fd8cf8d 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -23,13 +23,13 @@ from main import app # from schemas.sensor import ValidateSensor +from schemas import DT_FMT from tests import ( client, cleanup_post_test, cleanup_patch_test, override_authentication, groundwater_level_parameter_id, - DT_FMT, ) diff --git a/tests/test_thing.py b/tests/test_thing.py index 9c684a394..03ab9ac09 100644 --- a/tests/test_thing.py +++ b/tests/test_thing.py @@ -29,12 +29,12 @@ from main import app from schemas.location import LocationResponse from schemas.thing import ValidateWell +from schemas import DT_FMT from tests import ( client, override_authentication, cleanup_post_test, cleanup_patch_test, - DT_FMT, ) From 39eb87eee65595e3db93d03c168f33eb8625ee2f Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 29 Oct 2025 14:56:49 -0600 Subject: [PATCH 2/5] refactor: remove microseconds and simplify response model --- schemas/__init__.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/schemas/__init__.py b/schemas/__init__.py index 1e45f0f6c..87f5688c3 100644 --- a/schemas/__init__.py +++ b/schemas/__init__.py @@ -20,14 +20,13 @@ ConfigDict, AwareDatetime, field_validator, - model_serializer, ) from pydantic.json_schema import JsonSchemaValue from pydantic_core import core_schema from core.enums import ReleaseStatus -DT_FMT = "%Y-%m-%dT%H:%M:%S.%fZ" +DT_FMT = "%Y-%m-%dT%H:%M:%SZ" class ResourceNotFoundResponse(BaseModel): @@ -68,7 +67,7 @@ def serialize_dt(value: datetime) -> str: if value.tzinfo != timezone.utc: value = value.astimezone(timezone.utc) # Format with Z suffix - return value.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + return value.strftime(DT_FMT) # Use generate_schema instead of calling handler directly python_schema = handler.generate_schema(datetime) @@ -98,14 +97,6 @@ class BaseResponseModel(BaseModel): populate_by_name=True, ) - @model_serializer - def serialize(self): - data = self.__dict__.copy() - # If release_status is an enum, convert to string - if hasattr(data.get("release_status"), "value"): - data["release_status"] = data["release_status"].value - return data - # TODO: write function to convert any datetime field to UTC for use throughout # for schema field_validators From c2b52662defe35361a57c25e5dda974075123132 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 29 Oct 2025 14:59:45 -0600 Subject: [PATCH 3/5] feat: add MODE to .env documentation --- .env.example | 3 ++- README.md | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 2bc9062cd..227db2d9d 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,8 @@ POSTGRES_DB= GCS_BUCKET_NAME= GOOGLE_APPLICATION_CREDENTIALS=/path/to/gcs_credentials.json - +# set to development for lexicon and parameter to be populated and enable the enums to work +MODE=development # disable authentication (for development only) AUTHENTIK_DISABLE_AUTHENTICATION=1 diff --git a/README.md b/README.md index e22059596..ca79d6363 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ pre-commit install cp .env.example .env ``` +In development set `MODE=development` to allow lexicon enums to be populated. + #### 5. Database and server From f5e8bff24dc929103672d094540fa1407c094e5a Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 29 Oct 2025 15:00:57 -0600 Subject: [PATCH 4/5] feat: create SensorType enum --- core/enums.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/enums.py b/core/enums.py index ff20004ab..52e37d805 100644 --- a/core/enums.py +++ b/core/enums.py @@ -66,4 +66,5 @@ Unit: type[Enum] = build_enum_from_lexicon_category("unit") Vertical_datum: type[Enum] = build_enum_from_lexicon_category("vertical_datum") ScreenType: type[Enum] = build_enum_from_lexicon_category("screen_type") +SensorType: type[Enum] = build_enum_from_lexicon_category("sensor_type") # ============= EOF ============================================= From 93da2bb840f14b52b644df8b9519735eb184cd13 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 29 Oct 2025 15:04:23 -0600 Subject: [PATCH 5/5] refactor: use enums where applicable --- schemas/field.py | 4 ++-- schemas/location.py | 6 +++--- schemas/observation.py | 8 ++++---- schemas/parameter.py | 7 ++++--- schemas/publication.py | 5 +++-- schemas/sample.py | 6 +++--- schemas/sensor.py | 8 ++++---- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/schemas/field.py b/schemas/field.py index 9f32ed72f..b8152ffa2 100644 --- a/schemas/field.py +++ b/schemas/field.py @@ -1,14 +1,14 @@ from pydantic import AwareDatetime from schemas import BaseResponseModel - +from core.enums import ActivityType # RESPONSE --------------------------------------------------------------------- class FieldActivityResponse(BaseResponseModel): field_event_id: int - activity_type: str + activity_type: ActivityType class FieldEventResponse(BaseResponseModel): diff --git a/schemas/location.py b/schemas/location.py index fa367a01f..7b2d5420f 100644 --- a/schemas/location.py +++ b/schemas/location.py @@ -70,11 +70,11 @@ class LocationResponse(BaseResponseModel): point: str elevation: float | None horizontal_datum: str = "WGS84" - vertical_daum: str = "NAVD88" + vertical_datum: str = "NAVD88" elevation_accuracy: float | None - elevation_method: str | None + elevation_method: ElevationMethod | None coordinate_accuracy: float | None - coordinate_method: str | None + coordinate_method: CoordinateMethod | None state: str | None county: str | None quad_name: str | None diff --git a/schemas/observation.py b/schemas/observation.py index 0b9ecac55..2012f002f 100644 --- a/schemas/observation.py +++ b/schemas/observation.py @@ -32,7 +32,7 @@ UTCAwareDatetime, ) from schemas.parameter import ParameterResponse - +from core.enums import Unit # class GeothermalMixin: # depth: float @@ -69,7 +69,7 @@ class CreateBaseObservation(BaseCreateModel, ValidateObservation): sensor_id: int parameter_id: int value: float | None - unit: str | None + unit: Unit | None class CreateGroundwaterLevelObservation(CreateBaseObservation): @@ -90,7 +90,7 @@ class UpdateBaseObservation(BaseUpdateModel, ValidateObservation): sensor_id: int | None = None parameter_id: int | None = None value: float | None | None = None - unit: str | None = None + unit: Unit | None = None class UpdateGroundwaterLevelObservation(UpdateBaseObservation): @@ -110,7 +110,7 @@ class BaseObservationResponse(BaseResponseModel): observation_datetime: UTCAwareDatetime parameter: ParameterResponse value: float | None - unit: str + unit: Unit class GroundwaterLevelObservationResponse(BaseObservationResponse): diff --git a/schemas/parameter.py b/schemas/parameter.py index 4e3cea140..45dc28d6b 100644 --- a/schemas/parameter.py +++ b/schemas/parameter.py @@ -1,4 +1,5 @@ from schemas import BaseResponseModel +from core.enums import ParameterType, ParameterName, Unit # -------- RESPONSE ------- @@ -8,8 +9,8 @@ class ParameterResponse(BaseResponseModel): This model can be extended to include additional fields as needed. """ - parameter_name: str + parameter_name: ParameterName matrix: str - parameter_type: str | None + parameter_type: ParameterType | None cas_number: str | None - default_unit: str + default_unit: Unit | None diff --git a/schemas/publication.py b/schemas/publication.py index 28e9a6b5a..a2da9d5f8 100644 --- a/schemas/publication.py +++ b/schemas/publication.py @@ -14,6 +14,7 @@ # limitations under the License. # =============================================================================== from pydantic import BaseModel +from core.enums import PublicationType # -------- CREATE ---------- @@ -28,7 +29,7 @@ class CreatePublication(BaseModel): doi: str | None = None url: str | None = None - publication_type: str + publication_type: PublicationType # -------- RESPONSE ---------- @@ -54,7 +55,7 @@ class PublicationResponse(BaseModel): year: int doi: str | None = None url: str | None = None - publication_type: str + publication_type: PublicationType # -------- UPDATE ---------- diff --git a/schemas/sample.py b/schemas/sample.py index 71e782660..4d821e578 100644 --- a/schemas/sample.py +++ b/schemas/sample.py @@ -133,9 +133,9 @@ class SampleResponse(BaseResponseModel): contact: ContactResponse sample_date: UTCAwareDatetime sample_name: str - sample_matrix: str - sample_method: str - qc_type: str + sample_matrix: SampleMatrix + sample_method: SampleMethod + qc_type: SampleQcType notes: str | None depth_top: float | None depth_bottom: float | None diff --git a/schemas/sensor.py b/schemas/sensor.py index 1d5e8d169..d76d17047 100644 --- a/schemas/sensor.py +++ b/schemas/sensor.py @@ -22,7 +22,7 @@ # model_validator, # field_validator, # ) - +from core.enums import SensorType from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel # ------- VALIDATION ------ @@ -58,7 +58,7 @@ class CreateSensor(BaseCreateModel): """ name: str - sensor_type: str + sensor_type: SensorType model: str | None = None serial_no: str | None = None pcn_number: str | None = None @@ -70,7 +70,7 @@ class CreateSensor(BaseCreateModel): # -------- UPDATE ---------- class UpdateSensor(BaseUpdateModel): name: str | None = None - sensor_type: str | None = None + sensor_type: SensorType | None = None model: str | None = None serial_no: str | None = None pcn_number: str | None = None @@ -82,7 +82,7 @@ class UpdateSensor(BaseUpdateModel): # -------- RESPONSE ---------- class SensorResponse(BaseResponseModel): name: str - sensor_type: str + sensor_type: SensorType model: str | None # = Column(String(50)) serial_no: str | None # = Column(String(50)) pcn_number: str | None