Skip to content

Commit b6fa6ad

Browse files
authored
Merge pull request #121 from DataIntegrationGroup/jab-well-screen-updates
BDMS 57: well screen & other updates
2 parents 03e4509 + 5dcb5cb commit b6fa6ad

4 files changed

Lines changed: 150 additions & 70 deletions

File tree

db/contact.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,22 @@
2424

2525
class ThingContactAssociation(Base, AutoBaseMixin):
2626
thing_id: Mapped[int] = mapped_column(
27-
Integer, ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
27+
ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
2828
)
2929
contact_id: Mapped[int] = mapped_column(
30-
Integer, ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
30+
ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
3131
)
3232

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

3636

3737
class Contact(Base, AutoBaseMixin, ReleaseMixin):
38-
name: Mapped[str | None] = mapped_column(String(100))
39-
organization: Mapped[str | None] = mapped_column(String(100))
40-
role: Mapped[str] = lexicon_term()
41-
contact_type: Mapped[str] = lexicon_term()
42-
nma_pk_owners: Mapped[str | None] = mapped_column(String(100))
38+
name: Mapped[str] = mapped_column(String(100), nullable=True)
39+
organization: Mapped[str] = mapped_column(String(100), nullable=True)
40+
role: Mapped[str] = lexicon_term(nullable=False)
41+
contact_type: Mapped[str] = lexicon_term(nullable=False)
42+
nma_pk_owners: Mapped[str] = mapped_column(String(100), nullable=True)
4343

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

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

102102
class Address(Base, AutoBaseMixin, ReleaseMixin):
103103
contact_id: Mapped[int] = mapped_column(
104-
Integer, ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
104+
ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
105105
)
106106
address_line_1: Mapped[str] = mapped_column(String(255), nullable=False)
107107
address_line_2: Mapped[str | None] = mapped_column(String(255), nullable=True)
108108
city: Mapped[str] = mapped_column(String(100), nullable=False)
109109
state: Mapped[str] = mapped_column(String(50), nullable=False)
110110
postal_code: Mapped[str] = mapped_column(String(20), nullable=False)
111-
country: Mapped[str] = lexicon_term(nullable=False, default="United States")
111+
country: Mapped[str] = lexicon_term(default="United States", nullable=False)
112112
address_type: Mapped[str] = lexicon_term(nullable=False)
113113

