gedcom-x 0.5.7__py3-none-any.whl → 0.5.9__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 (69) hide show
  1. {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/METADATA +1 -1
  2. gedcom_x-0.5.9.dist-info/RECORD +56 -0
  3. gedcomx/Extensions/rs10/rsLink.py +110 -60
  4. gedcomx/TopLevelTypeCollection.py +1 -1
  5. gedcomx/__init__.py +43 -42
  6. gedcomx/address.py +217 -0
  7. gedcomx/{Agent.py → agent.py} +107 -34
  8. gedcomx/attribution.py +115 -0
  9. gedcomx/{Conclusion.py → conclusion.py} +120 -51
  10. gedcomx/{Converter.py → converter.py} +261 -116
  11. gedcomx/coverage.py +64 -0
  12. gedcomx/{Date.py → date.py} +43 -9
  13. gedcomx/{Document.py → document.py} +60 -12
  14. gedcomx/{Event.py → event.py} +88 -31
  15. gedcomx/evidence_reference.py +20 -0
  16. gedcomx/{Fact.py → fact.py} +81 -74
  17. gedcomx/{Gedcom.py → gedcom.py} +10 -0
  18. gedcomx/{Gedcom5x.py → gedcom5x.py} +31 -21
  19. gedcomx/gedcom7/Exceptions.py +9 -0
  20. gedcomx/gedcom7/GedcomStructure.py +94 -0
  21. gedcomx/gedcom7/Specification.py +347 -0
  22. gedcomx/gedcom7/__init__.py +26 -0
  23. gedcomx/gedcom7/g7interop.py +205 -0
  24. gedcomx/gedcom7/gedcom7.py +160 -0
  25. gedcomx/gedcom7/logger.py +19 -0
  26. gedcomx/{GedcomX.py → gedcomx.py} +109 -106
  27. gedcomx/gender.py +91 -0
  28. gedcomx/group.py +72 -0
  29. gedcomx/{Identifier.py → identifier.py} +48 -21
  30. gedcomx/{LoggingHub.py → logging_hub.py} +19 -0
  31. gedcomx/{Mutations.py → mutations.py} +59 -30
  32. gedcomx/{Name.py → name.py} +88 -47
  33. gedcomx/note.py +105 -0
  34. gedcomx/online_account.py +19 -0
  35. gedcomx/{Person.py → person.py} +61 -41
  36. gedcomx/{PlaceDescription.py → place_description.py} +71 -23
  37. gedcomx/{PlaceReference.py → place_reference.py} +32 -10
  38. gedcomx/{Qualifier.py → qualifier.py} +20 -4
  39. gedcomx/relationship.py +156 -0
  40. gedcomx/resource.py +112 -0
  41. gedcomx/serialization.py +794 -0
  42. gedcomx/source_citation.py +37 -0
  43. gedcomx/source_description.py +401 -0
  44. gedcomx/{SourceReference.py → source_reference.py} +56 -21
  45. gedcomx/subject.py +122 -0
  46. gedcomx/textvalue.py +89 -0
  47. gedcomx/{Translation.py → translation.py} +4 -4
  48. gedcomx/uri.py +273 -0
  49. gedcom_x-0.5.7.dist-info/RECORD +0 -49
  50. gedcomx/Address.py +0 -131
  51. gedcomx/Attribution.py +0 -91
  52. gedcomx/Coverage.py +0 -37
  53. gedcomx/EvidenceReference.py +0 -11
  54. gedcomx/Gender.py +0 -65
  55. gedcomx/Group.py +0 -37
  56. gedcomx/Note.py +0 -73
  57. gedcomx/OnlineAccount.py +0 -10
  58. gedcomx/Relationship.py +0 -97
  59. gedcomx/Resource.py +0 -85
  60. gedcomx/Serialization.py +0 -816
  61. gedcomx/SourceCitation.py +0 -25
  62. gedcomx/SourceDescription.py +0 -314
  63. gedcomx/Subject.py +0 -59
  64. gedcomx/TextValue.py +0 -35
  65. gedcomx/URI.py +0 -105
  66. {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/WHEEL +0 -0
  67. {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/top_level.txt +0 -0
  68. /gedcomx/{Exceptions.py → exceptions.py} +0 -0
  69. /gedcomx/{ExtensibleEnum.py → extensible_enum.py} +0 -0
@@ -1,9 +1,34 @@
1
- from typing import Optional
1
+ from typing import Any, Optional, Dict
2
2
  from datetime import datetime, timezone
3
3
  from dateutil import parser
4
- import time
5
-
6
-
4
+ """
5
+ ======================================================================
6
+ Project: Gedcom-X
7
+ File: date.py
8
+ Author: David J. Cartwright
9
+ Purpose:
10
+
11
+ Created: 2025-08-25
12
+ Updated:
13
+ - 2025-09-03: _from_json refactored
14
+
15
+ ======================================================================
16
+ """
17
+
18
+ """
19
+ ======================================================================
20
+ GEDCOM Module Types
21
+ ======================================================================
22
+ """
23
+ from .logging_hub import hub, logging
24
+ """
25
+ ======================================================================
26
+ Logging
27
+ ======================================================================
28
+ """
29
+ log = logging.getLogger("gedcomx")
30
+ serial_log = "gedcomx.serialization"
31
+ #=====================================================================
7
32
 
8
33
 
9
34
  class DateFormat:
@@ -25,20 +50,29 @@ class Date:
25
50
 
26
51
  @property
27
52
  def _as_dict_(self):
28
- from .Serialization import Serialization
53
+ from .serialization import Serialization
29
54
  type_as_dict = {}
30
55
  if self.original:
31
56
  type_as_dict['original'] = self.original
32
57
  if self.formal:
33
58
  type_as_dict['formal'] = self.formal
59
+ return type_as_dict if type_as_dict != {} else None
34
60
  return Serialization.serialize_dict(type_as_dict)
35
61
 
36
62
  @classmethod
37
- def _from_json_(cls,data):
38
- original = data.get('original',None)
39
- formal = data.get('formal',None)
63
+ def _from_json_(cls,data: Any, context=None):
64
+ if not isinstance(data, dict):
65
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)} data:{data}")
66
+
67
+ date_data: Dict[str, Any] = {}
68
+
69
+ # Scalars
70
+ if (orig := data.get("original")) is not None:
71
+ date_data["original"] = orig
72
+ if (formal := data.get("formal")) is not None:
73
+ date_data["formal"] = formal
40
74
 
41
- return Date(original=original,formal=formal)
75
+ return cls(**date_data)
42
76
 
43
77
 
44
78
 
@@ -1,12 +1,38 @@
1
1
  from enum import Enum
2
- from typing import Optional, List
2
+ from typing import Any, Dict, List, Optional
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: document.py
7
+ Author: David J. Cartwright
8
+ Purpose:
3
9
 
4
- from .Attribution import Attribution
5
- from .Note import Note
6
- from .SourceReference import SourceReference
7
- from .Resource import Resource
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-09-03: _from_json_ refactored
13
+
14
+ ======================================================================
15
+ """
8
16
 
9
- from .Conclusion import Conclusion
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
22
+ from .attribution import Attribution
23
+ from .conclusion import Conclusion, ConfidenceLevel
24
+ from .note import Note
25
+ from .resource import Resource
26
+ from .source_reference import SourceReference
27
+ from .logging_hub import hub, logging
28
+ """
29
+ ======================================================================
30
+ Logging
31
+ ======================================================================
32
+ """
33
+ log = logging.getLogger("gedcomx")
34
+ serial_log = "gedcomx.serialization"
35
+ #=====================================================================
10
36
 
11
37
 
12
38
  class DocumentType(Enum):
@@ -38,7 +64,7 @@ class Document(Conclusion):
38
64
  sources: Optional[List[SourceReference]] = None,
39
65
  analysis: Optional[Resource] = None,
40
66
  notes: Optional[List[Note]] = None,
41
- confidence: Optional[object] = None, # ConfidenceLevel
67
+ confidence: Optional[ConfidenceLevel] = None, # ConfidenceLevel
42
68
  attribution: Optional[Attribution] = None,
43
69
  type: Optional[DocumentType] = None,
44
70
  extracted: Optional[bool] = None, # Default to False
@@ -53,7 +79,7 @@ class Document(Conclusion):
53
79
 
54
80
  @property
55
81
  def _as_dict(self):
56
- from .Serialization import Serialization
82
+ from .serialization import Serialization
57
83
  type_as_dict = super()._as_dict_
58
84
  if self.type:
59
85
  type_as_dict['type'] = self.type.value
@@ -66,9 +92,31 @@ class Document(Conclusion):
66
92
  return Serialization.serialize_dict(type_as_dict)
67
93
 
68
94
  @classmethod
69
- def _from_json_(cls, data: dict):
95
+ def _from_json_(cls, data: Any, context: Any = None) -> "Document":
70
96
  """
71
- Create a Person instance from a JSON-dict (already parsed).
97
+ Build a Document from JSON.
98
+ Shorthand: a bare string becomes {'text': <string>}.
72
99
  """
73
- type_as_dict = Serialization.get_class_fields('Document')
74
- return Serialization.deserialize(data, type_as_dict)
100
+ if not isinstance(data, dict):
101
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
102
+
103
+ obj: Dict[str, Any] = Conclusion._dict_from_json_(data,context)
104
+
105
+ # type (enum)
106
+ if (typ := data.get("type")) is not None:
107
+ obj["type"] = DocumentType(typ)
108
+
109
+
110
+ # extracted (bool; accept common string forms)
111
+ if (ex := data.get("extracted")) is not None:
112
+ obj["extracted"] = bool(ex)
113
+
114
+ # textType (enum)
115
+ if (tt := data.get("textType")) is not None:
116
+ obj["textType"] = TextType(tt)
117
+
118
+ # text (string)
119
+ if (tx := data.get("text")) is not None:
120
+ obj["text"] = str(tx)
121
+
122
+ return cls(**obj)
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import List, Optional
2
+ from typing import Any, Dict, List, Optional
3
3
 
4
4
  """
5
5
  ======================================================================
@@ -11,6 +11,8 @@ from typing import List, Optional
11
11
  Created: 2025-08-25
12
12
  Updated:
13
13
  - 2025-08-31: fixed mutible [] in init, replaced List[Identifer] with IdentifierList
14
+ - 2025-09-03: _from_json_ refactor
15
+ - 2025-09-04: changed _as_dict_ to std dict creation and removed call to serailization
14
16
 
15
17
  ======================================================================
16
18
  """
@@ -20,17 +22,24 @@ from typing import List, Optional
20
22
  GEDCOM Module Type Imports
21
23
  ======================================================================
22
24
  """
23
- from .Attribution import Attribution
24
- from .Conclusion import ConfidenceLevel, Conclusion
25
- from .Date import Date
26
- from .EvidenceReference import EvidenceReference
27
- from .Identifier import IdentifierList
28
- from .Note import Note
29
- from .PlaceReference import PlaceReference
30
- from .Resource import Resource
31
- from .SourceReference import SourceReference
32
- from .Subject import Subject
33
-
25
+ from .attribution import Attribution
26
+ from .conclusion import ConfidenceLevel, Conclusion
27
+ from .date import Date
28
+ from .evidence_reference import EvidenceReference
29
+ from .identifier import IdentifierList
30
+ from .note import Note
31
+ from .place_reference import PlaceReference
32
+ from .resource import Resource
33
+ from .source_reference import SourceReference
34
+ from .subject import Subject
35
+ from .logging_hub import hub, logging
36
+ """
37
+ ======================================================================
38
+ Logging
39
+ ======================================================================
40
+ """
41
+ log = logging.getLogger("gedcomx")
42
+ serial_log = "gedcomx.serialization"
34
43
  #=====================================================================
35
44
 
36
45
  class EventRoleType(Enum):
@@ -103,7 +112,6 @@ class EventRole(Conclusion):
103
112
 
104
113
  @property
105
114
  def _as_dict_(self):
106
- from .Serialization import Serialization
107
115
  type_as_dict = super()._as_dict_
108
116
  if self.person:
109
117
  type_as_dict['person'] = Resource(target=self.person)._as_dict_
@@ -112,8 +120,34 @@ class EventRole(Conclusion):
112
120
  if self.details:
113
121
  type_as_dict['details'] = self.details
114
122
 
115
- return Serialization.serialize_dict(type_as_dict)
123
+ return type_as_dict if type_as_dict != {} else None
124
+
125
+
126
+ @classmethod
127
+ def _from_json_(cls,data,context):
128
+
129
+ if not isinstance(data, dict):
130
+ print("Event: from:",data)
131
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
132
+ event_role_data = Conclusion._dict_from_json_(data,context)
133
+ event_role_data: Dict[str, Any] = {}
134
+
135
+ # person: Person | Resource | URI string
136
+ if (p := data.get("person")) is not None:
137
+ event_role_data["person"] = Resource._from_json_(p, context)
138
+
139
+
140
+ # type: EventRoleType (enum-ish)
141
+ if (typ := data.get("type")) is not None:
142
+ event_role_data["type"] = EventRoleType(typ)
143
+
116
144
 
145
+ # details: TextValue | str
146
+ if (det := data.get("details")) is not None:
147
+ event_role_data["details"] = det # str or passthrough
148
+
149
+ return cls(**event_role_data)
150
+
117
151
  class EventType(Enum):
118
152
  Adoption = "http://gedcomx.org/Adoption"
119
153
  AdultChristening = "http://gedcomx.org/AdultChristening"
@@ -268,22 +302,45 @@ class Event(Subject):
268
302
 
269
303
  @property
270
304
  def _as_dict_(self):
271
- from .Serialization import Serialization
272
- type_as_dict = super()._as_dict_
273
- type_as_dict.update({
274
- 'type': self.type.value if self.type else None,
275
- 'date': self.date,
276
- 'place': self.place._as_dict_ if self.place else None,
277
- 'roles': [role._as_dict_ for role in self.roles],
278
- })
279
-
280
- return Serialization.serialize_dict(type_as_dict)
305
+ from .serialization import Serialization
306
+ type_as_dict = {}
307
+ if self.type:
308
+ type_as_dict['type'] = self.type.value
309
+ if self.date:
310
+ type_as_dict['date'] = self.date._as_dict_
311
+ if self.place:
312
+ type_as_dict['place'] = self.place._as_dict_
313
+ if self.roles:
314
+ #print(self.roles)
315
+ for role in self.roles:
316
+ if isinstance(role,list):
317
+ assert False
318
+ type_as_dict['roles'] = [role._as_dict_ for role in self.roles]
319
+
320
+ return type_as_dict if type_as_dict != {} else None
321
+
281
322
 
282
323
  @classmethod
283
- def _from_json_(cls, data: dict):
284
- """
285
- Create a Person instance from a JSON-dict (already parsed).
286
- """
287
- #type_as_dict = Serialization.get_class_fields('Event')
288
- from .Serialization import Serialization
289
- return Serialization.deserialize(data, Event)
324
+ def _from_json_(cls, data: Dict[str, Any], context: Any = None) -> "Event":
325
+ if not isinstance(data, dict):
326
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
327
+
328
+ event_data: Dict[str, Any] = Subject._dict_from_json_(data,context)
329
+
330
+ # Enum / type
331
+ if (typ := data.get("type")) is not None:
332
+ event_data["type"] = EventType(typ)
333
+
334
+
335
+ # Objects
336
+ if (dt := data.get("date")) is not None:
337
+ event_data["date"] = Date._from_json_(dt, context)
338
+
339
+ if (pl := data.get("place")) is not None:
340
+ event_data["place"] = PlaceReference._from_json_(pl, context)
341
+
342
+ # Lists
343
+ if (rls := data.get("roles")) is not None:
344
+ event_data["roles"] = [EventRole._from_json_(r, context) for r in rls]
345
+
346
+ return cls(**event_data)
@@ -0,0 +1,20 @@
1
+ from typing import Optional
2
+
3
+ from .attribution import Attribution
4
+ from .resource import Resource
5
+ from .logging_hub import hub, logging
6
+ """
7
+ ======================================================================
8
+ Logging
9
+ ======================================================================
10
+ """
11
+ log = logging.getLogger("gedcomx")
12
+ serial_log = "gedcomx.serialization"
13
+ #=====================================================================
14
+
15
+ class EvidenceReference:
16
+ identifier = 'http://gedcomx.org/v1/EvidenceReference'
17
+ version = 'http://gedcomx.org/conceptual-model/v1'
18
+
19
+ def __init__(self, resource: Resource, attribution: Optional[Attribution]) -> None:
20
+ pass
@@ -1,28 +1,46 @@
1
1
  import difflib
2
2
  import re
3
3
 
4
- from datetime import datetime
5
4
  from enum import Enum
6
5
  from typing import List, Optional, Dict, Any
6
+ """
7
+ ======================================================================
8
+ Project: Gedcom-X
9
+ File: fact.py
10
+ Author: David J. Cartwright
11
+ Purpose:
7
12
 
8
- from .Attribution import Attribution
9
- from .Conclusion import ConfidenceLevel
10
- from .Document import Document
11
- from .Date import Date
12
- from .Note import Note
13
- from .PlaceReference import PlaceReference
14
- from .SourceReference import SourceReference
13
+ Created: 2025-08-25
14
+ Updated:
15
+ - 2025-09-03: _from_json_ refactor
16
+
17
+ ======================================================================
18
+ """
15
19
 
16
- from .Resource import Resource
17
-
18
- from .Conclusion import Conclusion
19
- from .Qualifier import Qualifier
20
-
21
- from enum import Enum
22
-
23
- from collections.abc import Sized
24
-
25
- from .Extensions.rs10.rsLink import rsLink, _rsLinkList
20
+ """
21
+ ======================================================================
22
+ GEDCOM Module Types
23
+ ======================================================================
24
+ """
25
+ from .attribution import Attribution
26
+ from .conclusion import Conclusion, ConfidenceLevel
27
+ from .date import Date
28
+ from .document import Document
29
+ from .Extensions.rs10.rsLink import _rsLinks
30
+ from .note import Note
31
+ from .place_reference import PlaceReference
32
+ from .qualifier import Qualifier
33
+ from .resource import Resource
34
+ from .source_reference import SourceReference
35
+ from .logging_hub import hub, logging
36
+ """
37
+ ======================================================================
38
+ Logging
39
+ ======================================================================
40
+ """
41
+ log = logging.getLogger("gedcomx")
42
+ serial_log = "gedcomx.serialization"
43
+ #=====================================================================
26
44
 
27
45
 
28
46
  class FactType(Enum):
@@ -370,7 +388,6 @@ class FactType(Enum):
370
388
  return keywords_to_fact_type[matches[0]]
371
389
  return None
372
390
 
373
-
374
391
  class FactQualifier(Enum):
375
392
  Age = "http://gedcomx.org/Age"
376
393
  Cause = "http://gedcomx.org/Cause"
@@ -389,7 +406,6 @@ class FactQualifier(Enum):
389
406
  }
