gedcom-x 0.5.8__py3-none-any.whl → 0.5.10__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 (50) hide show
  1. {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/METADATA +1 -1
  2. gedcom_x-0.5.10.dist-info/RECORD +58 -0
  3. gedcomx/Extensions/rs10/rsLink.py +109 -59
  4. gedcomx/__init__.py +4 -1
  5. gedcomx/address.py +102 -16
  6. gedcomx/agent.py +81 -24
  7. gedcomx/attribution.py +52 -28
  8. gedcomx/conclusion.py +98 -46
  9. gedcomx/converter.py +209 -79
  10. gedcomx/coverage.py +10 -1
  11. gedcomx/date.py +42 -8
  12. gedcomx/document.py +37 -7
  13. gedcomx/event.py +77 -20
  14. gedcomx/evidence_reference.py +9 -0
  15. gedcomx/extensible.py +86 -0
  16. gedcomx/fact.py +53 -54
  17. gedcomx/gedcom.py +10 -0
  18. gedcomx/gedcom5x.py +30 -20
  19. gedcomx/gedcom7/GedcomStructure.py +1 -3
  20. gedcomx/gedcom7/__init__.py +2 -2
  21. gedcomx/gedcom7/{Gedcom7.py → gedcom7.py} +3 -3
  22. gedcomx/gedcom7/specification.py +4817 -0
  23. gedcomx/gedcomx.py +95 -93
  24. gedcomx/gender.py +21 -9
  25. gedcomx/group.py +9 -0
  26. gedcomx/identifier.py +47 -20
  27. gedcomx/logging_hub.py +19 -0
  28. gedcomx/mutations.py +10 -5
  29. gedcomx/name.py +74 -33
  30. gedcomx/note.py +50 -18
  31. gedcomx/online_account.py +9 -0
  32. gedcomx/person.py +46 -27
  33. gedcomx/place_description.py +54 -8
  34. gedcomx/place_reference.py +30 -8
  35. gedcomx/qualifier.py +19 -3
  36. gedcomx/relationship.py +55 -14
  37. gedcomx/resource.py +45 -18
  38. gedcomx/schemas.py +328 -0
  39. gedcomx/serialization.py +400 -421
  40. gedcomx/source_citation.py +16 -4
  41. gedcomx/source_description.py +181 -94
  42. gedcomx/source_reference.py +51 -16
  43. gedcomx/subject.py +59 -14
  44. gedcomx/textvalue.py +66 -12
  45. gedcomx/translation.py +3 -3
  46. gedcomx/uri.py +155 -3
  47. gedcom_x-0.5.8.dist-info/RECORD +0 -56
  48. gedcomx/gedcom7/Specification.py +0 -347
  49. {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/WHEEL +0 -0
  50. {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/top_level.txt +0 -0
gedcomx/name.py CHANGED
@@ -11,6 +11,7 @@ from typing import List,Optional
11
11
  Created: 2025-08-25
12
12
  Updated:
13
13
  - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
14
+ - 2025-09-03: _from_json_ refactor
14
15
 
15
16
  ======================================================================
16
17
  """
@@ -25,10 +26,19 @@ from .attribution import Attribution
25
26
  from .conclusion import Conclusion, ConfidenceLevel
26
27
  from .date import Date
27
28
  from .document import Document
29
+ from .Extensions.rs10.rsLink import _rsLinks
28
30
  from .note import Note
29
31
  from .resource import Resource
30
32
  from .source_reference import SourceReference
31
- #======================================================================
33
+ from .logging_hub import hub, logging
34
+ """
35
+ ======================================================================
36
+ Logging
37
+ ======================================================================
38
+ """
39
+ log = logging.getLogger("gedcomx")
40
+ serial_log = "gedcomx.serialization"
41
+ #=====================================================================
32
42
 
33
43
 
34
44
  class NameType(Enum):
@@ -39,6 +49,7 @@ class NameType(Enum):
39
49
  AdoptiveName = "http://gedcomx.org/AdoptiveName"
40
50
  FormalName = "http://gedcomx.org/FormalName"
41
51
  ReligiousName = "http://gedcomx.org/ReligiousName"
52
+ Other = "other"
42
53
 
43
54
  @property
44
55
  def description(self):
@@ -153,14 +164,28 @@ class NamePart:
153
164
  type_as_dict['value'] = self.value
154
165
  if self.qualifiers:
155
166
  type_as_dict['qualifiers'] = [q.value for q in self.qualifiers]
156
- Serialization.serialize_dict(type_as_dict)
167
+ return type_as_dict if type_as_dict != {} else None
157
168
 
158
169
  @classmethod
159
- def _from_json_(cls,data):
160
- name_part =NamePart(type=NamePartType(data['type']) if 'type' in data else None,
161
- value=data.get('value'),
162
- qualifiers=[NamePartQualifier(q) for q in data.get('qualifiers')])
163
- return name_part
170
+ def _from_json_(cls, data: dict, context=None) -> "NamePart":
171
+ if not isinstance(data, dict):
172
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
173
+
174
+ name_part = {}
175
+
176
+ # Enum / type
177
+ if (typ := data.get("type")) is not None:
178
+ name_part["type"] = NamePartType(typ)
179
+
180
+ # String value
181
+ if (val := data.get("value")) is not None:
182
+ name_part["value"] = val
183
+
184
+ # List of qualifiers
185
+ if (quals := data.get("qualifiers")) is not None:
186
+ name_part["qualifiers"] = [NamePartQualifier(q) for q in quals]
187
+
188
+ return cls(**name_part)
164
189
 
165
190
  def __eq__(self, other):
166
191
  if not isinstance(other, NamePart):
@@ -233,17 +258,29 @@ class NameForm:
233
258
  if self.fullText:
234
259
  type_as_dict['fullText'] = self.fullText
235
260
  if self.parts:
236
- type_as_dict['parts'] = [part._as_dict_ for part in self.parts if part]
261
+ type_as_dict['parts'] = [part._as_dict_ for part in self.parts if part is not None]
262
+ return type_as_dict if type_as_dict != {} else None
237
263
  return Serialization.serialize_dict(type_as_dict)
238
264
 
239
265
  @classmethod
240
- def _from_json_(cls, data: dict) -> "NameForm":
241
- """Build a NameForm from JSON-like dict."""
242
- return cls(
243
- lang=data.get("lang", "en"),
244
- fullText=data.get("fullText"),
245
- parts=[NamePart._from_json_(p) for p in ensure_list(data.get("parts"))],
246
- )
266
+ def _from_json_(cls, data: dict, context=None) -> "NameForm":
267
+ if not isinstance(data, dict):
268
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
269
+
270
+ name_form = {}
271
+
272
+ # Scalars
273
+ if (lang := data.get("lang")) is not None:
274
+ name_form["lang"] = lang
275
+
276
+ if (full := data.get("fullText")) is not None:
277
+ name_form["fullText"] = full
278
+
279
+ # List of parts
280
+ if (parts := data.get("parts")) is not None:
281
+ name_form["parts"] = [NamePart._from_json_(p, context) for p in parts if p]
282
+
283
+ return cls(**name_form)
247
284
 
248
285
  def _fulltext_parts(self):
249
286
  pass
@@ -315,8 +352,9 @@ class Name(Conclusion):
315
352
  attribution: Optional[Attribution] = None,
316
353
  type: Optional[NameType] = None,
317
354
  nameForms: Optional[List[NameForm]]= None,
318
- date: Optional[Date] = None) -> None:
319
- super().__init__(id, lang, sources, analysis, notes, confidence, attribution)
355
+ date: Optional[Date] = None,
356
+ links: Optional[_rsLinks] = None) -> None:
357
+ super().__init__(id, lang, sources, analysis, notes, confidence, attribution,links=links)
320
358
  self.type = type
321
359
  self.nameForms = nameForms if nameForms else []
322
360
  self.date = date
@@ -330,8 +368,7 @@ class Name(Conclusion):
330
368
 
331
369
  @property
332
370
  def _as_dict_(self):
333
- from .serialization import Serialization
334
- type_as_dict = super()._as_dict_
371
+ type_as_dict = super()._as_dict_ or {}
335
372
  if self.type:
336
373
  type_as_dict['type'] = getattr(self.type, 'value', self.type)
337
374
  if self.nameForms:
@@ -339,23 +376,27 @@ class Name(Conclusion):
339
376
  if self.date:
340
377
  type_as_dict['date'] = self.date._as_dict_
341
378
 
342
- return Serialization.serialize_dict(type_as_dict)
379
+ return type_as_dict if type_as_dict != {} else None
380
+
343
381
 
344
382
  @classmethod
345
- def _from_json_(cls, data: dict) -> "Name":
383
+ def _from_json_(cls, data: dict,context = None) -> "Name":
346
384
  """Build a Name from JSON-like dict."""
347
- return cls(
348
- id=data.get("id"),
349
- lang=data.get("lang", "en"),
350
- sources=[SourceReference._from_json_(s) for s in ensure_list(data.get("sources"))],
351
- analysis=Resource._from_json_(data["analysis"]) if data.get("analysis") else None,
352
- notes=[Note._from_json_(n) for n in ensure_list(data.get("notes"))],
353
- confidence=ConfidenceLevel._from_json_(data["confidence"]) if data.get("confidence") else None,
354
- attribution=Attribution._from_json_(data["attribution"]) if data.get("attribution") else None,
355
- type=NameType(data["type"]) if data.get("type") else None,
356
- nameForms=[NameForm._from_json_(nf) for nf in ensure_list(data.get("nameForms"))],
357
- date=Date._from_json_(data["date"]) if data.get("date") else None,
358
- )
385
+ name = Conclusion._dict_from_json_(data)
386
+
387
+ # Enum
388
+ if (typ := data.get("type")) is not None:
389
+ name["type"] = NameType(typ)
390
+
391
+ # List
392
+ if (forms := data.get("nameForms")) is not None:
393
+ name["nameForms"] = [NameForm._from_json_(f, context) for f in forms]
394
+
395
+ # Object
396
+ if (date := data.get("date")) is not None:
397
+ name["date"] = Date._from_json_(date, context)
398
+
399
+ return cls(**name)
359
400
 
360
401
  def __str__(self) -> str:
361
402
  """Return a human-readable string for the Name-like object."""
gedcomx/note.py CHANGED
@@ -1,6 +1,33 @@
1
- from typing import Optional
1
+ from typing import Any, Optional
2
+ """
3
+ ======================================================================
4
+ Project: Gedcom-X
5
+ File: note.py
6
+ Author: David J. Cartwright
7
+ Purpose: Python Object representation of GedcomX Name, NameType, NameForm, NamePart Types
2
8
 
9
+ Created: 2025-08-25
10
+ Updated:
11
+ - 2025-09-03: _from_json_ refactor
12
+
13
+ ======================================================================
14
+ """
15
+
16
+ """
17
+ ======================================================================
18
+ GEDCOM Module Types
19
+ ======================================================================
20
+ """
3
21
  from .attribution import Attribution
22
+ from .logging_hub import hub, logging
23
+ """
24
+ ======================================================================
25
+ Logging
26
+ ======================================================================
27
+ """
28
+ log = logging.getLogger("gedcomx")
29
+ serial_log = "gedcomx.serialization"
30
+ #=====================================================================
4
31
 
5
32
  class Note:
6
33
  identifier = 'http://gedcomx.org/v1/Note'
@@ -35,6 +62,7 @@ class Note:
35
62
  if self.attribution:
36
63
  # If attribution exposes `_as_dict_` as a property, use it; otherwise include as-is
37
64
  type_as_dict["attribution"] = getattr(self.attribution, "_as_dict_", self.attribution)
65
+ return type_as_dict if type_as_dict != {} else None
38
66
  return Serialization.serialize_dict(type_as_dict)
39
67
 
40
68
  def __eq__(self, other):
@@ -52,22 +80,26 @@ class Note:
52
80
  )
53
81
 
54
82
  @classmethod
55
- def _from_json_(cls, data: dict):
56
- """
57
- Create a Note instance from a JSON-dict (already parsed).
58
- """
59
- # Basic scalar fields
60
- lang = data.get('lang', 'en')
61
- text = data.get('text')
62
- subject = data.get('subject')
63
- # Add other fields as needed
83
+ def _from_json_(cls, data: Any, context=None) -> "Note":
84
+ # Allow shorthand: "some note text"
85
+ #if isinstance(data, str):
86
+ # return cls(text=data)
64
87
 
65
- # Build the instance
66
- inst = cls(
67
- lang = lang,
68
- text = text,
69
- subject = subject,
70
- # Add other fields as needed
71
- )
88
+ if not isinstance(data, dict):
89
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
90
+
91
+ obj: dict[str, Any] = {}
92
+
93
+ # Scalars
94
+ if (lang := data.get("lang")) is not None:
95
+ obj["lang"] = lang
96
+ if (subject := data.get("subject")) is not None:
97
+ obj["subject"] = subject
98
+ if (text := data.get("text")) is not None:
99
+ obj["text"] = text
100
+
101
+ # Object
102
+ if (attr := data.get("attribution")) is not None:
103
+ obj["attribution"] = Attribution._from_json_(attr, context)
72
104
 
73
- return inst
105
+ return cls(**obj)
gedcomx/online_account.py CHANGED
@@ -1,6 +1,15 @@
1
1
  from typing import Optional
2
2
 
3
3
  from .resource import Resource
4
+ from .logging_hub import hub, logging
5
+ """
6
+ ======================================================================
7
+ Logging
8
+ ======================================================================
9
+ """
10
+ log = logging.getLogger("gedcomx")
11
+ serial_log = "gedcomx.serialization"
12
+ #=====================================================================
4
13
 
5
14
  class OnlineAccount:
6
15
  identifier = 'http://gedcomx.org/v1/OnlineAccount'
gedcomx/person.py CHANGED
@@ -11,6 +11,7 @@ from urllib.parse import urljoin
11
11
  Created: 2025-08-25
12
12
  Updated:
13
13
  - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
14
+ - 2025-09-03: _from_json_ refactor
14
15
 
15
16
  ======================================================================
16
17
  """
@@ -24,17 +25,31 @@ from .attribution import Attribution
24
25
  from .conclusion import ConfidenceLevel
25
26
  from .date import Date
26
27
  from .evidence_reference import EvidenceReference
27
- from .Extensions.rs10.rsLink import _rsLinkList
28
+ from .Extensions.rs10.rsLink import _rsLinks
29
+ from .extensible import Extensible
28
30
  from .fact import Fact, FactType
29
31
  from .gender import Gender, GenderType
30
32
  from .identifier import IdentifierList
33
+ from .logging_hub import hub, logging
31
34
  from .name import Name, QuickName
32
35
  from .note import Note
33
36
  from .resource import Resource
34
37
  from .source_reference import SourceReference
35
38
  from .subject import Subject
39
+ from .logging_hub import hub, logging
40
+ """
41
+ ======================================================================
42
+ Logging
43
+ ======================================================================
44
+ """
45
+ log = logging.getLogger("gedcomx")
46
+ serial_log = "gedcomx.serialization"
47
+ deserial_log = "degedcomx.serialization"
48
+ #=====================================================================
49
+
36
50
 
37
- class Person(Subject):
51
+
52
+ class Person(Extensible,Subject):
38
53
  """A person in the system.
39
54
 
40
55
  Args:
@@ -60,12 +75,12 @@ class Person(Subject):
60
75
  evidence: Optional[List[EvidenceReference]] = None,
61
76
  media: Optional[List[SourceReference]] = None,
62
77
  identifiers: Optional[IdentifierList] = None,
63
- private: Optional[bool] = False,
78
+ private: Optional[bool] = None,
64
79
  gender: Optional[Gender] = Gender(type=GenderType.Unknown),
65
80
  names: Optional[List[Name]] = None,
66
81
  facts: Optional[List[Fact]] = None,
67
- living: Optional[bool] = False,
68
- links: Optional[_rsLinkList] = None,
82
+ living: Optional[bool] = None,
83
+ links: Optional[_rsLinks] = None,
69
84
  uri: Optional[Resource] = None) -> None:
70
85
  # Call superclass initializer if needed
71
86
  super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links,uri=uri)
@@ -130,29 +145,33 @@ class Person(Subject):
130
145
  @property
131
146
  def _as_dict_(self):
132
147
  from .serialization import Serialization
133
- type_as_dict = super()._as_dict_
134
- if self.private is not None:
135
- type_as_dict['private'] = self.private
136
- if self.living is not None:
137
- type_as_dict['living'] = self.living
138
- if self.gender:
139
- type_as_dict['gender'] = self.gender._as_dict_
140
- if self.names:
141
- type_as_dict['names'] = [n._as_dict_ for n in self.names if n]
142
- if self.facts:
143
- type_as_dict['facts'] = [f._as_dict_ for f in self.facts if f]
144
- if self.uri:
145
- type_as_dict['uri'] = self.uri._as_dict_
146
-
147
- return Serialization.serialize_dict(type_as_dict)
148
+ return Serialization.serialize(self)
149
+
148
150
 
149
151
  @classmethod
150
- def _from_json_(cls, data: dict):
151
- """
152
- Create a Person instance from a JSON-dict (already parsed).
153
- """
154
- from .serialization import Serialization
155
- return Serialization.deserialize(data, Person)
152
+ def _from_json_(cls, data: dict, context = None) -> "Person":
153
+ def _to_bool(v):
154
+ if isinstance(v, str):
155
+ return v.strip().lower() in ("1", "true", "yes", "y", "t")
156
+ return bool(v)
157
+ with hub.use(deserial_log):
158
+ log.debug(f"Deserializing a Person")
159
+ person_data: dict = Subject._dict_from_json_(data,context)
160
+ if (private := data.get("private")) is not None:
161
+ person_data["private"] = _to_bool(private)
162
+ if (living := data.get("living")) is not None:
163
+ person_data["living"] = _to_bool(living)
164
+ if (gender := data.get("gender")) is not None:
165
+ person_data["gender"] = Gender._from_json_(gender, context)
166
+ if (names := data.get("names")) is not None:
167
+ person_data["names"] = [Name._from_json_(n, context) for n in names]
168
+ if (facts := data.get("facts")) is not None:
169
+ person_data["facts"] = [Fact._from_json_(f, context) for f in facts]
170
+
171
+ diff = data.keys() - person_data.keys()
172
+ log.debug(f"Desserialization found keys {diff} that are not in deserialization list")
173
+
174
+ return cls(**person_data)
156
175
 
157
176
  @classmethod
158
177
  def from_familysearch(cls, pid: str, token: str, *, base_url: Optional[str] = None):
@@ -186,7 +205,7 @@ class Person(Subject):
186
205
  raise ValueError(f"FamilySearch returned no person for PID {pid}")
187
206
 
188
207
  # Keep your existing deserialization helper
189
- return Serialization.deserialize(person_json, Person)
208
+ return Person._from_json_(person_json)
190
209
 
191
210
  class QuickPerson:
192
211
  """A GedcomX Person Data Type created with basic information.
@@ -1,4 +1,4 @@
1
- from typing import List, Optional
1
+ from typing import Any, Dict, List, Optional
2
2
 
3
3
  """
4
4
  ======================================================================
@@ -10,6 +10,7 @@ from typing import List, Optional
10
10
  Created: 2025-08-25
11
11
  Updated:
12
12
  - 2025-09-01: filename PEP8 standard
13
+ - 2025-09-03: _from_json_ refactored
13
14
 
14
15
  ======================================================================
15
16
  """
@@ -23,6 +24,7 @@ from .attribution import Attribution
23
24
  from .conclusion import ConfidenceLevel
24
25
  from .date import Date
25
26
  from .evidence_reference import EvidenceReference
27
+ from .Extensions.rs10.rsLink import _rsLinks #new
26
28
  from .identifier import IdentifierList
27
29
  from .note import Note
28
30
  from .resource import Resource
@@ -30,6 +32,14 @@ from .source_reference import SourceReference
30
32
  from .subject import Subject
31
33
  from .textvalue import TextValue
32
34
  from .uri import URI
35
+ from .logging_hub import hub, logging
36
+ """
37
+ ======================================================================
38
+ Logging
39
+ ======================================================================
40
+ """
41
+ log = logging.getLogger("gedcomx")
42
+ serial_log = "gedcomx.serialization"
33
43
  #=====================================================================
34
44
 
35
45
 
@@ -78,9 +88,11 @@ class PlaceDescription(Subject):
78
88
  latitude: Optional[float] = None,
79
89
  longitude: Optional[float] = None,
80
90
  temporalDescription: Optional[Date] = None,
81
- spatialDescription: Optional[Resource] = None,) -> None:
91
+ spatialDescription: Optional[Resource] = None,
92
+ links: Optional[_rsLinks] = None
93
+ ) -> None:
82
94
 
83
- super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
95
+ super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links)
84
96
  self.names = names
