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/gedcomx.py CHANGED
@@ -3,8 +3,10 @@ DEBUG = False
3
3
  import json
4
4
  import random
5
5
  import string
6
+ import orjson
6
7
 
7
- from typing import Any, Dict, Optional
8
+ from functools import lru_cache
9
+ from typing import Any, Dict, List, Optional
8
10
 
9
11
  """
10
12
  ======================================================================
@@ -15,8 +17,9 @@ from typing import Any, Dict, Optional
15
17
 
16
18
  Created: 2025-07-25
17
19
  Updated:
18
- - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data,
20
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data,
19
21
  id_index functionality, will be used for resolution of Resources
22
+ - 2025-09-03: _from_json_ refactor
20
23
 
21
24
  ======================================================================
22
25
  """
@@ -32,7 +35,7 @@ from .document import Document
32
35
  from .event import Event
33
36
  from .group import Group
34
37
  from .identifier import make_uid
35
- from .Logging import get_logger
38
+ from .logging_hub import logging, hub, ChannelConfig
36
39
  from .person import Person
37
40
  from .place_description import PlaceDescription
38
41
  from .relationship import Relationship, RelationshipType
@@ -42,6 +45,11 @@ from .textvalue import TextValue
42
45
  from .uri import URI
43
46
  #=====================================================================
44
47
 
48
+ log = logging.getLogger("gedcomx")
49
+ serial_log = "gedcomx.serialization"
50
+ deserial_log = "gedcomx.serialization"
51
+
52
+
45
53
 
46
54
  def TypeCollection(item_type):