390
407
  return descriptions.get(self, "No description available.")
391
408
 
392
-
393
409
  class Fact(Conclusion):
394
410
  identifier = 'http://gedcomx.org/v1/Fact'
395
411
  version = 'http://gedcomx.org/conceptual-model/v1'
@@ -397,9 +413,9 @@ class Fact(Conclusion):
397
413
  def __init__(self,
398
414
  id: Optional[str] = None,
399
415
  lang: Optional[str] = None,
400
- sources: Optional[List[SourceReference]] = [],
416
+ sources: Optional[List[SourceReference]] = None,
401
417
  analysis: Optional[Resource | Document] = None,
402
- notes: Optional[List[Note]] = [],
418
+ notes: Optional[List[Note]] = None,
403
419
  confidence: Optional[ConfidenceLevel] = None,
404
420
  attribution: Optional[Attribution] = None,
405
421
  type: Optional[FactType] = None,
@@ -407,7 +423,7 @@ class Fact(Conclusion):
407
423
  place: Optional[PlaceReference] = None,
408
424
  value: Optional[str] = None,
409
425
  qualifiers: Optional[List[FactQualifier]] = None,
410
- links: Optional[_rsLinkList] = None):
426
+ links: Optional[_rsLinks] = None):
411
427
  super().__init__(id, lang, sources, analysis, notes, confidence, attribution, links=links)
