gedcom-x 0.5.1__py3-none-any.whl → 0.5.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gedcomx/GedcomX.py CHANGED
@@ -13,16 +13,18 @@ from .Agent import Agent
13
13
  from .Attribution import Attribution
14
14
  from .Conclusion import Conclusion
15
15
  from .Coverage import Coverage
16
- from .Date import Date
16
+ from .Date import Date, date_to_timestamp
17
17
  from .Document import Document
18
18
  from .EvidenceReference import EvidenceReference
19
+ from .Exceptions import TagConversionError
19
20
  from .Event import Event,EventType,EventRole,EventRoleType
20
21
  from .Fact import Fact, FactType, FactQualifier
21
22
  from .Gedcom import Gedcom
22
- from .Gedcom import GedcomRecord
23
+ from .Gedcom5x import GedcomRecord
23
24
  from .Gender import Gender, GenderType
24
25
  from .Group import Group
25
- from .Identifier import Identifier, IdentifierType
26
+ from .Identifier import Identifier, IdentifierType, make_uid, IdentifierList
27
+ from .Logging import get_logger
26
28
  from .Name import Name, NameType, NameForm, NamePart, NamePartType, NamePartQualifier
27
29
  from .Note import Note
28
30
  from .OnlineAccount import OnlineAccount
@@ -37,7 +39,10 @@ from .SourceReference import SourceReference, KnownSourceReference
37
39
  from .Subject import Subject
38
40
  from .TextValue import TextValue
39
41
  from .TopLevelTypeCollection import TopLevelTypeCollection
40
- from .URI import URI
42
+ from .Resource import Resource, URI
43
+
44
+ import_log = get_logger('import')
45
+ convert_log = get_logger('conversion')
41
46
 
42
47
  def TypeCollection(item_type):
43
48
  class Collection:
@@ -108,8 +113,10 @@ def TypeCollection(item_type):
108
113
 
109
114
  def byName(self, sname: str):
110
115
  # Use the name index for fast lookup
111
- sname = sname.strip()
112
- return self._name_index.get(sname, [])
116
+ if sname:
117
+ sname = sname.strip()
118
+ return self._name_index.get(sname, [])
119
+ return None
113
120
 
114
121
  def byId(self, id):
115
122
  # Use the id index for fast lookup
@@ -122,7 +129,12 @@ def TypeCollection(item_type):
122
129
  def append(self, item):
123
130
  if not isinstance(item, item_type):
124
131
  raise TypeError(f"Expected item of type {item_type.__name__}, got {type(item).__name__}")
125
- item._uri._path = f'{str(item_type.__name__)}s' if item._uri._path is None else item._uri._path
132
+ if item.uri:
133
+ item.uri.path = f'{str(item_type.__name__)}s' if (item.uri.path is None or item.uri.path == "") else item.uri.path
134
+ else:
135
+ item.uri = URI(path=f'/{item_type.__name__}s/')
136
+
137
+
126
138
  #if isinstance(item,Agent):
127
139
  # item._uri._path = 'Agents'
128
140
  # print(item._uri._as_dict_)
@@ -163,30 +175,52 @@ def TypeCollection(item_type):
163
175
 
164
176
  @property
165
177
  def _items_as_dict(self) -> dict:
166
- print(self.item_type)
167
-
168
- #return {f'{str(item_type.__name__)}s': [item._as_dict_ for item in self._items]}
169
- return {f'{str(item_type.__name__)}s': [item._as_dict_ for item in self._items]}
170
-
171
- @property
172
- def json(self):
173
- return json.dumps(self._as_dict, indent=4)
178
+ return {f'{str(item_type.__name__)}s': [item._as_dict_ for item in self._items]}
174
179
 
175
180
  @property
176
181
  def _as_dict_(self):
177
- return {"Collection": [item._as_dict_ for item in self._items]}
182
+ return {f'{str(item_type.__name__).lower()}s': [item._as_dict_ for item in self._items]}
183
+
184
+ @property
185
+ def json(self) -> str:
186
+
187
+ return json.dumps(self._as_dict_, indent=4)
178
188
 
179
189
  return Collection()
180
190
 
181
191
  class GedcomX:
192
+ """
193
+ Main GedcomX Object representing a Genealogy. Stores collections of Top Level Gedcom-X Types.
194
+ complies with GEDCOM X Conceptual Model V1 (http://gedcomx.org/conceptual-model/v1)
195
+
196
+ Parameters
197
+ ----------
198
+ id : str
199
+ Unique identifier for this Genealogy.
200
+ attribution : Attribution Object
201
+ Attribution information for the Genealogy
202
+ filepath : str
203
+ Not Implimented.
204
+ description : str
205
+ Description of the Genealogy: ex. 'My Family Tree'
206
+
207
+ Raises
208
+ ------
209
+ ValueError
210
+ If `id` is not a valid UUID.
211
+ """
182
212
  version = 'http://gedcomx.org/conceptual-model/v1'
183
213
 
184
- def __init__(self, id: str = None, attribution: Attribution = None, filepath: str = None) -> None:
214
+ def __init__(self, id: Optional[str] = None,
215
+ attribution: Optional[Attribution] = None,
216
+ filepath: Optional[str] = None,
217
+ description: Optional[str] = None) -> None:
218
+
185
219
  self.id = id
186
220
  self.attribution = attribution
187
-
188
221
  self._filepath = None
189
222
 
223
+ self.description = description
190
224
  self.source_descriptions = TypeCollection(SourceDescription)
191
225
  self.persons = TypeCollection(Person)
192
226
  self.relationships = TypeCollection(Relationship)
@@ -196,10 +230,11 @@ class GedcomX:
196
230
  self.places = TypeCollection(PlaceDescription)
197
231
  self.groups = TypeCollection(Group)
198
232
 
199
- self.person_relationships_lookup = {}
233
+ self.relationship_table = {}
234
+
235
+ self.default_id_generator = make_uid
200
236
 
201
- def _add(self,gedcomx_type_object):
202
- #print(type(gedcomx_type_object))
237
+ def add(self,gedcomx_type_object):
203
238
  if gedcomx_type_object:
204
239
  if isinstance(gedcomx_type_object,Person):
205
240
  self.add_person(gedcomx_type_object)
@@ -228,16 +263,27 @@ class GedcomX:
228
263
  raise ValueError(f"When adding a SourceDescription, value must be of type SourceDescription, type {type(sourceDescription)} was provided")
229
264
 
230
265
  def add_person(self,person: Person):
266
+ """Add a Person object to the Genealogy
267
+
268
+ Args:
269
+ person: Person Object
270
+
271
+ Returns:
272
+ None
273
+
274
+ Raises:
275
+ ValueError: If `person` is not of type Person.
276
+ """
231
277
  if person and isinstance(person,Person):
232
278
  if person.id is None:
233
279
  person.id =self.personURIgenerator()
234
280
  self.persons.append(item=person)
235
281
  else:
236
- raise ValueError()
282
+ raise ValueError(f'person must be a Person Object not type: {type(person)}')
237
283
 
238
284
  def add_relationship(self,relationship: Relationship):
239
285
  if relationship and isinstance(relationship,Relationship):
240
- if isinstance(relationship.person1,URI) and isinstance(relationship.person2,URI):
286
+ if isinstance(relationship.person1,Resource) and isinstance(relationship.person2,Resource):
241
287
  print("Adding unresolved Relationship")
242
288
  self.relationships.append(relationship)
243
289
  return
@@ -247,24 +293,24 @@ class GedcomX:
247
293
  relationship.person1.id = self.personURIgenerator()
248
294
  if not self.persons.byId(relationship.person1.id):
249
295
  self.persons.append(relationship.person1)
