Skip to content

Commit 596f37f

Browse files
Merge pull request #534 from DataIntegrationGroup/BDMS-565-enhance-name-generation-logic-to-use-OwnerKey-as-fallback
fix: enhance name generation logic to use OwnerKey as fallback
2 parents 7b128dd + 727bea1 commit 596f37f

3 files changed

Lines changed: 168 additions & 7 deletions

File tree

schemas/contact.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class CreateContact(BaseCreateModel, ValidateContact):
150150
organization: str | None = None
151151
role: Role
152152
contact_type: ContactType = "Primary"
153+
nma_pk_owners: str | None = None
153154
# description: str | None = None
154155
# email: str | None = None
155156
# phone: str | None = None

tests/transfers/test_contact_with_multiple_wells.py

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@
1414
# limitations under the License.
1515
# ===============================================================================
1616

17-
from db import ThingContactAssociation, Thing, Notes
17+
from types import SimpleNamespace
18+
from uuid import uuid4
19+
20+
from db import ThingContactAssociation, Thing, Notes, Contact
1821
from db.engine import session_ctx
19-
from transfers.contact_transfer import ContactTransfer
22+
from transfers.contact_transfer import ContactTransfer, _add_first_contact
2023
from transfers.well_transfer import WellTransferer
2124

2225

2326
def _run_contact_transfer(pointids: list[str]):
2427
wt = WellTransferer(pointids=pointids)
25-
wt.transfer()
28+
wt.transfer_parallel()
2629

2730
ct = ContactTransfer(pointids=pointids)
2831
ct.transfer()
@@ -87,4 +90,131 @@ def test_owner_comment_absent_skips_notes():
8790
assert note_count == 0
8891

8992

93+
def test_ownerkey_fallback_name_when_name_and_org_missing(water_well_thing):
94+
with session_ctx() as sess:
95+
thing = sess.get(Thing, water_well_thing.id)
96+
row = SimpleNamespace(
97+
FirstName=None,
98+
LastName=None,
99+
OwnerKey="Fallback OwnerKey Name",
100+
Email=None,
101+
CtctPhone=None,
102+
Phone=None,
103+
CellPhone=None,
104+
StreetAddress=None,
105+
Address2=None,
106+
City=None,
107+
State=None,
108+
Zip=None,
109+
MailingAddress=None,
110+
MailCity=None,
111+
MailState=None,
112+
MailZipCode=None,
113+
PhysicalAddress=None,
114+
PhysicalCity=None,
115+
PhysicalState=None,
116+
PhysicalZipCode=None,
117+
)
118+
119+
# Should not raise "Either name or organization must be provided."
120+
contact = _add_first_contact(
121+
sess, row=row, thing=thing, organization=None, added=[]
122+
)
123+
sess.flush()
124+
125+
assert contact is not None
126+
assert contact.name == "Fallback OwnerKey Name"
127+
assert contact.organization is None
128+
129+
130+
def test_ownerkey_dedupes_when_fallback_name_differs(water_well_thing):
131+
owner_key = f"OwnerKey-{uuid4()}"
132+
with session_ctx() as sess:
133+
first_thing = sess.get(Thing, water_well_thing.id)
134+
second_thing = Thing(
135+
name=f"Second Well {uuid4()}",
136+
thing_type="water well",
137+
release_status="draft",
138+
)
139+
sess.add(second_thing)
140+
sess.flush()
141+
142+
complete_row = SimpleNamespace(
143+
FirstName="Casey",
144+
LastName="Owner",
145+
OwnerKey=owner_key,
146+
Email=None,
147+
CtctPhone=None,
148+
Phone=None,
149+
CellPhone=None,
150+
StreetAddress=None,
151+
Address2=None,
152+
City=None,
153+
State=None,
154+
Zip=None,
155+
MailingAddress=None,
156+
MailCity=None,
157+
MailState=None,
158+
MailZipCode=None,
159+
PhysicalAddress=None,
160+
PhysicalCity=None,
161+
PhysicalState=None,
162+
PhysicalZipCode=None,
163+
)
164+
fallback_row = SimpleNamespace(
165+
FirstName=None,
166+
LastName=None,
167+
OwnerKey=owner_key,
168+
Email=None,
169+
CtctPhone=None,
170+
Phone=None,
171+
CellPhone=None,
172+
StreetAddress=None,
173+
Address2=None,
174+
City=None,
175+
State=None,
176+
Zip=None,
177+
MailingAddress=None,
178+
MailCity=None,
179+
MailState=None,
180+
MailZipCode=None,
181+
PhysicalAddress=None,
182+
PhysicalCity=None,
183+
PhysicalState=None,
184+
PhysicalZipCode=None,
185+
)
186+
187+
added = []
188+
first_contact = _add_first_contact(
189+
sess, row=complete_row, thing=first_thing, organization=None, added=added
190+
)
191+
assert first_contact is not None
192+
assert first_contact.name == "Casey Owner"
193+
194+
second_contact = _add_first_contact(
195+
sess, row=fallback_row, thing=second_thing, organization=None, added=added
196+
)
197+
sess.flush()
198+
199+
# Reused existing contact; no duplicate fallback-name contact created.
200+
assert second_contact is None
201+
contacts = (
202+
sess.query(Contact)
203+
.filter(
204+
Contact.nma_pk_owners == owner_key,
205+
Contact.contact_type == "Primary",
206+
)
207+
.all()
208+
)
209+
assert len(contacts) == 1
210+
assert contacts[0].name == "Casey Owner"
211+
212+
assoc_count = (
213+
sess.query(ThingContactAssociation)
214+
.filter(ThingContactAssociation.contact_id == contacts[0].id)
215+
.count()
216+
)
217+
assert assoc_count == 2
218+
219+
90220
# ============= EOF =============================================