85
97
  self.type = type
86
98
  self.place = place
@@ -93,7 +105,7 @@ class PlaceDescription(Subject):
93
105
  @property
94
106
  def _as_dict_(self):
95
107
  from .serialization import Serialization
96
- type_as_dict = super()._as_dict_
108
+ type_as_dict = super()._as_dict_ or {}
97
109
 
98
110
  if self.names:
99
111
  type_as_dict["names"] = [n._as_dict_ for n in self.names if n]
@@ -112,12 +124,46 @@ class PlaceDescription(Subject):
112
124
  if self.spatialDescription:
113
125
  type_as_dict["spatialDescription"] = self.spatialDescription._as_dict_
114
126
 
127
+ return type_as_dict if type_as_dict != {} else None
115
128
  return Serialization.serialize_dict(type_as_dict)
116
129
 
117
130
  @classmethod
118
- def _from_json_(cls, data: dict):
131
+ def _from_json_(cls, data: Any, context: Any = None) -> "PlaceDescription":
119
132
  """
120
133
  Create a PlaceDescription instance from a JSON-dict (already parsed).
121
- """
122
- from .serialization import Serialization
123
- return Serialization.deserialize(data, PlaceDescription)
134
+ """
135
+ if not isinstance(data, dict):
136
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
137
+
138
+ person_data: Dict[str, Any] = Subject._dict_from_json_(data,context)
139
+
140
+ # names (allow both list and a single 'name' alias)
141
+ if (names := data.get("names")) is not None:
142
+ person_data["names"] = [TextValue._from_json_(n, context) for n in names]
143
+
144
+ # type (string for now; promote to enum later)
145
+ if (typ := data.get("type")) is not None:
146
+ person_data["type"] = typ
147
+
148
+ # place: URI (accept string or dict)
149
+ if (pl := data.get("place")) is not None:
150
+ person_data["place"] = URI(pl)
151
+
152
+ # jurisdiction: Resource | PlaceDescription
153
+ if (jur := data.get("jurisdiction")) is not None:
154
+ person_data["jurisdiction"] = Resource._from_json_(jur, context)
155
+
156
+ # coordinates
157
+ if (lat := data.get("latitude")) is not None:
158
+ person_data["latitude"] = float(lat)
159
+ if (lon := data.get("longitude")) is not None:
160
+ person_data["longitude"] = float(lon)
161
+
162
+ # temporal / spatial descriptions
163
+ if (td := data.get("temporalDescription")) is not None:
164
+ person_data["temporalDescription"] = Date._from_json_(td, context)
165
+
166
+ if (sd := data.get("spatialDescription")) is not None:
167
+ person_data["spatialDescription"] = Resource._from_json_(sd, context)
168
+
169
+ return cls(**person_data)
@@ -13,6 +13,7 @@ if TYPE_CHECKING:
13
13
  Created: 2025-08-25
14
14
  Updated:
15
15
  - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
16
+ - 2025-09-03: _from_json refactored
16
17
 
17
18
  ======================================================================
18
19
  """
