diff --git a/api/well_inventory.py b/api/well_inventory.py index 5f4b072ab..4f7769609 100644 --- a/api/well_inventory.py +++ b/api/well_inventory.py @@ -84,6 +84,14 @@ def _make_location(model) -> Location: def _make_contact(model: WellInventoryRow, well: Thing, idx) -> dict: # add contact + notes = [] + for content, note_type in ( + (model.result_communication_preference, "Communication"), + (model.contact_special_instructions, "General"), + ): + if content is not None: + notes.append({"content": content, "note_type": note_type}) + emails = [] phones = [] addresses = [] @@ -126,6 +134,7 @@ def _make_contact(model: WellInventoryRow, well: Thing, idx) -> dict: "emails": emails, "phones": phones, "addresses": addresses, + "notes": notes, } @@ -482,7 +491,8 @@ def _add_csv_row(session: Session, group: Group, model: WellInventoryRow, user) for note_content, note_type in ( (model.specific_location_of_well, "Access"), (model.special_requests, "General"), - (model.well_measuring_notes, "Measuring"), + (model.well_measuring_notes, "Sampling Procedure"), + (model.sampling_scenario_notes, "Sampling Procedure"), ): if note_content is not None: well_notes.append({"content": note_content, "note_type": note_type}) diff --git a/core/enums.py b/core/enums.py index a2c73f521..f0c837c3d 100644 --- a/core/enums.py +++ b/core/enums.py @@ -80,4 +80,5 @@ GeographicScale: type[Enum] = build_enum_from_lexicon_category("geographic_scale") Lithology: type[Enum] = build_enum_from_lexicon_category("lithology") FormationCode: type[Enum] = build_enum_from_lexicon_category("formation_code") +NoteType: type[Enum] = build_enum_from_lexicon_category("note_type") # ============= EOF ============================================= diff --git a/core/lexicon.json b/core/lexicon.json index 8b4a2d263..ac6941650 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -1172,6 +1172,7 @@ {"categories": ["note_type"], "term": "Water", "definition": "Water bearing zone information and other info from ose reports"}, {"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": ["note_type"], "term": "Communication", "definition": "Notes about communication preferences/requests for a contact"}, {"categories": ["well_pump_type"], "term": "Submersible", "definition": "Submersible"}, {"categories": ["well_pump_type"], "term": "Jet", "definition": "Jet Pump"}, {"categories": ["well_pump_type"], "term": "Line Shaft", "definition": "Line Shaft"}, diff --git a/db/contact.py b/db/contact.py index 558724df9..fa3146df1 100644 --- a/db/contact.py +++ b/db/contact.py @@ -21,6 +21,7 @@ from sqlalchemy_utils import TSVectorType from db.base import Base, AutoBaseMixin, ReleaseMixin, lexicon_term +from db.notes import NotesMixin if TYPE_CHECKING: from db.field import FieldEventParticipant, FieldEvent @@ -45,7 +46,7 @@ class ThingContactAssociation(Base, AutoBaseMixin): ) -class Contact(Base, AutoBaseMixin, ReleaseMixin): +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) @@ -124,6 +125,14 @@ class Contact(Base, AutoBaseMixin, ReleaseMixin): UniqueConstraint("name", "organization", name="uq_contact_name_organization"), ) + @property + def communication_notes(self): + return self._get_notes("Communication") + + @property + def general_notes(self): + return self._get_notes("General") + class IncompleteNMAPhone(Base, AutoBaseMixin): """ diff --git a/schemas/contact.py b/schemas/contact.py index eeecd6bfd..6f475abae 100644 --- a/schemas/contact.py +++ b/schemas/contact.py @@ -22,6 +22,7 @@ from core.enums import Role, ContactType, PhoneType, EmailType, AddressType from schemas import BaseResponseModel, BaseCreateModel, BaseUpdateModel +from schemas.notes import CreateNote, NoteResponse # -------- VALIDATORS ---------- @@ -157,6 +158,7 @@ class CreateContact(BaseCreateModel, ValidateContact): emails: list[CreateEmail] | None = None phones: list[CreatePhone] | None = None addresses: list[CreateAddress] | None = None + notes: list[CreateNote] | None = None # -------- RESPONSE ---------- @@ -221,6 +223,8 @@ class ContactResponse(BaseResponseModel): phones: List[PhoneResponse] = [] addresses: List[AddressResponse] = [] things: List[ThingResponseForContact] = [] + communication_notes: List[NoteResponse] = [] + general_notes: List[NoteResponse] = [] @field_validator("incomplete_nma_phones", mode="before") def make_incomplete_nma_phone_str(cls, v: list) -> list: diff --git a/schemas/notes.py b/schemas/notes.py index 85c47ed9b..8b8d8c438 100644 --- a/schemas/notes.py +++ b/schemas/notes.py @@ -2,6 +2,9 @@ Pydantic models for the Notes table. """ +from core.enums import NoteType + +from pydantic import BaseModel from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel # -------- BASE SCHEMA: ---------- @@ -10,8 +13,8 @@ """ -class BaseNote: - note_type: str +class BaseNote(BaseModel): + note_type: NoteType content: str diff --git a/schemas/well_inventory.py b/schemas/well_inventory.py index 969d962a4..aa4079664 100644 --- a/schemas/well_inventory.py +++ b/schemas/well_inventory.py @@ -235,9 +235,9 @@ class WellInventoryRow(BaseModel): well_hole_status: Optional[str] = None monitoring_frequency: MonitoryFrequencyField = None - result_communication_preference: Optional[str] = None # TODO: needs as home - contact_special_requests_notes: Optional[str] = None # TODO: needs a home - sampling_scenario_notes: Optional[str] = None # TODO: needs a home + result_communication_preference: Optional[str] = None + contact_special_requests_notes: Optional[str] = None + sampling_scenario_notes: Optional[str] = None well_measuring_notes: Optional[str] = None sample_possible: OptionalBool = None # TODO: needs a home diff --git a/services/contact_helper.py b/services/contact_helper.py index fb241cf05..5c5245683 100644 --- a/services/contact_helper.py +++ b/services/contact_helper.py @@ -62,6 +62,7 @@ def add_contact(session: Session, data: CreateContact | dict, user: dict) -> Con phone_data = data.pop("phones", []) address_data = data.pop("addresses", []) thing_id = data.pop("thing_id", None) + notes_data = data.pop("notes", None) contact_data = data """ @@ -103,14 +104,22 @@ def add_contact(session: Session, data: CreateContact | dict, user: dict) -> Con audit_add(user, location_contact_association) - session.add(location_contact_association) - # owner_contact_association = OwnerContactAssociation() - # owner_contact_association.owner_id = owner.id - # owner_contact_association.contact_id = contact.id - # session.add(owner_contact_association) + session.add(location_contact_association) + session.flush() session.commit() + + if notes_data is not None: + for n in notes_data: + note = contact.add_note(n["content"], n["note_type"]) + session.add(note) + + session.commit() session.refresh(contact) + + for note in contact.notes: + session.refresh(note) + except Exception as e: session.rollback() raise e diff --git a/services/thing_helper.py b/services/thing_helper.py index ec4e330d5..d6b563f23 100644 --- a/services/thing_helper.py +++ b/services/thing_helper.py @@ -355,6 +355,9 @@ def add_thing( session.commit() session.refresh(thing) + for note in thing.notes: + session.refresh(note) + except Exception as e: session.rollback() raise e diff --git a/tests/conftest.py b/tests/conftest.py index cd27b3cea..b8bbd9227 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,7 @@ def location(): session.commit() session.refresh(loc) - note = loc.add_note("these are some test notes", "Other") + note = loc.add_note("these are some test notes", "General") session.add(note) session.commit() session.refresh(loc) @@ -356,6 +356,14 @@ def contact(water_well_thing): session.commit() session.refresh(association) + for content, note_type in [ + ("Communication note", "Communication"), + ("General note", "General"), + ]: + note = contact.add_note(content, note_type) + session.add(note) + session.commit() + yield contact session.delete(contact) session.delete(association) diff --git a/tests/test_contact.py b/tests/test_contact.py index 68422b0a6..2076168ad 100644 --- a/tests/test_contact.py +++ b/tests/test_contact.py @@ -108,6 +108,12 @@ def test_add_contact(spring_thing): "address_type": "Primary", } ], + "notes": [ + { + "note_type": "General", + "content": "This is a general note for the contact.", + } + ], } response = client.post("/contact", json=payload) data = response.json() @@ -158,6 +164,12 @@ def test_add_contact(spring_thing): ) assert data["release_status"] == payload["release_status"] + assert data["general_notes"][0]["note_type"] == "General" + assert ( + data["general_notes"][0]["content"] == "This is a general note for the contact." + ) + assert len(data["communication_notes"]) == 0 + cleanup_post_test(Contact, data["id"]) @@ -429,6 +441,11 @@ def test_get_contacts( assert data["items"][0]["addresses"][0]["address_type"] == address.address_type assert data["items"][0]["addresses"][0]["release_status"] == address.release_status + assert data["items"][0]["general_notes"][0]["note_type"] == "General" + assert data["items"][0]["general_notes"][0]["content"] == "General note" + assert data["items"][0]["communication_notes"][0]["note_type"] == "Communication" + assert data["items"][0]["communication_notes"][0]["content"] == "Communication note" + def test_get_contacts_by_thing_id(contact, second_contact, water_well_thing): response = client.get(f"/contact?thing_id={water_well_thing.id}") @@ -495,6 +512,11 @@ def test_get_contact_by_id( assert data["addresses"][0]["address_type"] == address.address_type assert data["addresses"][0]["release_status"] == address.release_status + assert data["general_notes"][0]["note_type"] == "General" + assert data["general_notes"][0]["content"] == "General note" + assert data["communication_notes"][0]["note_type"] == "Communication" + assert data["communication_notes"][0]["content"] == "Communication note" + def test_get_contact_by_id_404_not_found(contact): bad_contact_id = 99999 diff --git a/transfers/contact_transfer.py b/transfers/contact_transfer.py index 9168eab77..d5a9a44ad 100644 --- a/transfers/contact_transfer.py +++ b/transfers/contact_transfer.py @@ -365,8 +365,7 @@ def _make_contact_and_assoc(session, data, thing, added): from schemas.contact import CreateContact contact = CreateContact(**data) - contact_data = contact.model_dump() - contact_data.pop("thing_id") + contact_data = contact.model_dump(exclude=["thing_id", "notes"]) contact = Contact(**contact_data) session.add(contact)