250
- if relationship.person1.id not in self.person_relationships_lookup:
251
- self.person_relationships_lookup[relationship.person1.id] = []
252
- self.person_relationships_lookup[relationship.person1.id].append(relationship)
296
+ if relationship.person1.id not in self.relationship_table:
297
+ self.relationship_table[relationship.person1.id] = []
298
+ self.relationship_table[relationship.person1.id].append(relationship)
253
299
  relationship.person1._add_relationship(relationship)
254
300
  else:
255
- raise ValueError
301
+ pass
256
302
 
257
303
  if relationship.person2:
258
304
  if relationship.person2.id is None:
259
305
  relationship.person2.id = self.personURIgenerator() #TODO
260
306
  if not self.persons.byId(relationship.person2.id):
261
307
  self.persons.append(relationship.person2)
262
- if relationship.person2.id not in self.person_relationships_lookup:
263
- self.person_relationships_lookup[relationship.person2.id] = []
264
- self.person_relationships_lookup[relationship.person2.id].append(relationship)
308
+ if relationship.person2.id not in self.relationship_table:
309
+ self.relationship_table[relationship.person2.id] = []
310
+ self.relationship_table[relationship.person2.id].append(relationship)
265
311
  relationship.person2._add_relationship(relationship)
266
312
  else:
267
- raise ValueError
313
+ pass
268
314
 
269
315
  self.relationships.append(relationship)
270
316
  else:
@@ -273,13 +319,25 @@ class GedcomX:
273
319
  def add_place_description(self,placeDescription: PlaceDescription):
274
320
  if placeDescription and isinstance(placeDescription,PlaceDescription):
275
321
  if placeDescription.id is None:
276
- placeDescription.id = self.PlaceDescriptionURIGenerator()
322
+ Warning("PlaceDescription has no id")
277
323
  self.places.append(placeDescription)
278
324
 
279
325
  def add_agent(self,agent: Agent):
326
+ """Add a Agent object to the Genealogy
327
+
328
+ Args:
329
+ agent: Agent Object
330
+
331
+ Returns:
332
+ None
333
+
334
+ Raises:
335
+ ValueError: If `agent` is not of type Agent.
336
+ """
280
337
  if agent and isinstance(agent,Agent):
338
+ if agent in self.agents:
339
+ return
281
340
  if agent.id is None:
282
- raise("Empty ID")
283
341
  agent.id = Agent.default_id_generator()
284
342
  if self.agents.byId(agent.id):
285
343
  raise ValueError
@@ -293,7 +351,7 @@ class GedcomX:
293
351
  return
294
352
  self.events.append(event_to_add)
295
353
 
296
- def person(self,id: str):
354
+ def get_person_by_id(self,id: str):
297
355
  filtered = [person for person in self.persons if getattr(person, 'id') == id]
298
356
  if filtered: return filtered[0]
299
357
  return None
@@ -303,11 +361,28 @@ class GedcomX:
303
361
  if filtered: return filtered[0]
304
362
  return None
305
363
 
364
+ def json(self):
365
+ """
366
+ JSON Representation of the GedcomX Genealogy.
367
+
368
+ Returns:
369
+ str: JSON Representation of the GedcomX Genealogy in the GEDCOM X JSON Serialization Format
370
+ """
371
+ gedcomx_json = {
372
+ 'persons': [person._as_dict_ for person in self.persons],
373
+ 'sourceDescriptions' : [sourceDescription._as_dict_ for sourceDescription in self.source_descriptions],
374
+ 'relationships': [relationship._as_dict_ for relationship in self.relationships],
375
+ 'agents': [agent._as_dict_ for agent in self.agents],
376
+ 'events': [event._as_dict_ for event in self.events],
377
+ 'places': [place._as_dict_ for place in self.places],
378
+ 'documents': [document._as_dict_ for document in self.documents],
379
+ }
380
+ return json.dumps(gedcomx_json, indent=4)
306
381
 
307
382
  class Translater():
308
- def __init__(self,gedcom) -> None:
383
+ def __init__(self,gedcom: Gedcom) -> None:
309
384
  self.handlers = {}
310
- self.gedcom: Gedcom = gedcom if gedcom else None
385
+ self.gedcom: Gedcom = gedcom
311
386
  self.gedcomx = GedcomX()
312
387
 
313
388
  self.object_stack = []
@@ -316,6 +391,7 @@ class Translater():
316
391
 
317
392
  self.translate()
318
393
 
394
+
319
395
  gedcom_even_to_fact = {
320
396
  # Person Fact Types
321
397
  "ADOP": FactType.Adoption,
@@ -367,15 +443,55 @@ class Translater():
367
443
  "ADOP": FactType.AdoptiveParent
368
444
  }
369
445
 
446
+ gedcom_even_to_evnt = {
447
+ # Person Fact Types
448
+ "ADOP": EventType.Adoption,
449
+ "CHR": EventType.AdultChristening,
450
+ "BAPM": EventType.Baptism,
451
+ "BARM": EventType.BarMitzvah,
452
+ "BASM": EventType.BatMitzvah,
453
+ "BIRT": EventType.Birth,
454
+ "BIRT, CHR": EventType.Birth,
455
+ "BLES": EventType.Blessing,
456
+ "BURI": EventType.Burial,
457
+
458
+ "CENS": EventType.Census,
459
+ "CIRC": EventType.Circumcision,
460
+ "CONF": EventType.Confirmation,
461
+ "CREM": EventType.Cremation,
462
+ "DEAT": EventType.Death,
463
+ "EDUC": EventType.Education,
464
+ "EMIG": EventType.Emigration,
465
+ "FCOM": EventType.FirstCommunion,
466
+
467
+ "IMMI": EventType.Immigration,
468
+
469
+ "NATU": EventType.Naturalization,
470
+
471
+ "ORDN": EventType.Ordination,
472
+
473
+
474
+ # Couple Relationship Fact Types
475
+ "ANUL": EventType.Annulment,
476
+ "DIV": EventType.Divorce,
477
+ "DIVF": EventType.DivorceFiling,
478
+ "ENGA": EventType.Engagement,
479
+ "MARR": EventType.Marriage
480
+
481
+ }
370
482
 
483
+ @staticmethod
371
484
  def clean_str(text: str) -> str:
372
485
  # Regular expression to match HTML/XML tags
486
+ if text is None or text.strip() == '':
487
+ return None
373
488
  clean_text = re.sub(r'<[^>]+>', '', text)
374
489
 
375
490
  return clean_text
376
491
 
377
492
  def translate(self):
378
- for repository in self.gedcom.repositories:
493
+ for n, repository in enumerate(self.gedcom.repositories):
494
+ print(f"Parsing Repository {n}")
379
495
  self.parse_record(repository)
380
496
  print(f"Translated {len(self.gedcomx.agents)} 'REPO' records to Agents")
381
497
  for source in self.gedcom.sources:
@@ -402,7 +518,7 @@ class Translater():
402
518
 
403
519
  print(f"Translated {len(self.gedcomx.events)} 'EVEN' records to Events")
404
520
 
405
- def find_urls(sef,text: str):
521
+ def find_urls(self,text: str):
406
522
  # Regular expression pattern to match URLs
407
523
  url_pattern = re.compile(r'https?://[^\s]+')
408
524
  # Find all URLs using the pattern
@@ -411,37 +527,36 @@ class Translater():
411
527
 
412
528
  @property
413
529
  def event_type_conversion_table(self):
414
- return {'BIRT':EventType.BIRTH,
415
- 'OBIT':FactType.OBITUARY}
530
+ return {'BIRT':EventType.Birth,
531
+ 'OBIT':FactType.Obituary}
416
532
 