412
428
  self.type = type
413
429
  self.date = date
@@ -428,60 +444,51 @@ class Fact(Conclusion):
428
444
 
429
445
  @property
430
446
  def _as_dict_(self):
431
- from .Serialization import Serialization
432
- type_as_dcit = super()._as_dict_
433
- # Only add Relationship-specific fields
434
- if self.type:
435
- type_as_dcit['type'] = getattr(self.type, 'value', self.type)
436
- if self.date:
437
- type_as_dcit['date'] = self.date._as_dict_
438
- if self.place:
439
- type_as_dcit['place'] = self.place._as_dict_
440
- if self.value:
441
- type_as_dcit['value'] = self.value
442
- if self.qualifiers:
443
- type_as_dcit['qualifiers'] = [getattr(q, 'value', q) for q in self.qualifiers]
447
+ '''
448
+ Standard GedcomX Type JSON Serialization
449
+ Returns: dict that contains only field for which the object has data in
450
+ '''
451
+ with hub.use(serial_log):
452
+ log.debug(f"Serializing 'Fact' with id: {self.id}")
453
+ type_as_dict = super()._as_dict_
454
+ if type_as_dict is None:
455
+ log.debug(f"Subject had no fields, creating new dict")
456
+ type_as_dict ={}
457
+ # Only add Relationship-specific fields
458
+ if self.type:
459
+ type_as_dict['type'] = getattr(self.type, 'value', self.type)
460
+ if self.date:
461
+ type_as_dict['date'] = self.date._as_dict_
462
+ if self.place:
463
+ type_as_dict['place'] = self.place._as_dict_
464
+ if self.value:
465
+ type_as_dict['value'] = self.value
466
+ if self.qualifiers and self.qualifiers != []:
467
+ type_as_dict['qualifiers'] = [getattr(q, 'value', q) for q in self.qualifiers]
468
+ log.debug(f"'Fact' serialized with fields: {type_as_dict.keys()}")
469
+ if type_as_dict == {}: log.warning("serializing and empty 'Fact'")
444
470
 
