gedcom-x 0.5.6__py3-none-any.whl → 0.5.8__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.
Files changed (64) hide show
  1. {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.8.dist-info}/METADATA +1 -1
  2. gedcom_x-0.5.8.dist-info/RECORD +56 -0
  3. gedcomx/Extensions/__init__.py +1 -0
  4. gedcomx/Extensions/rs10/__init__.py +1 -0
  5. gedcomx/Extensions/rs10/rsLink.py +116 -0
  6. gedcomx/TopLevelTypeCollection.py +1 -1
  7. gedcomx/__init__.py +43 -41
  8. gedcomx/{Address.py → address.py} +13 -13
  9. gedcomx/{Agent.py → agent.py} +52 -24
  10. gedcomx/{Attribution.py → attribution.py} +36 -9
  11. gedcomx/{Conclusion.py → conclusion.py} +49 -21
  12. gedcomx/converter.py +1049 -0
  13. gedcomx/coverage.py +55 -0
  14. gedcomx/{Date.py → date.py} +11 -4
  15. gedcomx/{Document.py → document.py} +27 -8
  16. gedcomx/{Event.py → event.py} +102 -27
  17. gedcomx/{EvidenceReference.py → evidence_reference.py} +2 -2
  18. gedcomx/{Fact.py → fact.py} +45 -34
  19. gedcomx/{Gedcom5x.py → gedcom5x.py} +78 -61
  20. gedcomx/gedcom7/Exceptions.py +9 -0
  21. gedcomx/gedcom7/Gedcom7.py +160 -0
  22. gedcomx/gedcom7/GedcomStructure.py +94 -0
  23. gedcomx/gedcom7/Specification.py +347 -0
  24. gedcomx/gedcom7/__init__.py +26 -0
  25. gedcomx/gedcom7/g7interop.py +205 -0
  26. gedcomx/gedcom7/logger.py +19 -0
  27. gedcomx/gedcomx.py +501 -0
  28. gedcomx/{Gender.py → gender.py} +29 -17
  29. gedcomx/group.py +63 -0
  30. gedcomx/{Identifier.py → identifier.py} +13 -16
  31. gedcomx/{LoggingHub.py → logging_hub.py} +21 -0
  32. gedcomx/{Mutations.py → mutations.py} +50 -26
  33. gedcomx/name.py +396 -0
  34. gedcomx/{Note.py → note.py} +17 -10
  35. gedcomx/{OnlineAccount.py → online_account.py} +1 -1
  36. gedcomx/{Person.py → person.py} +52 -29
  37. gedcomx/place_description.py +123 -0
  38. gedcomx/place_reference.py +62 -0
  39. gedcomx/qualifier.py +54 -0
  40. gedcomx/{Relationship.py → relationship.py} +33 -13
  41. gedcomx/resource.py +85 -0
  42. gedcomx/serialization.py +815 -0
  43. gedcomx/{SourceDescription.py → source_description.py} +144 -85
  44. gedcomx/{SourceReference.py → source_reference.py} +15 -14
  45. gedcomx/{Subject.py → subject.py} +30 -28
  46. gedcomx/{GedcomX.py → translation.py} +283 -446
  47. gedcomx/{URI.py → uri.py} +42 -26
  48. gedcom_x-0.5.6.dist-info/RECORD +0 -45
  49. gedcomx/Coverage.py +0 -36
  50. gedcomx/Group.py +0 -37
  51. gedcomx/Name.py +0 -276
  52. gedcomx/PlaceDescription.py +0 -70
  53. gedcomx/PlaceReference.py +0 -30
  54. gedcomx/Qualifier.py +0 -27
  55. gedcomx/Resource.py +0 -75
  56. gedcomx/Serialization.py +0 -401
  57. gedcomx/Translation.py +0 -219
  58. {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.8.dist-info}/WHEEL +0 -0
  59. {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.8.dist-info}/top_level.txt +0 -0
  60. /gedcomx/{Exceptions.py → exceptions.py} +0 -0
  61. /gedcomx/{ExtensibleEnum.py → extensible_enum.py} +0 -0
  62. /gedcomx/{Gedcom.py → gedcom.py} +0 -0
  63. /gedcomx/{SourceCitation.py → source_citation.py} +0 -0
  64. /gedcomx/{TextValue.py → textvalue.py} +0 -0
