gedcom-x 0.5.1__py3-none-any.whl → 0.5.5__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.1.dist-info → gedcom_x-0.5.5.dist-info}/METADATA +1 -1
- gedcom_x-0.5.5.dist-info/RECORD +43 -0
- gedcomx/Address.py +42 -11
- gedcomx/Agent.py +136 -23
- gedcomx/Attribution.py +39 -91
- gedcomx/Conclusion.py +132 -53
- gedcomx/Coverage.py +10 -0
- gedcomx/Date.py +47 -11
- gedcomx/Document.py +43 -12
- gedcomx/Event.py +24 -5
- gedcomx/EvidenceReference.py +2 -2
- gedcomx/Exceptions.py +16 -0
- gedcomx/Fact.py +73 -50
- gedcomx/Gedcom.py +40 -333
- gedcomx/Gedcom5x.py +558 -0
- gedcomx/GedcomX.py +439 -194
- gedcomx/Gender.py +27 -8
- gedcomx/Group.py +3 -3
- gedcomx/Identifier.py +192 -55
- gedcomx/Logging.py +19 -0
- gedcomx/Mutations.py +228 -0
- gedcomx/Name.py +73 -38
- gedcomx/Note.py +5 -4
- gedcomx/OnlineAccount.py +2 -2
- gedcomx/Person.py +106 -92
- gedcomx/PlaceDescription.py +39 -16
- gedcomx/PlaceReference.py +14 -15
- gedcomx/Relationship.py +35 -56
- gedcomx/Resource.py +75 -0
- gedcomx/Serialization.py +394 -30
- gedcomx/SourceCitation.py +6 -1
- gedcomx/SourceDescription.py +89 -75
- gedcomx/SourceReference.py +33 -88
- gedcomx/Subject.py +12 -10
- gedcomx/TextValue.py +2 -1
- gedcomx/Translation.py +219 -0
- gedcomx/URI.py +96 -61
- gedcomx/Zip.py +1 -0
- gedcomx/__init__.py +11 -3
- gedcom_x-0.5.1.dist-info/RECORD +0 -37
- gedcomx/_Resource.py +0 -11
- {gedcom_x-0.5.1.dist-info → gedcom_x-0.5.5.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.1.dist-info → gedcom_x-0.5.5.dist-info}/top_level.txt +0 -0
gedcomx/Gender.py
CHANGED
@@ -5,10 +5,13 @@ from gedcomx.Attribution import Attribution
|
|
5
5
|
from gedcomx.Conclusion import ConfidenceLevel
|
6
6
|
from gedcomx.Note import Note
|
7
7
|
from gedcomx.SourceReference import SourceReference
|
8
|
-
from gedcomx.
|
8
|
+
from gedcomx.Resource import Resource
|
9
9
|
|
10
10
|
from .Conclusion import Conclusion
|
11
11
|
from .Qualifier import Qualifier
|
12
|
+
from .Serialization import Serialization
|
13
|
+
|
14
|
+
from collections.abc import Sized
|
12
15
|
|
13
16
|
class GenderType(Enum):
|
14
17
|
Male = "http://gedcomx.org/Male"
|
@@ -33,16 +36,32 @@ class Gender(Conclusion):
|
|
33
36
|
def __init__(self,
|
34
37
|
id: Optional[str] = None,
|
35
38
|
lang: Optional[str] = 'en',
|
36
|
-
sources: Optional[List[SourceReference]] =
|
37
|
-
analysis: Optional[
|
38
|
-
notes: Optional[List[Note]] =
|
39
|
+
sources: Optional[List[SourceReference]] = None,
|
40
|
+
analysis: Optional[Resource] = None,
|
41
|
+
notes: Optional[List[Note]] = None,
|
39
42
|
confidence: Optional[ConfidenceLevel] = None,
|
40
43
|
attribution: Optional[Attribution] = None,
|
41
|
-
type: GenderType = None
|
44
|
+
type: Optional[GenderType] = None
|
42
45
|
) -> None:
|
43
|
-
super().__init__(id, lang, sources, analysis, notes, confidence, attribution)
|
46
|
+
super().__init__(id=id, lang=lang, sources=sources, analysis=analysis, notes=notes, confidence=confidence, attribution=attribution)
|
44
47
|
self.type = type
|
45
48
|
|
49
|
+
@property
|
50
|
+
def _as_dict_(self):
|
51
|
+
|
52
|
+
|
53
|
+
type_as_dict = super()._as_dict_ # Start with base class fields
|
54
|
+
# Only add Relationship-specific fields
|
55
|
+
type_as_dict.update({
|
56
|
+
'type':self.type.value if self.type else None
|
57
|
+
|
58
|
+
})
|
59
|
+
|
60
|
+
return Serialization.serialize_dict(type_as_dict)
|
61
|
+
|
46
62
|
@classmethod
|
47
|
-
def _from_json_(cls,
|
48
|
-
|
63
|
+
def _from_json_(cls,data):
|
64
|
+
|
65
|
+
return Serialization.deserialize(data, Gender)
|
66
|
+
|
67
|
+
|
gedcomx/Group.py
CHANGED
@@ -9,7 +9,7 @@ from .Identifier import Identifier
|
|
9
9
|
from .Note import Note
|
10
10
|
from .PlaceReference import PlaceReference
|
11
11
|
from .SourceReference import SourceReference
|
12
|
-
from .
|
12
|
+
from .Resource import Resource
|
13
13
|
|
14
14
|
from .TextValue import TextValue
|
15
15
|
from .Subject import Subject
|
@@ -22,14 +22,14 @@ class GroupRole:
|
|
22
22
|
identifier = 'http://gedcomx.org/v1/GroupRole'
|
23
23
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
24
24
|
|
25
|
-
def __init__(self, person:
|
25
|
+
def __init__(self, person: Resource,type: Optional[Enum], date: Optional[Date],details: Optional[str]) -> None:
|
26
26
|
pass
|
27
27
|
|
28
28
|
class Group(Subject):
|
29
29
|
identifier = 'http://gedcomx.org/v1/Group'
|
30
30
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
31
31
|
|
32
|
-
def __init__(self, id: str | None, lang: str | None, sources: SourceReference | None, analysis:
|
32
|
+
def __init__(self, id: str | None, lang: str | None, sources: SourceReference | None, analysis: Resource | None, notes: Note | None, confidence: ConfidenceLevel | None, attribution: Attribution | None, extracted: bool | None, evidence: List[EvidenceReference] | None, media: List[SourceReference] | None, identifiers: List[Identifier] | None,
|
33
33
|
names: TextValue,
|
34
34
|
date: Optional[Date],
|
35
35
|
place: Optional[PlaceReference],
|
gedcomx/Identifier.py
CHANGED
@@ -3,87 +3,224 @@ from enum import Enum
|
|
3
3
|
|
4
4
|
from typing import List, Optional, Dict, Any
|
5
5
|
|
6
|
-
from .
|
6
|
+
from collections.abc import Iterator
|
7
|
+
import json
|
8
|
+
from .Resource import Resource
|
7
9
|
from .URI import URI
|
10
|
+
from .Extensible.extensibles import ExtensibleEnum
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
12
|
+
import secrets
|
13
|
+
import string
|
14
|
+
import json
|
15
|
+
|
16
|
+
def make_uid(length: int = 10, alphabet: str = string.ascii_letters + string.digits) -> str:
|
17
|
+
"""
|
18
|
+
Generate a cryptographically secure alphanumeric UID.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
length: Number of characters to generate (must be > 0).
|
22
|
+
alphabet: Characters to choose from (default: A-Za-z0-9).
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
A random string of `length` characters from `alphabet`.
|
26
|
+
"""
|
27
|
+
if length <= 0:
|
28
|
+
raise ValueError("length must be > 0")
|
29
|
+
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
30
|
+
|
31
|
+
class IdentifierType(ExtensibleEnum):
|
32
|
+
"""Enumeration of identifier types."""
|
33
|
+
pass
|
34
|
+
IdentifierType.register("Primary", "http://gedcomx.org/Primary")
|
35
|
+
IdentifierType.register("Authority", "http://gedcomx.org/Authority")
|
36
|
+
IdentifierType.register("Deprecated", "http://gedcomx.org/Deprecated")
|
37
|
+
IdentifierType.register("Persistent", "http://gedcomx.org/Persistent")
|
38
|
+
IdentifierType.register("External", "https://gedcom.io/terms/v7/EXID")
|
39
|
+
IdentifierType.register("Other", "user provided")
|
33
40
|
|
34
41
|
class Identifier:
|
35
42
|
identifier = 'http://gedcomx.org/v1/Identifier'
|
36
43
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
37
44
|
|
38
|
-
def __init__(self, value: Optional[URI], type: Optional[IdentifierType] = IdentifierType.Primary) -> None:
|
39
|
-
|
45
|
+
def __init__(self, value: Optional[List[URI]], type: Optional[IdentifierType] = IdentifierType.Primary) -> None:
|
46
|
+
if not isinstance(value,list):
|
47
|
+
value = [value] if value else []
|
40
48
|
self.type = type
|
41
|
-
|
49
|
+
self.values = value if value else []
|
42
50
|
|
43
51
|
@property
|
44
52
|
def _as_dict_(self):
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
elif isinstance(value, dict):
|
49
|
-
return {k: _serialize(v) for k, v in value.items()}
|
50
|
-
elif isinstance(value, (list, tuple, set)):
|
51
|
-
return [_serialize(v) for v in value]
|
52
|
-
elif hasattr(value, "_as_dict_"):
|
53
|
-
return value._as_dict_
|
54
|
-
else:
|
55
|
-
return str(value) # fallback for unknown objects
|
56
|
-
|
57
|
-
|
58
|
-
identifier_fields = {
|
59
|
-
'value': self.value if self.value else None,
|
53
|
+
from .Serialization import Serialization
|
54
|
+
type_as_dict = {
|
55
|
+
'value': [v for v in self.values] if self.values else None,
|
60
56
|
'type': self.type.value if self.type else None
|
61
57
|
|
62
58
|
}
|
63
59
|
|
64
|
-
|
65
|
-
for key, value in identifier_fields.items():
|
66
|
-
if value is not None:
|
67
|
-
identifier_fields[key] = _serialize(value)
|
68
|
-
|
69
|
-
return identifier_fields
|
60
|
+
return Serialization.serialize_dict(type_as_dict)
|
70
61
|
|
71
62
|
@classmethod
|
72
|
-
def _from_json_(cls, data: Dict[str, Any]) -> 'Identifier':
|
63
|
+
def _from_json_(cls, data: Dict[str, Any]) -> 'Identifier | None':
|
73
64
|
"""
|
74
65
|
Construct an Identifier from a dict parsed from JSON.
|
75
66
|
"""
|
76
|
-
#
|
67
|
+
#for name, member in IdentifierType.__members__.items():
|
68
|
+
# print(name)
|
69
|
+
|
70
|
+
|
77
71
|
|
78
72
|
for key in data.keys():
|
79
73
|
type = key
|
80
74
|
value = data[key]
|
81
|
-
uri_obj: Optional[
|
75
|
+
uri_obj: Optional[Resource] = None
|
82
76
|
# TODO DO THIS BETTER
|
83
77
|
|
84
78
|
# Parse type
|
85
79
|
raw_type = data.get('type')
|
86
|
-
|
87
|
-
|
80
|
+
if raw_type is None:
|
81
|
+
return None
|
82
|
+
id_type: Optional[IdentifierType] = IdentifierType(raw_type) if raw_type else None
|
83
|
+
return cls(value=value, type=id_type)
|
84
|
+
|
85
|
+
class IdentifierList:
|
86
|
+
def __init__(self) -> None:
|
87
|
+
# maps identifier-type (e.g., str or IdentifierType.value) -> list of values
|
88
|
+
self.identifiers: dict[str, list] = {}
|
89
|
+
|
90
|
+
# -------------------- hashing/uniqueness helpers --------------------
|
91
|
+
def make_hashable(self, obj):
|
92
|
+
"""Convert any object into a hashable representation."""
|
93
|
+
if isinstance(obj, dict):
|
94
|
+
return tuple(sorted((k, self.make_hashable(v)) for k, v in obj.items()))
|
95
|
+
elif isinstance(obj, (list, set, tuple)):
|
96
|
+
return tuple(self.make_hashable(i) for i in obj)
|
97
|
+
elif isinstance(obj, URI):
|
98
|
+
return obj._as_dict_
|
99
|
+
elif hasattr(obj, "_as_dict_"):
|
100
|
+
d = getattr(obj, "_as_dict_")
|
101
|
+
return tuple(sorted((k, self.make_hashable(v)) for k, v in d.items()))
|
102
|
+
else:
|
103
|
+
return obj
|
104
|
+
|
105
|
+
def unique_list(self, items):
|
106
|
+
"""Return a list without duplicates, preserving order."""
|
107
|
+
seen = set()
|
108
|
+
result = []
|
109
|
+
for item in items:
|
110
|
+
h = self.make_hashable(item)
|
111
|
+
if h not in seen:
|
112
|
+
seen.add(h)
|
113
|
+
result.append(item)
|
114
|
+
return result
|
115
|
+
|
116
|
+
# -------------------- public mutation API --------------------
|
117
|
+
def append(self, identifier: "Identifier"):
|
118
|
+
if isinstance(identifier, Identifier):
|
119
|
+
self.add_identifier(identifier)
|
120
|
+
else:
|
121
|
+
raise ValueError("append expects an Identifier instance")
|
122
|
+
|
123
|
+
# keep the old name working; point it at the corrected spelling
|
124
|
+
def add_identifer(self, identifier: "Identifier"): # backward-compat alias
|
125
|
+
return self.add_identifier(identifier)
|
126
|
+
|
127
|
+
def add_identifier(self, identifier: "Identifier"):
|
128
|
+
"""Add/merge an Identifier (which may contain multiple values)."""
|
129
|
+
if not (identifier and isinstance(identifier, Identifier) and identifier.type):
|
130
|
+
raise ValueError("The 'identifier' must be a valid Identifier instance with a type.")
|
131
|
+
|
132
|
+
key = identifier.type.value if hasattr(identifier.type, "value") else str(identifier.type)
|
133
|
+
existing = self.identifiers.get(key, [])
|
134
|
+
merged = self.unique_list(list(existing) + list(identifier.values))
|
135
|
+
self.identifiers[key] = merged
|
136
|
+
|
137
|
+
# -------------------- queries --------------------
|
138
|
+
def contains(self, identifier: "Identifier") -> bool:
|
139
|
+
"""Return True if any of the identifier's values are present under that type."""
|
140
|
+
if not (identifier and isinstance(identifier, Identifier) and identifier.type):
|
141
|
+
return False
|
142
|
+
key = identifier.type.value if hasattr(identifier.type, "value") else str(identifier.type)
|
143
|
+
if key not in self.identifiers:
|
144
|
+
return False
|
145
|
+
pool = self.identifiers[key]
|
146
|
+
# treat values as a list on the incoming Identifier
|
147
|
+
for v in getattr(identifier, "values", []):
|
148
|
+
if any(self.make_hashable(v) == self.make_hashable(p) for p in pool):
|
149
|
+
return True
|
150
|
+
return False
|
151
|
+
|
152
|
+
# -------------------- mapping-like dunder methods --------------------
|
153
|
+
def __iter__(self) -> Iterator[str]:
|
154
|
+
"""Iterate over identifier *types* (keys)."""
|
155
|
+
return iter(self.identifiers)
|
156
|
+
|
157
|
+
def __len__(self) -> int:
|
158
|
+
"""Number of identifier types (keys)."""
|
159
|
+
return len(self.identifiers)
|
160
|
+
|
161
|
+
def __contains__(self, key) -> bool:
|
162
|
+
"""Check if a type key exists (accepts str or enum with .value)."""
|
163
|
+
k = key.value if hasattr(key, "value") else str(key)
|
164
|
+
return k in self.identifiers
|
165
|
+
|
166
|
+
def __getitem__(self, key):
|
167
|
+
"""Lookup values by type key (accepts str or enum with .value)."""
|
168
|
+
k = key.value if hasattr(key, "value") else str(key)
|
169
|
+
return self.identifiers[k]
|
170
|
+
|
171
|
+
# (optional) enable assignment via mapping syntax
|
172
|
+
def __setitem__(self, key, values):
|
173
|
+
"""Set/replace the list of values for a type key."""
|
174
|
+
k = key.value if hasattr(key, "value") else str(key)
|
175
|
+
vals = values if isinstance(values, list) else [values]
|
176
|
+
self.identifiers[k] = self.unique_list(vals)
|
177
|
+
|
178
|
+
def __delitem__(self, key):
|
179
|
+
k = key.value if hasattr(key, "value") else str(key)
|
180
|
+
del self.identifiers[k]
|
181
|
+
|
182
|
+
# -------------------- dict-style convenience --------------------
|
183
|
+
def keys(self):
|
184
|
+
return self.identifiers.keys()
|
185
|
+
|
186
|
+
def values(self):
|
187
|
+
return self.identifiers.values()
|
188
|
+
|
189
|
+
def items(self):
|
190
|
+
return self.identifiers.items()
|
191
|
+
|
192
|
+
def iter_pairs(self) -> Iterator[tuple[str, object]]:
|
193
|
+
"""Flattened iterator over (type_key, value) pairs."""
|
194
|
+
for k, vals in self.identifiers.items():
|
195
|
+
for v in vals:
|
196
|
+
yield (k, v)
|
197
|
+
|
198
|
+
@classmethod
|
199
|
+
def _from_json_(cls, data):
|
200
|
+
if isinstance(data, dict):
|
201
|
+
identifier_list = IdentifierList()
|
202
|
+
for key, vals in data.items():
|
203
|
+
# Accept both single value and list in JSON
|
204
|
+
vals = vals if isinstance(vals, list) else [vals]
|
205
|
+
identifier_list.add_identifier(
|
206
|
+
Identifier(value=vals, type=IdentifierType(key))
|
207
|
+
)
|
208
|
+
return identifier_list
|
209
|
+
else:
|
210
|
+
raise ValueError("Data must be a dict of identifiers.")
|
211
|
+
|
212
|
+
@property
|
213
|
+
def _as_dict_(self):
|
214
|
+
# If you want a *copy*, return `dict(self.identifiers)`
|
215
|
+
return self.identifiers
|
216
|
+
|
217
|
+
def __repr__(self) -> str:
|
218
|
+
return json.dumps(self._as_dict_, indent=4)
|
219
|
+
|
220
|
+
def __str__(self) -> str:
|
221
|
+
return json.dumps(self._as_dict_)
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
|
88
226
|
|
89
|
-
return cls(value=value, type=id_type)
|
gedcomx/Logging.py
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
def get_logger(name='gedcomx.log'):
|
4
|
+
logger = logging.getLogger(name)
|
5
|
+
if not logger.handlers:
|
6
|
+
logger.setLevel(logging.DEBUG)
|
7
|
+
|
8
|
+
formatter = logging.Formatter('[%(asctime)s] %(levelname)s - %(name)s - %(message)s')
|
9
|
+
|
10
|
+
console_handler = logging.StreamHandler()
|
11
|
+
console_handler.setFormatter(formatter)
|
12
|
+
logger.addHandler(console_handler)
|
13
|
+
|
14
|
+
# Optional: file logging
|
15
|
+
file_handler = logging.FileHandler(f"{name}.log", encoding="utf-8")
|
16
|
+
file_handler.setFormatter(formatter)
|
17
|
+
logger.addHandler(file_handler)
|
18
|
+
|
19
|
+
return logger
|
gedcomx/Mutations.py
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
from .Gedcom5x import GedcomRecord
|
2
|
+
from .Fact import Fact, FactType
|
3
|
+
from .Event import Event, EventType
|
4
|
+
|
5
|
+
fact_event_table = {
|
6
|
+
# Person Fact / Event Types
|
7
|
+
"ADOP": {
|
8
|
+
"Fact": FactType.AdoptiveParent,
|
9
|
+
"Event": EventType.Adoption,
|
10
|
+
},
|
11
|
+
"CHR": {
|
12
|
+
"Fact": FactType.AdultChristening,
|
13
|
+
"Event": EventType.AdultChristening,
|
14
|
+
},
|
15
|
+
"EVEN": {
|
16
|
+
"Fact": FactType.Amnesty,
|
17
|
+
# no Event
|
18
|
+
},
|
19
|
+
"BAPM": {
|
20
|
+
"Fact": FactType.Baptism,
|
21
|
+
"Event": EventType.Baptism,
|
22
|
+
},
|
23
|
+
"BARM": {
|
24
|
+
"Fact": FactType.BarMitzvah,
|
25
|
+
"Event": EventType.BarMitzvah,
|
26
|
+
},
|
27
|
+
"BASM": {
|
28
|
+
"Fact": FactType.BatMitzvah,
|
29
|
+
"Event": EventType.BatMitzvah,
|
30
|
+
},
|
31
|
+
"BIRT": {
|
32
|
+
"Fact": FactType.Birth,
|
33
|
+
"Event": EventType.Birth,
|
34
|
+
},
|
35
|
+
"BIRT, CHR": {
|
36
|
+
"Fact": FactType.Birth,
|
37
|
+
"Event": EventType.Birth,
|
38
|
+
},
|
39
|
+
"BLES": {
|
40
|
+
"Fact": FactType.Blessing,
|
41
|
+
"Event": EventType.Blessing,
|
42
|
+
},
|
43
|
+
"BURI": {
|
44
|
+
"Fact": FactType.Burial,
|
45
|
+
"Event": EventType.Burial,
|
46
|
+
},
|
47
|
+
"CAST": {
|
48
|
+
"Fact": FactType.Caste,
|
49
|
+
# no Event
|
50
|
+
},
|
51
|
+
"CENS": {
|
52
|
+
"Fact": FactType.Census,
|
53
|
+
"Event": EventType.Census,
|
54
|
+
},
|
55
|
+
"CIRC": {
|
56
|
+
"Fact": FactType.Circumcision,
|
57
|
+
"Event": EventType.Circumcision,
|
58
|
+
},
|
59
|
+
"CONF": {
|
60
|
+
"Fact": FactType.Confirmation,
|
61
|
+
"Event": EventType.Confirmation,
|
62
|
+
},
|
63
|
+
"CREM": {
|
64
|
+
"Fact": FactType.Cremation,
|
65
|
+
"Event": EventType.Cremation,
|
66
|
+
},
|
67
|
+
"DEAT": {
|
68
|
+
"Fact": FactType.Death,
|
69
|
+
"Event": EventType.Death,
|
70
|
+
},
|
71
|
+
"EDUC": {
|
72
|
+
"Fact": FactType.Education,
|
73
|
+
"Event": EventType.Education,
|
74
|
+
},
|
75
|
+
"EMIG": {
|
76
|
+
"Fact": FactType.Emigration,
|
77
|
+
"Event": EventType.Emigration,
|
78
|
+
},
|
79
|
+
"FCOM": {
|
80
|
+
"Fact": FactType.FirstCommunion,
|
81
|
+
"Event": EventType.FirstCommunion,
|
82
|
+
},
|
83
|
+
"GRAD": {
|
84
|
+
"Fact": FactType.Graduation,
|
85
|
+
# no Event
|
86
|
+
},
|
87
|
+
"IMMI": {
|
88
|
+
"Fact": FactType.Immigration,
|
89
|
+
"Event": EventType.Immigration,
|
90
|
+
},
|
91
|
+
"MIL": {
|
92
|
+
"Fact": FactType.MilitaryService,
|
93
|
+
# no Event
|
94
|
+
},
|
95
|
+
"NATI": {
|
96
|
+
"Fact": FactType.Nationality,
|
97
|
+
# no Event
|
98
|
+
},
|
99
|
+
"NATU": {
|
100
|
+
"Fact": FactType.Naturalization,
|
101
|
+
"Event": EventType.Naturalization,
|
102
|
+
},
|
103
|
+
"OCCU": {
|
104
|
+
"Fact": FactType.Occupation,
|
105
|
+
# no Event
|
106
|
+
},
|
107
|
+
"ORDN": {
|
108
|
+
"Fact": FactType.Ordination,
|
109
|
+
"Event": EventType.Ordination,
|
110
|
+
},
|
111
|
+
"DSCR": {
|
112
|
+
"Fact": FactType.PhysicalDescription,
|
113
|
+
# no Event
|
114
|
+
},
|
115
|
+
"PROB": {
|
116
|
+
"Fact": FactType.Probate,
|
117
|
+
# no Event
|
118
|
+
},
|
119
|
+
"PROP": {
|
120
|
+
"Fact": FactType.Property,
|
121
|
+
# no Event
|
122
|
+
},
|
123
|
+
"RELI": {
|
124
|
+
"Fact": FactType.Religion,
|
125
|
+
# no Event
|
126
|
+
},
|
127
|
+
"RESI": {
|
128
|
+
"Fact": FactType.Residence,
|
129
|
+
# no Event
|
130
|
+
},
|
131
|
+
"WILL": {
|
132
|
+
"Fact": FactType.Will,
|
133
|
+
# no Event
|
134
|
+
},
|
135
|
+
|
136
|
+
# Couple Relationship Fact / Event Types
|
137
|
+
"ANUL": {
|
138
|
+
"Fact": FactType.Annulment,
|
139
|
+
"Event": EventType.Annulment,
|
140
|
+
},
|
141
|
+
"DIV": {
|
142
|
+
"Fact": FactType.Divorce,
|
143
|
+
"Event": EventType.Divorce,
|
144
|
+
},
|
145
|
+
"DIVF": {
|
146
|
+
"Fact": FactType.DivorceFiling,
|
147
|
+
"Event": EventType.DivorceFiling,
|
148
|
+
},
|
149
|
+
"ENGA": {
|
150
|
+
"Fact": FactType.Engagement,
|
151
|
+
"Event": EventType.Engagement,
|
152
|
+
},
|
153
|
+
"MARR": {
|
154
|
+
"Fact": FactType.Marriage,
|
155
|
+
"Event": EventType.Marriage,
|
156
|
+
},
|
157
|
+
"MARB": {
|
158
|
+
"Fact": FactType.MarriageBanns,
|
159
|
+
# no Event
|
160
|
+
},
|
161
|
+
"MARC": {
|
162
|
+
"Fact": FactType.MarriageContract,
|
163
|
+
# no Event
|
164
|
+
},
|
165
|
+
"MARL": {
|
166
|
+
"Fact": FactType.MarriageLicense,
|
167
|
+
# no Event
|
168
|
+
},
|
169
|
+
"MARS":{
|
170
|
+
"Fact":EventType.MarriageSettlment
|
171
|
+
|
172
|
+
},
|
173
|
+
"SEPA": {
|
174
|
+
"Fact": FactType.Separation,
|
175
|
+
# no Event
|
176
|
+
},
|
177
|
+
|
178
|
+
}
|
179
|
+
|
180
|
+
class GedcomXObject:
|
181
|
+
def __init__(self,record: GedcomRecord | None = None) -> None:
|
182
|
+
self.created_with_tag: str = record.tag if record and isinstance(record, GedcomRecord) else None
|
183
|
+
self.created_at_level: int = record.level if record and isinstance(record, GedcomRecord) else None
|
184
|
+
self.created_at_line_number: int = record.line_number if record and isinstance(record, GedcomRecord) else None
|
185
|
+
|
186
|
+
class GedcomXSourceOrDocument(GedcomXObject):
|
187
|
+
def __init__(self,record: GedcomRecord | None = None) -> None:
|
188
|
+
super().__init__(record)
|
189
|
+
self.title: str = None
|
190
|
+
self.citation: str = None
|
191
|
+
self.page: str = None
|
192
|
+
self.contributor: str = None
|
193
|
+
self.publisher: str = None
|
194
|
+
self.rights: str = None
|
195
|
+
self.url: str = None
|
196
|
+
self.medium: str = None
|
197
|
+
self.type: str = None
|
198
|
+
self.format: str = None
|
199
|
+
self.created: str = None
|
200
|
+
self.modified: str = None
|
201
|
+
self.language: str = None
|
202
|
+
self.relation: str = None
|
203
|
+
self.identifier: str = None
|
204
|
+
self.description: str = None
|
205
|
+
|
206
|
+
class GedcomXEventOrFact(GedcomXObject):
|
207
|
+
def __new__(cls,record: GedcomRecord | None = None, object_stack: dict | None = None) -> object:
|
208
|
+
super().__init__(record)
|
209
|
+
if record.tag in fact_event_table.keys():
|
210
|
+
|
211
|
+
if 'Fact' in fact_event_table[record.tag].keys():
|
212
|
+
obj = Fact(type=fact_event_table[record.tag]['Fact'])
|
213
|
+
return obj
|
214
|
+
elif 'Event' in fact_event_table[record.tag].keys():
|
215
|
+
obj = Event(type=fact_event_table[record.tag]['Fact'])
|
216
|
+
else:
|
217
|
+
raise ValueError
|
218
|
+
else:
|
219
|
+
raise ValueError(f"{record.tag} not found in map")
|
220
|
+
|
221
|
+
class GedcomXRelationshipBuilder(GedcomXObject):
|
222
|
+
def __new__(cls,record: GedcomRecord | None = None, object_stack: dict | None = None) -> object:
|
223
|
+
last_relationship = object_stack.get('lastrelationship',None)
|
224
|
+
last_relationship_data = object_stack.get('lastrelationshipdata',None)
|
225
|
+
if not isinstance(last_relationship_data,dict):
|
226
|
+
last_relationship_data = None
|
227
|
+
|
228
|
+
|