445
- return Serialization.serialize_dict(type_as_dcit)
446
-
447
- @classmethod
448
- def _from_json_(cls, data: Dict[str, Any]) -> 'Fact':
471
+ return type_as_dict if type_as_dict != {} else None
449
472
 
450
- # Extract fields, no trailing commas!
451
- id_ = data.get('id')
452
- lang = data.get('lang', None)
453
- sources = [SourceReference._from_json_(s) for s in data.get('sources',[])]
454
- analysis = (Resource._from_json_(data['analysis'])
455
- if data.get('analysis') else None)
456
- notes = [Note._from_json_(n) for n in data.get('notes',[])]
457
- confidence = (ConfidenceLevel._from_json_(data['confidence'])
458
- if data.get('confidence') else None)
459
- attribution = (Attribution._from_json_(data['attribution']) if data.get('attribution') else None)
460
- fact_type = (FactType.from_value(data['type'])
461
- if data.get('type') else None)
462
- date = (Date._from_json_(data['date'])
463
- if data.get('date') else None)
464
- place = (PlaceReference._from_json_(data['place'])
465
- if data.get('place') else None)
466
- value = data.get('value')
467
- qualifiers = [Qualifier._from_json_(q) for q in data.get('qualifiers', [])]
468
- links = _rsLinkList._from_json_(data.get('links')) if data.get('links') else None
473
+ @classmethod
474
+ def _from_json_(cls, data: Dict[str, Any], context: Any = None) -> "Fact":
475
+ if not isinstance(data, dict):
476
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
477
+ fact_data = Conclusion._dict_from_json_(data,context)
478
+ if (val := data.get("value")) is not None:
479
+ fact_data["value"] = val
480
+ if (date := data.get("date")) is not None:
481
+ fact_data["date"] = Date._from_json_(date, context)
482
+ if (place := data.get("place")) is not None:
483
+ fact_data["place"] = PlaceReference._from_json_(place, context)
484
+ if (ft := data.get("type")) is not None:
485
+ fact_data["type"] = FactType(ft)
486
+ if (quals := data.get("qualifiers")) is not None:
487
+ fact_data["qualifiers"] = [Qualifier._from_json_(q) for q in quals if q is not None]
488
+
489
+ return cls(**fact_data)
469
490
 
