Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 10 additions & 10 deletions db/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@

class ThingContactAssociation(Base, AutoBaseMixin):
thing_id: Mapped[int] = mapped_column(
Integer, ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
)
contact_id: Mapped[int] = mapped_column(
Integer, ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
)

contact: Mapped[List["Contact"]] = relationship("Contact")
thing: Mapped[List["Thing"]] = relationship("Thing") # noqa: F821


class Contact(Base, AutoBaseMixin, ReleaseMixin):
name: Mapped[str | None] = mapped_column(String(100))
organization: Mapped[str | None] = mapped_column(String(100))
role: Mapped[str] = lexicon_term()
contact_type: Mapped[str] = lexicon_term()
nma_pk_owners: Mapped[str | None] = mapped_column(String(100))
name: Mapped[str] = mapped_column(String(100), nullable=True)
organization: Mapped[str] = mapped_column(String(100), nullable=True)
role: Mapped[str] = lexicon_term(nullable=False)
contact_type: Mapped[str] = lexicon_term(nullable=False)
nma_pk_owners: Mapped[str] = mapped_column(String(100), nullable=True)

phones: Mapped[List["Phone"]] = relationship(
"Phone", back_populates="contact", passive_deletes=True
Expand Down Expand Up @@ -74,7 +74,7 @@ class Contact(Base, AutoBaseMixin, ReleaseMixin):

class Phone(Base, AutoBaseMixin, ReleaseMixin):
contact_id: Mapped[int] = mapped_column(
Integer, ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
)
phone_number: Mapped[str] = mapped_column(String(20), nullable=False)
phone_type: Mapped[str] = lexicon_term(nullable=False)
Expand All @@ -101,14 +101,14 @@ class Email(Base, AutoBaseMixin, ReleaseMixin):

class Address(Base, AutoBaseMixin, ReleaseMixin):
contact_id: Mapped[int] = mapped_column(
Integer, ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
)
address_line_1: Mapped[str] = mapped_column(String(255), nullable=False)
address_line_2: Mapped[str | None] = mapped_column(String(255), nullable=True)
city: Mapped[str] = mapped_column(String(100), nullable=False)
state: Mapped[str] = mapped_column(String(50), nullable=False)
postal_code: Mapped[str] = mapped_column(String(20), nullable=False)
country: Mapped[str] = lexicon_term(nullable=False, default="United States")
country: Mapped[str] = lexicon_term(default="United States", nullable=False)
address_type: Mapped[str] = lexicon_term(nullable=False)

contact: Mapped["Contact"] = relationship(
Expand Down
24 changes: 13 additions & 11 deletions db/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# ===============================================================================
from sqlalchemy import Integer, ForeignKey, String, Column, Float
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relationship, mapped_column
from sqlalchemy.orm import relationship, mapped_column, Mapped
from sqlalchemy_utils import TSVectorType

from db import lexicon_term
Expand Down Expand Up @@ -98,22 +98,24 @@ class ThingIdLink(Base, AutoBaseMixin, ReleaseMixin):


class WellScreen(Base, AutoBaseMixin, ReleaseMixin):
thing_id = Column(
Integer, ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
thing_id: Mapped[int] = mapped_column(
ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
)
screen_depth_top = Column(
Float, nullable=False, info={"unit": "feet below ground surface"}
screen_depth_top: Mapped[float] = mapped_column(
info={"unit": "feet below ground surface"}, nullable=True
)
screen_depth_bottom = Column(
Float, nullable=False, info={"unit": "feet below ground surface"}
screen_depth_bottom: Mapped[float] = mapped_column(
info={"unit": "feet below ground surface"}, nullable=True
)
screen_type = lexicon_term() # e.g., "PVC", "Steel", etc.
screen_type: Mapped[str] = lexicon_term(nullable=True) # e.g., "PVC", "Steel", etc.

screen_description = Column(
String(1000), nullable=True, info={"unit": "description of the screen"}
screen_description: Mapped[str] = mapped_column(
String(1000), info={"unit": "description of the screen"}, nullable=True
)
nma_pk_wellscreens: Mapped[str] = mapped_column(String(100), nullable=True)

# Define a relationship to well if needed
thing = relationship("Thing")
thing: Mapped["Thing"] = relationship("Thing")


# TODO: this could be the model used to handle AMP monitoring
Expand Down
167 changes: 121 additions & 46 deletions transfers/contact_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from transfers.util import read_csv, filter_to_valid_point_ids
from db import Thing, Contact, ThingContactAssociation, Email, Phone, Address

from schemas.contact import CreateContact


def extract_owner_role(comment):
# if comment is None:
Expand All @@ -32,6 +34,14 @@ def extract_owner_role(comment):
return "Owner"


"""
Developer's notes

Use Pydantic to perform model validations since all restrictions will
be built into the models
"""


def transfer_contacts(session):

odf = read_csv("ownersdata.csv")
Expand All @@ -41,96 +51,161 @@ def transfer_contacts(session):
for i, row in odf.iterrows():
thing = session.query(Thing).where(Thing.name == row.PointID).first()
if thing is None:
print(f"Thing with PointID {row.PointID} not foaund. Skipping owner.")
print(f"Thing with PointID {row.PointID} not found. Skipping owner.")
continue

# TODO: extract role from OwnerComment
# role = extract_owner_role(row.OwnerComment)
role = "Owner"
release_status = "private"

# TODO: put in guards for null values
# name OR organization must be defined, otherwise skip
if not (row.FirstName or row.LastName) and not row.Company:
print(
f"Skipping first contact for PointID {row.PointID} due to missing name and organization."
)
else:
print(f"Transferring first contact for PointID {row.PointID}")
contact1 = Contact(
name=f"{row.FirstName} {row.LastName}",
role=role,
contact_type="Primary",
organization=row.Company, # assumes organization applies to both contacts
nma_pk_owners=row.OwnerKey,
)
try:

if row.FirstName is None and row.LastName is None:
name = None
elif row.FirstName is not None and row.LastName is None:
name = row.FirstName
elif row.FirstName is None and row.LastName is not None:
name = row.LastName
else:
name = f"{row.FirstName} {row.LastName}"

first_contact_data = {
"thing_id": thing.id,
"release_status": release_status,
"name": name,
"role": role,
"contact_type": "Primary",
"organization": row.Company,
"nma_pk_owners": row.OwnerKey,
}

CreateContact.model_validate(first_contact_data)

first_contact_data.pop("thing_id")
first_contact = Contact(**first_contact_data)

assoc = ThingContactAssociation()
assoc.thing = thing
assoc.contact = contact1
session.add(assoc)
session.add(contact1)
assoc.contact = first_contact

if row.Email:
contact1.emails.append(Email(email=row.Email, email_type="Primary"))
first_contact.emails.append(
Email(
email=row.Email,
email_type="Primary",
release_status=release_status,
)
)
if row.Phone:
contact1.phones.append(
Phone(phone_number=row.Phone, phone_type="Primary")
first_contact.phones.append(
Phone(
phone_number=row.Phone,
phone_type="Primary",
release_status=release_status,
)
)
if row.CellPhone:
contact1.phones.append(
Phone(phone_number=row.CellPhone, phone_type="Mobile")
first_contact.phones.append(
Phone(
phone_number=row.CellPhone,
phone_type="Mobile",
release_status=release_status,
)
)

if row.MailingAddress:
contact1.addresses.append(
first_contact.addresses.append(
Address(
address_line_1=row.MailingAddress,
city=row.MailCity,
state=row.MailState,
postal_code=row.MailZipCode,
address_type="Mailing",
release_status=release_status,
)
)

contact1.addresses.append(
first_contact.addresses.append(
Address(
address_line_1=row.PhysicalAddress,
city=row.PhysicalCity,
state=row.PhysicalState,
postal_code=row.PhysicalZipCode,
address_type="Physical",
release_status=release_status,
)
)

# TODO: put in guards for null values
if not (row.SecondFirstName or row.SecondLastName) and not row.Company:
session.add(assoc)
session.add(first_contact)
session.commit()

except Exception as e:
print(
f"Skipping second contact for PointID {row.PointID} due to missing name and organization."
)
else:
print(f"Transferring second contact for PointID {row.PointID}")
contact2 = Contact(
name=f"{row.SecondFirstName} {row.SecondLastName}",
role="Owner",
contact_type="Secondary",
organization=row.Company, # Assumes organization applies to both contacts
nma_pk_owners=row.OwnerKey,
f"Skipping first contact for PointID {row.PointID} due to validation error: {e}"
)
from pprint import pprint

pprint(e)
session.rollback()

try:
if row.SecondFirstName is None and row.SecondLastName is None:
name = None
elif row.SecondFirstName is not None and row.SecondLastName is None:
name = row.SecondFirstName
elif row.SecondFirstName is None and row.SecondLastName is not None:
name = row.SecondLastName
else:
name = f"{row.SecondFirstName} {row.SecondLastName}"

second_contact_data = {
"thing_id": thing.id,
"release_status": release_status,
"name": name,
"role": "Owner",
"contact_type": "Secondary",
"organization": row.Company,
"nma_pk_owners": row.OwnerKey,
}

CreateContact.model_validate(second_contact_data)

second_contact_data.pop("thing_id")
second_contact = Contact(**second_contact_data)

assoc = ThingContactAssociation()
assoc.thing = thing
assoc.contact = second_contact

if row.SecondCtctEmail:
contact2.emails.append(
Email(email=row.SecondCtctEmail, email_type="Primary")
second_contact.emails.append(
Email(
email=row.SecondCtctEmail,
email_type="Primary",
release_status=release_status,
)
)

if row.SecondCtctPhone:
contact2.phones.append(
Phone(phone_number=row.SecondCtctPhone, phone_type="Primary")
second_contact.phones.append(
Phone(
phone_number=row.SecondCtctPhone,
phone_type="Primary",
release_status=release_status,
)
)

assoc = ThingContactAssociation()
assoc.thing = thing
assoc.contact = contact2
session.add(assoc)
session.add(contact2)
session.add(second_contact)

session.commit()
except Exception as e:
print(
f"Skipping second contact for PointID {row.PointID} due to validation error: {e}"
)
session.rollback()


# ============= EOF =============================================
9 changes: 6 additions & 3 deletions transfers/well_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from sqlalchemy import select

from db import LocationThingAssociation, Thing, WellScreen, Location
from services.crud_helper import model_adder
from schemas.thing import CreateWellScreen
from services.lexicon_helper import add_lexicon_term
from services.thing_helper import add_thing
Expand Down Expand Up @@ -141,10 +140,14 @@ def transfer_wellscreens(session, limit=None):
# "screen_type": row.ScreenType,
"screen_description": row.ScreenDescription,
"release_status": "draft",
"nma_pk_wellscreens": row.GlobalID,
}
try:
model = CreateWellScreen.model_validate(well_screen_data)
model_adder(session, WellScreen, model)
# TODO: add validation logic here to ensure no overlapping screens for the same well
CreateWellScreen.model_validate(well_screen_data)
well_screen = WellScreen(**well_screen_data)
session.add(well_screen)
session.commit()
except ValidationError as e:
print(f"Validation error for row {i} with PointID {row.PointID}: {e}")
continue
Expand Down
Loading