47
55
  """
@@ -105,17 +113,20 @@ def TypeCollection(item_type):
105
113
  assert False
106
114
 
107
115
  # Update the name index
108
- ''' #TODO Fix name handling on persons
116
+ #TODO Fix name handling on persons
109
117
  if hasattr(item, 'names'):
110
118
  names = getattr(item, 'names')
111
119
  for name in names:
112
- print(name._as_dict_)
113
- name_value = name.value if isinstance(name, TextValue) else name
114
- if name_value in self._name_index:
115
- self._name_index[name_value].append(item)
120
+ #print(name._as_dict_)
121
+ if isinstance(name, TextValue):
122
+ name_value = name.value
123
+ if name_value in self._name_index:
124
+ self._name_index[name_value].append(item)
125
+ else:
126
+ self._name_index[name_value] = [item]
116
127
  else:
117
- self._name_index[name_value] = [item]
118
- '''
128
+ pass
129
+
119
130
  @property
120
131
  def id_index(self):
121
132
  return self._id_index
@@ -155,6 +166,7 @@ def TypeCollection(item_type):
155
166
  def append(self, item):
156
167
  if not isinstance(item, item_type):
157
168
  raise TypeError(f"Expected item of type {item_type.__name__}, got {type(item).__name__}")
169
+ #if item.id in self.id_index: assert False
158
170
  if item.uri:
159
171
  item.uri.path = f'{str(item_type.__name__)}s' if (item.uri.path is None or item.uri.path == "") else item.uri.path
160
172
  else:
@@ -163,6 +175,13 @@ def TypeCollection(item_type):
163
175
  self._items.append(item)
164
176
  self._update_indexes(item)
165
177
 
178
+ def extend(self, items: list):
179
+ """Add multiple items to the collection at once."""
180
+ if not isinstance(items, (list, tuple)):
181
+ raise TypeError("extend() expects a list or tuple of items")
182
+ for item in items:
183
+ self.append(item)
184
+
166
185
  def remove(self, item):
167
186
  if item not in self._items:
168
187
  raise ValueError("Item not found in the collection.")
@@ -235,30 +254,40 @@ class GedcomX:
235
254
  def __init__(self, id: Optional[str] = None,
236
255
  attribution: Optional[Attribution] = None,
237
256
  filepath: Optional[str] = None,
238
- description: Optional[str] = None) -> None:
257
+ description: Optional[str] = None,
258
+ persons: Optional[List[Person]] = None,
259
+ relationships: Optional[List[Relationship]] = None,
260
+ sourceDescriptions: Optional[List[SourceDescription]] = None,
261
+ agents: Optional[List[Agent]] = None,
262
+ places: Optional[List[PlaceDescription]] = None) -> None:
239
263
 
240
264
  self.id = id
241
265
  self.attribution = attribution
242
266
  self._filepath = None
243
267
 
244
268
  self.description = description
245
- self.source_descriptions = TypeCollection(SourceDescription)
269
+ self.sourceDescriptions = TypeCollection(SourceDescription)
270
+ if sourceDescriptions: self.sourceDescriptions.extend(sourceDescriptions)
246
271
  self.persons = TypeCollection(Person)
247
- self.relationships = TypeCollection(Relationship)
272
+ if persons: self.persons.extend(persons)
273
+ self.relationships = TypeCollection(Relationship)
274
+ if relationships: self.relationships.extend(relationships)
248
275
  self.agents = TypeCollection(Agent)
276
+ if agents: self.agents.extend(agents)
249
277
  self.events = TypeCollection(Event)
250
278
  self.documents = TypeCollection(Document)
251
279
  self.places = TypeCollection(PlaceDescription)
280
+ if places: self.places.extend(places)
252
281
  self.groups = TypeCollection(Group)
253
282
 
254
283
  self.relationship_table = {}
255
284
 
256
- self.default_id_generator = make_uid
285
+ #self.default_id_generator = make_uid
257
286
 
258
287
  @property
259
288
  def contents(self):
260
289
  return {
261
- "source_descriptions": len(self.source_descriptions),
290
+ "source_descriptions": len(self.sourceDescriptions),
262
291
  "persons": len(self.persons),
263
292
  "relationships": len(self.relationships),
264
293
  "agents": len(self.agents),
@@ -290,8 +319,8 @@ class GedcomX:
290
319
  def add_source_description(self,sourceDescription: SourceDescription):
291
320
  if sourceDescription and isinstance(sourceDescription,SourceDescription):
292
321
  if sourceDescription.id is None:
293
- sourceDescription.id =self.default_id_generator()
294
- self.source_descriptions.append(item=sourceDescription)
322
+ assert False
323
+ self.sourceDescriptions.append(item=sourceDescription)
295
324
  self.lastSourceDescriptionAdded = sourceDescription
296
325
  else:
297
326
  raise ValueError(f"When adding a SourceDescription, value must be of type SourceDescription, type {type(sourceDescription)} was provided")
@@ -318,7 +347,6 @@ class GedcomX:
318
347
  def add_relationship(self,relationship: Relationship):
319
348
  if relationship and isinstance(relationship,Relationship):
320
349
  if isinstance(relationship.person1,Resource) and isinstance(relationship.person2,Resource):
321
- print("Adding unresolved Relationship")
322
350
  self.relationships.append(relationship)
323
351
  return
324
352
  elif isinstance(relationship.person1,Person) and isinstance(relationship.person2,Person):
@@ -358,26 +386,13 @@ class GedcomX:
358
386
  self.places.append(placeDescription)
359
387
 
360
388
  def add_agent(self,agent: Agent):
361
- """Add a Agent object to the Genealogy
362
-
363
- Args:
364
- agent: Agent Object
365
-
366
- Returns:
367
- None
368
-
369
- Raises:
370
- ValueError: If `agent` is not of type Agent.
371
- """
372
- if agent and isinstance(agent,Agent):
373
- if agent in self.agents:
374
- return
375
- if agent.id is None:
376
- agent.id = make_uid()
377
- if self.agents.byId(agent.id):
378
- pass #TODO Deal with duplicates
379
- #raise ValueError
389
+ if isinstance(agent,Agent) and agent is not None:
390
+ if self.agents.byId(agent.id) is not None:
391
+ print(f"Did not add agent with Duplicate ID")
392
+ return False
380
393
  self.agents.append(agent)
394
+ else:
395
+ raise ValueError()
381
396
 
382
397
  def add_event(self,event_to_add: Event):
383
398
  if event_to_add and isinstance(event_to_add,Event):
@@ -387,24 +402,27 @@ class GedcomX:
387
402
  print("DUPLICATE EVENT")
388
403
  print(event_to_add._as_dict_)
389
404
  print(current_event._as_dict_)
405
+
390
406
  return
391
407
  self.events.append(event_to_add)
392
408
  else:
393
409
  raise ValueError
394
410
 
411
+ @lru_cache(maxsize=65536)
395
412
  def get_person_by_id(self,id: str):
396
413
  filtered = [person for person in self.persons if getattr(person, 'id') == id]
397
414
  if filtered: return filtered[0]
398
415
  return None
399
-
400
- def source(self,id: str):
401
- filtered = [source for source in self.source_descriptions if getattr(source, 'id') == id]
416
+
417
+ @lru_cache(maxsize=65536)
418
+ def source_by_id(self,id: str):
419
+ filtered = [source for source in self.sourceDescriptions if getattr(source, 'id') == id]
402
420
  if filtered: return filtered[0]
403
421
  return None
404
422
 
405
423
  @property
406
424
  def id_index(self):
407
- combined = {**self.source_descriptions.id_index,
425
+ combined = {**self.sourceDescriptions.id_index,
408
426
  **self.persons.id_index,
409
427
  **self.relationships.id_index,
410
428
  **self.agents.id_index,
@@ -419,78 +437,62 @@ class GedcomX:
419
437
 
420
438
  @property
421
439
  def _as_dict(self) -> dict[str, Any]:
422
- type_as_dict: Dict[str, Any] = {}
423
-
424
- if self.persons and len(self.persons) > 0:
425
- type_as_dict["persons"] = [person._as_dict_ for person in self.persons]
426
-
427
- if self.source_descriptions:
428
- type_as_dict["sourceDescriptions"] = [
429
- sd._as_dict_ for sd in self.source_descriptions
430
- ]
431
-
432
- if self.relationships:
433
- type_as_dict["relationships"] = [
434
- rel._as_dict_ for rel in self.relationships
435
- ]
436
-
437
- if self.agents:
438
- type_as_dict["agents"] = [agent._as_dict_ for agent in self.agents]
439
-
440
- if self.events:
441
- type_as_dict["events"] = [event._as_dict_ for event in self.events]
442
-
443
- if self.places:
444
- type_as_dict["places"] = [place._as_dict_ for place in self.places]
445
-
446
- if self.documents:
447
- type_as_dict["documents"] = [doc._as_dict_ for doc in self.documents]
448
-
449
- return type_as_dict
440
+ from .serialization import Serialization
441
+ return Serialization.serialize(self)
442
+
450
443
 
451
444
  @property
452
- def json(self):
445
+ def json(self) -> bytes:
453
446
  """