470
- return cls(
471
- id=id_,
472
- lang=lang,
473
- sources=sources,
474
- analysis=analysis,
475
- notes=notes,
476
- confidence=confidence,
477
- attribution=attribution,
478
- type=fact_type,
479
- date=date,
480
- place=place,
481
- value=value,
482
- qualifiers=qualifiers,
483
- links=links
484
- )
491
+
485
492
 
486
493
 
487
494
 
@@ -1,5 +1,15 @@
1
1
  import re
2
2
 
3
+ from .logging_hub import hub, logging
4
+ """
5
+ ======================================================================
6
+ Logging
7
+ ======================================================================
8
+ """
9
+ log = logging.getLogger("gedcomx")
10
+ serial_log = "gedcomx.serialization"
11
+ #=====================================================================
12
+
3
13
  class Gedcom():
4
14
  def __init__(self) -> None:
5
15
  pass
@@ -9,7 +9,16 @@ from collections import defaultdict
9
9
  from typing import Iterable, Iterator, List, Optional, Tuple, Union
10
10
 
11
11
  import logging
12
- from .LoggingHub import hub, ChannelConfig
12
+ from .logging_hub import hub, ChannelConfig
13
+ from .logging_hub import hub, logging
14
+ """
15
+ ======================================================================
16
+ Logging
17
+ ======================================================================
18
+ """
19
+ log = logging.getLogger("gedcomx")
20
+ serial_log = "gedcomx.serialization"
21
+ #=====================================================================
13
22
 