417
533
  def parse_record(self,record: GedcomRecord):
418
-
419
- if DEBUG: print(record.describe())
534
+
420
535
  handler_name = 'handle_' + record.tag.lower()
421
536
 
422
- if hasattr(self,handler_name):
423
-
424
- handler = getattr(self,handler_name)
537
+ if hasattr(self,handler_name):
538
+ convert_log.info(f'Parsing Record: {record.describe()}')
539
+ handler = getattr(self,handler_name)
425
540
  handler(record)
426
541
  else:
427
542
  if record.tag in self.missing_handler_count:
428
543
  self.missing_handler_count[record.tag] += 1
429
544
  else:
430
545
  self.missing_handler_count[record.tag] = 1
431
- if not record.tag.startswith("_"):
432
- print(f"\033[1;31mCould not find handler for GEDCOM tag {record.tag}\n{record.describe()}\033[0m")
546
+
547
+ convert_log.error(f'Failed Parsing Record: {record.describe()}')
433
548
  for sub_record in record.subRecords():
434
549
  self.parse_record(sub_record)
435
-
436
- def handle__apid(self, record):
550
+
551
+ def handle__apid(self, record: GedcomRecord):
437
552
  if isinstance(self.object_map[record.level-1], SourceReference):
438
- self.object_map[record.level-1]._description_object.add_identifier(Identifier(value=URI.from_url('APID://' + record.value)))
553
+ self.object_map[record.level-1].description.add_identifier(Identifier(value=URI.from_url('APID://' + record.value)))
439
554
  elif isinstance(self.object_map[record.level-1], SourceDescription):
440
555
  self.object_map[record.level-1].add_identifier(Identifier(value=URI.from_url('APID://' + record.value)))
441
556
  else:
442
557
  raise ValueError(f"Could not handle '_APID' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
443
558
 
444
- def handle__meta(self, record):
559
+ def handle__meta(self, record: GedcomRecord):
445
560
  if isinstance(self.object_map[record.level-1], SourceDescription):
446
561
  gxobject = Note(text=Translater.clean_str(record.value))
447
562
  self.object_map[record.level-1].add_note(gxobject)
@@ -450,14 +565,14 @@ class Translater():
450
565
  else:
451
566
  raise ValueError(f"Could not handle 'WWW' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
452
567
 
453
- def handle__wlnk(self, record):
568
+ def handle__wlnk(self, record: GedcomRecord):
454
569
  return self.handle_sour(record)
455
570
 
456
- def handle_addr(self, record):
571
+ def handle_addr(self, record: GedcomRecord):
457
572
  if isinstance(self.object_map[record.level-1], Agent):
458
573
  # TODO CHeck if URL?
459
574
  if Translater.clean_str(record.value):
460
- gxobject = URI(value=Translater.clean_str(record.value))
575
+ gxobject = Address(value=Translater.clean_str(record.value))
461
576
  else:
462
577
  gxobject = Address()
463
578
  self.object_map[record.level-1].address = gxobject
@@ -466,26 +581,80 @@ class Translater():
466
581
  else:
467
582
  raise ValueError(f"I do not know how to handle an 'ADDR' tag for a {type(self.object_map[record.level-1])}")
468
583
 
469
- def handle_adr1(self, record):
584
+ def handle_adr1(self, record: GedcomRecord):
470
585
  if isinstance(self.object_map[record.level-1], Address):
471
586
  if Translater.clean_str(record.value):
472
- self.object_map[record.level-1].street = Translater.clean_str(record.value)
473
-
587
+ self.object_map[record.level-1].street = Translater.clean_str(record.value)
474
588
  else:
475
- raise ValueError(f"I do not know how to handle an 'ADDR' tag for a {type(self.object_map[record.level-1])}")
589
+ raise ValueError(f"I do not know how to handle an 'ADR1' tag for a {type(self.object_map[record.level-1])}")
590
+
591
+ def handle_adr2(self, record: GedcomRecord):
592
+ if isinstance(self.object_map[record.level-1], Address):
593
+ if Translater.clean_str(record.value):
594
+ self.object_map[record.level-1].street2 = Translater.clean_str(record.value)
595
+ else:
596
+ raise ValueError(f"I do not know how to handle an 'ADR2' tag for a {type(self.object_map[record.level-1])}")
597
+
598
+ def handle_adr3(self, record: GedcomRecord):
599
+ if isinstance(self.object_map[record.level-1], Address):
600
+ if Translater.clean_str(record.value):
601
+ self.object_map[record.level-1].street3 = Translater.clean_str(record.value)
602
+ else:
603
+ raise ValueError(f"I do not know how to handle an 'ADR3' tag for a {type(self.object_map[record.level-1])}")
604
+
605
+ def handle_adr4(self, record: GedcomRecord):
606
+ if isinstance(self.object_map[record.level-1], Address):
607
+ if Translater.clean_str(record.value):
608
+ self.object_map[record.level-1].street4 = Translater.clean_str(record.value)
609
+ else:
610
+ raise ValueError(f"I do not know how to handle an 'ADR4' tag for a {type(self.object_map[record.level-1])}")
611
+
612
+ def handle_adr5(self, record: GedcomRecord):
613
+ if isinstance(self.object_map[record.level-1], Address):
614
+ if Translater.clean_str(record.value):
615
+ self.object_map[record.level-1].street5 = Translater.clean_str(record.value)
616
+ else:
617
+ raise ValueError(f"I do not know how to handle an 'ADR5' tag for a {type(self.object_map[record.level-1])}")
618
+
619
+ def handle_adr6(self, record: GedcomRecord):
620
+ if isinstance(self.object_map[record.level-1], Address):
621
+ if Translater.clean_str(record.value):
622
+ self.object_map[record.level-1].street5 = Translater.clean_str(record.value)
623
+ else:
624
+ raise ValueError(f"I do not know how to handle an 'ADR6' tag for a {type(self.object_map[record.level-1])}")
625
+
626
+ def handle_phon(self, record: GedcomRecord):
627
+ if isinstance(self.object_map[record.level-1], Agent):
628
+ if Translater.clean_str(record.value):
629
+ self.object_map[record.level-1].phones.append(Translater.clean_str(record.value))
630
+ else:
631
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
632
+
633
+ def handle_email(self, record: GedcomRecord):
634
+ if isinstance(self.object_map[record.level-1], Agent):
635
+ if Translater.clean_str(record.value):
636
+ self.object_map[record.level-1].emails.append(Translater.clean_str(record.value))
637
+ else:
638
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
639
+
640
+ def handle_fax(self, record: GedcomRecord):
641
+ if isinstance(self.object_map[record.level-1], Agent):
642
+ if Translater.clean_str(record.value):
643
+ self.object_map[record.level-1].emails.append('FAX:' + Translater.clean_str(record.value))
644
+ else:
645
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
476
646
 
477
- def handle_adop(self, record):
647
+ def handle_adop(self, record: GedcomRecord):
478
648
  if isinstance(self.object_map[record.level-1], Person):
479
- gxobject = Fact(type=FactType.ADOPTION)
649
+ gxobject = Fact(type=FactType.Adoption)
480
650
  self.object_map[record.level-1].add_fact(gxobject)
481
651
 
482
652
  self.object_stack.append(gxobject)
483
653
  self.object_map[record.level] = gxobject
484
654
  else:
485
- print("Cannot translate 'BAPM")
486
- assert False
655
+ raise TagConversionError(record=record,levelstack=self.object_map)
487
656
 
488
- def handle_auth(self, record):
657
+ def handle_auth(self, record: GedcomRecord):
489
658
  if isinstance(self.object_map[record.level-1], SourceDescription):
490
659
  if self.gedcomx.agents.byName(record.value):
491
660
  gxobject = self.gedcomx.agents.byName(record.value)[0]
@@ -497,10 +666,9 @@ class Translater():
497
666
  self.object_stack.append(gxobject)
498
667
  self.object_map[record.level] = gxobject
499
668
  else:
500
- print("Cannot translate 'AUTH")
501
- assert False
669
+ raise TagConversionError(record=record,levelstack=self.object_map)
502
670
 
503
- def handle_bapm(self, record):
671
+ def handle_bapm(self, record: GedcomRecord):
504
672
  if isinstance(self.object_map[record.level-1], Person):
505
673
  gxobject = Fact(type=FactType.Baptism)
506
674
  self.object_map[record.level-1].add_fact(gxobject)
@@ -508,10 +676,9 @@ class Translater():
508
676
  self.object_stack.append(gxobject)
509
677
  self.object_map[record.level] = gxobject
510
678
  else:
511
- print("Cannot translate 'BAPM")
512
- assert False
679
+ raise TagConversionError(record=record,levelstack=self.object_map)
513
680
 
514
- def handle_birt(self, record):
681
+ def handle_birt(self, record: GedcomRecord):
515
682
  if isinstance(self.object_map[record.level-1], Person):
516
683
  #gxobject = Event(type=EventType.BIRTH, roles=[EventRole(person=self.object_map[record.level-1], type=EventRoleType.Principal)])
517
684
  gxobject = Fact(type=FactType.Birth)
@@ -521,10 +688,9 @@ class Translater():
521
688
  self.object_stack.append(gxobject)
522
689
  self.object_map[record.level] = gxobject
523
690
  else:
524
- print("Cannot translate 'AUTH")
525
- assert False
691
+ raise TagConversionError(record=record,levelstack=self.object_map)
526
692
 
527
- def handle_buri(self, record):
693
+ def handle_buri(self, record: GedcomRecord):
528
694
  if isinstance(self.object_map[record.level-1], Person):
529
695
  gxobject = Fact(type=FactType.Burial)
530
696
  self.object_map[record.level-1].add_fact(gxobject)
@@ -532,10 +698,9 @@ class Translater():
532
698
  self.object_stack.append(gxobject)
533
699
  self.object_map[record.level] = gxobject
534
700
  else:
535
- print("Cannot translate 'BURI")
536
- assert False
701
+ raise TagConversionError(record=record,levelstack=self.object_map)
537
702
 
538
- def handle_caln(self, record):
703
+ def handle_caln(self, record: GedcomRecord):
539
704
  if isinstance(self.object_map[record.level-1], SourceReference):
540
705
  self.object_map[record.level-1].description.add_identifier(Identifier(value=URI.from_url('Call Number:' + record.value)))
541
706
  elif isinstance(self.object_map[record.level-1], SourceDescription):
@@ -546,7 +711,19 @@ class Translater():
546
711
  else:
547
712
  raise ValueError(f"Could not handle 'CALN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
548
713
 
549
- def handle_chr(self, record):
714
+ def handle_chan(self, record: GedcomRecord):
715
+ if isinstance(self.object_map[record.level-1], SourceDescription):
716
+ self.object_map[record.level-1].created = Date(record.subRecord('DATE'))
717
+ elif isinstance(self.object_map[record.level-1], Agent):
718
+ if self.object_map[record.level-1].attribution is None:
719
+ gxobject = Attribution()
720
+ self.object_map[record.level-1].attribution = gxobject
721
+ self.object_stack.append(gxobject)
722
+ self.object_map[record.level] = gxobject
723
+ else:
724
+ raise ValueError()
725
+
726
+ def handle_chr(self, record: GedcomRecord):
550
727
  if isinstance(self.object_map[record.level-1], Person):
551
728
  gxobject = Fact(type=FactType.Christening)
552
729
  self.object_map[record.level-1].add_fact(gxobject)
@@ -554,16 +731,15 @@ class Translater():
554
731
  self.object_stack.append(gxobject)
555
732
  self.object_map[record.level] = gxobject
556
733
  else:
557
- print("Cannot translate 'CHR")
558
- assert False
734
+ raise TagConversionError(record=record,levelstack=self.object_map)
559
735
 
560
- def handle_city(self, record):
736
+ def handle_city(self, record: GedcomRecord):
561
737
  if isinstance(self.object_map[record.level-1], Address):
562
738
  self.object_map[record.level-1].city = Translater.clean_str(record.value)
563
739
  else:
564
740
  raise ValueError(f"I do not know how to handle an 'CITY' tag for a {type(self.object_map[record.level-1])}")
565
741
 
566
- def handle_conc(self, record):
742
+ def handle_conc(self, record: GedcomRecord):
567
743
  if isinstance(self.object_map[record.level-1], Note):
568
744
  gxobject = Translater.clean_str(str(record.value))
569
745
  self.object_map[record.level-1].append(gxobject)
@@ -582,69 +758,85 @@ class Translater():
582
758
  self.object_map[record.level-1].notes[0].text += record.value
583
759
 
584
760
  else:
585
- print(f"Cannot translate 'CONC', {self.object_map[record.level-1]}")
586
- assert False
761
+ raise TagConversionError(record=record,levelstack=self.object_map)
587
762
 
588
- def handle_cont(self, record):
763
+ def handle_cont(self, record: GedcomRecord):
589
764
  if isinstance(self.object_map[record.level-1], Note):
590
- gxobject = str("\n" + record.value)
765
+ gxobject = str("\n" + record.value if record.value else '')
591
766
  self.object_map[record.level-1].append(gxobject)
592
767
  elif isinstance(self.object_map[record.level-1], Agent):
593
- gxobject = str("\n" + record.value)
768
+ gxobject = str("\n" + record.value if record.value else '')
594
769
  elif isinstance(self.object_map[record.level-1], Qualifier):
595
- gxobject = str("\n" + record.value)
770
+ gxobject = str("\n" + record.value if record.value else '')
596
771
  self.object_map[record.level-1].append(gxobject)
597
772
  elif isinstance(self.object_map[record.level-1], TextValue):
598
773
  #gxobject = TextValue(value="\n" + record.value)
599
- self.object_map[record.level-1]._append_to_value(record.value)
774
+ self.object_map[record.level-1]._append_to_value(record.value if record.value else '\n')
600
775
  elif isinstance(self.object_map[record.level-1], SourceReference):
601
776
  self.object_map[record.level-1].append(record.value)
777
+ elif isinstance(self.object_map[record.level-1], Address):
778
+ self.object_map[record.level-1]._append(record.value)
602
779
  else:
603
- print(f"Cannot translate 'CONT', {type(self.object_map[record.level-1])}")
604
- assert False
780
+ raise TagConversionError(record=record,levelstack=self.object_map)
605
781
 
606
- def handle__crea(self, record):
782
+ def handle_crea(self, record: GedcomRecord):
607
783
  if isinstance(self.object_map[record.level-1], SourceDescription):
608
- self.object_map[record.level-1].created = Date(original=record.value)
784
+ self.object_map[record.level-1].created = Date(original=record.subRecord('DATE'))
785
+
786
+ elif isinstance(self.object_map[record.level-1], Agent):
787
+ if self.object_map[record.level-1].attribution is None:
788
+ gxobject = Attribution()
789
+ self.object_map[record.level-1].attribution = gxobject
790
+ self.object_stack.append(gxobject)
791
+ self.object_map[record.level] = gxobject
792
+ else:
793
+ convert_log.info(f"[{record.tag}] Attribution already exists for SourceDescription with id: {self.object_map[record.level-1].id}")
609
794
  else:
610
- raise ValueError(f"Could not handle '_URL' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
795
+ raise ValueError(f"Could not handle '{record.tag}' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
611
796
 
612
- def handle_ctry(self, record):
797
+ def handle_ctry(self, record: GedcomRecord):
613
798
  if isinstance(self.object_map[record.level-1], Address):
614
799
  self.object_map[record.level-1].country = Translater.clean_str(record.value)
615
800
  else:
616
- raise ValueError(f"I do not know how to handle an 'CTRY' tag for a {type(self.object_map[record.level-1])}")
801
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
617
802
 
618
803
  def handle_data(self, record: GedcomRecord) -> None:
619
- if record.value != '':
804
+ if record.value != '' and record.value == 'None':
620
805
  assert False
621
- self.object_map[record.level] = None
806
+ self.object_map[record.level] = self.object_map[record.level-1]
622
807
 
623
808
  def handle_date(self, record: GedcomRecord):
624
809
  if record.parent.tag == 'PUBL':
625
- gxobject = Date(original=record.value)
626
- self.object_map[0].published = gxobject
627
-
628
- self.object_stack.append(gxobject)
629
- self.object_map[record.level] = gxobject
810
+ #gxobject = Date(original=record.value) #TODO Make a parser for solid timestamps
811
+ #self.object_map[0].published = gxobject
812
+ #self.object_map[0].published = date_to_timestamp(record.value) if record.value else None
813
+ self.object_map[0].published = record.value
814
+ #self.object_stack.append(gxobject)
815
+ #self.object_map[record.level] = gxobject
630
816
  elif isinstance(self.object_map[record.level-1], Event):
631
817
  self.object_map[record.level-1].date = Date(original=record.value)
632
818
  elif isinstance(self.object_map[record.level-1], Fact):
633
819
  self.object_map[record.level-1].date = Date(original=record.value)
634
820
  elif record.parent.tag == 'DATA' and isinstance(self.object_map[record.level-2], SourceReference):
635
821
  gxobject = Note(text='Date: ' + record.value)
636
- self.object_map[record.level-2]._description_object.add_note(gxobject)
822
+ self.object_map[record.level-2].description.add_note(gxobject)
637
823
  self.object_stack.append(gxobject)
638
824
  self.object_map[record.level] = gxobject
639
825
  elif isinstance(self.object_map[record.level-1], SourceDescription):
640
826
 
641
827
  self.object_map[record.level-1].ctreated = record.value #TODO String to timestamp
642
-
828
+ elif isinstance(self.object_map[record.level-1], Attribution):
829
+ if record.parent.tag == 'CREA':
830
+ self.object_map[record.level-1].created = record.value #TODO G7
831
+ elif record.parent.tag == "CHAN":
832
+ self.object_map[record.level-1].modified = record.value #TODO G7
833
+ elif record.parent.tag in ['CREA','CHAN']:
834
+ pass
835
+
643
836
  else:
644
- print(f"Cannot translate 'DATE {type(self.object_map[record.level-1])}")
645
- assert False
837
+ raise TagConversionError(record=record,levelstack=self.object_map)
646
838
 
647
- def handle_deat(self, record):
839
+ def handle_deat(self, record: GedcomRecord):
648
840
  if isinstance(self.object_map[record.level-1], Person):
649
841
  gxobject = Fact(type=FactType.Death)
650
842
  self.object_map[record.level-1].add_fact(gxobject)
@@ -652,12 +844,11 @@ class Translater():
652
844
  self.object_stack.append(gxobject)
653
845
  self.object_map[record.level] = gxobject
654
846
  else:
655
- print("Cannot translate 'DEAT")
656
- assert False
847
+ raise TagConversionError(record=record,levelstack=self.object_map)
657
848
 
658
849
  def handle_even(self, record: GedcomRecord):
659
850
  # TODO If events in a @S, check if only 1 person matches?
660
- if not record.value.strip() == '':
851
+ if record.value and (not record.value.strip() == ''):
661
852
  values = [value.strip() for value in record.value.split(",")]
662
853
  for value in values:
663
854
  if value in Translater.gedcom_even_to_fact.keys():
@@ -667,18 +858,23 @@ class Translater():
667
858
 
668
859
  self.object_stack.append(gxobject)
669
860
  self.object_map[record.level] = gxobject
670
- print(self.object_map)
861
+
862
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
863
+ gxobject = Event(type=Translater.gedcom_even_to_evnt[value],sources=[self.object_map[record.level-1]])
864
+ self.gedcomx.add_event(gxobject)
865
+ self.object_stack.append(gxobject)
866
+ self.object_map[record.level] = gxobject
671
867
  else:
672
- print(record.describe())
868
+ convert_log.warning(f"Could not convert EVEN '{value}' for object of type {type(self.object_map[record.level-1])} in record {record.describe()}")
869
+ return
870
+ raise TagConversionError(record=record,levelstack=self.object_map)
673
871
  assert False
674
872
  # TODO: Fix, this. making an event to cacth subtags, why are these fact tied to a source? GEDCOM is horrible
675
873
  gxobject = Event(type=EventType.UNKNOWN)
676
874
  self.object_stack.append(gxobject)
677
875
  self.object_map[record.level] = gxobject
678
876
  else:
679
-
680
-
681
- print("This event does not look like a Fact")
877
+ raise TagConversionError(record=record,levelstack=self.object_map)
682
878
 
683
879
  else:
684
880
  possible_fact = FactType.guess(record.subRecord('TYPE')[0].value)
@@ -708,6 +904,13 @@ class Translater():
708
904
  else:
709
905
  assert False
710
906
 
907
+ def handle_exid(self,record: GedcomRecord):
908
+ gxobject = Identifier(type=IdentifierType.External,value=[record.value])
909
+ self.object_map[record.level-1].add_identifier(gxobject)
910
+
911
+ self.object_stack.append(gxobject)
912
+ self.object_map[record.level] = gxobject
913
+
711
914
  def handle_fam(self, record: GedcomRecord) -> None:
712
915
  if record.tag != 'FAM' or record.level != 0:
713
916
  raise ValueError("Invalid record: Must be a level 0 FAM record")
@@ -716,16 +919,16 @@ class Translater():
716
919
 
717
920
  husband_record = record.subRecords('HUSB')
718
921
  if husband_record:
719
- husband = self.gedcomx.person(husband_record[0].value.replace('@',''))
922
+ husband = self.gedcomx.get_person_by_id(husband_record[0].xref)
720
923
 
721
924
  wife_record = record.subRecords('WIFE')
722
925
  if wife_record:
723
- wife = self.gedcomx.person(wife_record[0].value.replace('@',''))
926
+ wife = self.gedcomx.get_person_by_id(wife_record[0].xref)
724
927
 
725
928
  children_records = record.subRecords('CHIL')
726
929
  if children_records:
727
930
  for child_record in children_records:
728
- child = self.gedcomx.person(child_record.value.replace('@',''))
931
+ child = self.gedcomx.get_person_by_id(child_record.xref)
729
932
  if child:
730
933
  children.append(child)
731
934
 
@@ -748,41 +951,43 @@ class Translater():
748
951
  return
749
952
 
750
953
  def handle_file(self, record: GedcomRecord):
751
- if record.value.strip() != '':
954
+ if record.value and record.value.strip() != '':
752
955
  #raise ValueError(f"I did not expect the 'FILE' tag to have a value: {record.value}")
753
956
  #TODO Handle files referenced here
754
957
  ...
755
958
  elif isinstance(self.object_map[record.level-1], SourceDescription):
756
959
  ...
757
960
  self.object_map[record.level-1].resourceType = ResourceType.DigitalArtifact
758
-
759
-
961
+
760
962
  def handle_form(self, record: GedcomRecord):
761
963
  if record.parent.tag == 'FILE' and isinstance(self.object_map[record.level-2], SourceDescription):
762
- if record.value.strip() != '':
964
+ if record.value and record.value.strip() != '':
763
965
  mime_type, _ = mimetypes.guess_type('placehold.' + record.value)
764
966
  if mime_type:
765
967
  self.object_map[record.level-2].mediaType = mime_type
766
968
  else:
767
969
  print(f"Could not determing mime type from {record.value}")
970
+ elif isinstance(self.object_map[record.level-1], PlaceDescription):
971
+ self.object_map[record.level-1].names.append(TextValue(value=record.value))
972
+ elif record.parent.tag == 'TRAN':
973
+ pass #TODO
768
974
  else:
769
- raise ValueError(f"I do not know how to translate the 'FORM' tag with value '{record.value}' for a {type(self.object_map[record.level-1])}")
975
+ raise TagConversionError(record=record,levelstack=self.object_map)
770
976
 
771
- def handle_givn(self, record):
977
+ def handle_givn(self, record: GedcomRecord):
772
978
  if isinstance(self.object_map[record.level-1], Name):
773
979
  given_name = NamePart(value=record.value, type=NamePartType.Given)
774
980
  self.object_map[record.level-1]._add_name_part(given_name)
775
981
  else:
776
- print(f"{record.describe()}, Cannot translate 'NAME', {self.object_map[record.level-1]}")
777
- assert False
982
+ raise TagConversionError(record=record,levelstack=self.object_map)
778
983
 
779
- def handle_indi(self, record):
984
+ def handle_indi(self, record: GedcomRecord):
780
985
  person = Person(id=record.xref.replace('@',''))
781
986
  self.gedcomx.add_person(person)
782
987
  self.object_stack.append(person)
783
988
  self.object_map[record.level] = person
784
989
 
785
- def handle_immi(self, record):
990
+ def handle_immi(self, record: GedcomRecord):
786
991
  if isinstance(self.object_map[record.level-1], Person):
787
992
  gxobject = Fact(type=FactType.Immigration)
788
993
  self.object_map[record.level-1].add_fact(gxobject)
@@ -790,10 +995,9 @@ class Translater():
790
995
  self.object_stack.append(gxobject)
791
996
  self.object_map[record.level] = gxobject
792
997
  else:
793
- print("Cannot translate 'IMMI")
794
- assert False
998
+ raise TagConversionError(record=record,levelstack=self.object_map)
795
999
 
796
- def handle_marr(self, record):
1000
+ def handle_marr(self, record: GedcomRecord):
797
1001
  if isinstance(self.object_map[record.level-1], Person):
798
1002
  gxobject = Fact(type=FactType.Marriage)
799
1003
  self.object_map[record.level-1].add_fact(gxobject)
@@ -801,10 +1005,9 @@ class Translater():
801
1005
  self.object_stack.append(gxobject)
802
1006
  self.object_map[record.level] = gxobject
803
1007
  else:
804
- print("Cannot translate 'MARR")
805
- assert False
1008
+ raise TagConversionError(record=record,levelstack=self.object_map)
806
1009
 
807
- def handle_name(self, record):
1010
+ def handle_name(self, record: GedcomRecord):
808
1011
  if isinstance(self.object_map[record.level-1], Person):
809
1012
  gxobject = Name.simple(record.value)
810
1013
  #gxobject = Name(nameForms=[NameForm(fullText=record.value)], type=NameType.BirthName)
@@ -816,10 +1019,9 @@ class Translater():
816
1019
  gxobject = TextValue(value=record.value)
817
1020
  self.object_map[record.level-1].add_name(gxobject)
818
1021
  else:
819
- print(f"{record.describe()}, Cannot translate 'NAME', {self.object_map[record.level-1]}")
820
- assert False
1022
+ raise TagConversionError(record=record,levelstack=self.object_map)
821
1023
 
822
- def handle_note(self, record):
1024
+ def handle_note(self, record: GedcomRecord):
823
1025
  if isinstance(self.object_map[record.level-1], SourceDescription):
824
1026
  gxobject = Note(text=Translater.clean_str(record.value))
825
1027
  self.object_map[record.level-1].add_note(gxobject)
@@ -828,7 +1030,7 @@ class Translater():
828
1030
  self.object_map[record.level] = gxobject
829
1031
  elif isinstance(self.object_map[record.level-1], SourceReference):
830
1032
  gxobject = Note(text=Translater.clean_str(record.value))
831
- self.object_map[record.level-1]._description_object.add_note(gxobject)
1033
+ self.object_map[record.level-1].description.add_note(gxobject)
832
1034
 
833
1035
  self.object_stack.append(gxobject)
834
1036
  self.object_map[record.level] = gxobject
@@ -838,19 +1040,30 @@ class Translater():
838
1040
 
839
1041
  self.object_stack.append(gxobject)
840
1042
  self.object_map[record.level] = gxobject
1043
+ elif isinstance(self.object_map[record.level-1], Agent):
1044
+ gxobject = Note(text=record.value)
1045
+ self.object_map[record.level-1].add_note(gxobject)
1046
+
1047
+ self.object_stack.append(gxobject)
1048
+ self.object_map[record.level] = gxobject
1049
+ elif isinstance(self.object_map[record.level-1], Attribution):
1050
+ if self.object_map[record.level-1].changeMessage is None:
1051
+ self.object_map[record.level-1].changeMessage = record.value
1052
+ else:
1053
+ self.object_map[record.level-1].changeMessage = self.object_map[record.level-1].changeMessage + '' + record.value
1054
+
841
1055
  else:
842
1056
  raise ValueError(f"Could not handle 'NOTE' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
843
1057
  assert False
844
1058
 
845
- def handle_nsfx(self, record):
1059
+ def handle_nsfx(self, record: GedcomRecord):
846
1060
  if isinstance(self.object_map[record.level-1], Name):
847
1061
  surname = NamePart(value=record.value, type=NamePartType.Suffix)
848
1062
  self.object_map[record.level-1]._add_name_part(surname)
849
1063
  else:
850
- print(f"{record.describe()}, Cannot translate 'NSFX', {self.object_map[record.level-1]}")
851
- assert False
1064
+ raise TagConversionError(record=record,levelstack=self.object_map)
852
1065
 
853
- def handle_occu(self, record):
1066
+ def handle_occu(self, record: GedcomRecord):
854
1067
  if isinstance(self.object_map[record.level-1], Person):
855
1068
  gxobject = Fact(type=FactType.Occupation)
856
1069
  self.object_map[record.level-1].add_fact(gxobject)
@@ -858,16 +1071,15 @@ class Translater():
858
1071
  self.object_stack.append(gxobject)
859
1072
  self.object_map[record.level] = gxobject
860
1073
  else:
861
- print("Cannot translate 'OCCU")
862
- assert False
1074
+ raise TagConversionError(record=record,levelstack=self.object_map)
863
1075
 
864
1076
  def handle_obje(self, record: GedcomRecord):
865
1077
  self.handle_sour(record)
866
1078
 
867
- def handle_page(self, record):
1079
+ def handle_page(self, record: GedcomRecord):
868
1080
  if isinstance(self.object_map[record.level-1], SourceReference):
869
1081
  self.object_map[record.level-1].descriptionId = record.value
870
- self.object_map[record.level-1].add_qualifier(KnownSourceReference.Page)
1082
+ self.object_map[record.level-1].add_qualifier(KnownSourceReference(name=str(KnownSourceReference.Page),value=record.value))
871
1083
 
872
1084
  #self.object_stack.append(gxobject)
873
1085
  #self.object_map[record.level] = gxobject
@@ -875,7 +1087,7 @@ class Translater():
875
1087
  else:
876
1088
  raise ValueError(f"Could not handle 'PAGE' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
877
1089
 
878
- def handle_plac(self, record):
1090
+ def handle_plac(self, record: GedcomRecord):
879
1091
  if isinstance(self.object_map[record.level-1], Agent):
880
1092
  gxobject = Address(value=record.value)
881
1093
  self.object_map[record.level-1].add_address(gxobject)
@@ -884,36 +1096,38 @@ class Translater():
884
1096
  self.object_map[record.level] = gxobject
885
1097
  elif isinstance(self.object_map[record.level-1], Event):
886
1098
  if self.gedcomx.places.byName(record.value):
887
- self.object_map[record.level-1].place = PlaceReference(original=record.value, descriptionRef=self.gedcomx.places.byName(record.value)[0])
1099
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=self.gedcomx.places.byName(record.value)[0])
888
1100
  else:
889
1101
  place_des = PlaceDescription(names=[TextValue(value=record.value)])
890
1102
  self.gedcomx.add_place_description(place_des)
891
- self.object_map[record.level-1].place = PlaceReference(original=record.value, descriptionRef=place_des)
1103
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=place_des)
1104
+ if len(record.subRecords()) > 0:
1105
+ self.object_map[record.level]= place_des
1106
+
892
1107
  elif isinstance(self.object_map[record.level-1], Fact):
893
1108
  if self.gedcomx.places.byName(record.value):
894
- self.object_map[record.level-1].place = PlaceReference(original=record.value, descriptionRef=self.gedcomx.places.byName(record.value)[0])
1109
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=self.gedcomx.places.byName(record.value)[0])
895
1110
  else:
896
1111
  place_des = PlaceDescription(names=[TextValue(value=record.value)])
897
1112
  self.gedcomx.add_place_description(place_des)
898
- self.object_map[record.level-1].place = PlaceReference(original=record.value, descriptionRef=place_des)
1113
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=place_des)
899
1114
  elif isinstance(self.object_map[record.level-1], SourceDescription):
900
1115
  gxobject = Note(text='Place: ' + record.value)
901
1116
  self.object_map[record.level-1].add_note(gxobject)
902
1117
  self.object_stack.append(gxobject)
903
1118
  self.object_map[record.level] = gxobject
904
1119
  else:
905
- print("Cannot translate 'PLACE")
906
- assert False
1120
+ raise TagConversionError(record=record,levelstack=self.object_map)
907
1121
 
908
- def handle_post(self, record):
1122
+ def handle_post(self, record: GedcomRecord):
909
1123
  if isinstance(self.object_map[record.level-1], Address):
910
1124
  self.object_map[record.level-1].postalCode = Translater.clean_str(record.value)
911
1125
  else:
912
1126
  raise ValueError(f"I do not know how to handle an 'POST' tag for a {type(self.object_map[record.level-1])}")
913
1127
 
914
- def handle_publ(self, record):
1128
+ def handle_publ(self, record: GedcomRecord):
915
1129
  if isinstance(self.object_map[record.level-1], SourceDescription):
916
- if self.gedcomx.agents.byName(record.value):
1130
+ if record.value and self.gedcomx.agents.byName(record.value):
917
1131
  gxobject = self.gedcomx.agents.byName(record.value)[0]
918
1132
  else:
919
1133
  gxobject = Agent(names=[TextValue(record.value)])
@@ -923,10 +1137,9 @@ class Translater():
923
1137
  self.object_stack.append(gxobject)
924
1138
  self.object_map[record.level] = gxobject
925
1139
  else:
926
- print("Cannot translate 'PUBL")
927
- assert False
1140
+ raise TagConversionError(record=record,levelstack=self.object_map)
928
1141
 
929
- def handle_prob(self, record):
1142
+ def handle_prob(self, record: GedcomRecord):
930
1143
  if isinstance(self.object_map[record.level-1], Person):
931
1144
  gxobject = Fact(type=FactType.Probate)
932
1145
  self.object_map[record.level-1].add_fact(gxobject)
@@ -934,28 +1147,46 @@ class Translater():
934
1147
  self.object_stack.append(gxobject)
935
1148
  self.object_map[record.level] = gxobject
936
1149
  else:
937
- print("Cannot translate 'PROB")
938
- assert False
1150
+ raise TagConversionError(record=record,levelstack=self.object_map)
939
1151
 
940
- def handle_refn(self, record):
1152
+ def handle_uid(self, record: GedcomRecord):
1153
+ if isinstance(self.object_map[record.level-1], Agent):
1154
+ gxobject = Identifier(value=['UID:' + record.value],type=IdentifierType.Primary)
1155
+ self.object_map[record.level-1].add_identifier(gxobject) #NOTE GC7
1156
+ self.object_stack.append(gxobject)
1157
+ self.object_map[record.level] = gxobject
1158
+
1159
+ def handle_refn(self, record: GedcomRecord):
941
1160
  if isinstance(self.object_map[record.level-1], Person) or isinstance(self.object_map[record.level-1], SourceDescription):
942
- self.object_map[record.level-1].add_identifier(Identifier(value=URI.from_url('Reference Number:' + record.value)))
1161
+ gxobject = Identifier(value=[URI.from_url('Reference Number:' + record.value)])
1162
+ self.object_map[record.level-1].add_identifier(gxobject)
1163
+ self.object_stack.append(gxobject)
1164
+ self.object_map[record.level] = gxobject
1165
+ elif isinstance(self.object_map[record.level-1], Agent):
1166
+ gxobject = Identifier(value=['Reference Number:' + record.value])
1167
+ self.object_map[record.level-1].add_identifier(gxobject) #NOTE GC7
1168
+ self.object_stack.append(gxobject)
1169
+ self.object_map[record.level] = gxobject
943
1170
  else:
944
1171
  raise ValueError(f"Could not handle 'REFN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
945
1172
 
946
- def handle_repo(self, record):
1173
+ def handle_repo(self, record: GedcomRecord):
1174
+
947
1175
  if record.level == 0:
948
1176
 
949
- gxobject = Agent(id=record.value.replace('@',''))
1177
+ gxobject = Agent(id=record.xref)
950
1178
  self.gedcomx.add_agent(gxobject)
951
1179
  self.object_stack.append(gxobject)
952
1180
  self.object_map[record.level] = gxobject
953
1181
 
954
1182
  elif isinstance(self.object_map[record.level-1], SourceDescription):
955
- if self.gedcomx.agents.byId(record.value.replace('@','')):
1183
+ if self.gedcomx.agents.byId(record.xref) is not None:
1184
+
956
1185
  # TODO WHere and what to add this to?
957
- gxobject = self.gedcomx.agents.byId(record.value)
1186
+ gxobject = self.gedcomx.agents.byId(record.xref)
958
1187
  self.object_map[record.level-1].repository = gxobject
1188
+ self.object_map[record.level] = gxobject
1189
+
959
1190
  else:
960
1191
  print(record.describe())
961
1192
  raise ValueError()
@@ -967,20 +1198,19 @@ class Translater():
967
1198
  self.object_stack.append(gxobject)
968
1199
  self.object_map[record.level] = gxobject
969
1200
 
970
- def handle_resi(self, record):
1201
+ def handle_resi(self, record: GedcomRecord):
971
1202
  if isinstance(self.object_map[record.level-1], Person):
972
1203
  gxobject = Fact(type=FactType.Residence)
973
- if record.value.strip() != '':
1204
+ if record.value and record.value.strip() != '':
974
1205
  gxobject.add_note(Note(text=record.value))
975
1206
  self.object_map[record.level-1].add_fact(gxobject)
976
1207
 
977
1208
  self.object_stack.append(gxobject)
978
1209
  self.object_map[record.level] = gxobject
979
1210
  else:
980
- print("Cannot translate 'RESI")
981
- assert False
1211
+ raise TagConversionError(record=record,levelstack=self.object_map)
982
1212
 
983
- def handle_rin(self, record):
1213
+ def handle_rin(self, record: GedcomRecord):
984
1214
  if isinstance(self.object_map[record.level-1], SourceDescription):
985
1215
  self.object_map[record.level-1].id = record.value
986
1216
  self.object_map[record.level-1].add_note(Note(text=f"Source had RIN: of {record.value}"))
@@ -1004,51 +1234,59 @@ class Translater():
1004
1234
  else:
1005
1235
  assert False
1006
1236
 
1007
- def handle_sour(self, record):
1237
+ def handle_sour(self, record: GedcomRecord):
1008
1238
  if record.level == 0 or record.tag == '_WLNK' or (record.level == 0 and record.tag == 'OBJE'):
1009
- source_description = SourceDescription(id=record.xref)
1239
+ source_description = SourceDescription(id=record.xref.replace('@','') if record.xref else None)
1010
1240
  self.gedcomx.add_source_description(source_description)
1011
1241
  self.object_stack.append(source_description)
1012
1242
  self.object_map[record.level] = source_description
1013
1243
  else:
1244
+ # This 'SOUR' is a SourceReference
1245
+ if record.xref and record.xref.strip() == '':
1246
+ import_log.warning(f"SOUR points to nothing: {record.describe()}")
1247
+ return False
1014
1248
  if self.gedcomx.source_descriptions.byId(record.xref):
1015
1249
  gxobject = SourceReference(descriptionId=record.xref, description=self.gedcomx.source_descriptions.byId(record.xref))
1016
1250
  else:
1251
+ import_log.warning(f'Could not find source with id: {record.xref}')
1017
1252
  source_description = SourceDescription(id=record.xref)
1018
1253
  gxobject = SourceReference(descriptionId=record.value, description=source_description)
1019
- if isinstance(self.object_map[record.level-1],SourceReference):
1020
- self.object_map[record.level-1]._description_object.add_source(gxobject)
1021
- else:
1022
- self.object_map[record.level-1].add_source(gxobject)
1254
+ if isinstance(self.object_map[record.level-1],SourceReference):
1255
+ self.object_map[record.level-1].description.add_source(gxobject)
1256
+ elif record.parent.tag in ['NOTE']:
1257
+ pass
1258
+ else:
1259
+ self.object_map[record.level-1].add_source(gxobject)
1023
1260
  self.object_stack.append(gxobject)
1024
1261
  self.object_map[record.level] = gxobject
1025
-
1026
-
1027
- def handle_stae(self, record):
1262
+
1263
+ def handle_stae(self, record: GedcomRecord):
1028
1264
  if isinstance(self.object_map[record.level-1], Address):
1029
1265
  self.object_map[record.level-1].stateOrProvince = Translater.clean_str(record.value)
1030
1266
  else:
1031
1267
  raise ValueError(f"I do not know how to handle an 'STAE' tag for a {type(self.object_map[record.level-1])}")
1032
1268
 
1033
- def handle_surn(self, record):
1269
+ def handle_surn(self, record: GedcomRecord):
1034
1270
  if isinstance(self.object_map[record.level-1], Name):
1035
1271
  surname = NamePart(value=record.value, type=NamePartType.Surname)
1036
1272
  self.object_map[record.level-1]._add_name_part(surname)
1037
1273
  else:
1038
- print(f"{record.describe()}, Cannot translate 'NAME', {self.object_map[record.level-1]}")
1039
- assert False
1274
+ raise TagConversionError(record=record,levelstack=self.object_map)
1040
1275
 
1041
1276
  def handle_text(self, record: GedcomRecord):
1042
1277
  if record.parent.tag == 'DATA':
1043
1278
  if isinstance(self.object_map[record.level-2], SourceReference):
1044
1279
  gxobject = TextValue(value=record.value)
1045
- self.object_map[record.level-2]._description_object.add_description(gxobject)
1280
+ self.object_map[record.level-2].description.add_description(gxobject)
1046
1281
  self.object_stack.append(gxobject)
1047
1282
  self.object_map[record.level] = gxobject
1283
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
1284
+ gxobject = Document(text=record.value)
1285
+ self.object_map[record.level-1].analysis = gxobject
1048
1286
  else:
1049
1287
  assert False
1050
1288
 
1051
- def handle_titl(self, record):
1289
+ def handle_titl(self, record: GedcomRecord):
1052
1290
  if isinstance(self.object_map[record.level-1], SourceDescription):
1053
1291
 
1054
1292
  gxobject = TextValue(value=Translater.clean_str(record.value))
@@ -1068,12 +1306,12 @@ class Translater():
1068
1306
 
1069
1307
  self.object_map[record.level]._add_name_part(gxobject)
1070
1308
  else:
1071
- print(self.object_map)
1072
- print(self.object_map[record.level])
1073
- raise ValueError(f"Could not handle 'TITL' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
1074
- assert False
1309
+ raise TagConversionError(record=record,levelstack=self.object_map)
1310
+
1311
+ def handle_tran(self, record: GedcomRecord):
1312
+ pass
1075
1313
 
1076
- def handle_type(self, record):
1314
+ def handle_type(self, record: GedcomRecord):
1077
1315
  # peek to see if event or fact
1078
1316
  if isinstance(self.object_map[record.level-1], Event):
1079
1317
  if EventType.guess(record.value):
@@ -1084,6 +1322,11 @@ class Translater():
1084
1322
  elif isinstance(self.object_map[record.level-1], Fact):
1085
1323
  if not self.object_map[record.level-1].type:
1086
1324
  self.object_map[0].type = FactType.guess(record.value)
1325
+ elif isinstance(self.object_map[record.level-1], Identifier):
1326
+
1327
+ self.object_map[record.level-1].values.append(Translater.clean_str(record.value))
1328
+ self.object_map[record.level-1].type = IdentifierType.Other
1329
+
1087
1330
  elif record.parent.tag == 'FORM':
1088
1331
  if not self.object_map[0].mediaType:
1089
1332
  self.object_map[0].mediaType = record.value
@@ -1091,15 +1334,17 @@ class Translater():
1091
1334
  else:
1092
1335
  raise ValueError(f"I do not know how to handle 'TYPE' tag for {type(self.object_map[record.level-1])}")
1093
1336
 
1094
- def handle__url(self, record):
1337
+ def handle__url(self, record: GedcomRecord):
1095
1338
  if isinstance(self.object_map[record.level-2], SourceDescription):
1096
1339
  self.object_map[record.level-2].about = URI.from_url(record.value)
1097
1340
  else:
1098
1341
  raise ValueError(f"Could not handle '_URL' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
1099
1342
 
1100
- def handle_www(self, record):
1101
- if isinstance(self.object_map[record.level-2], SourceReference):
1102
- self.object_map[record.level-2]._description_object.add_identifier(Identifier(value=URI.from_url(record.value)))
1343
+ def handle_www(self, record: GedcomRecord):
1344
+ if isinstance(self.object_map[record.level-1], Agent):
1345
+ self.object_map[record.level-1].homepage = Translater.clean_str(record.value)
1346
+ elif isinstance(self.object_map[record.level-2], SourceReference):
1347
+ self.object_map[record.level-2].description.add_identifier(Identifier(value=URI.from_url(record.value)))
1103
1348
  else:
1104
1349
  raise ValueError(f"Could not handle 'WWW' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
1105
1350