454
447
  JSON Representation of the GedcomX Genealogy.
455
448
 
456
449
  Returns:
457
450
  str: JSON Representation of the GedcomX Genealogy in the GEDCOM X JSON Serialization Format
458
451
  """
459
- gedcomx_json = {
460
- 'persons': [person._as_dict_ for person in self.persons],
461
- 'sourceDescriptions' : [sourceDescription._as_dict_ for sourceDescription in self.source_descriptions],
462
- 'relationships': [relationship._as_dict_ for relationship in self.relationships],
463
- 'agents': [agent._as_dict_ for agent in self.agents],
464
- 'events': [event._as_dict_ for event in self.events],
465
- 'places': [place._as_dict_ for place in self.places],
466
- 'documents': [document._as_dict_ for document in self.documents],
467
- }
468
- return json.dumps(gedcomx_json, indent=4)
452
+ return orjson.dumps(self._as_dict,option= orjson.OPT_INDENT_2 | orjson.OPT_APPEND_NEWLINE)
453
+
469
454
 
470
455
  @staticmethod
471
456
  def from_json(data: dict):
472
457
  from .serialization import Serialization
473
458
  gx = GedcomX()
459
+
460
+ persons = data.get('persons', [])
461
+ for person in persons:
462
+ if (person := Person._from_json_(person)) is not None:
463
+ gx.add_person(person)
474
464
 
475
465
  source_descriptions = data.get('sourceDescriptions', [])
476
466
  for source in source_descriptions:
477
- gx.add_source_description(Serialization.deserialize(source,SourceDescription))
478
-
479
- persons = data.get('persons', [])
480
- for person in persons:
481
- gx.add_person(Serialization.deserialize(person,Person))
467
+ if (source_description := SourceDescription._from_json_(source)) is not None:
468
+ gx.add_source_description(source_description)
482
469
 
483
470
  relationships = data.get('relationships', [])
484
- for relationship in relationships:
485
- gx.add_relationship(Serialization.deserialize(relationship,Relationship))
486
-
487
- agents = data.get('agents', [])
488
- for agent in agents:
489
- gx.add_agent(Serialization.deserialize(agent,Agent))
471
+ for rel in relationships:
472
+ if (relationship := Relationship._from_json_(rel)) is not None:
473
+ gx.add_relationship(relationship)
490
474
 
475
+ agents = data.get('agents', [])
476
+ for agent_data in agents:
477
+ if (agent := Agent._from_json_(agent_data)) is not None:
478
+ gx.add_agent(agent)
479
+ else:
480
+ raise ValueError()
481
+
491
482
  events = data.get('events', [])
492
- for event in events:
493
- gx.add_event(Serialization.deserialize(event,Event))
483
+ for event_data in events:
484
+ if (event := Event._from_json_(event_data)) is not None:
485
+ gx.add_event(event)
486
+
487
+ places = data.get('places', [])
488
+ for place_data in places:
489
+ if (place := PlaceDescription._from_json_(place_data)) is not None:
490
+ gx.add_place_description(place)
491
+
492
+ documents = data.get('documents', [])
493
+ for doc_data in documents:
494
+ if (event := Document._from_json_(event_data)) is not None:
495
+ pass
494
496
 
495
497
  return gx
496
498
 
gedcomx/gender.py CHANGED
@@ -9,7 +9,7 @@ from typing import List, Optional
9
9
 
10
10
  Created: 2025-08-25
11
11
  Updated:
12
- - 2025-08-31:
12
+ - 2025-09-03: _from_json_ refactor
13
13
 
14
14
  ======================================================================
15
15
  """