14
23
  job_id = "gedcomx.parsing.GEDCOM5x"
15
24
 
@@ -64,18 +73,17 @@ from typing import List, Optional, Iterator, Union
64
73
 
65
74
 
66
75
  class Gedcom5xRecord():
67
- def __init__(
68
- self,
69
- line_num: Optional[int] = None,
70
- level: int = -1,
71
- tag: str | None = "NONR",
72
- xref: Optional[str] = None,
73
- value: Optional[str] = None,
74
- ) -> None:
76
+ def __init__(self,
77
+ line_num: Optional[int] = None,
78
+ level: int = -1,
79
+ tag: str | None = "NONR",
80
+ xref: Optional[str] = None,
81
+ value: Optional[str] = None,
82
+ ) -> None:
75
83
  self.line = line_num
76
84
  self._subRecords: List[Gedcom5xRecord] = []
77
85
  self.level = int(level)
78
- self.xref = xref
86
+ self.xref = xref.replace('@','') if xref else ''
79
87
  self.pointer: bool = False
80
88
  self.tag = str(tag).strip()
81
89
  self.value = value
@@ -144,7 +152,7 @@ class Gedcom5xRecord():
144
152
  # ───────────────────────────────
145
153
  def subRecord(self, tag: str):
146
154
  result = [r for r in self._subRecords if r.tag == tag]