@@ -23,6 +24,16 @@ GEDCOM Module Types
23
24
  ======================================================================
24
25
  """
25
26
  from .resource import Resource
27
+ from .logging_hub import hub, logging
28
+ from .uri import URI
29
+ """
30
+ ======================================================================
31
+ Logging
32
+ ======================================================================
33
+ """
34
+ log = logging.getLogger("gedcomx")
35
+ serial_log = "gedcomx.serialization"
36
+ #=====================================================================
26
37
 
27
38
  class PlaceReference:
28
39
  """defines a reference to a PlaceDescription.
@@ -39,24 +50,35 @@ class PlaceReference:
39
50
 
40
51
  def __init__(self,
41
52
  original: Optional[str] = None,
42
- description: Optional["Resource | PlaceDescription"] = None) -> None:
53
+ description: Optional["URI | PlaceDescription"] = None) -> None:
43
54
  self.original = original
44
- self.description = description
55
+ self.description = description # descriptionRef
45
56
 
46
57
  @property
47
58
  def _as_dict_(self):
48
- from .serialization import Serialization
59
+
49
60
  type_as_dict = {}
50
61
  if self.original:
51
62
  type_as_dict['original'] = self.original
52
63
  if self.description:
53
- type_as_dict['description'] = self.description._as_dict_
54
- return Serialization.serialize_dict(type_as_dict)
64
+ type_as_dict['description'] = URI(target=self.description)._as_dict_
65
+ return type_as_dict if type_as_dict != {} else None
66
+
55
67
 