@@ -21,9 +21,18 @@ GEDCOM Module Types
21
21
  """
22
22
  from .attribution import Attribution
23
23
  from .conclusion import ConfidenceLevel, Conclusion
24
+ from .Extensions.rs10.rsLink import _rsLinks
24
25
  from .note import Note
25
26
  from .resource import Resource
26
27
  from .source_reference import SourceReference
28
+ from .logging_hub import hub, logging
29
+ """
30
+ ======================================================================
31
+ Logging
32
+ ======================================================================
33
+ """
34
+ log = logging.getLogger("gedcomx")
35
+ serial_log = "gedcomx.serialization"
27
36
  #=====================================================================
28
37
 
29
38
 
@@ -55,25 +64,28 @@ class Gender(Conclusion):
55
64
  notes: Optional[List[Note]] = None,
56
65
  confidence: Optional[ConfidenceLevel] = None,
57
66
  attribution: Optional[Attribution] = None,
58
- type: Optional[GenderType] = None
67
+ type: Optional[GenderType] = None,
68
+ links: Optional[_rsLinks] = None
59
69
  ) -> None:
60
- super().__init__(id=id, lang=lang, sources=sources, analysis=analysis, notes=notes, confidence=confidence, attribution=attribution)
70
+ super().__init__(id=id, lang=lang, sources=sources, analysis=analysis, notes=notes, confidence=confidence, attribution=attribution, links=links)
61
71
  self.type = type
62
72
 
63
73
  @property
64
74
  def _as_dict_(self):
65
- from .serialization import Serialization
66
- type_as_dict = super()._as_dict_
75
+
76
+ type_as_dict = super()._as_dict_ or {}
67
77
  if self.type:
68
78
  type_as_dict['type'] = self.type.value if self.type else None
69
79
 
80
+ return type_as_dict if type_as_dict != {} else None
70
81
 
71
- return Serialization.serialize_dict(type_as_dict)
72
82
 
73
83
  @classmethod
74
- def _from_json_(cls,data):
75
- from .serialization import Serialization
84
+ def _from_json_(cls,data,context):
85
+ conclusion = Conclusion._dict_from_json_(data,context)
86
+ if (type_ := data.get("type")) is not None:
87
+ conclusion["type"] = GenderType(type_)
76
88
 
77
- return Serialization.deserialize(data, Gender)
89
+ return cls(**conclusion)
78
90
 
79
91
 
gedcomx/group.py CHANGED
@@ -32,6 +32,15 @@ from .resource import Resource
32
32
 
33
33
  from .textvalue import TextValue
34
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"
43
+ #=====================================================================
35
44
 
36
45
  class GroupRoleType(Enum): #TODO Impliment
37
46
  def __init__(self) -> None:
gedcomx/identifier.py CHANGED
@@ -1,18 +1,44 @@
1
-
2
- from enum import Enum
3
-
4
1
  from typing import List, Optional, Dict, Any
5
-
6
2
  from collections.abc import Iterator
7
3
  import json
8
- from .resource import Resource
9
- from .uri import URI
10
- from .extensible_enum import ExtensibleEnum
11
-
12
4
  import secrets
13
5
  import string
14
6
  import json
15
7
 
8
+ """
9
+ ======================================================================
10
+ Project: Gedcom-X
11
+ File: identifier.py
12
+ Author: David J. Cartwright
13
+ Purpose:
14
+
15
+ Created: 2025-08-25
16
+ Updated:
17
+ - 2025-09-03: _from_json_ refactor
18
+ - 2025-09-04: fixe identifier and identifieList json deserialization
19
+
20
+ ======================================================================
21
+ """
22
+
23
+ """
24
+ ======================================================================
25
+ GEDCOM Module Types
26
+ ======================================================================
27
+ """
28
+ from .extensible_enum import _EnumItem
29
+ from .resource import Resource
30
+ from .uri import URI
31
+ from .extensible_enum import ExtensibleEnum
32
+ from .logging_hub import hub, logging
33
+ """
34
+ ======================================================================
35
+ Logging
36
+ ======================================================================
37
+ """
38
+ log = logging.getLogger("gedcomx")
39
+ serial_log = "gedcomx.serialization"
40
+ #=====================================================================
41
+
16
42
  def make_uid(length: int = 10, alphabet: str = string.ascii_letters + string.digits) -> str:
17
43
  """
