Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1e0b253
feat(schemas): add alias validation for well inventory fields
ksmuczynski Mar 7, 2026
3b2db6b
test(well_inventory): add tests for schema alias handling
ksmuczynski Mar 7, 2026
6c38157
feat(schemas): enhance well inventory schema with flexible validation…
ksmuczynski Mar 9, 2026
1d3aa13
test(features): improve error messages and enhance contact field vali…
ksmuczynski Mar 9, 2026
4d74d1b
feat(core): expand lexicon with new terms for water-related categories
ksmuczynski Mar 9, 2026
a7e0632
feat(schemas): add `monitoring_status` field to `thing` schema
ksmuczynski Mar 9, 2026
42bae2d
feat(thing_helper): add handling for `monitoring_status` in status hi…
ksmuczynski Mar 9, 2026
81faed4
test(features): isolate well inventory scenarios with unique well ids…
ksmuczynski Mar 9, 2026
5bbff15
refactor(helpers): tighten helper transactions to avoid refresh and r…
ksmuczynski Mar 9, 2026
6c5d46e
feat(services): improve well inventory handling and align well invent…
ksmuczynski Mar 10, 2026
4a4e249
feat(tests): adjust validation scenarios to allow partial imports wit…
ksmuczynski Mar 10, 2026
c350555
Merge staging
ksmuczynski Mar 11, 2026
6302d80
test(features): enhance CSV reading to handle empty values and ensure…
ksmuczynski Mar 11, 2026
9742c03
fix(schemas): fix well inventory schema mismatch for `SampleMethod` a…
ksmuczynski Mar 11, 2026
86aa582
fix(contacts): allow nullable role and contact_type in well inventory…
ksmuczynski Mar 12, 2026
3072e41
test(well-inventory): preserve structural CSV fixtures in BDD setup
ksmuczynski Mar 12, 2026
dbe7074
test(well-inventory): require distinct matches for expected validatio…
ksmuczynski Mar 12, 2026
76a450c
test(well-inventory): align BDD expectations with best-effort import …
ksmuczynski Mar 12, 2026
ce742fd
test(well-inventory): align autogen placeholder tests with case-insen…
ksmuczynski Mar 13, 2026
ad86bf6
test(well-inventory): update expected values for `SampleMethod` and `…
ksmuczynski Mar 13, 2026
55872b2
test(well-inventory): expand contact tests for missing name and organ…
ksmuczynski Mar 13, 2026
8c9ea27
Merge branch 'staging' into kas-well-BDMS-626-inventory-ingestion-upd…
ksmuczynski Mar 13, 2026
7143ed3
refactor(enums): update `MonitoringStatus` to use `status_value` lexi…
ksmuczynski Mar 16, 2026
8e583ea
refactor(well-inventory): update field mappings and naming convention…
ksmuczynski Mar 16, 2026
a7bad53
fix(cli): handle UTF-8 BOM in CSV decoding for well inventory import
ksmuczynski Mar 16, 2026
6d2d810
fix(well-inventory): preserve attempted water-level records when dept…
ksmuczynski Mar 16, 2026
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
29 changes: 29 additions & 0 deletions alembic/versions/p9c1d2e3f4a5_make_contact_role_nullable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""make contact role nullable

Revision ID: p9c1d2e3f4a5
Revises: o8b9c0d1e2f3
Create Date: 2026-03-11 10:30:00.000000
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "p9c1d2e3f4a5"
down_revision: Union[str, Sequence[str], None] = "o8b9c0d1e2f3"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.alter_column(
"contact", "role", existing_type=sa.String(length=100), nullable=True
)


def downgrade() -> None:
op.alter_column(
"contact", "role", existing_type=sa.String(length=100), nullable=False
)
35 changes: 35 additions & 0 deletions alembic/versions/q0d1e2f3a4b5_make_contact_type_nullable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""make contact type nullable

Revision ID: q0d1e2f3a4b5
Revises: p9c1d2e3f4a5
Create Date: 2026-03-11 17:10:00.000000
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "q0d1e2f3a4b5"
down_revision: Union[str, Sequence[str], None] = "p9c1d2e3f4a5"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.alter_column(
"contact",
"contact_type",
existing_type=sa.String(length=100),
nullable=True,
)


def downgrade() -> None:
op.alter_column(
"contact",
"contact_type",
existing_type=sa.String(length=100),
nullable=False,
)
15 changes: 12 additions & 3 deletions cli/service_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def well_inventory_csv(source_file: Path | str):
payload = {"detail": "Empty file"}
return WellInventoryResult(1, json.dumps(payload), payload["detail"], payload)
try:
text = content.decode("utf-8")
# Accept UTF-8 CSVs saved with a BOM so the first header is parsed correctly.
text = content.decode("utf-8-sig")
except UnicodeDecodeError:
payload = {"detail": "File encoding error"}
return WellInventoryResult(1, json.dumps(payload), payload["detail"], payload)
Expand All @@ -61,8 +62,16 @@ def well_inventory_csv(source_file: Path | str):
except ValueError as exc:
payload = {"detail": str(exc)}
return WellInventoryResult(1, json.dumps(payload), payload["detail"], payload)
exit_code = 0 if not payload.get("validation_errors") else 1
return WellInventoryResult(exit_code, json.dumps(payload), "", payload)
exit_code = (
0 if not payload.get("validation_errors") and not payload.get("detail") else 1
)
stderr = ""
if exit_code != 0:
if payload.get("validation_errors"):
stderr = f"Validation errors: {json.dumps(payload.get('validation_errors'), indent=2)}"
else:
stderr = f"Error: {payload.get('detail')}"
return WellInventoryResult(exit_code, json.dumps(payload), stderr, payload)


def water_levels_csv(source_file: Path | str, *, pretty_json: bool = False):
Expand Down
2 changes: 1 addition & 1 deletion core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
)
LimitType: type[Enum] = build_enum_from_lexicon_category("limit_type")
MeasurementMethod: type[Enum] = build_enum_from_lexicon_category("measurement_method")
MonitoringStatus: type[Enum] = build_enum_from_lexicon_category("monitoring_status")
MonitoringStatus: type[Enum] = build_enum_from_lexicon_category("status_value")
ParameterName: type[Enum] = build_enum_from_lexicon_category("parameter_name")
Organization: type[Enum] = build_enum_from_lexicon_category("organization")
OriginType: type[Enum] = build_enum_from_lexicon_category("origin_type")
Expand Down
7 changes: 7 additions & 0 deletions core/lexicon.json
Original file line number Diff line number Diff line change
Expand Up @@ -8185,6 +8185,13 @@
"term": "Water",
"definition": "Water bearing zone information and other info from ose reports"
},
{
"categories": [
"note_type"
],
"term": "Water Quality",
"definition": "Water quality information"
},
{
"categories": [
"note_type"
Expand Down
6 changes: 3 additions & 3 deletions db/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from typing import List, TYPE_CHECKING
from typing import List, TYPE_CHECKING, Optional

from sqlalchemy import Integer, ForeignKey, String, UniqueConstraint
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
Expand Down Expand Up @@ -49,8 +49,8 @@ class ThingContactAssociation(Base, AutoBaseMixin):
class Contact(Base, AutoBaseMixin, ReleaseMixin, NotesMixin):
name: Mapped[str] = mapped_column(String(100), nullable=True)
organization: Mapped[str] = lexicon_term(nullable=True)
role: Mapped[str] = lexicon_term(nullable=False)
contact_type: Mapped[str] = lexicon_term(nullable=False)
role: Mapped[Optional[str]] = lexicon_term(nullable=True)
contact_type: Mapped[Optional[str]] = lexicon_term(nullable=True)

# primary keys of the nm aquifer tables from which the contacts originate
nma_pk_owners: Mapped[str] = mapped_column(String(100), nullable=True)
Expand Down
8 changes: 4 additions & 4 deletions schemas/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ class CreateContact(BaseCreateModel, ValidateContact):
thing_id: int
name: str | None = None
organization: str | None = None
role: Role
contact_type: ContactType = "Primary"
role: Role | None = None
contact_type: ContactType | None = None
nma_pk_owners: str | None = None
# description: str | None = None
# email: str | None = None
Expand Down Expand Up @@ -218,8 +218,8 @@ class ContactResponse(BaseResponseModel):

name: str | None
organization: str | None
role: Role
contact_type: ContactType
role: Role | None
contact_type: ContactType | None
incomplete_nma_phones: List[str] = []
emails: List[EmailResponse] = []
phones: List[PhoneResponse] = []
Expand Down
1 change: 1 addition & 0 deletions schemas/thing.py
Copy link
Contributor

Choose a reason for hiding this comment

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

If monitoring_status is added in CreateWell then I think that it should also be a field in WellResponse

Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class CreateWell(CreateBaseThing, ValidateWell):
is_suitable_for_datalogger: bool | None = None
is_open: bool | None = None
well_status: str | None = None
monitoring_status: str | None = None
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be restricted to MonitoringStatus enum values

formation_completion_code: FormationCode | None = None
nma_formation_zone: str | None = None

Expand Down
Loading
Loading