147
- return [] if not result else result
155
+ return None if not result else result
148
156
 
149
157
  def subRecords(self, tag: str | None = None) -> List['Gedcom5xRecord']:
150
158
  if not tag:
@@ -367,7 +375,8 @@ class Gedcom5x():
367
375
  import json
368
376
  return json.dumps({'Individuals': [indi._as_dict_ for indi in self._individuals]},indent=4)
369
377
 
370
- def stats(self):
378
+ @property
379
+ def contents(self):
371
380
  def print_table(pairs):
372
381
 
373
382
  # Calculate the width of the columns
@@ -385,16 +394,16 @@ class Gedcom5x():
385
394
  for name, value in pairs:
386
395
  print(f"{name.ljust(name_width)} | {str(value).ljust(value_width)}")
387
396
 
388
- imports_stats = [
389
- ('Top Level Records', len(self.records)),
390
- ('Individuals', len(self.individuals)),
391
- ('Family Group Records', len(self.families)),
392
- ('Repositories', len(self.repositories)),
393
- ('Sources', len(self.sources)),
394
- ('Objects', len(self.objects))
395
- ]
397
+ imports_stats = {
398
+ 'Top Level Records': len(self.records),
399
+ 'Individuals': len(self.individuals),
400
+ 'Family Group Records': len(self.families),
401
+ 'Repositories': len(self.repositories),
402
+ 'Sources': len(self.sources),
403
+ 'Objects': len(self.objects)
404
+ }
396
405
 
397
- print_table(imports_stats)
406
+ return imports_stats
398
407
 
399
408
  @property
400
409
  def sources(self) -> List[Gedcom5xRecord]:
@@ -475,6 +484,7 @@ class Gedcom5x():
475
484
 
476
485
  level = int(match.group("level"))
477
486
  xref_id = match.group("xref")
487
+ xref_id = xref_id.strip('@') if xref_id else None
478
488
  tag = match.group("tag")
479
489
  value = match.group("value")
480
490
  if value == 'None': value = None