gedcom-x 0.5.8__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.
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.9.dist-info}/METADATA +1 -1
- gedcom_x-0.5.9.dist-info/RECORD +56 -0
- gedcomx/Extensions/rs10/rsLink.py +109 -59
- gedcomx/__init__.py +1 -1
- gedcomx/address.py +102 -16
- gedcomx/agent.py +81 -24
- gedcomx/attribution.py +52 -28
- gedcomx/conclusion.py +97 -45
- 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/fact.py +53 -54
- gedcomx/gedcom.py +10 -0
- gedcomx/gedcom5x.py +30 -20
- gedcomx/gedcom7/__init__.py +1 -1
- 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 +44 -26
- 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/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
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.9.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.9.dist-info}/top_level.txt +0 -0
- /gedcomx/gedcom7/{Gedcom7.py → gedcom7.py} +0 -0
gedcomx/event.py
CHANGED
@@ -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
|
"""
|
@@ -30,7 +32,14 @@ from .place_reference import PlaceReference
|
|
30
32
|
from .resource import Resource
|
31
33
|
from .source_reference import SourceReference
|
32
34
|
from .subject import Subject
|
33
|
-
|
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
|
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"
|
@@ -269,21 +303,44 @@ class Event(Subject):
|
|
269
303
|
@property
|
270
304
|
def _as_dict_(self):
|
271
305
|
from .serialization import Serialization
|
272
|
-
type_as_dict =
|
273
|
-
|
274
|
-
'type'
|
275
|
-
|
276
|
-
'
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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:
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
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)
|
gedcomx/evidence_reference.py
CHANGED
@@ -2,6 +2,15 @@ from typing import Optional
|
|
2
2
|
|
3
3
|
from .attribution import Attribution
|
4
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
|
+
#=====================================================================
|
5
14
|
|
6
15
|
class EvidenceReference:
|
7
16
|
identifier = 'http://gedcomx.org/v1/EvidenceReference'
|
gedcomx/fact.py
CHANGED
@@ -12,7 +12,7 @@ from typing import List, Optional, Dict, Any
|
|
12
12
|
|
13
13
|
Created: 2025-08-25
|
14
14
|
Updated:
|
15
|
-
- 2025-
|
15
|
+
- 2025-09-03: _from_json_ refactor
|
16
16
|
|
17
17
|
======================================================================
|
18
18
|
"""
|
@@ -26,12 +26,20 @@ from .attribution import Attribution
|
|
26
26
|
from .conclusion import Conclusion, ConfidenceLevel
|
27
27
|
from .date import Date
|
28
28
|
from .document import Document
|
29
|
-
from .Extensions.rs10.rsLink import
|
29
|
+
from .Extensions.rs10.rsLink import _rsLinks
|
30
30
|
from .note import Note
|
31
31
|
from .place_reference import PlaceReference
|
32
32
|
from .qualifier import Qualifier
|
33
33
|
from .resource import Resource
|
34
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"
|
35
43
|
#=====================================================================
|
36
44
|
|
37
45
|
|
@@ -415,7 +423,7 @@ class Fact(Conclusion):
|
|
415
423
|
place: Optional[PlaceReference] = None,
|
416
424
|
value: Optional[str] = None,
|
417
425
|
qualifiers: Optional[List[FactQualifier]] = None,
|
418
|
-
links: Optional[
|
426
|
+
links: Optional[_rsLinks] = None):
|
419
427
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, links=links)
|
420
428
|
self.type = type
|
421
429
|
self.date = date
|
@@ -436,60 +444,51 @@ class Fact(Conclusion):
|
|
436
444
|
|
437
445
|
@property
|
438
446
|
def _as_dict_(self):
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
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'")
|
452
470
|
|
453
|
-
return
|
454
|
-
|
455
|
-
@classmethod
|
456
|
-
def _from_json_(cls, data: Dict[str, Any]) -> 'Fact':
|
471
|
+
return type_as_dict if type_as_dict != {} else None
|
457
472
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
qualifiers = [Qualifier._from_json_(q) for q in data.get('qualifiers', [])]
|
476
|
-
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)
|
477
490
|
|
478
|
-
|
479
|
-
id=id_,
|
480
|
-
lang=lang,
|
481
|
-
sources=sources,
|
482
|
-
analysis=analysis,
|
483
|
-
notes=notes,
|
484
|
-
confidence=confidence,
|
485
|
-
attribution=attribution,
|
486
|
-
type=fact_type,
|
487
|
-
date=date,
|
488
|
-
place=place,
|
489
|
-
value=value,
|
490
|
-
qualifiers=qualifiers,
|
491
|
-
links=links
|
492
|
-
)
|
491
|
+
|
493
492
|
|
494
493
|
|
495
494
|
|
gedcomx/gedcom.py
CHANGED
@@ -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
|
gedcomx/gedcom5x.py
CHANGED
@@ -10,6 +10,15 @@ from typing import Iterable, Iterator, List, Optional, Tuple, Union
|
|
10
10
|
|
11
11
|
import logging
|
12
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
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
|
-
|
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
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
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
|
-
|
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
|
gedcomx/gedcom7/__init__.py
CHANGED