18
44
  Generate a cryptographically secure alphanumeric UID.
@@ -38,6 +64,7 @@ IdentifierType.register("Deprecated", "http://gedcomx.org/Deprecated")
38
64
  IdentifierType.register("Persistent", "http://gedcomx.org/Persistent")
39
65
  IdentifierType.register("External", "https://gedcom.io/terms/v7/EXID")
40
66
  IdentifierType.register("Other", "user provided")
67
+ IdentifierType.register("ChildAndParentsRelationship","http://familysearch.org/v1/ChildAndParentsRelationship")
41
68
 
42
69
  class Identifier:
43
70
  identifier = 'http://gedcomx.org/v1/Identifier'
@@ -54,9 +81,9 @@ class Identifier:
54
81
  from .serialization import Serialization
55
82
  type_as_dict = {}
56
83
  if self.values:
57
- type_as_dict["value"] = list(self.values) # or [v for v in self.values]
84
+ type_as_dict["value"] = None # [v._as_dict_ for v in self.values]
58
85
  if self.type:
59
- type_as_dict["type"] = getattr(self.type, "value", self.type) # type: ignore[attr-defined]
86
+ type_as_dict["type"] = None #getattr(self.type, "value", self.type) # type: ignore[attr-defined]
60
87
 
61
88
  return Serialization.serialize_dict(type_as_dict)
62
89
 
@@ -116,16 +143,13 @@ class IdentifierList:
116
143
  self.add_identifier(identifier)
117
144
  else:
118
145
  raise ValueError("append expects an Identifier instance")
119
-
120
- # keep the old name working; point it at the corrected spelling
121
- def add_identifer(self, identifier: "Identifier"): # backward-compat alias
122
- return self.add_identifier(identifier)
123
-
146
+
124
147
  def add_identifier(self, identifier: "Identifier"):
125
148
  """Add/merge an Identifier (which may contain multiple values)."""
126
149
  if not (identifier and isinstance(identifier, Identifier) and identifier.type):
