gedcom-x 0.5.6__py3-none-any.whl → 0.5.7__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/Translation.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from . import *
2
2
  from .Mutations import GedcomXEventOrFact
3
-
3
+ import re
4
4
  '''
5
5
  {'type': 'Object | Objects | Propertyof',
6
6
  'Object': 'Class',
@@ -217,3 +217,978 @@ g7toXtable = {
217
217
  "https://gedcom.io/terms/v7/record-SUBM": {'type':'Object','Object':Resource,'Args':{'Id':'xref'}},
218
218
  }
219
219
 
220
+ class Translater():
221
+ def __init__(self,gedcom: Gedcom5x) -> None:
222
+ self.handlers = {}
223
+ self.gedcom: Gedcom = gedcom
224
+ self.gedcomx = GedcomX()
225
+
226
+ self.object_stack = []
227
+ self.object_map = {}
228
+ self.missing_handler_count = {}
229
+
230
+ self.translate()
231
+
232
+
233
+ gedcom_even_to_fact = {
234
+ # Person Fact Types
235
+ "ADOP": FactType.Adoption,
236
+ "CHR": FactType.AdultChristening,
237
+ "EVEN": FactType.Amnesty, # and other FactTypes with no direct GEDCOM tag
238
+ "BAPM": FactType.Baptism,
239
+ "BARM": FactType.BarMitzvah,
240
+ "BASM": FactType.BatMitzvah,
241
+ "BIRT": FactType.Birth,
242
+ "BIRT, CHR": FactType.Birth,
243
+ "BLES": FactType.Blessing,
244
+ "BURI": FactType.Burial,
245
+ "CAST": FactType.Caste,
246
+ "CENS": FactType.Census,
247
+ "CIRC": FactType.Circumcision,
248
+ "CONF": FactType.Confirmation,
249
+ "CREM": FactType.Cremation,
250
+ "DEAT": FactType.Death,
251
+ "EDUC": FactType.Education,
252
+ "EMIG": FactType.Emigration,
253
+ "FCOM": FactType.FirstCommunion,
254
+ "GRAD": FactType.Graduation,
255
+ "IMMI": FactType.Immigration,
256
+ "MIL": FactType.MilitaryService,
257
+ "NATI": FactType.Nationality,
258
+ "NATU": FactType.Naturalization,
259
+ "OCCU": FactType.Occupation,
260
+ "ORDN": FactType.Ordination,
261
+ "DSCR": FactType.PhysicalDescription,
262
+ "PROB": FactType.Probate,
263
+ "PROP": FactType.Property,
264
+ "RELI": FactType.Religion,
265
+ "RESI": FactType.Residence,
266
+ "WILL": FactType.Will,
267
+
268
+ # Couple Relationship Fact Types
269
+ "ANUL": FactType.Annulment,
270
+ "DIV": FactType.Divorce,
271
+ "DIVF": FactType.DivorceFiling,
272
+ "ENGA": FactType.Engagement,
273
+ "MARR": FactType.Marriage,
274
+ "MARB": FactType.MarriageBanns,
275
+ "MARC": FactType.MarriageContract,
276
+ "MARL": FactType.MarriageLicense,
277
+ "SEPA": FactType.Separation,
278
+
279
+ # Parent-Child Relationship Fact Types
280
+ # (Note: Only ADOPTION has a direct GEDCOM tag, others are under "EVEN")
281
+ "ADOP": FactType.AdoptiveParent
282
+ }
283
+
284
+ gedcom_even_to_evnt = {
285
+ # Person Fact Types
286
+ "ADOP": EventType.Adoption,
287
+ "CHR": EventType.AdultChristening,
288
+ "BAPM": EventType.Baptism,
289
+ "BARM": EventType.BarMitzvah,
290
+ "BASM": EventType.BatMitzvah,
291
+ "BIRT": EventType.Birth,
292
+ "BIRT, CHR": EventType.Birth,
293
+ "BLES": EventType.Blessing,
294
+ "BURI": EventType.Burial,
295
+
296
+ "CENS": EventType.Census,
297
+ "CIRC": EventType.Circumcision,
298
+ "CONF": EventType.Confirmation,
299
+ "CREM": EventType.Cremation,
300
+ "DEAT": EventType.Death,
301
+ "EDUC": EventType.Education,
302
+ "EMIG": EventType.Emigration,
303
+ "FCOM": EventType.FirstCommunion,
304
+
305
+ "IMMI": EventType.Immigration,
306
+
307
+ "NATU": EventType.Naturalization,
308
+
309
+ "ORDN": EventType.Ordination,
310
+
311
+
312
+ # Couple Relationship Fact Types
313
+ "ANUL": EventType.Annulment,
314
+ "DIV": EventType.Divorce,
315
+ "DIVF": EventType.DivorceFiling,
316
+ "ENGA": EventType.Engagement,
317
+ "MARR": EventType.Marriage
318
+
319
+ }
320
+
321
+ @staticmethod
322
+ def clean_str(text: str) -> str | None:
323
+ # Regular expression to match HTML/XML tags
324
+ if text is None or text.strip() == '':
325
+ return None
326
+ clean_text = re.sub(r'<[^>]+>', '', text)
327
+
328
+ return clean_text
329
+
330
+ def translate(self):
331
+ for n, repository in enumerate(self.gedcom.repositories):
332
+ print(f"Parsing Repository {n}")
333
+ self.parse_record(repository)
334
+ print(f"Translated {len(self.gedcomx.agents)} 'REPO' records to Agents")
335
+ for source in self.gedcom.sources:
336
+ self.parse_record(source)
337
+ print(f"Translated {len(self.gedcomx.source_descriptions)} 'SOUR' records to SourceDescription")
338
+
339
+ for object in self.gedcom.objects:
340
+ self.parse_record(object)
341
+ print(f"Translated {len(self.gedcom.objects)} 'OBJE' records to SourceDescriptions")
342
+
343
+ for individual in self.gedcom.individuals:
344
+ self.parse_record(individual)
345
+ print(f"Translated {len(self.gedcomx.persons)} 'INDI' records to Persons")
346
+
347
+ for key in self.missing_handler_count:
348
+ print(f"{key}: {self.missing_handler_count[key]}")
349
+
350
+
351
+
352
+ fam_count = len(self.gedcom.families)
353
+ for family in self.gedcom.families:
354
+ self.handle_fam(family)
355
+ print(f"Translated {fam_count} 'FAM' records to {len(self.gedcomx.relationships)} Relationship")
356
+
357
+ print(f"Translated {len(self.gedcomx.events)} 'EVEN' records to Events")
358
+
359
+ def find_urls(self,text: str):
360
+ # Regular expression pattern to match URLs
361
+ url_pattern = re.compile(r'https?://[^\s]+')
362
+ # Find all URLs using the pattern
363
+ urls = url_pattern.findall(text)
364
+ return urls
365
+
366
+ @property
367
+ def event_type_conversion_table(self):
368
+ return {'BIRT':EventType.Birth,
369
+ 'OBIT':FactType.Obituary}
370
+
371
+ def parse_record(self,record: Gedcom5xRecord):
372
+
373
+ handler_name = 'handle_' + record.tag.lower()
374
+
375
+ if hasattr(self,handler_name):
376
+ convert_log.info(f'Parsing Record: {record.describe()}')
377
+ handler = getattr(self,handler_name)
378
+ handler(record)
379
+ else:
380
+ if record.tag in self.missing_handler_count:
381
+ self.missing_handler_count[record.tag] += 1
382
+ else:
383
+ self.missing_handler_count[record.tag] = 1
384
+
385
+ convert_log.error(f'Failed Parsing Record: {record.describe()}')
386
+ for sub_record in record.subRecords():
387
+ self.parse_record(sub_record)
388
+
389
+ def handle__apid(self, record: Gedcom5xRecord):
390
+ if isinstance(self.object_map[record.level-1], SourceReference):
391
+ self.object_map[record.level-1].description.add_identifier(Identifier(value=URI.from_url('APID://' + record.value)))
392
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
393
+ self.object_map[record.level-1].add_identifier(Identifier(value=URI.from_url('APID://' + record.value)))
394
+ else:
395
+ raise ValueError(f"Could not handle '_APID' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
396
+
397
+ def handle__meta(self, record: Gedcom5xRecord):
398
+ if isinstance(self.object_map[record.level-1], SourceDescription):
399
+ gxobject = Note(text=Translater.clean_str(record.value))
400
+ self.object_map[record.level-1].add_note(gxobject)
401
+ self.object_stack.append(gxobject)
402
+ self.object_map[record.level] = gxobject
403
+ else:
404
+ raise ValueError(f"Could not handle 'WWW' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
405
+
406
+ def handle__wlnk(self, record: Gedcom5xRecord):
407
+ return self.handle_sour(record)
408
+
409
+ def handle_addr(self, record: Gedcom5xRecord):
410
+ if isinstance(self.object_map[record.level-1], Agent):
411
+ # TODO CHeck if URL?
412
+ if Translater.clean_str(record.value):
413
+ gxobject = Address(value=Translater.clean_str(record.value))
414
+ else:
415
+ gxobject = Address()
416
+ self.object_map[record.level-1].address = gxobject
417
+ self.object_stack.append(gxobject)
418
+ self.object_map[record.level] = gxobject
419
+ else:
420
+ raise ValueError(f"I do not know how to handle an 'ADDR' tag for a {type(self.object_map[record.level-1])}")
421
+
422
+ def handle_adr1(self, record: Gedcom5xRecord):
423
+ if isinstance(self.object_map[record.level-1], Address):
424
+ if Translater.clean_str(record.value):
425
+ self.object_map[record.level-1].street = Translater.clean_str(record.value)
426
+ else:
427
+ raise ValueError(f"I do not know how to handle an 'ADR1' tag for a {type(self.object_map[record.level-1])}")
428
+
429
+ def handle_adr2(self, record: Gedcom5xRecord):
430
+ if isinstance(self.object_map[record.level-1], Address):
431
+ if Translater.clean_str(record.value):
432
+ self.object_map[record.level-1].street2 = Translater.clean_str(record.value)
433
+ else:
434
+ raise ValueError(f"I do not know how to handle an 'ADR2' tag for a {type(self.object_map[record.level-1])}")
435
+
436
+ def handle_adr3(self, record: Gedcom5xRecord):
437
+ if isinstance(self.object_map[record.level-1], Address):
438
+ if Translater.clean_str(record.value):
439
+ self.object_map[record.level-1].street3 = Translater.clean_str(record.value)
440
+ else:
441
+ raise ValueError(f"I do not know how to handle an 'ADR3' tag for a {type(self.object_map[record.level-1])}")
442
+
443
+ def handle_adr4(self, record: Gedcom5xRecord):
444
+ if isinstance(self.object_map[record.level-1], Address):
445
+ if Translater.clean_str(record.value):
446
+ self.object_map[record.level-1].street4 = Translater.clean_str(record.value)
447
+ else:
448
+ raise ValueError(f"I do not know how to handle an 'ADR4' tag for a {type(self.object_map[record.level-1])}")
449
+
450
+ def handle_adr5(self, record: Gedcom5xRecord):
451
+ if isinstance(self.object_map[record.level-1], Address):
452
+ if Translater.clean_str(record.value):
453
+ self.object_map[record.level-1].street5 = Translater.clean_str(record.value)
454
+ else:
455
+ raise ValueError(f"I do not know how to handle an 'ADR5' tag for a {type(self.object_map[record.level-1])}")
456
+
457
+ def handle_adr6(self, record: Gedcom5xRecord):
458
+ if isinstance(self.object_map[record.level-1], Address):
459
+ if Translater.clean_str(record.value):
460
+ self.object_map[record.level-1].street5 = Translater.clean_str(record.value)
461
+ else:
462
+ raise ValueError(f"I do not know how to handle an 'ADR6' tag for a {type(self.object_map[record.level-1])}")
463
+
464
+ def handle_phon(self, record: Gedcom5xRecord):
465
+ if isinstance(self.object_map[record.level-1], Agent):
466
+ if Translater.clean_str(record.value):
467
+ self.object_map[record.level-1].phones.append(Translater.clean_str(record.value))
468
+ else:
469
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
470
+
471
+ def handle_email(self, record: Gedcom5xRecord):
472
+ if isinstance(self.object_map[record.level-1], Agent):
473
+ if Translater.clean_str(record.value):
474
+ self.object_map[record.level-1].emails.append(Translater.clean_str(record.value))
475
+ else:
476
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
477
+
478
+ def handle_fax(self, record: Gedcom5xRecord):
479
+ if isinstance(self.object_map[record.level-1], Agent):
480
+ if Translater.clean_str(record.value):
481
+ self.object_map[record.level-1].emails.append('FAX:' + Translater.clean_str(record.value))
482
+ else:
483
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
484
+
485
+ def handle_adop(self, record: Gedcom5xRecord):
486
+ if isinstance(self.object_map[record.level-1], Person):
487
+ gxobject = Fact(type=FactType.Adoption)
488
+ self.object_map[record.level-1].add_fact(gxobject)
489
+
490
+ self.object_stack.append(gxobject)
491
+ self.object_map[record.level] = gxobject
492
+ else:
493
+ raise TagConversionError(record=record,levelstack=self.object_map)
494
+
495
+ def handle_auth(self, record: Gedcom5xRecord):
496
+ if isinstance(self.object_map[record.level-1], SourceDescription):
497
+ if self.gedcomx.agents.byName(record.value):
498
+ gxobject = self.gedcomx.agents.byName(record.value)[0]
499
+ else:
500
+ gxobject = Agent(names=[TextValue(record.value)])
501
+ self.gedcomx.add_agent(gxobject)
502
+
503
+ self.object_map[record.level-1].author = gxobject
504
+ self.object_stack.append(gxobject)
505
+ self.object_map[record.level] = gxobject
506
+ else:
507
+ raise TagConversionError(record=record,levelstack=self.object_map)
508
+
509
+ def handle_bapm(self, record: Gedcom5xRecord):
510
+ if isinstance(self.object_map[record.level-1], Person):
511
+ gxobject = Fact(type=FactType.Baptism)
512
+ self.object_map[record.level-1].add_fact(gxobject)
513
+
514
+ self.object_stack.append(gxobject)
515
+ self.object_map[record.level] = gxobject
516
+ else:
517
+ raise TagConversionError(record=record,levelstack=self.object_map)
518
+
519
+ def handle_birt(self, record: Gedcom5xRecord):
520
+ if isinstance(self.object_map[record.level-1], Person):
521
+ #gxobject = Event(type=EventType.BIRTH, roles=[EventRole(person=self.object_map[record.level-1], type=EventRoleType.Principal)])
522
+ gxobject = Fact(type=FactType.Birth)
523
+ #self.gedcomx.add_event(gxobject)
524
+ self.object_map[record.level-1].add_fact(gxobject)
525
+
526
+ self.object_stack.append(gxobject)
527
+ self.object_map[record.level] = gxobject
528
+ else:
529
+ raise TagConversionError(record=record,levelstack=self.object_map)
530
+
531
+ def handle_buri(self, record: Gedcom5xRecord):
532
+ if isinstance(self.object_map[record.level-1], Person):
533
+ gxobject = Fact(type=FactType.Burial)
534
+ self.object_map[record.level-1].add_fact(gxobject)
535
+
536
+ self.object_stack.append(gxobject)
537
+ self.object_map[record.level] = gxobject
538
+ else:
539
+ raise TagConversionError(record=record,levelstack=self.object_map)
540
+
541
+ def handle_caln(self, record: Gedcom5xRecord):
542
+ if isinstance(self.object_map[record.level-1], SourceReference):
543
+ self.object_map[record.level-1].description.add_identifier(Identifier(value=URI.from_url('Call Number:' + record.value)))
544
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
545
+ self.object_map[record.level-1].add_identifier(Identifier(value=URI.from_url('Call Number:' + record.value)))
546
+ elif isinstance(self.object_map[record.level-1], Agent):
547
+ pass
548
+ # TODO Why is GEDCOM so shitty? A callnumber for a repository?
549
+ else:
550
+ raise ValueError(f"Could not handle 'CALN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
551
+
552
+ def handle_chan(self, record: Gedcom5xRecord):
553
+ if isinstance(self.object_map[record.level-1], SourceDescription):
554
+ self.object_map[record.level-1].created = Date(record.subRecord('DATE'))
555
+ elif isinstance(self.object_map[record.level-1], Agent):
556
+ if self.object_map[record.level-1].attribution is None:
557
+ gxobject = Attribution()
558
+ self.object_map[record.level-1].attribution = gxobject
559
+ self.object_stack.append(gxobject)
560
+ self.object_map[record.level] = gxobject
561
+ else:
562
+ raise ValueError()
563
+
564
+ def handle_chr(self, record: Gedcom5xRecord):
565
+ if isinstance(self.object_map[record.level-1], Person):
566
+ gxobject = Fact(type=FactType.Christening)
567
+ self.object_map[record.level-1].add_fact(gxobject)
568
+
569
+ self.object_stack.append(gxobject)
570
+ self.object_map[record.level] = gxobject
571
+ else:
572
+ raise TagConversionError(record=record,levelstack=self.object_map)
573
+
574
+ def handle_city(self, record: Gedcom5xRecord):
575
+ if isinstance(self.object_map[record.level-1], Address):
576
+ self.object_map[record.level-1].city = Translater.clean_str(record.value)
577
+ else:
578
+ raise ValueError(f"I do not know how to handle an 'CITY' tag for a {type(self.object_map[record.level-1])}")
579
+
580
+ def handle_conc(self, record: Gedcom5xRecord):
581
+ if isinstance(self.object_map[record.level-1], Note):
582
+ gxobject = Translater.clean_str(str(record.value))
583
+ self.object_map[record.level-1].append(gxobject)
584
+ elif isinstance(self.object_map[record.level-1], Agent):
585
+ gxobject = str(record.value)
586
+ self.object_map[record.level-1]._append_to_name(gxobject)
587
+ elif isinstance(self.object_map[record.level-1], Qualifier):
588
+ gxobject = str(record.value)
589
+ self.object_map[record.level-2].append(gxobject)
590
+ elif isinstance(self.object_map[record.level-1], TextValue):
591
+ #gxobject = TextValue(value=Translater.clean_str(record.value))
592
+ self.object_map[record.level-1]._append_to_value(record.value)
593
+ elif isinstance(self.object_map[record.level-1], SourceReference):
594
+ self.object_map[record.level-1].append(record.value)
595
+ elif isinstance(self.object_map[record.level-1], Fact):
596
+ self.object_map[record.level-1].notes[0].text += record.value
597
+
598
+ else:
599
+ raise TagConversionError(record=record,levelstack=self.object_map)
600
+
601
+ def handle_cont(self, record: Gedcom5xRecord):
602
+ if isinstance(self.object_map[record.level-1], Note):
603
+ gxobject = str("\n" + record.value if record.value else '')
604
+ self.object_map[record.level-1].append(gxobject)
605
+ elif isinstance(self.object_map[record.level-1], Agent):
606
+ gxobject = str("\n" + record.value if record.value else '')
607
+ elif isinstance(self.object_map[record.level-1], Qualifier):
608
+ gxobject = str("\n" + record.value if record.value else '')
609
+ self.object_map[record.level-1].append(gxobject)
610
+ elif isinstance(self.object_map[record.level-1], TextValue):
611
+ #gxobject = TextValue(value="\n" + record.value)
612
+ self.object_map[record.level-1]._append_to_value(record.value if record.value else '\n')
613
+ elif isinstance(self.object_map[record.level-1], SourceReference):
614
+ self.object_map[record.level-1].append(record.value)
615
+ elif isinstance(self.object_map[record.level-1], Address):
616
+ self.object_map[record.level-1]._append(record.value)
617
+ else:
618
+ raise TagConversionError(record=record,levelstack=self.object_map)
619
+
620
+ def handle_crea(self, record: Gedcom5xRecord):
621
+ if isinstance(self.object_map[record.level-1], SourceDescription):
622
+ self.object_map[record.level-1].created = Date(original=record.subRecord('DATE'))
623
+
624
+ elif isinstance(self.object_map[record.level-1], Agent):
625
+ if self.object_map[record.level-1].attribution is None:
626
+ gxobject = Attribution()
627
+ self.object_map[record.level-1].attribution = gxobject
628
+ self.object_stack.append(gxobject)
629
+ self.object_map[record.level] = gxobject
630
+ else:
631
+ convert_log.info(f"[{record.tag}] Attribution already exists for SourceDescription with id: {self.object_map[record.level-1].id}")
632
+ else:
633
+ raise ValueError(f"Could not handle '{record.tag}' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
634
+
635
+ def handle_ctry(self, record: Gedcom5xRecord):
636
+ if isinstance(self.object_map[record.level-1], Address):
637
+ self.object_map[record.level-1].country = Translater.clean_str(record.value)
638
+ else:
639
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
640
+
641
+ def handle_data(self, record: Gedcom5xRecord) -> None:
642
+ if record.value != '' and record.value == 'None':
643
+ assert False
644
+ self.object_map[record.level] = self.object_map[record.level-1]
645
+
646
+ def handle_date(self, record: Gedcom5xRecord):
647
+ if record.parent.tag == 'PUBL':
648
+ #gxobject = Date(original=record.value) #TODO Make a parser for solid timestamps
649
+ #self.object_map[0].published = gxobject
650
+ #self.object_map[0].published = date_to_timestamp(record.value) if record.value else None
651
+ self.object_map[0].published = record.value
652
+ #self.object_stack.append(gxobject)
653
+ #self.object_map[record.level] = gxobject
654
+ elif isinstance(self.object_map[record.level-1], Event):
655
+ self.object_map[record.level-1].date = Date(original=record.value)
656
+ elif isinstance(self.object_map[record.level-1], Fact):
657
+ self.object_map[record.level-1].date = Date(original=record.value)
658
+ elif record.parent.tag == 'DATA' and isinstance(self.object_map[record.level-2], SourceReference):
659
+ gxobject = Note(text='Date: ' + record.value)
660
+ self.object_map[record.level-2].description.add_note(gxobject)
661
+ self.object_stack.append(gxobject)
662
+ self.object_map[record.level] = gxobject
663
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
664
+
665
+ self.object_map[record.level-1].ctreated = record.value #TODO String to timestamp
666
+ elif isinstance(self.object_map[record.level-1], Attribution):
667
+ if record.parent.tag == 'CREA':
668
+ self.object_map[record.level-1].created = record.value #TODO G7
669
+ elif record.parent.tag == "CHAN":
670
+ self.object_map[record.level-1].modified = record.value #TODO G7
671
+ elif record.parent.tag in ['CREA','CHAN']:
672
+ pass
673
+
674
+ else:
675
+ raise TagConversionError(record=record,levelstack=self.object_map)
676
+
677
+ def handle_deat(self, record: Gedcom5xRecord):
678
+ if isinstance(self.object_map[record.level-1], Person):
679
+ gxobject = Fact(type=FactType.Death)
680
+ self.object_map[record.level-1].add_fact(gxobject)
681
+
682
+ self.object_stack.append(gxobject)
683
+ self.object_map[record.level] = gxobject
684
+ else:
685
+ raise TagConversionError(record=record,levelstack=self.object_map)
686
+
687
+ def handle_even(self, record: Gedcom5xRecord):
688
+ # TODO If events in a @S, check if only 1 person matches?
689
+ if record.value and (not record.value.strip() == ''):
690
+ values = [value.strip() for value in record.value.split(",")]
691
+ for value in values:
692
+ if value in Translater.gedcom_even_to_fact.keys():
693
+ if isinstance(self.object_map[record.level-1], Person):
694
+ gxobject = Fact(type=Translater.gedcom_even_to_fact[value])
695
+ self.object_map[record.level-1].add_fact(gxobject)
696
+
697
+ self.object_stack.append(gxobject)
698
+ self.object_map[record.level] = gxobject
699
+
700
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
701
+ gxobject = Event(type=Translater.gedcom_even_to_evnt[value],sources=[self.object_map[record.level-1]])
702
+ self.gedcomx.add_event(gxobject)
703
+ self.object_stack.append(gxobject)
704
+ self.object_map[record.level] = gxobject
705
+ else:
706
+ convert_log.warning(f"Could not convert EVEN '{value}' for object of type {type(self.object_map[record.level-1])} in record {record.describe()}")
707
+ return
708
+ raise TagConversionError(record=record,levelstack=self.object_map)
709
+ assert False
710
+ # TODO: Fix, this. making an event to cacth subtags, why are these fact tied to a source? GEDCOM is horrible
711
+ gxobject = Event(type=EventType.UNKNOWN)
712
+ self.object_stack.append(gxobject)
713
+ self.object_map[record.level] = gxobject
714
+ else:
715
+ raise TagConversionError(record=record,levelstack=self.object_map)
716
+
717
+ else:
718
+ possible_fact = FactType.guess(record.subRecord('TYPE')[0].value)
719
+ if possible_fact:
720
+ gxobject = Fact(type=possible_fact)
721
+ self.object_map[record.level-1].add_fact(gxobject)
722
+
723
+ self.object_stack.append(gxobject)
724
+ self.object_map[record.level] = gxobject
725
+ return
726
+ elif EventType.guess(record.subRecord('TYPE')[0].value):
727
+ if isinstance(self.object_map[record.level-1], Person):
728
+ gxobject = Event(type=EventType.guess(record.subRecord('TYPE')[0].value), roles=[EventRole(person=self.object_map[record.level-1], type=EventRoleType.Principal)])
729
+ self.gedcomx.add_event(gxobject)
730
+ self.object_stack.append(gxobject)
731
+ self.object_map[record.level] = gxobject
732
+ return
733
+ else:
734
+ if isinstance(self.object_map[record.level-1], Person):
735
+ gxobject = Event(type=None, roles=[EventRole(person=self.object_map[record.level-1], type=EventRoleType.Principal)])
736
+ gxobject.add_note(Note(subject='Event', text=record.value))
737
+ self.gedcomx.add_event(gxobject)
738
+ self.object_stack.append(gxobject)
739
+ self.object_map[record.level] = gxobject
740
+ return
741
+
742
+ else:
743
+ assert False
744
+
745
+ def handle_exid(self,record: Gedcom5xRecord):
746
+ gxobject = Identifier(type=IdentifierType.External,value=[record.value])
747
+ self.object_map[record.level-1].add_identifier(gxobject)
748
+
749
+ self.object_stack.append(gxobject)
750
+ self.object_map[record.level] = gxobject
751
+
752
+ def handle_fam(self, record: Gedcom5xRecord) -> None:
753
+ if record.tag != 'FAM' or record.level != 0:
754
+ raise ValueError("Invalid record: Must be a level 0 FAM record")
755
+
756
+ husband, wife, children = None, None, []
757
+
758
+ husband_record = record.subRecords('HUSB')
759
+ if husband_record:
760
+ husband = self.gedcomx.get_person_by_id(husband_record[0].xref)
761
+
762
+ wife_record = record.subRecords('WIFE')
763
+ if wife_record:
764
+ wife = self.gedcomx.get_person_by_id(wife_record[0].xref)
765
+
766
+ children_records = record.subRecords('CHIL')
767
+ if children_records:
768
+ for child_record in children_records:
769
+ child = self.gedcomx.get_person_by_id(child_record.xref)
770
+ if child:
771
+ children.append(child)
772
+
773
+ if husband:
774
+ for child in children:
775
+ relationship = Relationship(person1=husband, person2=child, type=RelationshipType.ParentChild)
776
+ self.gedcomx.add_relationship(relationship)
777
+ if wife:
778
+ for child in children:
779
+ relationship = Relationship(person1=wife, person2=child, type=RelationshipType.ParentChild)
780
+ self.gedcomx.add_relationship(relationship)
781
+ if husband and wife:
782
+ relationship = Relationship(person1=husband, person2=wife, type=RelationshipType.Couple)
783
+ self.gedcomx.add_relationship(relationship)
784
+
785
+ def handle_famc(self, record: Gedcom5xRecord) -> None:
786
+ return
787
+
788
+ def handle_fams(self, record: Gedcom5xRecord) -> None:
789
+ return
790
+
791
+ def handle_file(self, record: Gedcom5xRecord):
792
+ if record.value and record.value.strip() != '':
793
+ #raise ValueError(f"I did not expect the 'FILE' tag to have a value: {record.value}")
794
+ #TODO Handle files referenced here
795
+ ...
796
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
797
+ ...
798
+ self.object_map[record.level-1].resourceType = ResourceType.DigitalArtifact
799
+
800
+ def handle_form(self, record: Gedcom5xRecord):
801
+ if record.parent.tag == 'FILE' and isinstance(self.object_map[record.level-2], SourceDescription):
802
+ if record.value and record.value.strip() != '':
803
+ mime_type, _ = mimetypes.guess_type('placehold.' + record.value)
804
+ if mime_type:
805
+ self.object_map[record.level-2].mediaType = mime_type
806
+ else:
807
+ print(f"Could not determing mime type from {record.value}")
808
+ elif isinstance(self.object_map[record.level-1], PlaceDescription):
809
+ self.object_map[record.level-1].names.append(TextValue(value=record.value))
810
+ elif record.parent.tag == 'TRAN':
811
+ pass #TODO
812
+ else:
813
+ convert_log.error(f"raise TagConversionError(record=record,levelstack=self.object_map")
814
+
815
+ def handle_givn(self, record: Gedcom5xRecord):
816
+ if isinstance(self.object_map[record.level-1], Name):
817
+ given_name = NamePart(value=record.value, type=NamePartType.Given)
818
+ self.object_map[record.level-1]._add_name_part(given_name)
819
+ else:
820
+ raise TagConversionError(record=record,levelstack=self.object_map)
821
+
822
+ def handle_indi(self, record: Gedcom5xRecord):
823
+ person = Person(id=record.xref.replace('@',''))
824
+ self.gedcomx.add_person(person)
825
+ self.object_stack.append(person)
826
+ self.object_map[record.level] = person
827
+
828
+ def handle_immi(self, record: Gedcom5xRecord):
829
+ if isinstance(self.object_map[record.level-1], Person):
830
+ gxobject = Fact(type=FactType.Immigration)
831
+ self.object_map[record.level-1].add_fact(gxobject)
832
+
833
+ self.object_stack.append(gxobject)
834
+ self.object_map[record.level] = gxobject
835
+ else:
836
+ raise TagConversionError(record=record,levelstack=self.object_map)
837
+
838
+ def handle_marr(self, record: Gedcom5xRecord):
839
+ if isinstance(self.object_map[record.level-1], Person):
840
+ gxobject = Fact(type=FactType.Marriage)
841
+ self.object_map[record.level-1].add_fact(gxobject)
842
+
843
+ self.object_stack.append(gxobject)
844
+ self.object_map[record.level] = gxobject
845
+ else:
846
+ raise TagConversionError(record=record,levelstack=self.object_map)
847
+
848
+ def handle_name(self, record: Gedcom5xRecord):
849
+ if isinstance(self.object_map[record.level-1], Person):
850
+ gxobject = Name.simple(record.value)
851
+ #gxobject = Name(nameForms=[NameForm(fullText=record.value)], type=NameType.BirthName)
852
+ self.object_map[record.level-1].add_name(gxobject)
853
+
854
+ self.object_stack.append(gxobject)
855
+ self.object_map[record.level] = gxobject
856
+ elif isinstance(self.object_map[record.level-1], Agent):
857
+ gxobject = TextValue(value=record.value)
858
+ self.object_map[record.level-1].add_name(gxobject)
859
+ else:
860
+ raise TagConversionError(record=record,levelstack=self.object_map)
861
+
862
+ def handle_note(self, record: Gedcom5xRecord):
863
+ if isinstance(self.object_map[record.level-1], SourceDescription):
864
+ gxobject = Note(text=Translater.clean_str(record.value))
865
+ self.object_map[record.level-1].add_note(gxobject)
866
+
867
+ self.object_stack.append(gxobject)
868
+ self.object_map[record.level] = gxobject
869
+ elif isinstance(self.object_map[record.level-1], SourceReference):
870
+ gxobject = Note(text=Translater.clean_str(record.value))
871
+ self.object_map[record.level-1].description.add_note(gxobject)
872
+
873
+ self.object_stack.append(gxobject)
874
+ self.object_map[record.level] = gxobject
875
+ elif isinstance(self.object_map[record.level-1], Conclusion):
876
+ gxobject = Note(text=record.value)
877
+ self.object_map[record.level-1].add_note(gxobject)
878
+
879
+ self.object_stack.append(gxobject)
880
+ self.object_map[record.level] = gxobject
881
+ elif isinstance(self.object_map[record.level-1], Agent):
882
+ gxobject = Note(text=record.value)
883
+ self.object_map[record.level-1].add_note(gxobject)
884
+
885
+ self.object_stack.append(gxobject)
886
+ self.object_map[record.level] = gxobject
887
+ elif isinstance(self.object_map[record.level-1], Attribution):
888
+ if self.object_map[record.level-1].changeMessage is None:
889
+ self.object_map[record.level-1].changeMessage = record.value
890
+ else:
891
+ self.object_map[record.level-1].changeMessage = self.object_map[record.level-1].changeMessage + '' + record.value
892
+ elif isinstance(self.object_map[record.level-1], Note):
893
+ gxobject = Note(text=Translater.clean_str(record.value))
894
+ self.object_map[record.level-2].add_note(gxobject)
895
+
896
+ self.object_stack.append(gxobject)
897
+ self.object_map[record.level] = gxobject
898
+
899
+ else:
900
+ raise ValueError(f"Could not handle 'NOTE' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
901
+ assert False
902
+
903
+ def handle_nsfx(self, record: Gedcom5xRecord):
904
+ if isinstance(self.object_map[record.level-1], Name):
905
+ surname = NamePart(value=record.value, type=NamePartType.Suffix)
906
+ self.object_map[record.level-1]._add_name_part(surname)
907
+ else:
908
+ raise TagConversionError(record=record,levelstack=self.object_map)
909
+
910
+ def handle_occu(self, record: Gedcom5xRecord):
911
+ if isinstance(self.object_map[record.level-1], Person):
912
+ gxobject = Fact(type=FactType.Occupation)
913
+ self.object_map[record.level-1].add_fact(gxobject)
914
+
915
+ self.object_stack.append(gxobject)
916
+ self.object_map[record.level] = gxobject
917
+ else:
918
+ raise TagConversionError(record=record,levelstack=self.object_map)
919
+
920
+ def handle_obje(self, record: Gedcom5xRecord):
921
+ self.handle_sour(record)
922
+
923
+ def handle_page(self, record: Gedcom5xRecord):
924
+ if isinstance(self.object_map[record.level-1], SourceReference):
925
+ self.object_map[record.level-1].descriptionId = record.value
926
+ self.object_map[record.level-1].add_qualifier(KnownSourceReference(name=str(KnownSourceReference.Page),value=record.value))
927
+
928
+ #self.object_stack.append(gxobject)
929
+ #self.object_map[record.level] = gxobject
930
+ self.object_map[record.level] = self.object_map[record.level-1]
931
+ else:
932
+ raise ValueError(f"Could not handle 'PAGE' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
933
+
934
+ def handle_plac(self, record: Gedcom5xRecord):
935
+ if isinstance(self.object_map[record.level-1], Agent):
936
+ gxobject = Address(value=record.value)
937
+ self.object_map[record.level-1].add_address(gxobject)
938
+
939
+ self.object_stack.append(gxobject)
940
+ self.object_map[record.level] = gxobject
941
+ elif isinstance(self.object_map[record.level-1], Event):
942
+ if self.gedcomx.places.byName(record.value):
943
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=self.gedcomx.places.byName(record.value)[0])
944
+ else:
945
+ place_des = PlaceDescription(names=[TextValue(value=record.value)])
946
+ self.gedcomx.add_place_description(place_des)
947
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=place_des)
948
+ if len(record.subRecords()) > 0:
949
+ self.object_map[record.level]= place_des
950
+
951
+ elif isinstance(self.object_map[record.level-1], Fact):
952
+ if self.gedcomx.places.byName(record.value):
953
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=self.gedcomx.places.byName(record.value)[0])
954
+ else:
955
+ place_des = PlaceDescription(names=[TextValue(value=record.value)])
956
+ self.gedcomx.add_place_description(place_des)
957
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=place_des)
958
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
959
+ gxobject = Note(text='Place: ' + record.value)
960
+ self.object_map[record.level-1].add_note(gxobject)
961
+ self.object_stack.append(gxobject)
962
+ self.object_map[record.level] = gxobject
963
+ else:
964
+ raise TagConversionError(record=record,levelstack=self.object_map)
965
+
966
+ def handle_post(self, record: Gedcom5xRecord):
967
+ if isinstance(self.object_map[record.level-1], Address):
968
+ self.object_map[record.level-1].postalCode = Translater.clean_str(record.value)
969
+ else:
970
+ raise ValueError(f"I do not know how to handle an 'POST' tag for a {type(self.object_map[record.level-1])}")
971
+
972
+ def handle_publ(self, record: Gedcom5xRecord):
973
+ if isinstance(self.object_map[record.level-1], SourceDescription):
974
+ if record.value and self.gedcomx.agents.byName(record.value):
975
+ gxobject = self.gedcomx.agents.byName(record.value)[0]
976
+ else:
977
+ gxobject = Agent(names=[TextValue(record.value)])
978
+ self.gedcomx.add_agent(gxobject)
979
+ self.object_map[record.level-1].publisher = gxobject
980
+
981
+ self.object_stack.append(gxobject)
982
+ self.object_map[record.level] = gxobject
983
+ else:
984
+ raise TagConversionError(record=record,levelstack=self.object_map)
985
+
986
+ def handle_prob(self, record: Gedcom5xRecord):
987
+ if isinstance(self.object_map[record.level-1], Person):
988
+ gxobject = Fact(type=FactType.Probate)
989
+ self.object_map[record.level-1].add_fact(gxobject)
990
+
991
+ self.object_stack.append(gxobject)
992
+ self.object_map[record.level] = gxobject
993
+ else:
994
+ raise TagConversionError(record=record,levelstack=self.object_map)
995
+
996
+ def handle_uid(self, record: Gedcom5xRecord):
997
+ if isinstance(self.object_map[record.level-1], Agent):
998
+ gxobject = Identifier(value=['UID:' + record.value],type=IdentifierType.Primary)
999
+ self.object_map[record.level-1].add_identifier(gxobject) #NOTE GC7
1000
+ self.object_stack.append(gxobject)
1001
+ self.object_map[record.level] = gxobject
1002
+
1003
+ def handle_refn(self, record: Gedcom5xRecord):
1004
+ if isinstance(self.object_map[record.level-1], Person) or isinstance(self.object_map[record.level-1], SourceDescription):
1005
+ gxobject = Identifier(value=[URI.from_url('Reference Number:' + record.value)])
1006
+ self.object_map[record.level-1].add_identifier(gxobject)
1007
+ self.object_stack.append(gxobject)
1008
+ self.object_map[record.level] = gxobject
1009
+ elif isinstance(self.object_map[record.level-1], Agent):
1010
+ gxobject = Identifier(value=['Reference Number:' + record.value])
1011
+ self.object_map[record.level-1].add_identifier(gxobject) #NOTE GC7
1012
+ self.object_stack.append(gxobject)
1013
+ self.object_map[record.level] = gxobject
1014
+ else:
1015
+ raise ValueError(f"Could not handle 'REFN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
1016
+
1017
+ def handle_repo(self, record: Gedcom5xRecord):
1018
+
1019
+ if record.level == 0:
1020
+
1021
+ gxobject = Agent(id=record.xref)
1022
+ self.gedcomx.add_agent(gxobject)
1023
+ self.object_stack.append(gxobject)
1024
+ self.object_map[record.level] = gxobject
1025
+
1026
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
1027
+ if self.gedcomx.agents.byId(record.xref) is not None:
1028
+
1029
+ # TODO WHere and what to add this to?
1030
+ gxobject = self.gedcomx.agents.byId(record.xref)
1031
+ self.object_map[record.level-1].repository = gxobject
1032
+ self.object_map[record.level] = gxobject
1033
+
1034
+ else:
1035
+ print(record.describe())
1036
+ raise ValueError()
1037
+ gxobject = Agent(names=[TextValue(record.value)])
1038
+ else:
1039
+ raise ValueError(f"I do not know how to handle 'REPO' tag that is not a top-level, or sub-tag of {type(self.object_map[record.level-1])}")
1040
+
1041
+
1042
+ self.object_stack.append(gxobject)
1043
+ self.object_map[record.level] = gxobject
1044
+
1045
+ def handle_resi(self, record: Gedcom5xRecord):
1046
+ if isinstance(self.object_map[record.level-1], Person):
1047
+ gxobject = Fact(type=FactType.Residence)
1048
+ if record.value and record.value.strip() != '':
1049
+ gxobject.add_note(Note(text=record.value))
1050
+ self.object_map[record.level-1].add_fact(gxobject)
1051
+
1052
+ self.object_stack.append(gxobject)
1053
+ self.object_map[record.level] = gxobject
1054
+ else:
1055
+ raise TagConversionError(record=record,levelstack=self.object_map)
1056
+
1057
+ def handle_rin(self, record: Gedcom5xRecord):
1058
+ if isinstance(self.object_map[record.level-1], SourceDescription):
1059
+ self.object_map[record.level-1].id = record.value
1060
+ self.object_map[record.level-1].add_note(Note(text=f"Source had RIN: of {record.value}"))
1061
+
1062
+ else:
1063
+ raise ValueError(f"Could not handle 'RIN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
1064
+
1065
+ def handle_sex(self, record: Gedcom5xRecord):
1066
+
1067
+ if isinstance(self.object_map[record.level-1], Person):
1068
+ if record.value == 'M':
1069
+ gxobject = Gender(type=GenderType.Male)
1070
+ elif record.value == 'F':
1071
+ gxobject = Gender(type=GenderType.Female)
1072
+ else:
1073
+ gxobject = Gender(type=GenderType.Unknown)
1074
+ self.object_map[record.level-1].gender = gxobject
1075
+
1076
+ self.object_stack.append(gxobject)
1077
+ self.object_map[record.level] = gxobject
1078
+ else:
1079
+ assert False
1080
+
1081
+ def handle_sour(self, record: Gedcom5xRecord):
1082
+ if record.level == 0 or record.tag == '_WLNK' or (record.level == 0 and record.tag == 'OBJE'):
1083
+ source_description = SourceDescription(id=record.xref.replace('@','') if record.xref else None)
1084
+ self.gedcomx.add_source_description(source_description)
1085
+ self.object_stack.append(source_description)
1086
+ self.object_map[record.level] = source_description
1087
+ else:
1088
+ # This 'SOUR' is a SourceReference
1089
+ if record.xref and record.xref.strip() == '':
1090
+ import_log.warning(f"SOUR points to nothing: {record.describe()}")
1091
+ return False
1092
+ if self.gedcomx.source_descriptions.byId(record.xref):
1093
+ gxobject = SourceReference(descriptionId=record.xref, description=self.gedcomx.source_descriptions.byId(record.xref))
1094
+ else:
1095
+ import_log.warning(f'Could not find source with id: {record.xref}')
1096
+ source_description = SourceDescription(id=record.xref)
1097
+ gxobject = SourceReference(descriptionId=record.value, description=source_description)
1098
+ if isinstance(self.object_map[record.level-1],SourceReference):
1099
+ self.object_map[record.level-1].description.add_source(gxobject)
1100
+ elif record.parent.tag in ['NOTE']:
1101
+ pass
1102
+ else:
1103
+ self.object_map[record.level-1].add_source(gxobject)
1104
+ self.object_stack.append(gxobject)
1105
+ self.object_map[record.level] = gxobject
1106
+
1107
+ def handle_stae(self, record: Gedcom5xRecord):
1108
+ if isinstance(self.object_map[record.level-1], Address):
1109
+ self.object_map[record.level-1].stateOrProvince = Translater.clean_str(record.value)
1110
+ else:
1111
+ raise ValueError(f"I do not know how to handle an 'STAE' tag for a {type(self.object_map[record.level-1])}")
1112
+
1113
+ def handle_surn(self, record: Gedcom5xRecord):
1114
+ if isinstance(self.object_map[record.level-1], Name):
1115
+ surname = NamePart(value=record.value, type=NamePartType.Surname)
1116
+ self.object_map[record.level-1]._add_name_part(surname)
1117
+ else:
1118
+ raise TagConversionError(record=record,levelstack=self.object_map)
1119
+
1120
+ def handle_text(self, record: Gedcom5xRecord):
1121
+ if record.parent.tag == 'DATA':
1122
+ if isinstance(self.object_map[record.level-2], SourceReference):
1123
+ gxobject = TextValue(value=record.value)
1124
+ self.object_map[record.level-2].description.add_description(gxobject)
1125
+ self.object_stack.append(gxobject)
1126
+ self.object_map[record.level] = gxobject
1127
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
1128
+ gxobject = Document(text=record.value)
1129
+ self.object_map[record.level-1].analysis = gxobject
1130
+ else:
1131
+ assert False
1132
+
1133
+ def handle_titl(self, record: Gedcom5xRecord):
1134
+ if isinstance(self.object_map[record.level-1], SourceDescription):
1135
+
1136
+ gxobject = TextValue(value=Translater.clean_str(record.value))
1137
+ self.object_map[record.level-1].add_title(gxobject)
1138
+
1139
+ self.object_stack.append(gxobject)
1140
+ self.object_map[record.level] = gxobject
1141
+
1142
+ elif record.parent.tag == 'FILE' and isinstance(self.object_map[record.level-2], SourceDescription):
1143
+ gxobject = TextValue(value=record.value)
1144
+ self.object_map[record.level-2].add_title(gxobject)
1145
+
1146
+ self.object_stack.append(gxobject)
1147
+ self.object_map[record.level] = gxobject
1148
+ elif self.object_map[record.level] and isinstance(self.object_map[record.level], Name):
1149
+ gxobject = NamePart(value=record.value, qualifiers=[NamePartQualifier.Title])
1150
+
1151
+ self.object_map[record.level]._add_name_part(gxobject)
1152
+ else:
1153
+ convert_log.error(f"raise TagConversionError(record=record,levelstack=self.object_map)")
1154
+
1155
+ def handle_tran(self, record: Gedcom5xRecord):
1156
+ pass
1157
+
1158
+ def handle_type(self, record: Gedcom5xRecord):
1159
+ # peek to see if event or fact
1160
+ if isinstance(self.object_map[record.level-1], Event):
1161
+ if EventType.guess(record.value):
1162
+ self.object_map[record.level-1].type = EventType.guess(record.value)
1163
+ else:
1164
+ self.object_map[record.level-1].type = None
1165
+ self.object_map[record.level-1].add_note(Note(text=Translater.clean_str(record.value)))
1166
+ elif isinstance(self.object_map[record.level-1], Fact):
1167
+ if not self.object_map[record.level-1].type:
1168
+ self.object_map[0].type = FactType.guess(record.value)
1169
+ elif isinstance(self.object_map[record.level-1], Identifier):
1170
+
1171
+ self.object_map[record.level-1].values.append(Translater.clean_str(record.value))
1172
+ self.object_map[record.level-1].type = IdentifierType.Other
1173
+
1174
+ elif record.parent.tag == 'FORM':
1175
+ if not self.object_map[0].mediaType:
1176
+ self.object_map[0].mediaType = record.value
1177
+
1178
+ else:
1179
+ raise ValueError(f"I do not know how to handle 'TYPE' tag for {type(self.object_map[record.level-1])}")
1180
+
1181
+ def handle__url(self, record: Gedcom5xRecord):
1182
+ if isinstance(self.object_map[record.level-2], SourceDescription):
1183
+ self.object_map[record.level-2].about = URI.from_url(record.value)
1184
+ else:
1185
+ raise ValueError(f"Could not handle '_URL' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
1186
+
1187
+ def handle_www(self, record: Gedcom5xRecord):
1188
+ if isinstance(self.object_map[record.level-1], Agent):
1189
+ self.object_map[record.level-1].homepage = Translater.clean_str(record.value)
1190
+ elif isinstance(self.object_map[record.level-2], SourceReference):
1191
+ self.object_map[record.level-2].description.add_identifier(Identifier(value=URI.from_url(record.value)))
1192
+ else:
1193
+ raise ValueError(f"Could not handle 'WWW' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
1194
+