gedcomx/converter.py ADDED
@@ -0,0 +1,1049 @@
1
+ DEBUG = False
2
+ import logging
3
+ import mimetypes
4
+ import re
5
+ import xml.etree.ElementTree as ET
6
+ """
7
+ ======================================================================
8
+ Project: Gedcom-X
9
+ File: converter.py
10
+ Author: David J. Cartwright
11
+ Purpose: convert gedcom versions
12
+
13
+ Created: 2025-08-25
14
+ Updated:
15
+ - 2025-09-01: added docs and fixed imports for lowercase module names
16
+
17
+ ======================================================================
18
+ """
19
+
20
+ """
21
+ ======================================================================
22
+ GEDCOM Module Types
23
+ ======================================================================
24
+ """
25
+ from typing import List, Optional, Any
26
+ from xml.dom import minidom
27
+ from .address import Address
28
+ from .agent import Agent
29
+ from .attribution import Attribution
30
+ from .conclusion import Conclusion
31
+ from .coverage import Coverage
32
+ from .date import Date, date_to_timestamp
33
+ from .document import Document
34
+ from .evidence_reference import EvidenceReference
35
+ from .exceptions import TagConversionError
36
+ from .event import Event, EventType, EventRole, EventRoleType
37
+ from .fact import Fact, FactType, FactQualifier
38
+ from .gedcom import Gedcom
39
+ from .gedcom5x import Gedcom5x, Gedcom5xRecord
40
+ from .gedcomx import GedcomX
41
+ from .gender import Gender, GenderType
42
+ from .group import Group
43
+ from .identifier import Identifier, IdentifierType, make_uid, IdentifierList
44
+ from .Logging import get_logger
45
+ from .logging_hub import hub, ChannelConfig
46
+ from .name import Name, NameType, NameForm, NamePart, NamePartType, NamePartQualifier
47
+ from .note import Note
48
+ from .online_account import OnlineAccount
49
+ from .person import Person
50
+ from .place_description import PlaceDescription
51
+ from .place_reference import PlaceReference
52
+ from .qualifier import Qualifier
53
+ from .relationship import Relationship, RelationshipType
54
+ from .resource import Resource
55
+ from .source_citation import SourceCitation
56
+ from .source_description import SourceDescription, ResourceType
57
+ from .source_reference import SourceReference, KnownSourceReference
58
+ # from .subject import Subject
59
+ from .textvalue import TextValue
60
+ #from .topleveltypecollection import TopLevelTypeCollection
61
+ from .uri import URI
62
+
63
+ log = logging.getLogger("gedcomx")
64
+ job_id = "gedcomx.convert.GEDCOM5x"
65
+
66
+ class GedcomConverter():
67
+ def __init__(self) -> None:
68
+ self.gedcomx = GedcomX()
69
+ self.object_map: dict[Any, Any] = {-1:self.gedcomx}
70
+ self.missing_handler_count = {}
71
+
72
+ gedcom_even_to_fact = {
73
+ # Person Fact Types
74
+ "ADOP": FactType.Adoption,
75
+ "CHR": FactType.AdultChristening,
76
+ "EVEN": FactType.Amnesty, # and other FactTypes with no direct GEDCOM tag
77
+ "BAPM": FactType.Baptism,
78
+ "BARM": FactType.BarMitzvah,
79
+ "BASM": FactType.BatMitzvah,
80
+ "BIRT": FactType.Birth,
81
+ "BIRT, CHR": FactType.Birth,
82
+ "BLES": FactType.Blessing,
83
+ "BURI": FactType.Burial,
84
+ "CAST": FactType.Caste,
85
+ "CENS": FactType.Census,
86
+ "CIRC": FactType.Circumcision,
87
+ "CONF": FactType.Confirmation,
88
+ "CREM": FactType.Cremation,
89
+ "DEAT": FactType.Death,
90
+ "EDUC": FactType.Education,
91
+ "EMIG": FactType.Emigration,
92
+ "FCOM": FactType.FirstCommunion,
93
+ "GRAD": FactType.Graduation,
94
+ "IMMI": FactType.Immigration,
95
+ "MIL": FactType.MilitaryService,
96
+ "NATI": FactType.Nationality,
97
+ "NATU": FactType.Naturalization,
98
+ "OCCU": FactType.Occupation,
99
+ "ORDN": FactType.Ordination,
100
+ "DSCR": FactType.PhysicalDescription,
101
+ "PROB": FactType.Probate,
102
+ "PROP": FactType.Property,
103
+ "RELI": FactType.Religion,
104
+ "RESI": FactType.Residence,
105
+ "WILL": FactType.Will,
106
+
107
+ # Couple Relationship Fact Types
108
+ "ANUL": FactType.Annulment,
109
+ "DIV": FactType.Divorce,
110
+ "DIVF": FactType.DivorceFiling,
111
+ "ENGA": FactType.Engagement,
112
+ "MARR": FactType.Marriage,
113
+ "MARB": FactType.MarriageBanns,
114
+ "MARC": FactType.MarriageContract,
115
+ "MARL": FactType.MarriageLicense,
116
+ "SEPA": FactType.Separation,
117
+
118
+ # Parent-Child Relationship Fact Types
119
+ # (Note: Only ADOPTION has a direct GEDCOM tag, others are under "EVEN")
120
+ "ADOP": FactType.AdoptiveParent
121
+ }
122
+
123
+ gedcom_even_to_evnt = {
124
+ # Person Fact Types
125
+ "ADOP": EventType.Adoption,
126
+ "CHR": EventType.AdultChristening,
127
+ "BAPM": EventType.Baptism,
128
+ "BARM": EventType.BarMitzvah,
129
+ "BASM": EventType.BatMitzvah,
130
+ "BIRT": EventType.Birth,
131
+ "BIRT, CHR": EventType.Birth,
132
+ "BLES": EventType.Blessing,
133
+ "BURI": EventType.Burial,
134
+
135
+ "CENS": EventType.Census,
136
+ "CIRC": EventType.Circumcision,
137
+ "CONF": EventType.Confirmation,
138
+ "CREM": EventType.Cremation,
139
+ "DEAT": EventType.Death,
140
+ "EDUC": EventType.Education,
141
+ "EMIG": EventType.Emigration,
142
+ "FCOM": EventType.FirstCommunion,
143
+
144
+ "IMMI": EventType.Immigration,
145
+
146
+ "NATU": EventType.Naturalization,
147
+
148
+ "ORDN": EventType.Ordination,
149
+
150
+
151
+ # Couple Relationship Fact Types
152
+ "ANUL": EventType.Annulment,
153
+ "DIV": EventType.Divorce,
154
+ "DIVF": EventType.DivorceFiling,
155
+ "ENGA": EventType.Engagement,
156
+ "MARR": EventType.Marriage
157
+
158
+ }
159
+
160
+
161
+ def clean_str(self, text: str | None) -> str:
162
+ # Regular expression to match HTML/XML tags
163
+ if text is None or text.strip() == '':
164
+ return ""
165
+ clean_text = re.sub(r'<[^>]+>', '', text)
166
+
167
+ return clean_text
168
+
169
+ def parse_gedcom5x_recrod(self,record: Gedcom5xRecord):
170
+ if record:
171
+ with hub.use(job_id):
172
+ handler_name = 'handle_' + record.tag.lower()
173
+
174
+ if hasattr(self,handler_name):
175
+ log.info(f'Parsing Record: {record.describe()}')
176
+ handler = getattr(self,handler_name)
177
+ handler(record)
178
+ else:
179
+ if record.tag in self.missing_handler_count:
180
+ self.missing_handler_count[record.tag] += 1
181
+ else:
182
+ self.missing_handler_count[record.tag] = 1
183
+
184
+ log.error(f'Failed Parsing Record: {record.describe()}')
185
+ for sub_record in record.subRecords():
186
+ self.parse_gedcom5x_recrod(sub_record)
187
+
188
+ def handle__apid(self, record: Gedcom5xRecord):
189
+ if isinstance(self.object_map[record.level-1], SourceReference):
190
+ self.object_map[record.level-1].description.add_identifier(Identifier(value=[URI.from_url('APID://' + record.value if record.value else '')]))
191
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
192
+ self.object_map[record.level-1].add_identifier(Identifier(value=[URI.from_url('APID://' + record.value if record.value else '')]))
193
+ else:
194
+ raise ValueError(f"Could not handle '_APID' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
195
+
196
+ def handle__meta(self, record: Gedcom5xRecord):
197
+ if isinstance(self.object_map[record.level-1], SourceDescription):
198
+ gxobject = Note(text=self.clean_str(record.value if record.value else 'Warning: This NOTE had not content.'))
199
+ self.object_map[record.level-1].add_note(gxobject)
200
+
201
+ self.object_map[record.level] = gxobject
202
+ else:
203
+ raise ValueError(f"Could not handle 'WWW' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
204
+
205
+ def handle__wlnk(self, record: Gedcom5xRecord):
206
+ return self.handle_sour(record)
207
+
208
+ def handle_addr(self, record: Gedcom5xRecord):
209
+ if isinstance(self.object_map[record.level-1], Agent):
210
+ # TODO CHeck if URL?
211
+ if record.value is not None and self.clean_str(record.value):
212
+ gxobject = Address(value=self.clean_str(record.value))
213
+ else:
214
+ gxobject = Address()
215
+ self.object_map[record.level-1].address = gxobject
216
+
217
+ self.object_map[record.level] = gxobject
218
+ else:
219
+ raise ValueError(f"I do not know how to handle an 'ADDR' tag for a {type(self.object_map[record.level-1])}")
220
+
221
+ def handle_adr1(self, record: Gedcom5xRecord):
222
+ if isinstance(self.object_map[record.level-1], Address):
223
+ if record.value is not None and self.clean_str(record.value):
224
+ self.object_map[record.level-1].street = self.clean_str(record.value)
225
+ else:
226
+ raise ValueError(f"I do not know how to handle an 'ADR1' tag for a {type(self.object_map[record.level-1])}")
227
+
228
+ def handle_adr2(self, record: Gedcom5xRecord):
229
+ if isinstance(self.object_map[record.level-1], Address):
230
+ if record.value is not None and self.clean_str(record.value):
231
+ self.object_map[record.level-1].street2 = self.clean_str(record.value)
232
+ else:
233
+ raise ValueError(f"I do not know how to handle an 'ADR2' tag for a {type(self.object_map[record.level-1])}")
234
+
235
+ def handle_adr3(self, record: Gedcom5xRecord):
236
+ if isinstance(self.object_map[record.level-1], Address):
237
+ if record.value is not None and self.clean_str(record.value):
238
+ self.object_map[record.level-1].street3 = self.clean_str(record.value)
239
+ else:
240
+ raise ValueError(f"I do not know how to handle an 'ADR3' tag for a {type(self.object_map[record.level-1])}")
241
+
242
+ def handle_adr4(self, record: Gedcom5xRecord):
243
+ if isinstance(self.object_map[record.level-1], Address):
244
+ if record.value is not None and self.clean_str(record.value):
245
+ self.object_map[record.level-1].street4 = self.clean_str(record.value)
246
+ else:
247
+ raise ValueError(f"I do not know how to handle an 'ADR4' tag for a {type(self.object_map[record.level-1])}")
248
+
249
+ def handle_adr5(self, record: Gedcom5xRecord):
250
+ if isinstance(self.object_map[record.level-1], Address):
251
+ if record.value is not None and self.clean_str(record.value):
252
+ self.object_map[record.level-1].street5 = self.clean_str(record.value)
253
+ else:
254
+ raise ValueError(f"I do not know how to handle an 'ADR5' tag for a {type(self.object_map[record.level-1])}")
255
+
256
+ def handle_adr6(self, record: Gedcom5xRecord):
257
+ if isinstance(self.object_map[record.level-1], Address):
258
+ if record.value is not None and self.clean_str(record.value):
259
+ self.object_map[record.level-1].street5 = self.clean_str(record.value)
260
+ else:
261
+ raise ValueError(f"I do not know how to handle an 'ADR6' tag for a {type(self.object_map[record.level-1])}")
262
+
263
+ def handle_phon(self, record: Gedcom5xRecord):
264
+ if isinstance(self.object_map[record.level-1], Agent):
265
+ if record.value is not None and self.clean_str(record.value):
266
+ self.object_map[record.level-1].phones.append(self.clean_str(record.value))
267
+ else:
268
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
269
+
270
+ def handle_email(self, record: Gedcom5xRecord):
271
+ if isinstance(self.object_map[record.level-1], Agent):
272
+ if record.value is not None and self.clean_str(record.value):
273
+ self.object_map[record.level-1].emails.append(self.clean_str(record.value))
274
+ else:
275
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
276
+
277
+ def handle_fax(self, record: Gedcom5xRecord):
278
+ if isinstance(self.object_map[record.level-1], Agent):
279
+ if record.value is not None and self.clean_str(record.value):
280
+ self.object_map[record.level-1].emails.append('FAX:' + (self.clean_str(record.value) if record.value is not None else ''))
281
+ else:
282
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
283
+
284
+ def handle_adop(self, record: Gedcom5xRecord):
285
+ if isinstance(self.object_map[record.level-1], Person):
286
+ gxobject = Fact(type=FactType.Adoption)
287
+ self.object_map[record.level-1].add_fact(gxobject)
288
+
289
+
290
+ self.object_map[record.level] = gxobject
291
+ else:
292
+ return #TODO
293
+ raise TagConversionError(record=record,levelstack=self.object_map)
294
+
295
+ def handle_auth(self, record: Gedcom5xRecord):
296
+ if isinstance(self.object_map[record.level-1], SourceDescription):
297
+ if record.value is not None and self.gedcomx.agents.byName(record.value):
298
+ gxobject = self.gedcomx.agents.byName(record.value)[0]
299
+ else:
300
+ gxobject = Agent(names=[TextValue(record.value)])
301
+ self.gedcomx.add_agent(gxobject)
302
+
303
+ self.object_map[record.level-1].author = gxobject
304
+
305
+ self.object_map[record.level] = gxobject
306
+ else:
307
+
308
+ raise TagConversionError(record=record,levelstack=self.object_map)
309
+
310
+ def handle_bapm(self, record: Gedcom5xRecord):
311
+ if isinstance(self.object_map[record.level-1], Person):
312
+ gxobject = Fact(type=FactType.Baptism)
313
+ self.object_map[record.level-1].add_fact(gxobject)
314
+
315
+
316
+ self.object_map[record.level] = gxobject
317
+ else:
318
+ raise TagConversionError(record=record,levelstack=self.object_map)
319
+
320
+ def handle_birt(self, record: Gedcom5xRecord):
321
+ if isinstance(self.object_map[record.level-1], Person):
322
+ gxobject = Fact(type=FactType.Birth)
323
+ self.object_map[record.level-1].add_fact(gxobject)
324
+ self.object_map[record.level] = gxobject
325
+ else:
326
+ raise TagConversionError(record=record,levelstack=self.object_map)
327
+
328
+ def handle_buri(self, record: Gedcom5xRecord):
329
+ if isinstance(self.object_map[record.level-1], Person):
330
+ gxobject = Fact(type=FactType.Burial)
331
+ self.object_map[record.level-1].add_fact(gxobject)
332
+
333
+
334
+ self.object_map[record.level] = gxobject
335
+ else:
336
+ raise TagConversionError(record=record,levelstack=self.object_map)
337
+
338
+ def handle_caln(self, record: Gedcom5xRecord):
339
+ if isinstance(self.object_map[record.level-1], SourceReference):
340
+ self.object_map[record.level-1].description.add_identifier(Identifier(value=[URI.from_url('Call Number:' + record.value if record.value else '')]))
341
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
342
+ self.object_map[record.level-1].add_identifier(Identifier(value=[URI.from_url('Call Number:' + record.value if record.value else '')]))
343
+ elif isinstance(self.object_map[record.level-1], Agent):
344
+ pass
345
+ # TODO Why is GEDCOM so shitty? A callnumber for a repository?
346
+ else:
347
+ raise ValueError(f"Could not handle 'CALN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
348
+
349
+ def handle_chan(self, record: Gedcom5xRecord):
350
+ if isinstance(self.object_map[record.level-1], SourceDescription):
351
+ date = record.subRecord('DATE')
352
+ if date is not None:
353
+ self.object_map[record.level-1].created = Date(date[0].value)
354
+ elif isinstance(self.object_map[record.level-1], Agent):
355
+ if self.object_map[record.level-1].attribution is None:
356
+ gxobject = Attribution()
357
+ self.object_map[record.level-1].attribution = gxobject
358
+ self.object_map[record.level] = gxobject
359
+ elif isinstance(self.object_map[record.level-1], Person):
360
+ if self.object_map[record.level-1].attribution is None:
361
+ gxobject = Attribution()
362
+ self.object_map[record.level-1].attribution = gxobject
363
+ self.object_map[record.level] = gxobject
364
+ else:
365
+ raise TagConversionError(record=record,levelstack=self.object_map)
366
+
367
+ def handle_chr(self, record: Gedcom5xRecord):
368
+ if isinstance(self.object_map[record.level-1], Person):
369
+ gxobject = Fact(type=FactType.Christening)
370
+ self.object_map[record.level-1].add_fact(gxobject)
371
+
372
+
373
+ self.object_map[record.level] = gxobject
374
+ else:
375
+ raise TagConversionError(record=record,levelstack=self.object_map)
376
+
377
+ def handle_city(self, record: Gedcom5xRecord):
378
+ if isinstance(self.object_map[record.level-1], Address):
379
+ if record.value is not None:
380
+ self.object_map[record.level-1].city = self.clean_str(record.value)
381
+ else: raise ValueError('Record had no value')
382
+ else:
383
+ raise ValueError(f"I do not know how to handle an 'CITY' tag for a {type(self.object_map[record.level-1])}")
384
+
385
+ def handle_conc(self, record: Gedcom5xRecord):
386
+ if isinstance(self.object_map[record.level-1], Note):
387
+ gxobject = self.clean_str(str(record.value))
388
+ self.object_map[record.level-1].append(gxobject)
389
+ elif isinstance(self.object_map[record.level-1], Agent):
390
+ gxobject = str(record.value)
391
+ self.object_map[record.level-1]._append_to_name(gxobject)
392
+ elif isinstance(self.object_map[record.level-1], Qualifier):
393
+ gxobject = str(record.value)
394
+ self.object_map[record.level-2].append(gxobject)
395
+ elif isinstance(self.object_map[record.level-1], TextValue):
396
+ #gxobject = TextValue(value=self.clean_str(record.value))
397
+ self.object_map[record.level-1]._append_to_value(record.value)
398
+ elif isinstance(self.object_map[record.level-1], SourceReference):
399
+ self.object_map[record.level-1].append(record.value)
400
+ elif isinstance(self.object_map[record.level-1], Fact):
401
+ self.object_map[record.level-1].notes[0].text += record.value
402
+
403
+ else:
404
+ raise TagConversionError(record=record,levelstack=self.object_map)
405
+
406
+ def handle_cont(self, record: Gedcom5xRecord):
407
+ if isinstance(self.object_map[record.level-1], Note):
408
+ gxobject = str("\n" + record.value if record.value else '')
409
+ self.object_map[record.level-1].append(gxobject)
410
+ elif isinstance(self.object_map[record.level-1], Agent):
411
+ gxobject = str("\n" + record.value if record.value else '')
412
+ elif isinstance(self.object_map[record.level-1], Qualifier):
413
+ gxobject = str("\n" + record.value if record.value else '')
414
+ self.object_map[record.level-1].append(gxobject)
415
+ elif isinstance(self.object_map[record.level-1], TextValue):
416
+ #gxobject = TextValue(value="\n" + record.value)
417
+ self.object_map[record.level-1]._append_to_value(record.value if record.value else '\n')
418
+ elif isinstance(self.object_map[record.level-1], SourceReference):
419
+ self.object_map[record.level-1].append(record.value)
420
+ elif isinstance(self.object_map[record.level-1], Address):
421
+ self.object_map[record.level-1]._append(record.value)
422
+ else:
423
+ raise TagConversionError(record=record,levelstack=self.object_map)
424
+
425
+ def handle_crea(self, record: Gedcom5xRecord):
426
+ if isinstance(self.object_map[record.level-1], SourceDescription):
427
+ date = record.subRecord('DATE')
428
+ if date is not None and date != []:
429
+ self.object_map[record.level-1].created = Date(original=date[0].value)
430
+ else: raise ValueError('DATE had not value')
431
+
432
+ elif isinstance(self.object_map[record.level-1], Agent):
433
+ if self.object_map[record.level-1].attribution is None:
434
+ gxobject = Attribution()
435
+ self.object_map[record.level-1].attribution = gxobject
436
+
437
+ self.object_map[record.level] = gxobject
438
+ else:
439
+ log.info(f"[{record.tag}] Attribution already exists for SourceDescription with id: {self.object_map[record.level-1].id}")
440
+ else:
441
+ raise ValueError(f"Could not handle '{record.tag}' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
442
+
443
+ def handle_ctry(self, record: Gedcom5xRecord):
444
+ if isinstance(self.object_map[record.level-1], Address):
445
+ if record.value is not None:
446
+ self.object_map[record.level-1].country = self.clean_str(record.value)
447
+ else:
448
+ raise ValueError('Recrod had no value')
449
+ else:
450
+ raise ValueError(f"I do not know how to handle an '{record.tag}' tag for a {type(self.object_map[record.level-1])}")
451
+
452
+ def handle_data(self, record: Gedcom5xRecord) -> None:
453
+ if record.value != '' and record.value == 'None':
454
+ assert False
455
+ self.object_map[record.level] = self.object_map[record.level-1]
456
+
457
+ def handle_date(self, record: Gedcom5xRecord):
458
+ if record.parent is not None and record.parent.tag == 'PUBL':
459
+ #gxobject = Date(original=record.value) #TODO Make a parser for solid timestamps
460
+ #self.object_map[0].published = gxobject
461
+ #self.object_map[0].published = date_to_timestamp(record.value) if record.value else None
462
+ self.object_map[0].published = record.value
463
+ #
464
+ #self.object_map[record.level] = gxobject
465
+ elif isinstance(self.object_map[record.level-1], Event):
466
+ self.object_map[record.level-1].date = Date(original=record.value)
467
+ elif isinstance(self.object_map[record.level-1], Fact):
468
+ self.object_map[record.level-1].date = Date(original=record.value)
469
+ elif record.parent is not None and record.parent.tag == 'DATA' and isinstance(self.object_map[record.level-2], SourceReference):
470
+ gxobject = Note(text='Date: ' + record.value if record.value else '')
471
+ self.object_map[record.level-2].description.add_note(gxobject)
472
+
473
+ self.object_map[record.level] = gxobject
474
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
475
+
476
+ self.object_map[record.level-1].ctreated = record.value #TODO String to timestamp
477
+ elif isinstance(self.object_map[record.level-1], Attribution):
478
+ if record.parent is not None and record.parent.tag == 'CREA':
479
+ self.object_map[record.level-1].created = record.value #TODO G7
480
+ elif record.parent is not None and record.parent.tag == "CHAN":
481
+ self.object_map[record.level-1].modified = record.value #TODO G7
482
+ elif record.parent is not None and record.parent.tag in ['CREA','CHAN']:
483
+ pass
484
+
485
+ else:
486
+ raise TagConversionError(record=record,levelstack=self.object_map)
487
+
488
+ def handle_deat(self, record: Gedcom5xRecord):
489
+ if isinstance(self.object_map[record.level-1], Person):
490
+ gxobject = Fact(type=FactType.Death)
491
+ self.object_map[record.level-1].add_fact(gxobject)
492
+
493
+
494
+ self.object_map[record.level] = gxobject
495
+ else:
496
+ raise TagConversionError(record=record,levelstack=self.object_map)
497
+
498
+ def handle_even(self, record: Gedcom5xRecord):
499
+ # TODO If events in a @S, check if only 1 person matches?
500
+ if record.value and (not record.value.strip() == ''):
501
+ values = [value.strip() for value in record.value.split(",")]
502
+ for value in values:
503
+ if value in self.gedcom_even_to_fact.keys():
504
+ if isinstance(self.object_map[record.level-1], Person):
505
+ gxobject = Fact(type=self.gedcom_even_to_fact[value])
506
+ self.object_map[record.level-1].add_fact(gxobject)
507
+
508
+
509
+ self.object_map[record.level] = gxobject
510
+
511
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
512
+ gxobject = Event(type=self.gedcom_even_to_evnt[value],sources=[self.object_map[record.level-1]])
513
+ self.gedcomx.add_event(gxobject)
514
+
515
+ self.object_map[record.level] = gxobject
516
+ else:
517
+ log.warning(f"Could not convert EVEN '{value}' for object of type {type(self.object_map[record.level-1])} in record {record.describe()}")
518
+ return
519
+ raise TagConversionError(record=record,levelstack=self.object_map)
520
+ assert False
521
+ # TODO: Fix, this. making an event to cacth subtags, why are these fact tied to a source? GEDCOM is horrible
522
+ gxobject = Event(type=EventType.UNKNOWN)
523
+
524
+ self.object_map[record.level] = gxobject
525
+ else:
526
+ raise TagConversionError(record=record,levelstack=self.object_map)
527
+
528
+ else:
529
+ possible_fact = FactType.guess(record.subRecord('TYPE')[0].value)
530
+ if possible_fact:
531
+ gxobject = Fact(type=possible_fact)
532
+ self.object_map[record.level-1].add_fact(gxobject)
533
+
534
+
535
+ self.object_map[record.level] = gxobject
536
+ return
537
+ elif EventType.guess(record.subRecord('TYPE')[0].value):
538
+ if isinstance(self.object_map[record.level-1], Person):
539
+ gxobject = Event(type=EventType.guess(record.subRecord('TYPE')[0].value), roles=[EventRole(person=self.object_map[record.level-1], type=EventRoleType.Principal)])
540
+ self.gedcomx.add_event(gxobject)
541
+
542
+ self.object_map[record.level] = gxobject
543
+ return
544
+ else:
545
+ if isinstance(self.object_map[record.level-1], Person):
546
+ gxobject = Event(type=None, roles=[EventRole(person=self.object_map[record.level-1], type=EventRoleType.Principal)])
547
+ gxobject.add_note(Note(subject='Event', text=record.value))
548
+ self.gedcomx.add_event(gxobject)
549
+
550
+ self.object_map[record.level] = gxobject
551
+ return
552
+
553
+ else:
554
+ assert False
555
+
556
+ def handle_exid(self,record: Gedcom5xRecord):
557
+ if record.value:
558
+ gxobject = Identifier(type=IdentifierType.External,value=[URI(record.value) if record.value else URI()]) # type: ignore
559
+ self.object_map[record.level-1].add_identifier(gxobject)
560
+ self.object_map[record.level] = gxobject
561
+ else: raise ValueError('Record had no value')
562
+
563
+ def handle_fam(self, record: Gedcom5xRecord) -> None:
564
+ if record.tag != 'FAM' or record.level != 0:
565
+ raise ValueError("Invalid record: Must be a level 0 FAM record")
566
+
567
+ husband, wife, children = None, None, []
568
+
569
+ husband_record = record.subRecords('HUSB')
570
+ if husband_record is not None:
571
+ id = husband_record[0].xref if len(husband_record) > 0 else None
572
+ if id:
573
+ husband = self.gedcomx.get_person_by_id(id)
574
+
575
+ wife_record = record.subRecords('WIFE')
576
+ if wife_record:
577
+ id = wife_record[0].xref if len(wife_record) > 0 else None
578
+ if id:
579
+ wife = self.gedcomx.get_person_by_id(id)
580
+
581
+ children_records = record.subRecords('CHIL')
582
+ if children_records:
583
+ for child_record in children_records:
584
+ id = child_record.xref
585
+ if id:
586
+ child = self.gedcomx.get_person_by_id(id)
587
+ if child:
588
+ children.append(child)
589
+
590
+ if husband:
591
+ for child in children:
592
+ relationship = Relationship(person1=husband, person2=child, type=RelationshipType.ParentChild)
593
+ self.gedcomx.add_relationship(relationship)
594
+ if wife:
595
+ for child in children:
596
+ relationship = Relationship(person1=wife, person2=child, type=RelationshipType.ParentChild)
597
+ self.gedcomx.add_relationship(relationship)
598
+ if husband and wife:
599
+ relationship = Relationship(person1=husband, person2=wife, type=RelationshipType.Couple)
600
+ self.gedcomx.add_relationship(relationship)
601
+
602
+ def handle_famc(self, record: Gedcom5xRecord) -> None:
603
+ return
604
+
605
+ def handle_fams(self, record: Gedcom5xRecord) -> None:
606
+ return
607
+
608
+ def handle_file(self, record: Gedcom5xRecord):
609
+ if record.value and record.value.strip() != '':
610
+ #raise ValueError(f"I did not expect the 'FILE' tag to have a value: {record.value}")
611
+ #TODO Handle files referenced here
612
+ ...
613
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
614
+ ...
615
+ self.object_map[record.level-1].resourceType = ResourceType.DigitalArtifact
616
+
617
+ def handle_form(self, record: Gedcom5xRecord):
618
+ if record.parent is not None and record.parent.tag == 'FILE' and isinstance(self.object_map[record.level-2], SourceDescription):
619
+ if record.value and record.value.strip() != '':
620
+ mime_type, _ = mimetypes.guess_type('placehold.' + record.value)
621
+ if mime_type:
622
+ self.object_map[record.level-2].mediaType = mime_type
623
+ else:
624
+ print(f"Could not determing mime type from {record.value}")
625
+ elif isinstance(self.object_map[record.level-1], PlaceDescription):
626
+ self.object_map[record.level-1].names.append(TextValue(value=record.value))
627
+ elif record.parent is not None and record.parent.tag == 'TRAN':
628
+ pass #TODO
629
+ else:
630
+ log.error(f"raise TagConversionError(record=record,levelstack=self.object_map")
631
+
632
+ def handle_givn(self, record: Gedcom5xRecord):
633
+ if isinstance(self.object_map[record.level-1], Name):
634
+ given_name = NamePart(value=record.value, type=NamePartType.Given)
635
+ self.object_map[record.level-1]._add_name_part(given_name)
636
+ else:
637
+ raise TagConversionError(record=record,levelstack=self.object_map)
638
+
639
+ def handle_indi(self, record: Gedcom5xRecord):
640
+ person = self.gedcomx.persons.byId(record.xref)
641
+ if person is None:
642
+ log.warning('Had to create person with id {recrod.xref}')
643
+ if isinstance(record.xref,str):
644
+ person = Person(id=record.xref.replace('@',''))
645
+ else:
646
+ raise ValueError('INDI Record had no XREF')
647
+ self.gedcomx.add_person(person)
648
+ self.object_map[record.level] = person
649
+
650
+ def handle_immi(self, record: Gedcom5xRecord):
651
+ if isinstance(self.object_map[record.level-1], Person):
652
+ gxobject = Fact(type=FactType.Immigration)
653
+ self.object_map[record.level-1].add_fact(gxobject)
654
+
655
+
656
+ self.object_map[record.level] = gxobject
657
+ else:
658
+ raise TagConversionError(record=record,levelstack=self.object_map)
659
+
660
+ def handle_marr(self, record: Gedcom5xRecord):
661
+ if isinstance(self.object_map[record.level-1], Person):
662
+ gxobject = Fact(type=FactType.Marriage)
663
+ self.object_map[record.level-1].add_fact(gxobject)
664
+
665
+
666
+ self.object_map[record.level] = gxobject
667
+ else:
668
+ raise TagConversionError(record=record,levelstack=self.object_map)
669
+
670
+ def handle_name(self, record: Gedcom5xRecord):
671
+ if isinstance(self.object_map[record.level-1], Person):
672
+ gxobject = Name.simple(record.value if record.value else 'WARNING: NAME had no value')
673
+ #gxobject = Name(nameForms=[NameForm(fullText=record.value)], type=NameType.BirthName)
674
+ self.object_map[record.level-1].add_name(gxobject)
675
+
676
+
677
+ self.object_map[record.level] = gxobject
678
+ elif isinstance(self.object_map[record.level-1], Agent):
679
+ gxobject = TextValue(value=record.value)
680
+ self.object_map[record.level-1].add_name(gxobject)
681
+ else:
682
+ raise TagConversionError(record=record,levelstack=self.object_map)
683
+
684
+ def handle_note(self, record: Gedcom5xRecord):
685
+ if isinstance(self.object_map[record.level-1], SourceDescription):
686
+ gxobject = Note(text=self.clean_str(record.value))
687
+ self.object_map[record.level-1].add_note(gxobject)
688
+
689
+
690
+ self.object_map[record.level] = gxobject
691
+ elif isinstance(self.object_map[record.level-1], SourceReference):
692
+ gxobject = Note(text=self.clean_str(record.value))
693
+ self.object_map[record.level-1].description.add_note(gxobject)
694
+
695
+
696
+ self.object_map[record.level] = gxobject
697
+ elif isinstance(self.object_map[record.level-1], Conclusion):
698
+ gxobject = Note(text=record.value)
699
+ self.object_map[record.level-1].add_note(gxobject)
700
+
701
+
702
+ self.object_map[record.level] = gxobject
703
+ elif isinstance(self.object_map[record.level-1], Agent):
704
+ gxobject = Note(text=record.value)
705
+ self.object_map[record.level-1].add_note(gxobject)
706
+
707
+
708
+ self.object_map[record.level] = gxobject
709
+ elif isinstance(self.object_map[record.level-1], Attribution):
710
+ if self.object_map[record.level-1].changeMessage is None:
711
+ self.object_map[record.level-1].changeMessage = record.value
712
+ else:
713
+ self.object_map[record.level-1].changeMessage = self.object_map[record.level-1].changeMessage + '' + record.value
714
+ elif isinstance(self.object_map[record.level-1], Note):
715
+ gxobject = Note(text=self.clean_str(record.value))
716
+ self.object_map[record.level-2].add_note(gxobject)
717
+
718
+
719
+ self.object_map[record.level] = gxobject
720
+
721
+ else:
722
+ raise ValueError(f"Could not handle 'NOTE' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
723
+ assert False
724
+
725
+ def handle_nsfx(self, record: Gedcom5xRecord):
726
+ if isinstance(self.object_map[record.level-1], Name):
727
+ surname = NamePart(value=record.value, type=NamePartType.Suffix)
728
+ self.object_map[record.level-1]._add_name_part(surname)
729
+ else:
730
+ raise TagConversionError(record=record,levelstack=self.object_map)
731
+
732
+ def handle_occu(self, record: Gedcom5xRecord):
733
+ if isinstance(self.object_map[record.level-1], Person):
734
+ gxobject = Fact(type=FactType.Occupation)
735
+ self.object_map[record.level-1].add_fact(gxobject)
736
+
737
+
738
+ self.object_map[record.level] = gxobject
739
+ else:
740
+ raise TagConversionError(record=record,levelstack=self.object_map)
741
+
742
+ def handle_obje(self, record: Gedcom5xRecord):
743
+ self.handle_sour(record)
744
+
745
+ def handle_page(self, record: Gedcom5xRecord):
746
+ if isinstance(self.object_map[record.level-1], SourceReference):
747
+ self.object_map[record.level-1].descriptionId = record.value
748
+ gx_object = KnownSourceReference(name=KnownSourceReference.Page,value=record.value)
749
+ self.object_map[record.level-1].add_qualifier(gx_object)
750
+ self.object_map[record.level] = self.object_map[record.level-1]
751
+ else:
752
+ raise ValueError(f"Could not handle 'PAGE' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
753
+
754
+ def handle_plac(self, record: Gedcom5xRecord):
755
+ if isinstance(self.object_map[record.level-1], Agent):
756
+ gxobject = Address(value=record.value)
757
+ self.object_map[record.level-1].add_address(gxobject)
758
+
759
+
760
+ self.object_map[record.level] = gxobject
761
+ elif isinstance(self.object_map[record.level-1], Event):
762
+ if self.gedcomx.places.byName(record.value):
763
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=self.gedcomx.places.byName(record.value)[0])
764
+ else:
765
+ place_des = PlaceDescription(names=[TextValue(value=record.value)])
766
+ self.gedcomx.add_place_description(place_des)
767
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=place_des)
768
+ if len(record.subRecords()) > 0:
769
+ self.object_map[record.level]= place_des
770
+
771
+ elif isinstance(self.object_map[record.level-1], Fact):
772
+ if self.gedcomx.places.byName(record.value):
773
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=self.gedcomx.places.byName(record.value)[0])
774
+ else:
775
+ place_des = PlaceDescription(names=[TextValue(value=record.value)])
776
+ self.gedcomx.add_place_description(place_des)
777
+ self.object_map[record.level-1].place = PlaceReference(original=record.value, description=place_des)
778
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
779
+ gxobject = Note(text='Place: ' + record.value if record.value else 'WARNING: NOTE had no value')
780
+ self.object_map[record.level-1].add_note(gxobject)
781
+
782
+ self.object_map[record.level] = gxobject
783
+ else:
784
+ raise TagConversionError(record=record,levelstack=self.object_map)
785
+
786
+ def handle_post(self, record: Gedcom5xRecord):
787
+ if isinstance(self.object_map[record.level-1], Address):
788
+ self.object_map[record.level-1].postalCode = self.clean_str(record.value)
789
+ else:
790
+ raise ValueError(f"I do not know how to handle an 'POST' tag for a {type(self.object_map[record.level-1])}")
791
+
792
+ def handle_publ(self, record: Gedcom5xRecord):
793
+ if isinstance(self.object_map[record.level-1], SourceDescription):
794
+ if record.value and self.gedcomx.agents.byName(record.value):
795
+ gxobject = self.gedcomx.agents.byName(record.value)[0]
796
+ else:
797
+ gxobject = Agent(names=[TextValue(record.value)])
798
+ self.gedcomx.add_agent(gxobject)
799
+ self.object_map[record.level-1].publisher = gxobject
800
+
801
+
802
+ self.object_map[record.level] = gxobject
803
+ else:
804
+ raise TagConversionError(record=record,levelstack=self.object_map)
805
+
806
+ def handle_prob(self, record: Gedcom5xRecord):
807
+ if isinstance(self.object_map[record.level-1], Person):
808
+ gxobject = Fact(type=FactType.Probate)
809
+ self.object_map[record.level-1].add_fact(gxobject)
810
+
811
+
812
+ self.object_map[record.level] = gxobject
813
+ else:
814
+ raise TagConversionError(record=record,levelstack=self.object_map)
815
+
816
+ def handle_uid(self, record: Gedcom5xRecord):
817
+ if isinstance(self.object_map[record.level-1], Agent):
818
+ gxobject = Identifier(value=[URI('UID:' + record.value)] if record.value else [URI('WARNING: NOTE had no value')],type=IdentifierType.Primary) # type: ignore
819
+ self.object_map[record.level-1].add_identifier(gxobject) #NOTE GC7
820
+
821
+ self.object_map[record.level] = gxobject
822
+
823
+ def handle_refn(self, record: Gedcom5xRecord):
824
+ if isinstance(self.object_map[record.level-1], Person) or isinstance(self.object_map[record.level-1], SourceDescription):
825
+ gxobject = Identifier(value=[URI.from_url('Reference Number:' + record.value)] if record.value else [])
826
+ self.object_map[record.level-1].add_identifier(gxobject)
827
+
828
+ self.object_map[record.level] = gxobject
829
+ elif isinstance(self.object_map[record.level-1], Agent):
830
+ gxobject = Identifier(value=[URI('Reference Number:' + record.value)] if record.value else [])
831
+ self.object_map[record.level-1].add_identifier(gxobject) #NOTE GC7
832
+
833
+ self.object_map[record.level] = gxobject
834
+ else:
835
+ raise ValueError(f"Could not handle 'REFN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
836
+
837
+ def handle_repo(self, record: Gedcom5xRecord):
838
+
839
+ if record.level == 0:
840
+
841
+ gxobject = Agent(id=record.xref)
842
+ self.gedcomx.add_agent(gxobject)
843
+
844
+ self.object_map[record.level] = gxobject
845
+
846
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
847
+ if self.gedcomx.agents.byId(record.xref) is not None:
848
+
849
+ # TODO WHere and what to add this to?
850
+ gxobject = self.gedcomx.agents.byId(record.xref)
851
+ self.object_map[record.level-1].repository = gxobject
852
+ self.object_map[record.level] = gxobject
853
+
854
+ else:
855
+ print("handle_repo",record.describe())
856
+ raise ValueError()
857
+ gxobject = Agent(names=[TextValue(record.value)])
858
+ else:
859
+ 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])}")
860
+
861
+
862
+
863
+ self.object_map[record.level] = gxobject
864
+
865
+ def handle_resi(self, record: Gedcom5xRecord):
866
+ if isinstance(self.object_map[record.level-1], Person):
867
+ gxobject = Fact(type=FactType.Residence)
868
+ if record.value and record.value.strip() != '':
869
+ gxobject.add_note(Note(text=record.value))
870
+ self.object_map[record.level-1].add_fact(gxobject)
871
+
872
+
873
+ self.object_map[record.level] = gxobject
874
+ else:
875
+ raise TagConversionError(record=record,levelstack=self.object_map)
876
+
877
+ def handle_rin(self, record: Gedcom5xRecord):
878
+ if isinstance(self.object_map[record.level-1], SourceDescription):
879
+ self.object_map[record.level-1].id = record.value
880
+ self.object_map[record.level-1].add_note(Note(text=f"Source had RIN: of {record.value}"))
881
+
882
+ else:
883
+ raise ValueError(f"Could not handle 'RIN' tag in record {record.describe()}, last stack object {type(self.object_map[record.level-1])}")
884
+
885
+ def handle_sex(self, record: Gedcom5xRecord):
886
+
887
+ if isinstance(self.object_map[record.level-1], Person):
888
+ if record.value == 'M':
889
+ gxobject = Gender(type=GenderType.Male)
890
+ elif record.value == 'F':
891
+ gxobject = Gender(type=GenderType.Female)
892
+ else:
893
+ gxobject = Gender(type=GenderType.Unknown)
894
+ self.object_map[record.level-1].gender = gxobject
895
+
896
+
897
+ self.object_map[record.level] = gxobject
898
+ else:
899
+ assert False
900
+
901
+ def handle_sour(self, record: Gedcom5xRecord):
902
+ if record.level == 0 or record.tag == '_WLNK' or (record.level == 0 and record.tag == 'OBJE'):
903
+ source_description = SourceDescription(id=record.xref.replace('@','') if record.xref else None)
904
+ self.gedcomx.add_source_description(source_description)
905
+
906
+ self.object_map[record.level] = source_description
907
+ else:
908
+ # This 'SOUR' is a SourceReference
909
+ if record.xref is None or record.xref.strip() == '':
910
+ log.warning(f"SOUR points to nothing: {record.describe()}")
911
+ return False
912
+ if self.gedcomx.source_descriptions.byId(record.xref):
913
+ gxobject = SourceReference(descriptionId=record.xref, description=self.gedcomx.source_descriptions.byId(record.xref))
914
+ else:
915
+ log.warning(f'Could not find source with id: {record.xref}')
916
+ source_description = SourceDescription(id=record.xref)
917
+ gxobject = SourceReference(descriptionId=record.value, description=source_description)
918
+ if isinstance(self.object_map[record.level-1],SourceReference):
919
+ self.object_map[record.level-1].description.add_source(gxobject)
920
+ elif record.parent is not None and record.parent.tag in ['NOTE']:
921
+ pass
922
+ else:
923
+ self.object_map[record.level-1].add_source(gxobject)
924
+
925
+ self.object_map[record.level] = gxobject
926
+
927
+ def handle_stae(self, record: Gedcom5xRecord):
928
+ if isinstance(self.object_map[record.level-1], Address):
929
+ self.object_map[record.level-1].stateOrProvince = self.clean_str(record.value)
930
+ else:
931
+ raise ValueError(f"I do not know how to handle an 'STAE' tag for a {type(self.object_map[record.level-1])}")
932
+
933
+ def handle_surn(self, record: Gedcom5xRecord):
934
+ if isinstance(self.object_map[record.level-1], Name):
935
+ surname = NamePart(value=record.value, type=NamePartType.Surname)
936
+ self.object_map[record.level-1]._add_name_part(surname)
937
+ else:
938
+ raise TagConversionError(record=record,levelstack=self.object_map)
939
+
940
+ def handle_text(self, record: Gedcom5xRecord):
941
+ if record.parent is not None and record.parent.tag == 'DATA':
942
+ if isinstance(self.object_map[record.level-2], SourceReference):
943
+ gxobject = TextValue(value=record.value)
944
+ self.object_map[record.level-2].description.add_description(gxobject)
945
+
946
+ self.object_map[record.level] = gxobject
947
+ elif isinstance(self.object_map[record.level-1], SourceDescription):
948
+ gxobject = Document(text=record.value)
949
+ self.object_map[record.level-1].analysis = gxobject
950
+ else:
951
+ assert False
952
+
953
+ def handle_titl(self, record: Gedcom5xRecord):
954
+ if isinstance(self.object_map[record.level-1], SourceDescription):
955
+
956
+ gxobject = TextValue(value=self.clean_str(record.value))
957
+ self.object_map[record.level-1].add_title(gxobject)
958
+
959
+
960
+ self.object_map[record.level] = gxobject
961
+
962
+ elif record.parent is not None and record.parent.tag == 'FILE' and isinstance(self.object_map[record.level-2], SourceDescription):
963
+ gxobject = TextValue(value=record.value)
964
+ self.object_map[record.level-2].add_title(gxobject)
965
+
966
+
967
+ self.object_map[record.level] = gxobject
968
+ elif self.object_map[record.level] and isinstance(self.object_map[record.level], Name):
969
+ gxobject = NamePart(value=record.value, qualifiers=[NamePartQualifier.Title])
970
+
971
+ self.object_map[record.level]._add_name_part(gxobject)
972
+ else:
973
+ log.error(f"raise TagConversionError(record=record,levelstack=self.object_map)")
974
+
975
+ def handle_tran(self, record: Gedcom5xRecord):
976
+ pass
977
+
978
+ def handle_type(self, record: Gedcom5xRecord):
979
+ # peek to see if event or fact
980
+ if isinstance(self.object_map[record.level-1], Event):
981
+ if EventType.guess(record.value):
982
+ self.object_map[record.level-1].type = EventType.guess(record.value)
983
+ else:
984
+ log.warning(f"Could not determine type of event with value '{record.value}'")
985
+ assert False
986
+ self.object_map[record.level-1].type = None
987
+ self.object_map[record.level-1].add_note(Note(text=self.clean_str(record.value)))
988
+ elif isinstance(self.object_map[record.level-1], Fact):
989
+ if not self.object_map[record.level-1].type:
990
+ self.object_map[0].type = FactType.guess(record.value)
991
+ elif isinstance(self.object_map[record.level-1], Identifier):
992
+
993
+ self.object_map[record.level-1].values.append(self.clean_str(record.value))
994
+ self.object_map[record.level-1].type = IdentifierType.Other # type: ignore
995
+
996
+ elif record.parent is not None and record.parent.tag == 'FORM':
997
+ if not self.object_map[0].mediaType:
998
+ self.object_map[0].mediaType = record.value
999
+
1000
+ else:
1001
+ raise ValueError(f"I do not know how to handle 'TYPE' tag for {type(self.object_map[record.level-1])}")
1002
+
1003
+ def handle__url(self, record: Gedcom5xRecord):
1004
+ if isinstance(self.object_map[record.level-2], SourceDescription):
1005
+ self.object_map[record.level-2].about = URI.from_url(record.value) if record.value else None
1006
+ else:
1007
+ raise ValueError(f"Could not handle '_URL' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
1008
+
1009
+ def handle_www(self, record: Gedcom5xRecord):
1010
+ if isinstance(self.object_map[record.level-1], Agent):
1011
+ self.object_map[record.level-1].homepage = self.clean_str(record.value)
1012
+ elif isinstance(self.object_map[record.level-2], SourceReference):
1013
+ self.object_map[record.level-2].description.add_identifier(Identifier(value=[URI.from_url(record.value)] if record.value else []))
1014
+ else:
1015
+ raise ValueError(f"Could not handle 'WWW' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
1016
+
1017
+ def Gedcom5x_GedcomX(self, gedcom5x: Gedcom5x):
1018
+ print(f'Parsing GEDCOM Version {gedcom5x.version}')
1019
+ individual_ids = set()
1020
+ source_ids = set()
1021
+ repository_ids = set()
1022
+ family_ids = set()
1023
+
1024
+ if gedcom5x:
1025
+ for individual in gedcom5x.individuals:
1026
+ individual_ids.add(individual.xref)
1027
+ gx_obj = Person(id=individual.xref)
1028
+ self.gedcomx.add_person(gx_obj)
1029
+
1030
+
1031
+ for source in gedcom5x.sources:
1032
+ source_ids.add(source.xref)
1033
+ gx_obj = SourceDescription(id=source.xref)
1034
+ self.gedcomx.add_source_description(gx_obj)
1035
+
1036
+ for source in gedcom5x.repositories:
1037
+ repository_ids.add(source.xref)
1038
+ gx_obj = Agent(id=source.xref)
1039
+ self.gedcomx.add_agent(gx_obj)
1040
+
1041
+ for family in gedcom5x.families:
1042
+ family_ids.add(family.xref)
1043
+ self.handle_fam(family)
1044
+
1045
+ # Now Parse Zero Level Recrods
1046
+ for individual in gedcom5x.individuals:
1047
+ self.parse_gedcom5x_recrod(individual)
1048
+
1049
+ return self.gedcomx