ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/ns_dev/Python/NinoCode/Active_prgs/Redgrave/ATT_MasterAttorneyList.py
Revision: 973
Committed: Thu Feb 12 19:55:01 2026 UTC (6 weeks, 2 days ago) by nino.borges
Content type: text/x-python
File size: 33241 byte(s)
Log Message:
This version adds support for the return_str_string, making a Relatiity STR string from a particular name.

File Contents

# User Rev Content
1 nino.borges 913 """
2    
3     ATT_MasterAttorneyList
4    
5     Created by:
6     Emanuel Borges
7     07.14.2025
8    
9     A library for the creation and management of the ATT MAL.
10    
11     """
12    
13     import os, uuid, pickle, re
14 nino.borges 951 #import dill as pickle
15 nino.borges 913 from dataclasses import dataclass, field, fields
16     from typing import List, Tuple, Optional
17     from collections import namedtuple
18     from win32com.client import Dispatch
19    
20    
21 nino.borges 973 version = "1.3"
22 nino.borges 913
23     @dataclass
24     class Person:
25     first_name: Optional[str] = None
26     last_name: Optional[str] = None
27     alt_first_name: Optional[str] = None
28     ## TODO: Make these email addresses lists instead of strings
29     work_email_addresses: Optional[str] = None
30     #alt_work_email_addresses: Optional[str] = None
31     raw_email_addresses: Optional[str] = None
32     _id: uuid.UUID = field(default_factory=uuid.uuid4)
33     is_attorney: Optional[str] = None
34     split_role_date_range: Optional[str] = None
35     middle_initial: Optional[str] = None
36     company: Optional[str] = None
37     category: Optional[str] = None
38     job_title: Optional[str] = None
39     full_name_preferred: Optional[str] = None
40 nino.borges 916 lh_normalized_name: Optional[str] = None
41 nino.borges 913 user_id: Optional[str] = None
42     full_name_overide: Optional[str] = None
43     ## Only gather unique_attorney_row_number from the attorney and split role attorney tabs. NEVER from Non-Attorneys.
44     unique_attorney_row_number:Optional[str] = None
45     ## Will be saving this as a list of tuple pairs (startdate,enddate). Allowing None for now but may update this to forcing an empty list, to avoid mutable default issues.
46     dates_as_counsel:Optional[List[Tuple[str,str]]] = None
47    
48     def __post_init__(self):
49     """Convert all string fields to uppercase."""
50     if self.first_name:
51     self.first_name = self.first_name.strip().upper()
52     if self.alt_first_name:
53     self.alt_first_name = self.alt_first_name.strip().upper()
54     if self.last_name:
55     self.last_name = self.last_name.strip().upper()
56     if self.work_email_addresses:
57     self.work_email_addresses = self.work_email_addresses.strip().upper()
58     ## if self.alt_work_email_addresses:
59     ## self.alt_work_email_addresses = self.alt_work_email_addresses.strip().upper()
60     if self.raw_email_addresses:
61     self.raw_email_addresses = self.raw_email_addresses.strip().upper()
62     if self.is_attorney:
63     self.is_attorney = self.is_attorney.strip().upper()
64     if self.split_role_date_range:
65     self.split_role_date_range = self.split_role_date_range.strip().upper()
66     if self.middle_initial:
67     self.middle_initial = self.middle_initial.strip().upper()
68     if self.company:
69     self.company = self.company.strip().upper()
70     if self.category:
71     self.category = self.category.strip().upper()
72     if self.job_title:
73     self.job_title = self.job_title.strip().upper()
74     if self.full_name_preferred:
75     self.full_name_preferred = self.full_name_preferred.strip().upper()
76 nino.borges 916 if self.lh_normalized_name:
77     self.lh_normalized_name = self.lh_normalized_name.strip().upper()
78 nino.borges 913 if self.user_id:
79     self.user_id = self.user_id.strip().upper()
80    
81    
82     @dataclass
83     class PeopleList:
84     people: List[Person] = field(default_factory=list)
85    
86     def add_person(self, person: Person):
87     self.people.append(person)
88     #print(f"Added person: {person}")
89    
90    
91     def search_by_email(self, emailAddress:str) -> Optional[Person]:
92     """Returns the first matching emailAddress value. Assumes emailAddresses are unique"""
93     for person in self.people:
94 nino.borges 951 if person.work_email_addresses:
95     #print(person.work_email_addresses)
96     personEmailAddresses = person.work_email_addresses.split(";\n")
97     personEmailAddresses = [x.strip() for x in personEmailAddresses]
98     if emailAddress in personEmailAddresses:
99     #if person.work_email_addresses == emailAddress:
100     return person
101     ## elif person.alt_work_email_addresses == emailAddress:
102     ## return person
103 nino.borges 913 return None
104    
105 nino.borges 951 def search_by_login_id(self, loginID:str) -> Optional[Person]:
106     """Returns the first matching login user id value. Assumes login user ids are unique"""
107     for person in self.people:
108     if person.user_id:
109     personLoginIds = person.user_id.split(";\n")
110     personLoginIds = [x.strip() for x in personLoginIds]
111     if loginID in personLoginIds:
112     return person
113     return None
114 nino.borges 913
115     def search_by_unique_attorney_row_number(self,uniqueAttorneyRowNumber:str) -> Optional[Person]:
116     """Returns the first matching uniqueAttorneyRowNumber value. Assumes uniqueAttorneyRowNumbers are unique"""
117     for person in self.people:
118     if person.unique_attorney_row_number == uniqueAttorneyRowNumber:
119     return person
120     return None
121    
122     def search_by_id(self, idNumber):
123     """Returns the first matching idNumber value. Must be in format UUID('7414f78c-8289-4c9f-bd49-a5aaac35545f')."""
124     for person in self.people:
125     if person._id == idNumber:
126     return person
127     return None
128    
129     def return_list_of_matching_values(self,fieldName, value:str):
130     """Returns a full list of items where value is found in fieldName"""
131     matchingPeopleList = []
132     for person in self.people:
133 nino.borges 916 personVals = getattr(person,fieldName)
134     if personVals:
135     personVals = personVals.split(";\n")
136     personVals = [x.strip() for x in personVals]
137     if value in personVals:
138     matchingPeopleList.append(person)
139 nino.borges 913 return matchingPeopleList
140    
141     def list_people(self):
142     for person in self.people:
143     print(person)
144    
145     def update_full_Name_overide(self, emailAddress:str, fullNameOverideValue) -> Optional[Person]:
146     valueUpdated = False
147     for person in self.people:
148     if person.work_email_addresses == emailAddress.upper():
149     person.full_name_overide = fullNameOverideValue.upper()
150     valueUpdated = True
151     ## Give a quik warning as you add the override value into the database if the last name differs.
152     if "," in fullNameOverideValue:
153     lastName = fullNameOverideValue.split(",")[0]
154     else:
155     lastName = fullNameOverideValue.split(" ")[-1]
156     if lastName.upper() == person.last_name:
157     pass
158     else:
159     print(f"WARNING: Overide last name value {lastName.upper()} does not match {person.last_name}.")
160     if valueUpdated == False:
161     print(f"WARNING: No email address match for {emailAddress} found.")
162    
163 nino.borges 916 def return_person_all_name_variations(self, person):
164     """This will take a matched person and return a large list of all of the possible full name variations"""
165 nino.borges 951 last = person.last_name.strip() if person.last_name else None
166     firsts = [person.first_name.strip() if person.first_name else None]
167 nino.borges 916 if person.alt_first_name:
168     firsts.append(person.alt_first_name.strip())
169    
170     middle = person.middle_initial.replace(".","").strip() if person.middle_initial else None
171    
172     combos = set() ## Using a set here to avoid dupes.
173    
174     for first in firsts:
175     ## Some basic combinations
176     combos.add(f"{first} {last}")
177     combos.add(f"{last} {first}")
178     combos.add(f"{last}, {first}")
179    
180     ## Include middle initial variations if it exists
181     if middle:
182     combos.add(f"{first} {middle} {last}")
183     combos.add(f"{last} {first} {middle}")
184     combos.add(f"{last}, {first} {middle}")
185     combos.add(f"{first} {middle}. {last}")
186     combos.add(f"{last} {first} {middle}.")
187     combos.add(f"{last}, {first} {middle}.")
188    
189 nino.borges 951 fNamePrefered = person.full_name_preferred
190     if fNamePrefered:
191     fNamePrefered = fNamePrefered.split(";\n")
192     fNamePrefered = [x.strip() for x in fNamePrefered]
193     combos.update(fNamePrefered)
194     if person.lh_normalized_name:
195     combos.add(person.lh_normalized_name.strip())
196     ## Want to add the LH version of the name without the ESQ here.
197     combos.add(person.lh_normalized_name.upper().replace("(ESQ.)","").strip())
198 nino.borges 916 return list(combos)
199    
200 nino.borges 973
201     def return_str_string(self, person):## TODO: add similar names support soon includeSimilarNamesExclusion = True):
202     """returns the STR search string for a given person."""
203     namesList = []
204     #emailAddrList = []
205     emailAddrSet = set()
206     firstNamesSet = set()
207     lastNamesSet = set()
208    
209     if person.last_name:
210     lastNamesSet.add(person.last_name)
211     ## if person.alt_surnames:
212     ## for lastName in person.alt_surnames.split(";\n"):
213     ## lastNamesSet.add(lastName)
214     if person.first_name:
215     firstNamesSet.add(person.first_name)
216     if person.alt_first_name:
217     firstNamesSet.add(person.alt_first_name)
218     if person.full_name_preferred:
219     person.full_name_preferred = person.full_name_preferred.replace('(SHE, HER)',"(SHE HER)")
220     person.full_name_preferred = person.full_name_preferred.replace(",(LEGAL),"," (LEGAL),")
221     if "," in person.full_name_preferred:
222     parsedLast, parsedFirst = person.full_name_preferred.split(",", 1)
223     else:
224     parsedLast = person.full_name_preferred.split(" ")[-1]
225     parsedFirst = person.full_name_preferred.split(" ")[:-1]
226     parsedFirst = " ".join(parsedFirst)
227     lastNamesSet.add(parsedLast.strip())
228     firstNamesSet.add(parsedFirst.strip())
229    
230     ## within all values in both the first name and last name sets, replace any open or close parenthesis with a space.
231     firstNamesSet = {fn.replace("("," ").replace(")"," ") for fn in firstNamesSet}
232     lastNamesSet = {ln.replace("("," ").replace(")"," ") for ln in lastNamesSet}
233    
234     if len(lastNamesSet) >1:
235     namesList.append(f'({" OR ".join((lastNamesSet))})')
236     elif len(lastNamesSet) == 1:
237     namesList.append(list(lastNamesSet)[0])
238     if len(firstNamesSet) >1:
239     namesList.append(f'({" OR ".join((firstNamesSet))})')
240     elif len(firstNamesSet) == 1:
241     namesList.append(list(firstNamesSet)[0])
242    
243    
244     withinWordCount = len(str(firstNamesSet).split(" ")) + len(str(lastNamesSet).split(" "))
245     withinPhrase = f' W/{str(withinWordCount)} '
246    
247    
248     ## Assembling the list of email addresses and possibly adding the international domain parts.
249     if person.work_email_addresses:
250     emailAddrSet.add(person.work_email_addresses)
251     ## if includeInternationalDomains:
252     ## ## They elected to add the additional international domain parts.
253     ## for addr in list(emailAddrSet):
254     ## addrDomain = addr.split("@")[-1]
255     ## if addrDomain in self.internationalEmailDomainsSet:
256     ## for intDomain in self.internationalEmailDomainsSet:
257     ## emailAddrSet.add(addr.replace(addrDomain, intDomain))
258    
259    
260     emailAddrList = list(emailAddrSet)
261    
262     if namesList:
263     if emailAddrList:
264     if len(emailAddrList) >1:
265     strText = f"({withinPhrase.join(namesList)}) OR ({' OR '.join(emailAddrList)})"
266     else:
267     strText = f"({withinPhrase.join(namesList)}) OR {' OR '.join(emailAddrList)}"
268     else:
269     strText = f"{withinPhrase.join(namesList)}"
270     else:
271     if emailAddrList:
272     strText = f"{' OR '.join(emailAddrList)}"
273     else:
274     strText = "NONE"
275    
276     if person.user_id:
277     ## Only consider the login if it's longer than 4 characters. We may adjust this cutoff to more than 5 in the future.
278     if len(person.user_id) > 4:
279     if person.user_id in str(namesList):
280     pass
281     else:
282     strText = strText + f" OR {person.user_id.lower()}"
283    
284    
285     ## if includeSimilarNamesExclusion:
286     ## ## This will check the similar_names value for the person and will add the NOT clause at the end.
287     ## if person.similar_names:
288     ## #print("Similar names for this person exists")
289     ## similarNamesEmailList = []
290     ## similarNamesIDList = person.similar_names
291     ## for sID in similarNamesIDList:
292     ## sPerson = self.search_by_id(sID)
293     ## if sPerson.work_email_address:
294     ## similarNamesEmailList.append(sPerson.work_email_address)
295     ## if sPerson.alt_work_email_address:
296     ## similarNamesEmailList.append(sPerson.alt_work_email_address)
297     ## strText = f"({strText}) NOT W/2 ({' OR '.join(similarNamesEmailList)})"
298    
299     return strText
300    
301 nino.borges 913 class ATT_MasterAttorneyList(object):
302     """A class for building and performing functions against the ATT Master Attorney List."""
303     version = '0.01.0'
304    
305    
306     def __init__(self, masterAttorneyListFileName,fullNameOveridesFileName = False, forceNewPklFile = False, Encoding = 'UTF8'):
307     """Assumes the MAL is a spreadsheet (for now).MAL gets saved to a pkl file for performance reasons. pkl will be used unless forceNewPklFile is set to true"""
308     pklFileName = os.path.splitext(masterAttorneyListFileName)[0] + ".pkl"
309    
310     print("Initializing data structures...")
311     if forceNewPklFile:
312     print("Creating MAL structure...")
313     self.malPeopleList = PeopleList()
314     self.__IngestMALSpreadsheet(masterAttorneyListFileName)
315     print("MAL structure created.")
316     if fullNameOveridesFileName:
317     print("Loading full name overide values...")
318     self.__LoadFullNameOverideValues(fullNameOveridesFileName)
319     print("Full name overide values loaded.")
320     print("Creating pickle backup...")
321     self.__SaveMalToPkl(pklFileName)
322     print("Pickle backup created.")
323     else:
324     if os.path.exists(pklFileName):
325     print("Loading MAL structure from pickle file...")
326     self.malPeopleList = self.__LoadMalFromPkl(pklFileName)
327     print("MAL structure loaded.")
328     else:
329     print("Pickle file doesnt exist.")
330     print("Creating MAL structure...")
331     self.malPeopleList = PeopleList()
332     self.__IngestMALSpreadsheet(masterAttorneyListFileName)
333     print("MAL structure created.")
334     if fullNameOveridesFileName:
335     print("Loading full name overide values...")
336     self.__LoadFullNameOverideValues(fullNameOveridesFileName)
337     print("Full name overide values loaded.")
338     print("Creating pickle backup...")
339     self.__SaveMalToPkl(pklFileName)
340     print("Pickle backup created.")
341    
342    
343     def __IngestMALSpreadsheet(self, masterAttorneyListFileName):
344     """Pseudo-private method which will open an Excel spreadsheet and ingest the values into the peoplelist dataclass."""
345     ## There doenst seem to be a consistent value in the "row" column in the MAL, so setting these parameters here to avoid gap issues.
346    
347     ## excelTabParametersList should always be an ordered list because now order matters.
348 nino.borges 973 excelTabParametersList = [{"tabName":"Attorneys", "beginRowNumber":2, "endRowNumber":2534, "beginColNumber":1, "endColNumber":19},
349     {"tabName":"Non-Attorneys", "beginRowNumber":2, "endRowNumber":3435, "beginColNumber":1, "endColNumber":19},
350 nino.borges 913 {"tabName":"Split Role Attorneys", "beginRowNumber":2, "endRowNumber":21, "beginColNumber":1, "endColNumber":10}]
351    
352    
353     # spreadsheetFileMappingMatrix = {"First Name":"first_name", "Last Name":"last_name", "Work Email":"work_email_address", "Alt Work Email":"alt_work_email_address", "Is Attorney": "is_attorney",
354     # "Split Role - Attorney Capacity Date Range":"split_role_date_range", " Validated by OC??":"sidley_validated", "Category": "category", "Organization":"organization", "Job Title":"job_title",
355     # "Business Title":"business_title", "Full Name (Preferred)":"full_name_preferred", "Login":"login", "Department (Fine)":"department_fine", "Addressed during CAAG":"addressed_during_caag",
356     # "Last Updated":"last_updated"}
357    
358     xlApp = Dispatch('Excel.Application')
359     xlBook = xlApp.Workbooks.Open(masterAttorneyListFileName)
360    
361     for excelTab in excelTabParametersList:
362     sht = xlBook.Worksheets(excelTab['tabName'])
363     print(f"Ingesting sheet {excelTab['tabName']}.")
364     excelFieldPositionMatrix = {}
365     for col in range (excelTab['beginColNumber'], excelTab['endColNumber'] +1):
366     excelFieldPositionMatrix[sht.Cells(1,col).Value] = col
367     for row in range(excelTab['beginRowNumber'], excelTab['endRowNumber'] +1):
368     #print(row)
369     ## TODO: Refactor the excelTabParametersList later. Didnt realize columns were not consistent.
370     if excelTab['tabName'] == 'Attorneys':
371     self.malPeopleList.add_person(Person(is_attorney = sht.Cells(row,excelFieldPositionMatrix['Is Attorney']).Value,
372     #split_role_date_range = sht.Cells(row,excelFieldPositionMatrix['Split Role - Dates as Counsel']).Value,
373     company = sht.Cells(row,excelFieldPositionMatrix['Company']).Value,
374     category = sht.Cells(row,excelFieldPositionMatrix['Category']).Value,
375     last_name = sht.Cells(row,excelFieldPositionMatrix['Last Name']).Value,
376     first_name = sht.Cells(row,excelFieldPositionMatrix['First Name']).Value,
377     alt_first_name = sht.Cells(row,excelFieldPositionMatrix['Alt First Name']).Value,
378     middle_initial = sht.Cells(row,excelFieldPositionMatrix['Middle Initial']).Value,
379     work_email_addresses = sht.Cells(row,excelFieldPositionMatrix['Email Addresses']).Value,
380     #alt_work_email_address = sht.Cells(row,excelFieldPositionMatrix['Alt Work Email']).Value,
381     raw_email_addresses = sht.Cells(row,excelFieldPositionMatrix['EmailAddress RAW']).Value,
382     job_title = sht.Cells(row,excelFieldPositionMatrix['Title']).Value,
383     full_name_preferred = sht.Cells(row,excelFieldPositionMatrix['Full Name (Preferred)']).Value,
384 nino.borges 916 lh_normalized_name = sht.Cells(row,excelFieldPositionMatrix['Lighthouse Normalized Name']).Value,
385 nino.borges 913 user_id = sht.Cells(row,excelFieldPositionMatrix['UserID']).Value,
386 nino.borges 916 unique_attorney_row_number = sht.Cells(row,excelFieldPositionMatrix['MAL ID']).Value))
387 nino.borges 913
388     elif excelTab['tabName'] == 'Non-Attorneys':
389     ## Make sure to NOT grab the unique attorney row number from here
390     self.malPeopleList.add_person(Person(is_attorney = sht.Cells(row,excelFieldPositionMatrix['Is Attorney']).Value,
391     #split_role_date_range = sht.Cells(row,excelFieldPositionMatrix['Split Role - Dates as Counsel']).Value,
392     company = sht.Cells(row,excelFieldPositionMatrix['Company']).Value,
393     category = sht.Cells(row,excelFieldPositionMatrix['Category']).Value,
394     last_name = sht.Cells(row,excelFieldPositionMatrix['Last Name']).Value,
395     first_name = sht.Cells(row,excelFieldPositionMatrix['First Name']).Value,
396     alt_first_name = sht.Cells(row,excelFieldPositionMatrix['Alt First Name']).Value,
397     middle_initial = sht.Cells(row,excelFieldPositionMatrix['Middle Initial']).Value,
398     work_email_addresses = sht.Cells(row,excelFieldPositionMatrix['Email Addresses']).Value,
399     #alt_work_email_address = sht.Cells(row,excelFieldPositionMatrix['Alt Work Email']).Value,
400     raw_email_addresses = sht.Cells(row,excelFieldPositionMatrix['EmailAddress RAW']).Value,
401     job_title = sht.Cells(row,excelFieldPositionMatrix['Title']).Value,
402     full_name_preferred = sht.Cells(row,excelFieldPositionMatrix['Full Name (Preferred)']).Value,
403 nino.borges 916 lh_normalized_name = sht.Cells(row,excelFieldPositionMatrix['Lighthouse Normalized Name']).Value,
404 nino.borges 913 user_id = sht.Cells(row,excelFieldPositionMatrix['UserID']).Value,
405 nino.borges 916 unique_attorney_row_number = sht.Cells(row,excelFieldPositionMatrix['MAL ID']).Value))
406 nino.borges 913 elif excelTab['tabName'] == 'Split Role Attorneys':
407     ## Skip this tab for now.
408     pass
409     ## unique_attorney_row_number = sht.Cells(row,excelFieldPositionMatrix['Attorney Row']).Value
410     ## matchedPerson = self.malPeopleList.search_by_unique_attorney_row_number(unique_attorney_row_number)
411     ## if matchedPerson:
412     ##
413     ## ## dates_as_counsel should always be a two string value tuple (startdate,enddate).
414     ## datesAsCounselValue = sht.Cells(row,excelFieldPositionMatrix['Dates as Counsel']).Value
415     ## datesAsCounselList = []
416     ## ## First get rid of any extra data that is on a new line. Note that they shouldnt be seperating the date ranges by newline.
417     ## datesAsCounselValue = datesAsCounselValue.split("\n")[0]
418     ## ## Next split the ranges correctly by semicolon
419     ## dateRanges = datesAsCounselValue.split(";")
420     ## for dateRange in dateRanges:
421     ## ## Split out the start and end, allowing non-date words. (current, present, etc) however force these to be uppercase.
422     ## counselStartDate, counselEndDate = dateRange.split("-")
423     ## counselStartDate = counselStartDate.upper().strip()
424     ## counselEndDate = counselEndDate.upper().strip()
425     ## datesAsCounselList.append((counselStartDate,counselEndDate))
426     ## matchedPerson.dates_as_counsel = datesAsCounselList
427    
428     else:
429     print(f"ERROR UNKNOWN TAB! {excelTab['tabName']} HAVE NEEDED TAB NAMES CHANGED?")
430    
431    
432     xlBook.Close()
433    
434    
435    
436     def __SaveMalToPkl(self, pklFileName):
437     """Pseudo-private method which will save the current MAL people list object to a pkl file, for performance reasons."""
438     outputFile = open(pklFileName,'wb')
439     pickle.dump(self.malPeopleList,outputFile)
440     outputFile.close()
441    
442     def __LoadMalFromPkl(self, pklFileName):
443     """Pseudo-private method which will load a MAL people list object from a pkl file, for performance reasons."""
444     contents = open(pklFileName, 'rb')
445     obj = pickle.load(contents)
446     contents.close()
447     return obj
448    
449     def __LoadFullNameOverideValues(self, fullNameOveridesFileName):
450     """Pseudo-private method which will update the MAL people list object with the full name overide values."""
451     contents = open(fullNameOveridesFileName).readlines()
452     for line in contents:
453     line = line.replace("\n","")
454     emailAddress,fullNameOverideValue = line.split("|")
455    
456     self.malPeopleList.update_full_Name_overide(emailAddress, fullNameOverideValue)
457    
458     def SmartDedupeSet(self, currentSet):
459     """A method that attempts to do some additional deduplication of the values in a set by lowering all values and deduplicating. Returns a lowered deduplicated set."""
460     newSet = set()
461     for val in currentSet:
462     newSet.add(val.lower())
463     return newSet
464    
465    
466    
467     def RunMalEmailAddressIntegrityCheck(self):
468     """This method performs an integrity check on the MAL by analyzing and looking for duplicate email addresses."""
469     emailTestMatrix = {}
470     altTestMatrix = {}
471     print("Performing MAL email address integrity check...")
472     for i in range(0,len(self.malPeopleList.people)):
473     #altAddr = self.malPeopleList.people[i].alt_work_email_address
474     altAddr = None
475 nino.borges 916 ## Right now workaddrs are stored as a string that you need to parse to use.
476     workAddrs = self.malPeopleList.people[i].work_email_addresses
477 nino.borges 913 if altAddr != None:
478     altAddr = altAddr.strip()
479     if altAddr in list(emailTestMatrix.keys()):
480     print(f"ISSUE:{altAddr} is a dupe of an workAddr.")
481     if altAddr in list(altTestMatrix.keys()):
482     print(f"ISSUE:{altAddr} is a dupe!")
483     else:
484     altTestMatrix[altAddr] = 1
485 nino.borges 916 if workAddrs != None:
486     workAddrs = [w.upper().strip() for w in workAddrs.split(";\n")]
487     for workAddr in workAddrs:
488     workAddr = workAddr.strip()
489     if workAddr in list(altTestMatrix.keys()):
490     print(f"ISSUE:{workAddr} is a dupe of an altAddr.")
491     if workAddr in list(emailTestMatrix.keys()):
492     print(f"ISSUE:{workAddr} is a dupe!")
493     else:
494     emailTestMatrix[workAddr] = 1
495 nino.borges 913 print("\nEmail address integrity check complete.\n\n")
496    
497    
498 nino.borges 951 def RunMalLoginIdIntegrityCheck(self):
499     """This method performs an integrity check on the MAL by analyzing and looking for duplicate User Login ID values."""
500     loginIdTestMatrix = {}
501     print("Performing MAL user login ID integrity check...")
502     for i in range(0,len(self.malPeopleList.people)):
503     loginIds = self.malPeopleList.people[i].user_id
504     if loginIds != None:
505     loginIds = [w.upper().strip() for w in loginIds.split(";\n")]
506     for loginId in loginIds:
507     loginId = loginId.strip()
508     if loginId in list(loginIdTestMatrix.keys()):
509     print(f"ISSUE:{loginId} is a dupe!")
510     else:
511     loginIdTestMatrix[loginId] = 1
512     print("\nUser Login ID integrity check complete.\n\n")
513    
514 nino.borges 913 if __name__ == '__main__':
515 nino.borges 973 masterAttorneyListFileName = r"C:\Users\eborges\OneDrive - Redgrave LLP\Documents\Cases\AT&T\Cybersecurity FCA Case\_ATT_Current_MAL\RG - ATT Cross-Matter Master Attorney List (20260116)(20260116-0530).xlsx"
516 nino.borges 913 attMal = ATT_MasterAttorneyList(masterAttorneyListFileName)
517 nino.borges 951 test = attMal.malPeopleList.search_by_email('ahickey@mayerbrown.com'.upper())
518 nino.borges 916 #test = attMal.malPeopleList.search_by_email('LMENRIQUEZ@DIRECTV.COM')
519 nino.borges 951 #test = attMal.malPeopleList.return_list_of_matching_values('work_email_addresses','LMENRIQUEZ@DIRECTV.COM')
520     #test = attMal.malPeopleList.search_by_login_id('JS6637')
521 nino.borges 916 print(test)
522 nino.borges 951 print(attMal.malPeopleList.return_person_all_name_variations(test))
523 nino.borges 973 attMal.RunMalEmailAddressIntegrityCheck()
524     attMal.RunMalLoginIdIntegrityCheck()
525 nino.borges 913 ## emailCheckList = []
526     ## contents = open(r"C:\Test_Dir\ATT\JoshVerifyPrivResponse\Not_Verified_by_ATT.txt").readlines()
527     ## for line in contents:
528     ## line = line.replace("\n","")
529     ## emailCheckList.append(line)
530     ## print(f"There are {len(emailCheckList)} email addresses to check.")
531     ## notFoundList = []
532     ## for i in emailCheckList:
533     ## test = attMal.malPeopleList.search_by_email(i)
534     ## if test:
535     ## print(f"{test.first_name} {test.last_name} | {i}")
536     ## else:
537     ## notFoundList.append(i)
538     ## print("\n\n")
539     ## print(f"There are {len(notFoundList)} email addresses not found:")
540     ## notFoundList.sort()
541     ## for x in notFoundList:
542     ## print(x)
543    
544    
545    
546     ## lastNameCheckList = []
547     ## notFoundList = []
548     ## contents = open(r"C:\Test_Dir\ATT\JoshVerifyPrivResponse\Josh-ATT_No_title.txt").readlines()
549     ## for line in contents:
550     ## line = line.replace("\n","")
551     ## lName = line.split(",")[0]
552     ## lName = lName.strip()
553     ## lName = lName.upper()
554     ## test = attMal.malPeopleList.return_list_of_matching_values('last_name',lName)
555     ## if test:
556     ## #print(f"Possible match for {line}:")
557     ## for x in test:
558     ## print(f"{line}|{x.last_name},{x.first_name}|{x.company}|{x.job_title}|{x.is_attorney}")
559     ## else:
560     ## notFoundList.append(line)
561     ##
562     ## print("\n\n")
563     ## print(f"There are {len(notFoundList)} email addresses not found:")
564     ## notFoundList.sort()
565     ## for x in notFoundList:
566     ## print(x)
567    
568    
569    
570    
571     ## findList = ["JON.GREER@DIRECTV.COM","AJJOHNSON@GIBSONDUNN.COM","RLANG@GIBSONDUNN.COM","MATT.MILLER@CROWDSTRIKE.COM","ADAM.MONTGOMERY@FLEISHMAN.COM","ALPESHP@AMDOCS.COM",
572     ## "ERIC.PRATT@CROWDSTRIKE.COM","MROBERTS@GIBSONDUNN.COM","VVARGAS@KTSLAW.COM","TIM.WILLIAMS@FLEISHMAN.COM"]
573     ## for s in findList:
574     ## test = attMal.malPeopleList.search_by_email(s)
575     ## print(f"{s}|{test.last_name},{test.first_name}|{test.company}")
576    
577    
578 nino.borges 916 ## foundList = []
579     ## outputFile = open(r"C:\Test_Dir\ATT\20250716_ESQLogTest\NormalizedFromDOJLog(DB MATCH REPORT)2.txt",'w',encoding='UTF-8')
580     ## contents = open(r"C:\Test_Dir\ATT\20250716_ESQLogTest\NormalizedFromDOJLog(all-deduplicated-cleaned)2.txt").readlines()
581     ## for line in contents:
582     ## line = line.replace("\n","")
583     ## line = line.upper()
584     ## line = line.strip()
585     ## if "@" in line:
586     ## result = attMal.malPeopleList.search_by_email(line)
587     ## if result:
588     ## outputFile.write(f"{line}|{result.last_name},{result.first_name}|{result.is_attorney}\n")
589     ## results = attMal.malPeopleList.return_list_of_matching_values('full_name_preferred',line)
590     ## if results:
591     ## for result in results:
592     ## outputFile.write(f"{line}|{result.last_name},{result.first_name}|{result.is_attorney}\n")
593     ## elif "," in line:
594     ## lName = line.split(",")[0]
595     ## results = attMal.malPeopleList.return_list_of_matching_values('last_name',lName)
596     ## if results:
597     ## for result in results:
598     ## outputFile.write(f"{line}|{result.last_name},{result.first_name}|{result.is_attorney}\n")
599     ## else:
600     ## lName = line.split(" ")[-1]
601     ## results = attMal.malPeopleList.return_list_of_matching_values('last_name',lName)
602     ## if results:
603     ## for result in results:
604     ## outputFile.write(f"{line}|{result.last_name},{result.first_name}|{result.is_attorney}\n")
605     ## outputFile.close()