From 2203bfc34a7aaad0ffc48342025b48dc3fced145 Mon Sep 17 00:00:00 2001 From: jacob-a-brown Date: Tue, 9 Dec 2025 16:48:41 -0700 Subject: [PATCH 1/7] feat: add id to asset search properties & update things Add the id to the asset search properties and update the things associated with assets to correspond with the things associated with contacts --- .DS_Store | Bin 0 -> 8196 bytes api/search.py | 11 +++++++++-- tests/.DS_Store | Bin 0 -> 10244 bytes tests/features/.DS_Store | Bin 0 -> 8196 bytes tests/test_search.py | 20 ++++++++++++++++++-- 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .DS_Store create mode 100644 tests/.DS_Store create mode 100644 tests/features/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d5ef56cf852670816ca41e3deaef6c16927a0707 GIT binary patch literal 8196 zcmeHMO>fgc5S>lZ#vv3%s7Mt8$r9I6+EgGAmyosx4oIj$Z~$cDIBnfJc8HyZsw&DI z_zV06?)(qHaWTCQc)X~f-e4T5d zl+rV|UD;qS{C$?2S|>c+E!|(%1SE6RM~?_sUl4=greiR&2ZROYNyI7 zItfK5k63MFlu_uOnM`XhtqMjQ>E)aZEJwaXj zEem=?Rh-$TUD}{E8jzPme=DH76yUqSfL!var=!K~9J-G02pKeH@F^re$NH3GmBAVo zP``4htJr0P(PBz~57EXLAG_B_FYV`e70DY>KZpB~!ijjM^Ytv2?#fjRu-*vk?qlUA&@{-6!RlwnSOSuFIs%%RtY!jkOb0qsdLqkQrhUv9 zBR+ay_puf=k}o~P${pe=^vsRJzR8YL#VWgU-Z@$e_E8pHljsnPeYwZ^-pMj7dXDw6 zrhAwLv7@i2#9qqcud&BbnRAFadQy)BM+G&d_p;m?qJBq4|1;Qm(T{sYT+8^dA+>u? zW2}u;9Hc6;ehGb`L|-kze;WS^SY!Ci!QJEG?*h+h91DI?-{L%Q3yKJMNW)iWc=lAR zuZTUQp?P$~E*)b>J@j&TzI!zM0`>_M+B_1Txd`b_;$iv(`tA+`KTg)ye~MDMvandS zs#e{4+c}6Pour$L<92uSnvY&YQQ)5UyY5TBKXDq%TTz&F{jfih1mEie^5&HvdeNjE zjYBV%+>W|n)vTJ+xOsZo+`PZOYBx6@o~_!a8yn5$s{P>6=Gj@zx^d^;<6Y+{7=+Ph zL>7ujf0)pcI-a#((97)pxHAfaDCDOLGa6821ikrrv?mpHW=hTRGRLB(R;|F#>C4X( z@i7yNRYb!vA}Ek|%aQJ98F^XsY#XNY-ziX5J6hrVe+Qw+))b?FQQ&eZAS&CP?Kbv& z{tC-)pL1;+^&Kh~#!Z!#6f`m&hn49#?Cc+g=-YrYr dict: def _get_asset_results(session: Session, q: str, limit: int) -> list[dict]: vector = Asset.search_vector query = search( - select(Asset).join(AssetThingAssociation).join(Thing), + select(Asset) + .join(AssetThingAssociation) + .join(Thing) + .options(selectinload(Asset.things)), q, vector=vector, limit=limit, @@ -176,7 +179,11 @@ def _get_asset_results(session: Session, q: str, limit: int) -> list[dict]: "label": a.name, "group": "Assets", "properties": { - "things": [t.name for t in a.things], + "id": a.id, + "things": [ + {"label": t.name, "id": t.id, "thing_type": t.thing_type} + for t in a.things + ], "storage_service": a.storage_service, "storage_path": a.storage_path, "mime_type": a.mime_type, diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b290e14aa882d8d6507c67f6780529b19d3efc88 GIT binary patch literal 10244 zcmeHMO>fgM7=GQAE!93Kuw9TQMdHF?8eJ<8h)e0lfh$FD094X8>mt&mB<;Fs8tNVR z3;YG{{15mqoZxw#bU3Y3ZU_xKvfniE^V)vi$Byh0k!Xy%dqg%7S;%b5YbZ2{-*cWz zh4hIHD}X2RXhGHFz_!hfNwS@Yc;W^Gz=I93N+^6Ly9bd%MY_ch2^{Zjt`1vI?If`Y zoJ0aAk+v+-9g2{x182+XBJjqp zPnmExMGfebI^-Z9k;{H&P+#Uy9SYIXAx;Whic#`e!Ax6Xdj?gcF4`E9ll3k>%+#~s z*OfI#6tVxA`ak|uzdeoz>6pK^2fh~%E0yn}uvEOVykf0btJdr0Q8;c6o#7~MIMGY~ z^(+ki_VnIqKlj{mtGu}%21CaS+(-hvu8S(KUU)$_95=#I(2XUwnY^${R;g9Kd3IK< z-L34{)!O~@9s6u=uUg%)?>(rUpO>tyTX!BlZN2sTLHH4)KoaRvNbAY(i}Dlh=9oBc zMu8s&Jq98#Q=g8(yoUV(*HI(Aei5q{xQ%hS`nVOo%^4VhiXGN!1Gksv~K}XQ5R(ZsFRncn-`Vh*Ez?-JuQn4y-Q^EDn*AR1q zd5gfhht^s9IzOyv)z8+PALcQVbBNgpsi$XBu+~*i*XKONh(FIN52df`ja0&7A456s znUy>rtO?bJs-~VC$w;q5sRb3OM$!j^Cz45ysD??_f!I)VP*%pzyz#2VW6#4w2^sti z1s_6z(`YXA!n(+K*-*R;q%H>F-qnqOSGNpS?ZJRX=z}jASlk2XAmc^l{~f%rCbe^s zqo0f9)s=O9qU$-Y0~w?`>KMHD5%HW+bJQ1Ld3BHE*CsFK@0FNm+jXIo81asDedd`j z7GWcBoi}Sa$|_dnm}dxoTYI`zWsFtiwxSPSB^}vk{;EY-L71~1o^=qhR{BlM>l4Ra z^<0AItiPGBql~wTz{EAlFkl!k3>XFs1BQW1#=ufC9-GhqALsx7f5}E|hGQ5o45$o< z;z9GEfhW@gCG{oW^I5x%{05m5*PA3(LQu%#cvyKHj~{#-e;b+WR@idFeLc04#Nr9k YU;i_}?Em=;u0>}5|NDIVKimKR0UDaM0PzL@>2Kf6S!oxCg zvb9paI?zc80NKT~G>qj>f5?a@AQLBBD=~v+tSO{5HLk=ku5vKe9P5eGZ>_ZEWW+OY zoM+=I6ypjH6Nx&R#7gaz0cBvB0j}MCjGz{wZ1FZFbNfX0YG^T>)lu<=fjK0Zx zLwzdgGO)c3Ye>fVnaz#JP6k<=XoN>;|YMa4a%={Tm;glfI8gqBq3;YeaR1DDY0h2(KLx$x$Ew z16=?y$0r6KBYZM=wAsqp5({@{$l3}_apv2#l}s7VUf;{U=Oq;^=82>7SY*ymA9>G1 zX1qC0Q(%|8UygDL-sY?0+PT6N{nm@?wuiZHhH|Fg?@RB0u%S&Ecsc_c7NW=X|L2E) z|9`q8MNkHmfoIEr=?+c?huEIGTbDM Date: Tue, 9 Dec 2025 16:54:06 -0700 Subject: [PATCH 2/7] fix: remove .DS_Store --- .DS_Store | Bin 8196 -> 0 bytes tests/.DS_Store | Bin 10244 -> 0 bytes tests/features/.DS_Store | Bin 8196 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store delete mode 100644 tests/.DS_Store delete mode 100644 tests/features/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index d5ef56cf852670816ca41e3deaef6c16927a0707..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 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 Date: Wed, 10 Dec 2025 13:48:47 -0700 Subject: [PATCH 3/7] refactor: rename Measuring to Sampling Procedure in lexicon note_type This better captures the intent of the notes related to sampling activities. --- core/lexicon.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lexicon.json b/core/lexicon.json index 0d14be5ac..90ead61b9 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -1168,7 +1168,7 @@ {"categories": ["note_type"], "term": "Historical", "definition": "Historical information or context about the well or location."}, {"categories": ["note_type"], "term": "General", "definition": "Other types of notes that do not fit into the predefined categories."}, {"categories": ["note_type"], "term": "Water", "definition": "Water bearing zone information and other info from ose reports"}, - {"categories": ["note_type"], "term": "Measuring", "definition": "Notes about measuring/visiting the well, on Access form"}, + {"categories": ["note_type"], "term": "Sampling Procedure", "definition": "Notes about sampling procedures for all sample types, like water levels and water chemistry"}, {"categories": ["note_type"], "term": "Coordinate", "definition": "Notes about a location's coordinates"}, {"categories": ["well_pump_type"], "term": "Submersible", "definition": "Submersible"}, {"categories": ["well_pump_type"], "term": "Jet", "definition": "Jet Pump"}, From e929c025568a4ba710fb9384f742f9eadcb523e6 Mon Sep 17 00:00:00 2001 From: jacob-a-brown Date: Wed, 10 Dec 2025 13:55:39 -0700 Subject: [PATCH 4/7] refactor: update measuring notes to sampling procedure notes throughout This refactor enables all notes related to sampling procedures to be stored under a unified "Sampling Procedure" note type. --- db/thing.py | 4 ++-- schemas/thing.py | 2 +- tests/features/environment.py | 2 +- tests/features/steps/well-notes.py | 12 ++++++++---- tests/features/well-notes.feature | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/db/thing.py b/db/thing.py index 723f2da22..35d7482ba 100644 --- a/db/thing.py +++ b/db/thing.py @@ -361,8 +361,8 @@ def general_notes(self): return self._get_notes("General") @property - def measuring_notes(self): - return self._get_notes("Measuring") + def sampling_procedure_notes(self): + return self._get_notes("Sampling Procedure") @property def construction_notes(self): diff --git a/schemas/thing.py b/schemas/thing.py index 5aaa17985..befb5bb2d 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -244,7 +244,7 @@ class WellResponse(BaseThingResponse): measuring_point_description: str | None aquifers: list[dict] = [] water_notes: list[NoteResponse] | None = None - measuring_notes: list[NoteResponse] | None = None + sampling_procedure_notes: list[NoteResponse] | None = None construction_notes: list[NoteResponse] | None = None permissions: list[PermissionHistoryResponse] diff --git a/tests/features/environment.py b/tests/features/environment.py index 1d655a4da..aa31f3c4d 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -115,7 +115,7 @@ def add_well(context, session, location, name_num): for nt, c in ( ("General", "well notes"), ("Water", "water notes"), - ("Measuring", "measuring notes"), + ("Sampling Procedure", "sampling procedure notes"), ("Construction", "construction notes"), ): n = well.add_note(c, nt) diff --git a/tests/features/steps/well-notes.py b/tests/features/steps/well-notes.py index 9e20e84f3..9b424f98f 100644 --- a/tests/features/steps/well-notes.py +++ b/tests/features/steps/well-notes.py @@ -63,13 +63,17 @@ def step_impl(context): @then( - "the response should include measuring notes (notes about measuring/visiting the well, on Access form)" + "the response should include sampling procedure notes (notes about sampling procedures for all sample types, like water levels and water chemistry)" ) def step_impl(context): data = context.response.json() - assert "measuring_notes" in data, "Response does not include measuring notes" - assert data["measuring_notes"] is not None, "Measuring notes is null" - context.notes["measuring"] = data["measuring_notes"] + assert ( + "sampling_procedure_notes" in data + ), "Response does not include sampling procedure notes" + assert ( + data["sampling_procedure_notes"] is not None + ), "Sampling Procedure notes is null" + context.notes["sampling_procedure"] = data["sampling_procedure_notes"] @then( diff --git a/tests/features/well-notes.feature b/tests/features/well-notes.feature index 5cb5a502b..d172061f2 100644 --- a/tests/features/well-notes.feature +++ b/tests/features/well-notes.feature @@ -17,7 +17,7 @@ Feature: Retrieve well notes by well ID And the response should include location notes (i.e. driving directions and geographic well location notes) And the response should include construction notes (i.e. pump notes and other construction notes) And the response should include general well notes (catch all notes field) - And the response should include measuring notes (notes about measuring/visiting the well, on Access form) + And the response should include sampling procedure notes (notes about sampling procedures for all sample types, like water levels and water chemistry) And the response should include water notes (i.e. water bearing zone information and other info from ose reports) And the notes should be a non-empty string From 5013b262e37b85e77f7769c51a1ebafdf9cd0816 Mon Sep 17 00:00:00 2001 From: jacob-a-brown Date: Wed, 10 Dec 2025 14:05:43 -0700 Subject: [PATCH 5/7] refactor: all all thing types to have sampling_procedure_notes --- schemas/thing.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/schemas/thing.py b/schemas/thing.py index befb5bb2d..9f2a084e3 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -196,7 +196,8 @@ class BaseThingResponse(BaseResponseModel): monitoring_status: str | None links: list[ThingIdLinkResponse] = Field(default=[], alias="alternate_ids") monitoring_frequencies: list[MonitoringFrequencyResponse] = [] - general_notes: list[NoteResponse] | None = None + general_notes: list[NoteResponse] = [] + sampling_procedure_notes: list[NoteResponse] = [] @field_validator("monitoring_frequencies", mode="before") def remove_records_with_end_date(cls, monitoring_frequencies): @@ -243,10 +244,8 @@ class WellResponse(BaseThingResponse): measuring_point_height_unit: str = "ft" measuring_point_description: str | None aquifers: list[dict] = [] - water_notes: list[NoteResponse] | None = None - sampling_procedure_notes: list[NoteResponse] | None = None - - construction_notes: list[NoteResponse] | None = None + water_notes: list[NoteResponse] = [] + construction_notes: list[NoteResponse] = [] permissions: list[PermissionHistoryResponse] formation_completion_code: FormationCode | None From 7dd1aac4e0b049300a45faec11a866fc5fa302a7 Mon Sep 17 00:00:00 2001 From: Chase Martin Date: Wed, 10 Dec 2025 16:50:19 -0800 Subject: [PATCH 6/7] feat: add water level csv feature --- tests/features/water-level-csv.feature | 131 +++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/features/water-level-csv.feature diff --git a/tests/features/water-level-csv.feature b/tests/features/water-level-csv.feature new file mode 100644 index 000000000..699a01167 --- /dev/null +++ b/tests/features/water-level-csv.feature @@ -0,0 +1,131 @@ +@backend +@BDMS-TBD +@production +Feature: Bulk upload water level entries from CSV + As a hydrogeologist or data specialist + I want to upload a CSV file containing water level entry data for multiple wells + So that water level records can be created efficiently and accurately in the system + + Background: + Given a functioning api + And valid lexicon values exist for: + | lexicon category | + | sampler | + | sample_method | + | level_status | + | data_quality | + + @positive @happy_path @BDMS-TBD + Scenario: Uploading a valid water level entry CSV containing required and optional fields + Given a valid CSV file for bulk water level entry upload + And my CSV file is encoded in UTF-8 and uses commas as separators + And my CSV file contains multiple rows of water level entry data + And the CSV includes required fields: + | required field name | + | field_staff | + | well_name_point_id | + | field_event_date_time | + | measurement_date_time | + | sampler | + | sample_method | + | mp_height | + | level_status | + | depth_to_water_ft | + | data_quality | + And each "well_name_point_id" value matches an existing well + And "measurement_date_time" values are valid ISO 8601 timestamps with timezone offsets (e.g. "2025-02-15T10:30:00-08:00") + And the CSV includes optional fields when available: + | optional field name | + | water_level_notes | + When I upload the file to the bulk upload endpoint + Then the system returns a 201 Created status code + And the system should return a response in JSON format + And the response includes a summary containing: + | summary_field | value | + | total_rows_processed | 2 | + | total_rows_imported | 2 | + | validation_errors_or_warnings | 0 | + And the response includes an array of created water level entry objects + + @positive @validation @column_order @BDMS-TBD + Scenario: Upload succeeds when required columns are present but in a different order + Given my CSV file contains all required headers but in a different column order + And the CSV includes required fields: + | required field name | + | well_name_point_id | + | measurement_date_time | + | sampler | + | sample_method | + | mp_height | + | level_status | + | depth_to_water_ft | + | data_quality | + When I upload the file to the bulk upload endpoint + Then the system returns a 201 Created status code + And the system should return a response in JSON format + And all water level entries are imported + + @positive @validation @extra_columns @BDMS-TBD + Scenario: Upload succeeds when CSV contains extra, unknown columns + Given my CSV file contains extra columns but is otherwise valid + When I upload the file to the bulk upload endpoint + Then the system returns a 201 Created status code + And the system should return a response in JSON format + And all water level entries are imported + + ########################################################################### + # NEGATIVE VALIDATION SCENARIOS + ########################################################################### + + @negative @validation @BDMS-TBD + Scenario: No water level entries are imported when any row fails validation + Given my CSV file contains 3 rows of data with 2 valid rows and 1 row missing the required "well_name_point_id" + When I upload the file to the bulk upload endpoint + Then the system returns a 422 Unprocessable Entity status code + And the system should return a response in JSON format + And the response includes a validation error for the row missing "well_name_point_id" + And no water level entries are imported + + @negative @validation @required_fields @BDMS-TBD + Scenario Outline: Upload fails when a required field is missing + Given my CSV file contains a row missing the required "" field + When I upload the file to the bulk upload endpoint + Then the system returns a 422 Unprocessable Entity status code + And the system should return a response in JSON format + And the response includes a validation error for the "" field + And no water level entries are imported + + Examples: + | required_field | + | well_name_point_id | + | measurement_date_time | + | sampler | + | sample_method | + | mp_height | + | level_status | + | depth_to_water_ft | + | data_quality | + + @negative @validation @date_formats @BDMS-TBD + Scenario: Upload fails due to invalid date formats + Given my CSV file contains invalid ISO 8601 date values in the "measurement_date_time" field + When I upload the file to the bulk upload endpoint + Then the system returns a 422 Unprocessable Entity status code + And the response includes validation errors identifying the invalid field and row + And no water level entries are imported + + @negative @validation @numeric_fields @BDMS-TBD + Scenario: Upload fails due to invalid numeric fields + Given my CSV file contains values that cannot be parsed as numeric in numeric-required fields such as "mp_height" or "depth_to_water_ft" + When I upload the file to the bulk upload endpoint + Then the system returns a 422 Unprocessable Entity status code + And the response includes validation errors identifying the invalid field and row + And no water level entries are imported + + @negative @validation @lexicon_values @BDMS-TBD + Scenario: Upload fails due to invalid lexicon values + Given my CSV file contains invalid lexicon values for "sampler", "sample_method", "level_status", or "data_quality" + When I upload the file to the bulk upload endpoint + Then the system returns a 422 Unprocessable Entity status code + And the response includes validation errors identifying the invalid field and row + And no water level entries are imported \ No newline at end of file From 795fcfdf7a38caa21e75dcb1ab99ed9efe3a5dda Mon Sep 17 00:00:00 2001 From: Chase Martin Date: Wed, 10 Dec 2025 16:58:57 -0800 Subject: [PATCH 7/7] fix: skip water level feature in test suite --- tests/features/water-level-csv.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/features/water-level-csv.feature b/tests/features/water-level-csv.feature index 699a01167..afa96ec42 100644 --- a/tests/features/water-level-csv.feature +++ b/tests/features/water-level-csv.feature @@ -1,3 +1,4 @@ +@skip @backend @BDMS-TBD @production