127
150
  raise ValueError("The 'identifier' must be a valid Identifier instance with a type.")
128
-
151
+ if not isinstance(identifier.type,_EnumItem):
152
+ raise ValueError
129
153
  key = identifier.type.value if hasattr(identifier.type, "value") else str(identifier.type)
130
154
  existing = self.identifiers.get(key, [])
131
155
  merged = self.unique_list(list(existing) + list(identifier.values))
@@ -193,12 +217,12 @@ class IdentifierList:
193
217
  yield (k, v)
194
218
 
195
219
  @classmethod
196
- def _from_json_(cls, data):
220
+ def _from_json_(cls, data,context=None):
197
221
  if isinstance(data, dict):
198
222
  identifier_list = IdentifierList()
199
223
  for key, vals in data.items():
200
224
  # Accept both single value and list in JSON
201
- vals = vals if isinstance(vals, list) else [vals]
225
+ vals = [URI._from_json_(v) for v in vals]
202
226
  identifier_list.add_identifier(
203
227
  Identifier(value=vals, type=IdentifierType(key))
204
228
  )
@@ -208,8 +232,11 @@ class IdentifierList:
208
232
 
209
233
  @property
210
234
  def _as_dict_(self):
211
- # If you want a *copy*, return `dict(self.identifiers)`
212
- return self.identifiers
235
+ type_as_dict = {}
236
+ for k in self.identifiers.keys():
237
+ #print(k,self.identifiers[k],type(self.identifiers[k]))
238
+ type_as_dict[k] = [i._as_dict_ for i in self.identifiers[k]]
239
+ return type_as_dict if type_as_dict != {} else None
213
240
 
214
241
  def __repr__(self) -> str:
215
242
  return json.dumps(self._as_dict_, indent=4)
gedcomx/logging_hub.py CHANGED
@@ -203,5 +203,24 @@ hub.start_channel(
203
203
  make_current=True
204
204
  )
205
205
 
206
+ serial_log = "gedcomx.serialization"
207
+ deserial_log = "gedcomx.deserialization"
208
+
209
+ hub.start_channel(
210
+ ChannelConfig(
211
+ name=serial_log,
212
+ path=f"logs/{serial_log}.log",
213
+ level=logging.DEBUG,
214
+ rotation="size:10MB:3", # rotate by size, keep 3 backups
215
+ ))
216
+
217
+ hub.start_channel(
218
+ ChannelConfig(
219
+ name=deserial_log,
220
+ path=f"logs/{deserial_log}.log",
221
+ level=logging.DEBUG,
222
+ rotation="size:10MB:3", # rotate by size, keep 3 backups
223
+ ))
224
+
206
225
  # (optional) Also a console channel you can switch to
207
226
  hub.start_channel(ChannelConfig(name="console", path=None, level=logging.DEBUG))
gedcomx/mutations.py CHANGED
@@ -23,6 +23,14 @@ GEDCOM Module Types
23
23
  from .gedcom5x import Gedcom5xRecord
24
24
  from .fact import Fact, FactType
25
25
  from .event import Event, EventType
26
+ from .logging_hub import hub, logging
27
+ """
28
+ ======================================================================
29
+ Logging
30
+ ======================================================================
31
+ """
32
+ log = logging.getLogger("gedcomx")
33
+ serial_log = "gedcomx.serialization"
26
34
  #=====================================================================
27
35
 
28
36
  fact_event_table = {
@@ -243,10 +251,7 @@ class GedcomXEventOrFact(GedcomXObject):
243
251
  raise ValueError(f"{record.tag} not found in map")
244
252
 
245
253
  class GedcomXRelationshipBuilder(GedcomXObject):
246
- def __new__(cls,record: Gedcom5xRecord | None = None, object_stack: dict | None = None) -> object:
247
- last_relationship = object_stack.get('lastrelationship',None)
248
- last_relationship_data = object_stack.get('lastrelationshipdata',None)
249
- if not isinstance(last_relationship_data,dict):
250
- last_relationship_data = None
254
+ def __init__(self, record: Gedcom5xRecord) -> None:
255
+ super().__init__(record)
251
256
 
252
257