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.
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/METADATA +1 -1
- gedcom_x-0.5.10.dist-info/RECORD +58 -0
- gedcomx/Extensions/rs10/rsLink.py +109 -59
- gedcomx/__init__.py +4 -1
- gedcomx/address.py +102 -16
- gedcomx/agent.py +81 -24
- gedcomx/attribution.py +52 -28
- gedcomx/conclusion.py +98 -46
- gedcomx/converter.py +209 -79
- gedcomx/coverage.py +10 -1
- gedcomx/date.py +42 -8
- gedcomx/document.py +37 -7
- gedcomx/event.py +77 -20
- gedcomx/evidence_reference.py +9 -0
- gedcomx/extensible.py +86 -0
- gedcomx/fact.py +53 -54
- gedcomx/gedcom.py +10 -0
- gedcomx/gedcom5x.py +30 -20
- gedcomx/gedcom7/GedcomStructure.py +1 -3
- gedcomx/gedcom7/__init__.py +2 -2
- gedcomx/gedcom7/{Gedcom7.py → gedcom7.py} +3 -3
- gedcomx/gedcom7/specification.py +4817 -0
- gedcomx/gedcomx.py +95 -93
- gedcomx/gender.py +21 -9
- gedcomx/group.py +9 -0
- gedcomx/identifier.py +47 -20
- gedcomx/logging_hub.py +19 -0
- gedcomx/mutations.py +10 -5
- gedcomx/name.py +74 -33
- gedcomx/note.py +50 -18
- gedcomx/online_account.py +9 -0
- gedcomx/person.py +46 -27
- gedcomx/place_description.py +54 -8
- gedcomx/place_reference.py +30 -8
- gedcomx/qualifier.py +19 -3
- gedcomx/relationship.py +55 -14
- gedcomx/resource.py +45 -18
- gedcomx/schemas.py +328 -0
- gedcomx/serialization.py +400 -421
- gedcomx/source_citation.py +16 -4
- gedcomx/source_description.py +181 -94
- gedcomx/source_reference.py +51 -16
- gedcomx/subject.py +59 -14
- gedcomx/textvalue.py +66 -12
- gedcomx/translation.py +3 -3
- gedcomx/uri.py +155 -3
- gedcom_x-0.5.8.dist-info/RECORD +0 -56
- gedcomx/gedcom7/Specification.py +0 -347
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/WHEEL +0 -0
- {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
|
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
|
-
|
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 .
|
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
|
-
|
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
|
-
|
114
|
-
|
115
|
-
self._name_index
|
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
|
-
|
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
|
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.
|
269
|
+
self.sourceDescriptions = TypeCollection(SourceDescription)
|
270
|
+
if sourceDescriptions: self.sourceDescriptions.extend(sourceDescriptions)
|
246
271
|
self.persons = TypeCollection(Person)
|
247
|
-
self.
|
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.
|
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
|
-
|
294
|
-
self.
|
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
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
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
|
-
|
401
|
-
|
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.
|
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
|
-
|
423
|
-
|
424
|
-
|
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
|
-
|
460
|
-
|
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
|
-
|
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
|
485
|
-
|
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
|
493
|
-
|
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-
|
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
|
-
|
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
|
-
|
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
|
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"] =
|
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 =
|
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
|
-
|
212
|
-
|
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
|
247
|
-
|
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
|
|