56
68
  @classmethod
57
- def _from_json_(cls, data):
58
- from .serialization import Serialization
59
- return Serialization.deserialize(data, PlaceReference)
69
+ def _from_json_(cls, data, context=None) -> "PlaceReference":
70
+ if not isinstance(data, dict):
71
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
72
+
73
+ place_reference_data = {}
74
+
75
+ # Scalars
76
+ if (orig := data.get("original")) is not None:
77
+ place_reference_data["original"] = orig
78
+ if (desc := data.get("description")) is not None:
79
+ place_reference_data["description"] = URI._from_json_(desc, context)
80
+
81
+ return cls(**place_reference_data)
60
82
 
61
83
 
62
84
 
gedcomx/qualifier.py CHANGED
@@ -11,9 +11,19 @@ from typing import Optional
11
11
  Updated:
12
12
  - 2025-08-31: _as_dict_ to only create entries in dict for fields that
13
13
  hold data, updated _from_json
14
+ - 2025-09-03: _from_json_ refactor
14
15
 
15
16
  ======================================================================
16
17
  """
18
+ from .logging_hub import hub, logging
19
+ """
20
+ ======================================================================
21
+ Logging
22
+ ======================================================================
23
+ """
24
+ log = logging.getLogger("gedcomx")
25
+ serial_log = "gedcomx.serialization"
26
+ #=====================================================================
17
27
 
18
28
  class Qualifier:
19
29
  """defines the data structure used to supply additional details, annotations,
@@ -45,10 +55,16 @@ class Qualifier:
45
55
  if self.value:
46
56
  type_as_dict["value"] = self.value
47
57
 
58
+ return type_as_dict if type_as_dict != {} else None
48
59
  return Serialization.serialize_dict(type_as_dict)
49
60
 
50
61
  @classmethod
51
- def _from_json(cls,data):
52
- qualifier = Qualifier(name=data.get('name',"ERROR: This Qualifier require a 'name' but has none."),value=data.get('value'))
53
- return qualifier
62
+ def _from_json_(cls, data: dict, context=None) -> "Qualifier":
63
+ if not isinstance(data, dict):
64
+ raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
65
+
66
+ name = data.get("name")
67
+ value = data.get("value")
68
+
69
+ return cls(name=name, value=value)
54
70