transfers/contact_transfer.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def _add_first_contact(
232232
role = "Owner"
233233
release_status = "private"
234234

235-
name = _make_name(row.FirstName, row.LastName)
235+
name = _safe_make_name(row.FirstName, row.LastName, row.OwnerKey, organization)
236236

237237
contact_data = {
238238
"thing_id": thing.id,
@@ -326,6 +326,19 @@ def _add_first_contact(
326326
return contact
327327

328328

329+
def _safe_make_name(
330+
first: str | None, last: str | None, ownerkey: str, organization: str | None
331+
) -> str | None:
332+
name = _make_name(first, last)
333+
if name is None and organization is None:
334+
logger.warning(
335+
f"Missing both first and last name and organization for OwnerKey {ownerkey}; "
336+
f"using OwnerKey as fallback name."
337+
)
338+
return ownerkey
339+
return name
340+
341+
329342
def _add_second_contact(
330343
session: Session, row: pd.Series, thing: Thing, organization: str, added: list
331344
) -> None:
@@ -463,14 +476,31 @@ def _make_contact_and_assoc(
463476
session: Session, data: dict, thing: Thing, added: list
464477
) -> tuple[Contact, bool]:
465478
new_contact = True
466-
if (data["name"], data["organization"]) in added:
479+
contact = None
480+
481+
# Prefer OwnerKey-based dedupe so fallback names don't split the same owner
482+
# into multiple contacts when some rows have real names and others do not.
483+
owner_key = data.get("nma_pk_owners")
484+
contact_type = data.get("contact_type")
485+
if owner_key and contact_type:
486+
contact = (
487+
session.query(Contact)
488+
.filter_by(nma_pk_owners=owner_key, contact_type=contact_type)
489+
.first()
490+
)
491+
if contact is not None:
492+
new_contact = False
493+
494+
if contact is None and (data["name"], data["organization"]) in added:
467495
contact = (
468496
session.query(Contact)
469497
.filter_by(name=data["name"], organization=data["organization"])
470498
.first()
471499
)
472-
new_contact = False
473-
else:
500+
if contact is not None:
501+
new_contact = False
502+
503+
if contact is None:
474504

475505
from schemas.contact import CreateContact
476506

0 commit comments

Comments
 (0)