gedcom-x 0.5.9__tar.gz → 0.5.11__tar.gz
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.9 → gedcom_x-0.5.11}/PKG-INFO +1 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcom_x.egg-info/PKG-INFO +1 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcom_x.egg-info/SOURCES.txt +4 -2
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/Extensions/rs10/rsLink.py +2 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/__init__.py +8 -3
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/address.py +3 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/agent.py +11 -6
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/attribution.py +3 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/conclusion.py +10 -6
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/converter.py +92 -27
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/coverage.py +3 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/date.py +3 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/document.py +3 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/event.py +3 -0
- gedcom_x-0.5.11/gedcomx/evidence_reference.py +47 -0
- gedcom_x-0.5.11/gedcomx/extensible.py +86 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/fact.py +6 -2
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom5x.py +21 -3
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom7/GedcomStructure.py +1 -3
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom7/__init__.py +1 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom7/gedcom7.py +3 -3
- gedcom_x-0.5.11/gedcomx/gedcom7/specification.py +4817 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcomx.py +3 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gender.py +5 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/group.py +11 -2
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/identifier.py +5 -2
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/logging_hub.py +132 -22
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/name.py +15 -6
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/note.py +25 -10
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/online_account.py +20 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/person.py +8 -6
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/place_description.py +3 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/place_reference.py +5 -2
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/qualifier.py +2 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/relationship.py +8 -5
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/resource.py +20 -6
- gedcom_x-0.5.11/gedcomx/schemas.py +530 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/serialization.py +36 -16
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/source_citation.py +22 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/source_description.py +22 -18
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/source_reference.py +25 -3
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/subject.py +2 -3
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/textvalue.py +19 -4
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/uri.py +8 -6
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/pyproject.toml +1 -1
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/setup.py +1 -1
- gedcom_x-0.5.9/gedcomx/Logging.py +0 -19
- gedcom_x-0.5.9/gedcomx/evidence_reference.py +0 -20
- gedcom_x-0.5.9/gedcomx/gedcom7/Specification.py +0 -347
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/README.md +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcom_x.egg-info/dependency_links.txt +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcom_x.egg-info/top_level.txt +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/Extensions/__init__.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/Extensions/rs10/__init__.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/TopLevelTypeCollection.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/Zip.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/exceptions.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/extensible_enum.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom7/Exceptions.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom7/g7interop.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/gedcom7/logger.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/mutations.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/gedcomx/translation.py +0 -0
- {gedcom_x-0.5.9 → gedcom_x-0.5.11}/setup.cfg +0 -0
@@ -22,7 +22,6 @@ gedcomx/GedcomX.py
|
|
22
22
|
gedcomx/Gender.py
|
23
23
|
gedcomx/Group.py
|
24
24
|
gedcomx/Identifier.py
|
25
|
-
gedcomx/Logging.py
|
26
25
|
gedcomx/Mutations.py
|
27
26
|
gedcomx/Name.py
|
28
27
|
gedcomx/Note.py
|
@@ -49,6 +48,7 @@ gedcomx/document.py
|
|
49
48
|
gedcomx/event.py
|
50
49
|
gedcomx/evidence_reference.py
|
51
50
|
gedcomx/exceptions.py
|
51
|
+
gedcomx/extensible.py
|
52
52
|
gedcomx/extensible_enum.py
|
53
53
|
gedcomx/fact.py
|
54
54
|
gedcomx/gedcom.py
|
@@ -68,6 +68,7 @@ gedcomx/place_reference.py
|
|
68
68
|
gedcomx/qualifier.py
|
69
69
|
gedcomx/relationship.py
|
70
70
|
gedcomx/resource.py
|
71
|
+
gedcomx/schemas.py
|
71
72
|
gedcomx/serialization.py
|
72
73
|
gedcomx/source_citation.py
|
73
74
|
gedcomx/source_description.py
|
@@ -86,4 +87,5 @@ gedcomx/gedcom7/Specification.py
|
|
86
87
|
gedcomx/gedcom7/__init__.py
|
87
88
|
gedcomx/gedcom7/g7interop.py
|
88
89
|
gedcomx/gedcom7/gedcom7.py
|
89
|
-
gedcomx/gedcom7/logger.py
|
90
|
+
gedcomx/gedcom7/logger.py
|
91
|
+
gedcomx/gedcom7/specification.py
|
@@ -20,6 +20,7 @@ GEDCOM Module Types
|
|
20
20
|
"""
|
21
21
|
from ...exceptions import GedcomClassAttributeError
|
22
22
|
from ...logging_hub import hub, logging
|
23
|
+
from ...schemas import schema_class
|
23
24
|
from ...uri import URI
|
24
25
|
"""
|
25
26
|
======================================================================
|
@@ -31,7 +32,7 @@ serial_log = "gedcomx.serialization"
|
|
31
32
|
deserial_log = "gedcomx.deserialization"
|
32
33
|
#=====================================================================
|
33
34
|
|
34
|
-
|
35
|
+
@schema_class()
|
35
36
|
class rsLink():
|
36
37
|
"""A link description object. RS Extension to GedcomX by FamilySearch.
|
37
38
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from .schemas import SCHEMA
|
2
|
+
from .subject import Subject
|
1
3
|
from .agent import Agent
|
2
4
|
from .address import Address
|
3
5
|
from .attribution import Attribution
|
@@ -12,6 +14,8 @@ from .extensible_enum import ExtensibleEnum
|
|
12
14
|
from .event import Event
|
13
15
|
from .event import EventType
|
14
16
|
from .event import EventRole
|
17
|
+
from .extensible import Extensible
|
18
|
+
from. extensible import _ExtraField
|
15
19
|
from .fact import Fact
|
16
20
|
from .fact import FactQualifier
|
17
21
|
from .fact import FactType
|
@@ -21,7 +25,6 @@ from .gedcomx import GedcomX
|
|
21
25
|
from .gender import Gender, GenderType
|
22
26
|
from .group import Group, GroupRole
|
23
27
|
from .identifier import Identifier, IdentifierType, IdentifierList
|
24
|
-
from .Logging import get_logger
|
25
28
|
from .name import Name, NameForm, NamePart, NamePartType, NameType, NamePartQualifier
|
26
29
|
from .note import Note
|
27
30
|
from .online_account import OnlineAccount
|
@@ -35,10 +38,12 @@ from .source_citation import SourceCitation
|
|
35
38
|
from .source_description import SourceDescription
|
36
39
|
from .source_description import ResourceType
|
37
40
|
from .source_reference import SourceReference
|
38
|
-
|
41
|
+
|
39
42
|
from .textvalue import TextValue
|
40
43
|
from .resource import Resource
|
44
|
+
SCHEMA.set_resource_class(Resource)
|
41
45
|
from .uri import URI
|
46
|
+
SCHEMA.set_uri_class(URI)
|
42
47
|
|
43
48
|
|
44
49
|
from .Extensions.rs10.rsLink import rsLink
|
@@ -46,5 +51,5 @@ from .Extensions.rs10.rsLink import rsLink
|
|
46
51
|
from .gedcom7.gedcom7 import Gedcom7, GedcomStructure
|
47
52
|
from .translation import g7toXtable
|
48
53
|
|
49
|
-
|
54
|
+
SCHEMA.normalize_all()
|
50
55
|
|
@@ -10,6 +10,7 @@ from typing import Any, Dict, Optional, List
|
|
10
10
|
Created: 2025-08-25
|
11
11
|
Updated:
|
12
12
|
- 2025-09-03: _from_json_ refactoring
|
13
|
+
- 2025-09-09: added schema_class
|
13
14
|
|
14
15
|
======================================================================
|
15
16
|
"""
|
@@ -20,6 +21,7 @@ GEDCOM Module Types
|
|
20
21
|
======================================================================
|
21
22
|
"""
|
22
23
|
from .logging_hub import hub, logging
|
24
|
+
from .schemas import schema_class
|
23
25
|
"""
|
24
26
|
======================================================================
|
25
27
|
Logging
|
@@ -29,6 +31,7 @@ log = logging.getLogger("gedcomx")
|
|
29
31
|
serial_log = "gedcomx.serialization"
|
30
32
|
#=====================================================================
|
31
33
|
|
34
|
+
@schema_class()
|
32
35
|
class Address:
|
33
36
|
"""A GedcomX Address Data Type
|
34
37
|
A GedcomX Address Data Type.
|
@@ -1,7 +1,8 @@
|
|
1
|
-
import
|
2
|
-
import uuid
|
1
|
+
from __future__ import annotations
|
3
2
|
|
4
|
-
from typing import Any, Dict, List, Optional
|
3
|
+
from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from .person import Person
|
5
6
|
"""
|
6
7
|
======================================================================
|
7
8
|
Project: Gedcom-X
|
@@ -12,6 +13,7 @@ from typing import Any, Dict, List, Optional
|
|
12
13
|
Created: 2025-08-25
|
13
14
|
Updated:
|
14
15
|
- 2025-09-03: _from_json_ refactor
|
16
|
+
- 2025-09-09: added schema_class
|
15
17
|
|
16
18
|
======================================================================
|
17
19
|
"""
|
@@ -25,6 +27,7 @@ from .address import Address
|
|
25
27
|
from .identifier import Identifier, IdentifierList, make_uid
|
26
28
|
from .online_account import OnlineAccount
|
27
29
|
from .resource import Resource
|
30
|
+
from .schemas import schema_class
|
28
31
|
from .textvalue import TextValue
|
29
32
|
from .uri import URI
|
30
33
|
from .logging_hub import hub, logging
|
@@ -37,7 +40,7 @@ log = logging.getLogger("gedcomx")
|
|
37
40
|
serial_log = "gedcomx.serialization"
|
38
41
|
#=====================================================================
|
39
42
|
|
40
|
-
|
43
|
+
@schema_class(toplevel=True)
|
41
44
|
class Agent:
|
42
45
|
"""A GedcomX Agent Data Type.
|
43
46
|
|
@@ -80,10 +83,10 @@ class Agent:
|
|
80
83
|
emails: Optional[List[URI]] = [],
|
81
84
|
phones: Optional[List[URI]] = [],
|
82
85
|
addresses: Optional[List[Address]] = [],
|
83
|
-
person: Optional[
|
86
|
+
person: Optional[Union[Resource,Person]] = None, # should be of Type 'Person', 'object' to avoid circular imports
|
84
87
|
#xnotes: Optional[List[Note]] = None,
|
85
88
|
attribution: Optional[object] = None, # Added for compatibility with GEDCOM5/7 Imports
|
86
|
-
|
89
|
+
):
|
87
90
|
|
88
91
|
|
89
92
|
self.id = id if id else make_uid()
|
@@ -123,6 +126,8 @@ class Agent:
|
|
123
126
|
for current_name in self.names:
|
124
127
|
if name_to_add == current_name:
|
125
128
|
return
|
129
|
+
if name_to_add.value is None or name_to_add == '':
|
130
|
+
assert False
|
126
131
|
self.names.append(name_to_add)
|
127
132
|
else:
|
128
133
|
raise ValueError(f'name must be of type str or TextValue, recived {type(name_to_add)}')
|
@@ -13,6 +13,7 @@ from typing import Optional, Dict, Any
|
|
13
13
|
Updated:
|
14
14
|
- 2025-08-31: fixed _as_dict_ to deal with Resources and ignore empty fields
|
15
15
|
- 2025-09-03: _from_json_ refactor
|
16
|
+
- 2025-09-09: added schema_class
|
16
17
|
|
17
18
|
|
18
19
|
======================================================================
|
@@ -25,6 +26,7 @@ GEDCOM Module Types
|
|
25
26
|
"""
|
26
27
|
from .agent import Agent
|
27
28
|
from .resource import Resource
|
29
|
+
from .schemas import schema_class
|
28
30
|
from .logging_hub import hub, logging
|
29
31
|
"""
|
30
32
|
======================================================================
|
@@ -35,7 +37,7 @@ log = logging.getLogger("gedcomx")
|
|
35
37
|
serial_log = "gedcomx.serialization"
|
36
38
|
#=====================================================================
|
37
39
|
|
38
|
-
|
40
|
+
@schema_class()
|
39
41
|
class Attribution:
|
40
42
|
"""Attribution Information for a Genealogy, Conclusion, Subject and child classes
|
41
43
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
|
1
|
+
from __future__ import annotations
|
2
2
|
import warnings
|
3
3
|
|
4
|
-
from typing import Any, Optional, List
|
4
|
+
from typing import Any, Optional, List, Union, TYPE_CHECKING
|
5
5
|
|
6
6
|
"""
|
7
7
|
======================================================================
|
@@ -13,6 +13,7 @@ from typing import Any, Optional, List
|
|
13
13
|
Created: 2025-08-25
|
14
14
|
Updated:
|
15
15
|
- 2025-09-03: _from_json_ refactor
|
16
|
+
- 2025-09-09: added schema_class
|
16
17
|
|
17
18
|
======================================================================
|
18
19
|
"""
|
@@ -23,11 +24,14 @@ GEDCOM Module Type Imports
|
|
23
24
|
======================================================================
|
24
25
|
"""
|
25
26
|
from .attribution import Attribution
|
27
|
+
if TYPE_CHECKING:
|
28
|
+
from .document import Document
|
26
29
|
from .Extensions.rs10.rsLink import _rsLinks, rsLink
|
27
30
|
from .identifier import make_uid
|
28
31
|
from .note import Note
|
29
32
|
from .qualifier import Qualifier
|
30
33
|
from .resource import Resource, URI
|
34
|
+
from .schemas import schema_class
|
31
35
|
from .source_reference import SourceReference
|
32
36
|
from .logging_hub import hub, logging
|
33
37
|
"""
|
@@ -112,8 +116,8 @@ class ConfidenceLevel(Qualifier):
|
|
112
116
|
key = getattr(self, "value", self)
|
113
117
|
return descriptions.get(key, "No description available.")
|
114
118
|
|
115
|
-
|
116
|
-
class Conclusion:
|
119
|
+
@schema_class()
|
120
|
+
class Conclusion():
|
117
121
|
"""
|
118
122
|
Represents a conclusion in the GEDCOM X conceptual model. A conclusion is a
|
119
123
|
genealogical assertion about a person, relationship, or event, derived from
|
@@ -146,11 +150,10 @@ class Conclusion:
|
|
146
150
|
id: Optional[str] = None,
|
147
151
|
lang: Optional[str] = None,
|
148
152
|
sources: Optional[List[SourceReference]] = None,
|
149
|
-
analysis: Optional[
|
153
|
+
analysis: Optional[Union[Resource,Document]] = None,
|
150
154
|
notes: Optional[List[Note]] = None,
|
151
155
|
confidence: Optional[ConfidenceLevel] = None,
|
152
156
|
attribution: Optional[Attribution] = None,
|
153
|
-
uri: Optional[Resource] = None,
|
154
157
|
_max_note_count: int = 20,
|
155
158
|
links: Optional[_rsLinks] = None) -> None:
|
156
159
|
|
@@ -164,6 +167,7 @@ class Conclusion:
|
|
164
167
|
self.attribution = attribution
|
165
168
|
self.max_note_count = _max_note_count
|
166
169
|
self.links = links if links else _rsLinks() #NOTE This is not in specification, following FS format
|
170
|
+
self.uri = URI(fragment=id) if id else None
|
167
171
|
|
168
172
|
def add_note(self,note_to_add: Note):
|
169
173
|
if self.notes and len(self.notes) >= self.max_note_count:
|
@@ -1,10 +1,11 @@
|
|
1
|
-
DEBUG = False
|
2
1
|
import logging
|
3
2
|
import mimetypes
|
4
3
|
import re
|
5
4
|
import xml.etree.ElementTree as ET
|
6
5
|
|
7
6
|
from typing import Any, Mapping
|
7
|
+
from typing import Iterable, Callable, TypeVar, Hashable, List, Optional
|
8
|
+
|
8
9
|
import math
|
9
10
|
import shutil
|
10
11
|
|
@@ -73,6 +74,8 @@ serial_log = "gedcomx.serialization"
|
|
73
74
|
convert_log = "gedcomx.convert.GEDCOM5x"
|
74
75
|
#=====================================================================
|
75
76
|
|
77
|
+
|
78
|
+
|
76
79
|
hub.start_channel(
|
77
80
|
ChannelConfig(
|
78
81
|
name=convert_log,
|
@@ -81,16 +84,22 @@ hub.start_channel(
|
|
81
84
|
rotation="size:10MB:3", # rotate by size, keep 3 backups
|
82
85
|
))
|
83
86
|
|
87
|
+
T = TypeVar("T")
|
88
|
+
K = TypeVar("K", bound=Hashable)
|
84
89
|
|
85
90
|
class GedcomConverter():
|
86
91
|
def __init__(self) -> None:
|
87
|
-
self.gedcomx = GedcomX()
|
92
|
+
self.gedcomx: GedcomX = GedcomX()
|
88
93
|
self.object_map: dict[Any, Any] = {-1:self.gedcomx}
|
89
94
|
self.missing_handler_count = {}
|
90
95
|
|
91
96
|
type_name_type = {
|
92
97
|
'aka': NameType.AlsoKnownAs
|
93
98
|
}
|
99
|
+
|
100
|
+
personal_events = ["BARM", "BASM", "BLES", "CHRA", "CONF", "CENS", "CREM", "EMIG", "GRAD", "NATU", "ORDN", "RETI", "WILL"]
|
101
|
+
|
102
|
+
|
94
103
|
gedcom_even_to_fact = {
|
95
104
|
# Person Fact Types
|
96
105
|
"ADOP": FactType.Adoption,
|
@@ -193,24 +202,25 @@ class GedcomConverter():
|
|
193
202
|
|
194
203
|
def parse_gedcom5x_record(self,record: Gedcom5xRecord):
|
195
204
|
if record is not None:
|
196
|
-
with hub.use(convert_log):
|
197
|
-
handler_name = 'handle_' + record.tag.lower()
|
198
|
-
|
199
|
-
|
205
|
+
with hub.use(convert_log):
|
206
|
+
handler_name = 'handle_' + record.tag.lower()
|
207
|
+
if record.tag in self.personal_events:
|
208
|
+
self.handle_pevent(record)
|
209
|
+
elif hasattr(self,handler_name):
|
200
210
|
log.info(f'Using {handler_name} to pars Record: {record.describe()}')
|
201
211
|
handler = getattr(self,handler_name)
|
202
212
|
handler(record)
|
203
|
-
if record.tag != 'FAM':
|
204
|
-
for sub_record in record.subRecords():
|
205
|
-
log.debug(sub_record.describe())
|
206
|
-
self.parse_gedcom5x_record(sub_record)
|
207
213
|
else:
|
208
214
|
if record.tag in self.missing_handler_count:
|
209
215
|
self.missing_handler_count[record.tag] += 1
|
210
216
|
else:
|
211
217
|
self.missing_handler_count[record.tag] = 1
|
212
|
-
|
213
218
|
log.error(f'Failed Parsing Record: {record.describe()}')
|
219
|
+
return
|
220
|
+
if record.tag != 'FAM':
|
221
|
+
for sub_record in record.subRecords():
|
222
|
+
log.debug(sub_record.describe())
|
223
|
+
self.parse_gedcom5x_record(sub_record)
|
214
224
|
log.debug(f"{record.tag} with id: {record.xref} has {len(record.subRecords())} subRecords")
|
215
225
|
|
216
226
|
else:
|
@@ -436,12 +446,12 @@ class GedcomConverter():
|
|
436
446
|
|
437
447
|
def handle_cont(self, record: Gedcom5xRecord):
|
438
448
|
if isinstance(self.object_map[record.level-1], Note):
|
439
|
-
gxobject = str("
|
449
|
+
gxobject = str(" " + record.value if record.value else '')
|
440
450
|
self.object_map[record.level-1].append(gxobject)
|
441
451
|
elif isinstance(self.object_map[record.level-1], Agent):
|
442
|
-
gxobject = str("
|
452
|
+
gxobject = str(" " + record.value if record.value else '')
|
443
453
|
elif isinstance(self.object_map[record.level-1], Qualifier):
|
444
|
-
gxobject = str("
|
454
|
+
gxobject = str(" " + record.value if record.value else '')
|
445
455
|
self.object_map[record.level-1].append(gxobject)
|
446
456
|
elif isinstance(self.object_map[record.level-1], TextValue):
|
447
457
|
#gxobject = TextValue(value="\n" + record.value)
|
@@ -471,6 +481,12 @@ class GedcomConverter():
|
|
471
481
|
else:
|
472
482
|
raise ValueError(f"Could not handle '{record.tag}' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
|
473
483
|
|
484
|
+
def handle__crea(self, record: Gedcom5xRecord):
|
485
|
+
if isinstance(self.object_map[record.level-1], SourceDescription):
|
486
|
+
if record.value is not None:
|
487
|
+
self.object_map[record.level-1].created = Date(original=record.value)
|
488
|
+
else: raise ValueError('DATE had not value')
|
489
|
+
|
474
490
|
def handle_ctry(self, record: Gedcom5xRecord):
|
475
491
|
if isinstance(self.object_map[record.level-1], Address):
|
476
492
|
if record.value is not None:
|
@@ -526,6 +542,13 @@ class GedcomConverter():
|
|
526
542
|
else:
|
527
543
|
raise TagConversionError(record=record,levelstack=self.object_map)
|
528
544
|
|
545
|
+
def handle_pevent(self, record: Gedcom5xRecord):
|
546
|
+
if record.tag in self.gedcom_even_to_fact.keys():
|
547
|
+
if isinstance(self.object_map[record.level-1], Person):
|
548
|
+
gxobject = Fact(type=self.gedcom_even_to_fact[record.tag])
|
549
|
+
self.object_map[record.level-1].add_fact(gxobject)
|
550
|
+
self.object_map[record.level] = gxobject
|
551
|
+
|
529
552
|
def handle_even(self, record: Gedcom5xRecord):
|
530
553
|
# TODO If events in a @S, check if only 1 person matches?
|
531
554
|
if record.value and (not record.value.strip() == ''):
|
@@ -733,8 +756,9 @@ class GedcomConverter():
|
|
733
756
|
|
734
757
|
self.object_map[record.level] = gxobject
|
735
758
|
elif isinstance(self.object_map[record.level-1], Agent):
|
736
|
-
|
759
|
+
print(record.describe())
|
737
760
|
gxobject = TextValue(value=record.value)
|
761
|
+
print(gxobject)
|
738
762
|
self.object_map[record.level-1].add_name(gxobject)
|
739
763
|
|
740
764
|
else:
|
@@ -808,7 +832,7 @@ class GedcomConverter():
|
|
808
832
|
def handle_page(self, record: Gedcom5xRecord):
|
809
833
|
if isinstance(self.object_map[record.level-1], SourceReference):
|
810
834
|
#self.object_map[record.level-1].descriptionId = record.value
|
811
|
-
gx_object =
|
835
|
+
gx_object = Qualifier(name=KnownSourceReference.Page,value=record.value)
|
812
836
|
self.object_map[record.level-1].add_qualifier(gx_object)
|
813
837
|
self.object_map[record.level] = self.object_map[record.level-1]
|
814
838
|
else:
|
@@ -854,15 +878,19 @@ class GedcomConverter():
|
|
854
878
|
|
855
879
|
def handle_publ(self, record: Gedcom5xRecord):
|
856
880
|
if isinstance(self.object_map[record.level-1], SourceDescription):
|
857
|
-
if record.value
|
858
|
-
|
881
|
+
if record.value == None:
|
882
|
+
#check for date
|
883
|
+
if (date := record['DATE']) is not None:
|
884
|
+
self.object_map[record.level-1].published = date
|
885
|
+
print("found date")
|
859
886
|
else:
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
887
|
+
if record.value and self.gedcomx.agents.byName(record.value):
|
888
|
+
gxobject = self.gedcomx.agents.byName(record.value)[0]
|
889
|
+
else:
|
890
|
+
gxobject = Agent(names=[TextValue(record.value)])
|
891
|
+
self.gedcomx.add_agent(gxobject)
|
892
|
+
self.object_map[record.level-1].publisher = gxobject
|
893
|
+
self.object_map[record.level] = gxobject
|
866
894
|
else:
|
867
895
|
raise TagConversionError(record=record,levelstack=self.object_map)
|
868
896
|
|
@@ -900,8 +928,10 @@ class GedcomConverter():
|
|
900
928
|
def handle_repo(self, record: Gedcom5xRecord):
|
901
929
|
|
902
930
|
if record.level == 0:
|
903
|
-
if
|
904
|
-
gxobject =
|
931
|
+
if record.value is not None and self.gedcomx.agents.byName(record.value):
|
932
|
+
gxobject = self.gedcomx.agents.byName(record.value)[0]
|
933
|
+
else:
|
934
|
+
gxobject = Agent(id=record.xref,names = [TextValue(record.value)] if record.value else [])
|
905
935
|
self.gedcomx.add_agent(gxobject)
|
906
936
|
|
907
937
|
self.object_map[record.level] = gxobject
|
@@ -1128,6 +1158,34 @@ class GedcomConverter():
|
|
1128
1158
|
line.append(cell.ljust(cell_width))
|
1129
1159
|
print("".join(line).rstrip())
|
1130
1160
|
|
1161
|
+
def has_duplicates(self,seq) -> bool:
|
1162
|
+
"""Fast True/False check (works for hashable items)."""
|
1163
|
+
return len(seq) != len(set(seq))
|
1164
|
+
|
1165
|
+
def find_duplicates(self, seq):
|
1166
|
+
"""Return duplicate items once each, in the order they first repeat."""
|
1167
|
+
seen, dups = set(), []
|
1168
|
+
for x in seq:
|
1169
|
+
if x in seen and x not in dups:
|
1170
|
+
dups.append(x)
|
1171
|
+
seen.add(x)
|
1172
|
+
return dups
|
1173
|
+
|
1174
|
+
def unique(self, seq: Iterable[T], key: Optional[Callable[[T], K]] = None) -> List[T]:
|
1175
|
+
"""
|
1176
|
+
Return a list with duplicates removed, keeping the first occurrence.
|
1177
|
+
If `key` is provided, it’s used to compute a hashable identity for each item.
|
1178
|
+
"""
|
1179
|
+
|
1180
|
+
seen: set[K] = set()
|
1181
|
+
out: List[T] = []
|
1182
|
+
for item in seq:
|
1183
|
+
k = item if key is None else key(item)
|
1184
|
+
if k not in seen:
|
1185
|
+
seen.add(k)
|
1186
|
+
out.append(item)
|
1187
|
+
return out
|
1188
|
+
|
1131
1189
|
def Gedcom5x_GedcomX(self, gedcom5x: Gedcom5x):
|
1132
1190
|
print(f'Parsing GEDCOM Version {gedcom5x.version}')
|
1133
1191
|
individual_ids = set()
|
@@ -1173,7 +1231,14 @@ class GedcomConverter():
|
|
1173
1231
|
self.parse_gedcom5x_record(repo)
|
1174
1232
|
for family in gedcom5x.families:
|
1175
1233
|
self.parse_gedcom5x_record(family)
|
1176
|
-
|
1234
|
+
|
1235
|
+
#clean up notes, and TextValue descriptions:
|
1236
|
+
for sd in self.gedcomx.sourceDescriptions:
|
1237
|
+
log.debug(f"Removing dupliate notes in SourceDescriptions")
|
1238
|
+
sd.notes = self.unique(sd.notes,key=lambda n: n._key())
|
1239
|
+
log.debug(f"Removing dupliate descriptions in SourceDescriptions")
|
1240
|
+
sd.descriptions = self.unique(sd.descriptions,key=lambda n: n._key())
|
1241
|
+
|
1177
1242
|
self.print_counts_table(self.missing_handler_count)
|
1178
1243
|
|
1179
1244
|
return self.gedcomx
|
@@ -9,6 +9,7 @@ from typing import Optional
|
|
9
9
|
Created: 2025-08-25
|
10
10
|
Updated:
|
11
11
|
- 2025-09-03: _from_json_ refactor
|
12
|
+
- 2025-09-09: added schema_class
|
12
13
|
|
13
14
|
======================================================================
|
14
15
|
"""
|
@@ -21,6 +22,7 @@ GEDCOM Module Types
|
|
21
22
|
from .date import Date
|
22
23
|
from .place_reference import PlaceReference
|
23
24
|
from .logging_hub import hub, logging
|
25
|
+
from .schemas import schema_class
|
24
26
|
"""
|
25
27
|
======================================================================
|
26
28
|
Logging
|
@@ -30,7 +32,7 @@ log = logging.getLogger("gedcomx")
|
|
30
32
|
serial_log = "gedcomx.serialization"
|
31
33
|
#=====================================================================
|
32
34
|
|
33
|
-
|
35
|
+
@schema_class()
|
34
36
|
class Coverage:
|
35
37
|
identifier = 'http://gedcomx.org/v1/Coverage'
|
36
38
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
@@ -11,6 +11,7 @@ from dateutil import parser
|
|
11
11
|
Created: 2025-08-25
|
12
12
|
Updated:
|
13
13
|
- 2025-09-03: _from_json refactored
|
14
|
+
- 2025-09-09: added schema_class
|
14
15
|
|
15
16
|
======================================================================
|
16
17
|
"""
|
@@ -21,6 +22,7 @@ GEDCOM Module Types
|
|
21
22
|
======================================================================
|
22
23
|
"""
|
23
24
|
from .logging_hub import hub, logging
|
25
|
+
from .schemas import schema_class
|
24
26
|
"""
|
25
27
|
======================================================================
|
26
28
|
Logging
|
@@ -38,6 +40,7 @@ class DateFormat:
|
|
38
40
|
class DateNormalization():
|
39
41
|
pass
|
40
42
|
|
43
|
+
@schema_class()
|
41
44
|
class Date:
|
42
45
|
identifier = 'http://gedcomx.org/v1/Date'
|
43
46
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
@@ -10,6 +10,7 @@ from typing import Any, Dict, List, Optional
|
|
10
10
|
Created: 2025-08-25
|
11
11
|
Updated:
|
12
12
|
- 2025-09-03: _from_json_ refactored
|
13
|
+
- 2025-09-09: added schema_class
|
13
14
|
|
14
15
|
======================================================================
|
15
16
|
"""
|
@@ -23,6 +24,7 @@ from .attribution import Attribution
|
|
23
24
|
from .conclusion import Conclusion, ConfidenceLevel
|
24
25
|
from .note import Note
|
25
26
|
from .resource import Resource
|
27
|
+
from .schemas import schema_class
|
26
28
|
from .source_reference import SourceReference
|
27
29
|
from .logging_hub import hub, logging
|
28
30
|
"""
|
@@ -34,7 +36,7 @@ log = logging.getLogger("gedcomx")
|
|
34
36
|
serial_log = "gedcomx.serialization"
|
35
37
|
#=====================================================================
|
36
38
|
|
37
|
-
|
39
|
+
@schema_class(toplevel=True)
|
38
40
|
class DocumentType(Enum):
|
39
41
|
Abstract = "http://gedcomx.org/Abstract"
|
40
42
|
Transcription = "http://gedcomx.org/Transcription"
|
@@ -13,6 +13,7 @@ from typing import Any, Dict, List, Optional
|
|
13
13
|
- 2025-08-31: fixed mutible [] in init, replaced List[Identifer] with IdentifierList
|
14
14
|
- 2025-09-03: _from_json_ refactor
|
15
15
|
- 2025-09-04: changed _as_dict_ to std dict creation and removed call to serailization
|
16
|
+
- 2025-09-09: added schema_class
|
16
17
|
|
17
18
|
======================================================================
|
18
19
|
"""
|
@@ -30,6 +31,7 @@ from .identifier import IdentifierList
|
|
30
31
|
from .note import Note
|
31
32
|
from .place_reference import PlaceReference
|
32
33
|
from .resource import Resource
|
34
|
+
from .schemas import schema_class
|
33
35
|
from .source_reference import SourceReference
|
34
36
|
from .subject import Subject
|
35
37
|
from .logging_hub import hub, logging
|
@@ -272,6 +274,7 @@ class EventType(Enum):
|
|
272
274
|
|
273
275
|
return None # Default to UNKNOWN if no match is found
|
274
276
|
|
277
|
+
@schema_class(toplevel=True)
|
275
278
|
class Event(Subject):
|
276
279
|
identifier = 'http://gedcomx.org/v1/Event'
|
277
280
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from typing import Optional, TYPE_CHECKING
|
2
|
+
"""
|
3
|
+
======================================================================
|
4
|
+
Project: Gedcom-X
|
5
|
+
File: evidence_reference.py
|
6
|
+
Author: David J. Cartwright
|
7
|
+
Purpose:
|
8
|
+
|
9
|
+
Created: 2025-08-25
|
10
|
+
Updated:
|
11
|
+
- 2025-09-09: added schema_class
|
12
|
+
|
13
|
+
|
14
|
+
======================================================================
|
15
|
+
"""
|
16
|
+
|
17
|
+
"""
|
18
|
+
======================================================================
|
19
|
+
GEDCOM Module Type Imports
|
20
|
+
======================================================================
|
21
|
+
"""
|
22
|
+
from .attribution import Attribution
|
23
|
+
from .resource import Resource
|
24
|
+
from .schemas import schema_class
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from .subject import Subject
|
27
|
+
from .logging_hub import hub, logging
|
28
|
+
"""
|
29
|
+
======================================================================
|
30
|
+
Logging
|
31
|
+
======================================================================
|
32
|
+
"""
|
33
|
+
log = logging.getLogger("gedcomx")
|
34
|
+
serial_log = "gedcomx.serialization"
|
35
|
+
#=====================================================================
|
36
|
+
|
37
|
+
@schema_class()
|
38
|
+
class EvidenceReference:
|
39
|
+
identifier = 'http://gedcomx.org/v1/EvidenceReference'
|
40
|
+
version = 'http://gedcomx.org/conceptual-model/v1'
|
41
|
+
|
42
|
+
def __init__(self, resource: "Resource | Subject", attribution: Optional[Attribution]) -> None:
|
43
|
+
self.resource = resource
|
44
|
+
self.attribution: Attribution = attribution
|
45
|
+
|
46
|
+
def _validate(self):
|
47
|
+
raise NotImplemented
|