114114
contact: Mapped["Contact"] = relationship(

db/thing.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# ===============================================================================
1616
from sqlalchemy import Integer, ForeignKey, String, Column, Float
1717
from sqlalchemy.ext.associationproxy import association_proxy
18-
from sqlalchemy.orm import relationship, mapped_column
18+
from sqlalchemy.orm import relationship, mapped_column, Mapped
1919
from sqlalchemy_utils import TSVectorType
2020

2121
from db import lexicon_term
@@ -98,22 +98,24 @@ class ThingIdLink(Base, AutoBaseMixin, ReleaseMixin):
9898

9999

100100
class WellScreen(Base, AutoBaseMixin, ReleaseMixin):
101-
thing_id = Column(
102-
Integer, ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
101+
thing_id: Mapped[int] = mapped_column(
102+
ForeignKey("thing.id", ondelete="CASCADE"), nullable=False
103103
)
104-
screen_depth_top = Column(
105-
Float, nullable=False, info={"unit": "feet below ground surface"}
104+
screen_depth_top: Mapped[float] = mapped_column(
105+
info={"unit": "feet below ground surface"}, nullable=True
106106
)
107-
screen_depth_bottom = Column(
108-
Float, nullable=False, info={"unit": "feet below ground surface"}
107+
screen_depth_bottom: Mapped[float] = mapped_column(
108+
info={"unit": "feet below ground surface"}, nullable=True
109109
)
110-
screen_type = lexicon_term() # e.g., "PVC", "Steel", etc.
110+
screen_type: Mapped[str] = lexicon_term(nullable=True) # e.g., "PVC", "Steel", etc.
111111

112-
screen_description = Column(
113-
String(1000), nullable=True, info={"unit": "description of the screen"}
112+
screen_description: Mapped[str] = mapped_column(
113+
String(1000), info={"unit": "description of the screen"}, nullable=True
114114
)
115+
nma_pk_wellscreens: Mapped[str] = mapped_column(String(100), nullable=True)
116+
115117
# Define a relationship to well if needed
116-
thing = relationship("Thing")
118+
thing: Mapped["Thing"] = relationship("Thing")
117119

118120

119121
# TODO: this could be the model used to handle AMP monitoring

transfers/contact_transfer.py

Lines changed: 121 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from transfers.util import read_csv, filter_to_valid_point_ids
1919
from db import Thing, Contact, ThingContactAssociation, Email, Phone, Address
2020

21+
from schemas.contact import CreateContact
22+
2123

2224
def extract_owner_role(comment):
2325
# if comment is None:
@@ -32,6 +34,14 @@ def extract_owner_role(comment):
3234
return "Owner"
3335

3436

37+
"""
38+
Developer's notes
39+
40+
Use Pydantic to perform model validations since all restrictions will
41+
be built into the models
42+
"""
43+
44+
3545
def transfer_contacts(session):
3646

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

4757
# TODO: extract role from OwnerComment
4858
# role = extract_owner_role(row.OwnerComment)
4959
role = "Owner"
60+
release_status = "private"
5061

5162
# TODO: put in guards for null values
52-
# name OR organization must be defined, otherwise skip
53-
if not (row.FirstName or row.LastName) and not row.Company:
54-
print(
55-
f"Skipping first contact for PointID {row.PointID} due to missing name and organization."
56-
)
57-
else:
58-
print(f"Transferring first contact for PointID {row.PointID}")
59-
contact1 = Contact(
60-
name=f"{row.FirstName} {row.LastName}",
61-
role=role,
62-
contact_type="Primary",
63-
organization=row.Company, # assumes organization applies to both contacts
64-
nma_pk_owners=row.OwnerKey,
65-
)
63+
try:
64+
65+
if row.FirstName is None and row.LastName is None:
66+
name = None
67+
elif row.FirstName is not None and row.LastName is None:
68+
name = row.FirstName
69+
elif row.FirstName is None and row.LastName is not None:
70+
name = row.LastName
71+
else:
72+
name = f"{row.FirstName} {row.LastName}"
73+
74+
first_contact_data = {
75+
"thing_id": thing.id,
76+
"release_status": release_status,
77+
"name": name,
78+
"role": role,
79+
"contact_type": "Primary",
80+
"organization": row.Company,
81+
"nma_pk_owners": row.OwnerKey,
82+
}
83+
84+
CreateContact.model_validate(first_contact_data)
85+
86+
first_contact_data.pop("thing_id")
87+
first_contact = Contact(**first_contact_data)
88+
6689
assoc = ThingContactAssociation()
6790
assoc.thing = thing
68-
assoc.contact = contact1
69-
session.add(assoc)
70-
session.add(contact1)
91+
assoc.contact = first_contact
7192

7293
if row.Email:
73-
contact1.emails.append(Email(email=row.Email, email_type="Primary"))
94+
first_contact.emails.append(
95+
Email(
96+
email=row.Email,
97+
email_type="Primary",
98+
release_status=release_status,
99+
)
100+
)
74101
if row.Phone:
75-
contact1.phones.append(
76-
Phone(phone_number=row.Phone, phone_type="Primary")
102+
first_contact.phones.append(
103+
Phone(
104+
phone_number=row.Phone,
105+
phone_type="Primary",
106+
release_status=release_status,
107+
)
77108
)
78109
if row.CellPhone:
79-
contact1.phones.append(
80-
Phone(phone_number=row.CellPhone, phone_type="Mobile")
110+
first_contact.phones.append(
111+
Phone(
112+
phone_number=row.CellPhone,
113+
phone_type="Mobile",
114+
release_status=release_status,
115+
)
81116
)
82117

83118
if row.MailingAddress:
84-
contact1.addresses.append(
119+
first_contact.addresses.append(
85120
Address(
86121
address_line_1=row.MailingAddress,
87122
city=row.MailCity,
88123
state=row.MailState,
89124
postal_code=row.MailZipCode,
90125
address_type="Mailing",
126+
release_status=release_status,
91127
)
92128
)
93129

94-
contact1.addresses.append(
130+
first_contact.addresses.append(
95131
Address(
96132
address_line_1=row.PhysicalAddress,
97133
city=row.PhysicalCity,
98134
state=row.PhysicalState,
99135
postal_code=row.PhysicalZipCode,
100136
address_type="Physical",
137+
release_status=release_status,
101138
)
102139
)
103140

104-
# TODO: put in guards for null values
105-
if not (row.SecondFirstName or row.SecondLastName) and not row.Company:
141+
session.add(assoc)
142+
session.add(first_contact)
143+
session.commit()
144+
145+
except Exception as e:
106146
print(
107-
f"Skipping second contact for PointID {row.PointID} due to missing name and organization."
108-
)
109-
else:
110-
print(f"Transferring second contact for PointID {row.PointID}")
111-
contact2 = Contact(
112-
name=f"{row.SecondFirstName} {row.SecondLastName}",
113-
role="Owner",
114-
contact_type="Secondary",
115-
organization=row.Company, # Assumes organization applies to both contacts
116-
nma_pk_owners=row.OwnerKey,
147+
f"Skipping first contact for PointID {row.PointID} due to validation error: {e}"
117148
)
149+
from pprint import pprint
150+
151+
pprint(e)
152+
session.rollback()
153+
154+
try:
155+
if row.SecondFirstName is None and row.SecondLastName is None:
156+
name = None
157+
elif row.SecondFirstName is not None and row.SecondLastName is None:
158+
name = row.SecondFirstName
159+
elif row.SecondFirstName is None and row.SecondLastName is not None:
160+
name = row.SecondLastName
161+
else:
162+
name = f"{row.SecondFirstName} {row.SecondLastName}"
163+
164+
second_contact_data = {
165+
"thing_id": thing.id,
166+
"release_status": release_status,
167+
"name": name,
168+
"role": "Owner",
169+
"contact_type": "Secondary",
170+
"organization": row.Company,
171+
"nma_pk_owners": row.OwnerKey,
172+
}
173+
174+
CreateContact.model_validate(second_contact_data)
175+
176+
second_contact_data.pop("thing_id")
177+
second_contact = Contact(**second_contact_data)
178+
179+
assoc = ThingContactAssociation()
180+
assoc.thing = thing
181+
assoc.contact = second_contact
182+
118183
if row.SecondCtctEmail:
119-
contact2.emails.append(
120-
Email(email=row.SecondCtctEmail, email_type="Primary")
184+
second_contact.emails.append(
185+
Email(
186+
email=row.SecondCtctEmail,
187+
email_type="Primary",
188+
release_status=release_status,
189+
)
121190
)
191+
122192
if row.SecondCtctPhone:
123-
contact2.phones.append(
124-
Phone(phone_number=row.SecondCtctPhone, phone_type="Primary")
193+
second_contact.phones.append(
194+
Phone(
195+
phone_number=row.SecondCtctPhone,
196+
phone_type="Primary",
197+
release_status=release_status,
198+
)
125199
)
126200

127-
assoc = ThingContactAssociation()
128-
assoc.thing = thing
129-
assoc.contact = contact2
130201
session.add(assoc)
131-
session.add(contact2)
202+
session.add(second_contact)
132203

133-
session.commit()
204+
except Exception as e:
205+
print(
206+
f"Skipping second contact for PointID {row.PointID} due to validation error: {e}"
207+
)
208+
session.rollback()
134209

135210

136211
# ============= EOF =============================================

transfers/well_transfer.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from sqlalchemy import select
2222

2323
from db import LocationThingAssociation, Thing, WellScreen, Location
24-
from services.crud_helper import model_adder
2524
from schemas.thing import CreateWellScreen
2625
from services.lexicon_helper import add_lexicon_term
2726
from services.thing_helper import add_thing
@@ -141,10 +140,14 @@ def transfer_wellscreens(session, limit=None):
141140
# "screen_type": row.ScreenType,
142141
"screen_description": row.ScreenDescription,
143142
"release_status": "draft",
143+
"nma_pk_wellscreens": row.GlobalID,
144144
}
145145
try:
146-
model = CreateWellScreen.model_validate(well_screen_data)
147-
model_adder(session, WellScreen, model)
146+
# TODO: add validation logic here to ensure no overlapping screens for the same well
147+
CreateWellScreen.model_validate(well_screen_data)
148+
well_screen = WellScreen(**well_screen_data)
149+
session.add(well_screen)
150+
session.commit()
148151
except ValidationError as e:
149152
print(f"Validation error for row {i} with PointID {row.PointID}: {e}")
150153
continue

0 commit comments

Comments
 (0)