gedcom-x 0.5.5__py3-none-any.whl → 0.5.7__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.7.dist-info/METADATA +144 -0
- gedcom_x-0.5.7.dist-info/RECORD +49 -0
- gedcomx/Address.py +13 -13
- gedcomx/Agent.py +28 -16
- gedcomx/Attribution.py +34 -7
- gedcomx/Conclusion.py +24 -13
- gedcomx/Converter.py +1034 -0
- gedcomx/Coverage.py +7 -6
- gedcomx/Date.py +11 -4
- gedcomx/Document.py +2 -1
- gedcomx/Event.py +95 -20
- gedcomx/ExtensibleEnum.py +183 -0
- gedcomx/Extensions/__init__.py +1 -0
- gedcomx/Extensions/rs10/__init__.py +1 -0
- gedcomx/Extensions/rs10/rsLink.py +116 -0
- gedcomx/Fact.py +16 -13
- gedcomx/Gedcom5x.py +115 -77
- gedcomx/GedcomX.py +184 -1034
- gedcomx/Gender.py +7 -9
- gedcomx/Identifier.py +10 -13
- gedcomx/LoggingHub.py +207 -0
- gedcomx/Mutations.py +8 -8
- gedcomx/Name.py +207 -87
- gedcomx/Note.py +16 -9
- gedcomx/Person.py +39 -18
- gedcomx/PlaceDescription.py +70 -19
- gedcomx/PlaceReference.py +40 -8
- gedcomx/Qualifier.py +39 -12
- gedcomx/Relationship.py +5 -3
- gedcomx/Resource.py +38 -28
- gedcomx/Serialization.py +773 -358
- gedcomx/SourceDescription.py +133 -74
- gedcomx/SourceReference.py +10 -9
- gedcomx/Subject.py +5 -21
- gedcomx/Translation.py +976 -1
- gedcomx/URI.py +1 -1
- gedcomx/__init__.py +4 -2
- gedcom_x-0.5.5.dist-info/METADATA +0 -17
- gedcom_x-0.5.5.dist-info/RECORD +0 -43
- {gedcom_x-0.5.5.dist-info → gedcom_x-0.5.7.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.5.dist-info → gedcom_x-0.5.7.dist-info}/top_level.txt +0 -0
gedcomx/Coverage.py
CHANGED
@@ -2,7 +2,7 @@ from typing import Optional
|
|
2
2
|
|
3
3
|
from .Date import Date
|
4
4
|
from .PlaceReference import PlaceReference
|
5
|
-
|
5
|
+
|
6
6
|
|
7
7
|
class Coverage:
|
8
8
|
identifier = 'http://gedcomx.org/v1/Coverage'
|
@@ -16,11 +16,12 @@ class Coverage:
|
|
16
16
|
|
17
17
|
@property
|
18
18
|
def _as_dict_(self):
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
from .Serialization import Serialization
|
20
|
+
type_as_dict = {}
|
21
|
+
if self.spatial:
|
22
|
+
type_as_dict['spatial'] = getattr(self.spatial, '_as_dict_', self.spatial)
|
23
|
+
if self.temporal: # (fixed: no space after the dot)
|
24
|
+
type_as_dict['temporal'] = getattr(self.temporal, '_as_dict_', self.temporal)
|
24
25
|
return Serialization.serialize_dict(type_as_dict)
|
25
26
|
|
26
27
|
@classmethod
|
gedcomx/Date.py
CHANGED
@@ -4,6 +4,8 @@ from dateutil import parser
|
|
4
4
|
import time
|
5
5
|
|
6
6
|
|
7
|
+
|
8
|
+
|
7
9
|
class DateFormat:
|
8
10
|
def __init__(self) -> None:
|
9
11
|
pass
|
@@ -16,18 +18,23 @@ class Date:
|
|
16
18
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
17
19
|
|
18
20
|
def __init__(self, original: Optional[str],normalized: Optional[DateNormalization] = None ,formal: Optional[str | DateFormat] = None) -> None:
|
19
|
-
self.
|
21
|
+
self.original = original
|
20
22
|
self.formal = formal
|
21
23
|
|
22
24
|
self.normalized: DateNormalization | None = normalized if normalized else None
|
23
25
|
|
24
26
|
@property
|
25
27
|
def _as_dict_(self):
|
26
|
-
|
27
|
-
|
28
|
+
from .Serialization import Serialization
|
29
|
+
type_as_dict = {}
|
30
|
+
if self.original:
|
31
|
+
type_as_dict['original'] = self.original
|
32
|
+
if self.formal:
|
33
|
+
type_as_dict['formal'] = self.formal
|
34
|
+
return Serialization.serialize_dict(type_as_dict)
|
28
35
|
|
29
36
|
@classmethod
|
30
|
-
def _from_json_(
|
37
|
+
def _from_json_(cls,data):
|
31
38
|
original = data.get('original',None)
|
32
39
|
formal = data.get('formal',None)
|
33
40
|
|
gedcomx/Document.py
CHANGED
@@ -7,7 +7,7 @@ from .SourceReference import SourceReference
|
|
7
7
|
from .Resource import Resource
|
8
8
|
|
9
9
|
from .Conclusion import Conclusion
|
10
|
-
|
10
|
+
|
11
11
|
|
12
12
|
class DocumentType(Enum):
|
13
13
|
Abstract = "http://gedcomx.org/Abstract"
|
@@ -53,6 +53,7 @@ class Document(Conclusion):
|
|
53
53
|
|
54
54
|
@property
|
55
55
|
def _as_dict(self):
|
56
|
+
from .Serialization import Serialization
|
56
57
|
type_as_dict = super()._as_dict_
|
57
58
|
if self.type:
|
58
59
|
type_as_dict['type'] = self.type.value
|
gedcomx/Event.py
CHANGED
@@ -1,18 +1,37 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import List, Optional
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
"""
|
5
|
+
======================================================================
|
6
|
+
Project: Gedcom-X
|
7
|
+
File: Event.py
|
8
|
+
Author: David J. Cartwright
|
9
|
+
Purpose: Python Object representation of GedcomX Event Type, EventType, EventRole, EventRoleType Types
|
6
10
|
|
11
|
+
Created: 2025-08-25
|
12
|
+
Updated:
|
13
|
+
- 2025-08-31: fixed mutible [] in init, replaced List[Identifer] with IdentifierList
|
14
|
+
|
15
|
+
======================================================================
|
16
|
+
"""
|
17
|
+
|
18
|
+
"""
|
19
|
+
======================================================================
|
20
|
+
GEDCOM Module Type Imports
|
21
|
+
======================================================================
|
22
|
+
"""
|
7
23
|
from .Attribution import Attribution
|
8
|
-
from .Conclusion import
|
24
|
+
from .Conclusion import ConfidenceLevel, Conclusion
|
9
25
|
from .Date import Date
|
26
|
+
from .EvidenceReference import EvidenceReference
|
27
|
+
from .Identifier import IdentifierList
|
10
28
|
from .Note import Note
|
11
29
|
from .PlaceReference import PlaceReference
|
12
|
-
from .
|
30
|
+
from .Resource import Resource
|
13
31
|
from .SourceReference import SourceReference
|
14
32
|
from .Subject import Subject
|
15
|
-
|
33
|
+
|
34
|
+
#=====================================================================
|
16
35
|
|
17
36
|
class EventRoleType(Enum):
|
18
37
|
Principal = "http://gedcomx.org/Principal"
|
@@ -23,10 +42,10 @@ class EventRoleType(Enum):
|
|
23
42
|
@property
|
24
43
|
def description(self):
|
25
44
|
descriptions = {
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
45
|
+
EventRoleType.Principal: "The person is the principal person of the event. For example, the principal of a birth event is the person that was born.",
|
46
|
+
EventRoleType.Participant: "A participant in the event.",
|
47
|
+
EventRoleType.Official: "A person officiating the event.",
|
48
|
+
EventRoleType.Witness: "A witness of the event."
|
30
49
|
}
|
31
50
|
return descriptions.get(self, "No description available.")
|
32
51
|
|
@@ -36,19 +55,64 @@ class EventRole(Conclusion):
|
|
36
55
|
|
37
56
|
def __init__(self,
|
38
57
|
id: Optional[str] = None,
|
39
|
-
lang: Optional[str] =
|
58
|
+
lang: Optional[str] = None,
|
40
59
|
sources: Optional[List[SourceReference]] = [],
|
41
60
|
analysis: Optional[Resource] = None,
|
42
61
|
notes: Optional[List[Note]] = [],
|
43
62
|
confidence: Optional[ConfidenceLevel] = None,
|
44
63
|
attribution: Optional[Attribution] = None,
|
45
|
-
person: Resource = None,
|
64
|
+
person: Optional[Resource] = None,
|
46
65
|
type: Optional[EventRoleType] = None,
|
47
66
|
details: Optional[str] = None) -> None:
|
48
67
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution)
|
49
68
|
self.person = person
|
50
69
|
self.type = type
|
51
70
|
self.details = details
|
71
|
+
|
72
|
+
def __str__(self) -> str:
|
73
|
+
parts = []
|
74
|
+
if self.type is not None:
|
75
|
+
# assume enums expose .name
|
76
|
+
parts.append(f"type={getattr(self.type, 'name', str(self.type))}")
|
77
|
+
if self.person is not None:
|
78
|
+
# assume classes have meaningful __str__
|
79
|
+
parts.append(f"person={self.person}")
|
80
|
+
if self.details:
|
81
|
+
parts.append(f"details={self.details!r}")
|
82
|
+
if getattr(self, "id", None):
|
83
|
+
parts.append(f"id={self.id!r}")
|
84
|
+
return f"EventRole({', '.join(parts)})" if parts else "EventRole()"
|
85
|
+
|
86
|
+
def __repr__(self) -> str:
|
87
|
+
# assume enums expose .name and .value
|
88
|
+
if self.type is not None:
|
89
|
+
tcls = self.type.__class__.__name__
|
90
|
+
tname = getattr(self.type, "name", str(self.type))
|
91
|
+
tval = getattr(self.type, "value", self.type)
|
92
|
+
type_repr = f"<{tcls}.{tname}: {tval!r}>"
|
93
|
+
else:
|
94
|
+
type_repr = "None"
|
95
|
+
return (
|
96
|
+
f"{self.__class__.__name__}("
|
97
|
+
f"id={getattr(self, 'id', None)!r}, "
|
98
|
+
f"lang={getattr(self, 'lang', None)!r}, "
|
99
|
+
f"type={type_repr}, "
|
100
|
+
f"person={self.person!r}, "
|
101
|
+
f"details={self.details!r})"
|
102
|
+
)
|
103
|
+
|
104
|
+
@property
|
105
|
+
def _as_dict_(self):
|
106
|
+
from .Serialization import Serialization
|
107
|
+
type_as_dict = super()._as_dict_
|
108
|
+
if self.person:
|
109
|
+
type_as_dict['person'] = Resource(target=self.person)._as_dict_
|
110
|
+
if self.type is not None:
|
111
|
+
type_as_dict['type'] = getattr(self.type, 'value', self.type)
|
112
|
+
if self.details:
|
113
|
+
type_as_dict['details'] = self.details
|
114
|
+
|
115
|
+
return Serialization.serialize_dict(type_as_dict)
|
52
116
|
|
53
117
|
class EventType(Enum):
|
54
118
|
Adoption = "http://gedcomx.org/Adoption"
|
@@ -180,16 +244,17 @@ class Event(Subject):
|
|
180
244
|
|
181
245
|
def __init__(self,
|
182
246
|
id: Optional[str] = None,
|
183
|
-
lang: Optional[str] =
|
184
|
-
sources: Optional[List[SourceReference]] =
|
247
|
+
lang: Optional[str] = None,
|
248
|
+
sources: Optional[List[SourceReference]] = None,
|
185
249
|
analysis: Optional[Resource] = None,
|
186
|
-
notes: Optional[List[Note]] =
|
250
|
+
notes: Optional[List[Note]] = None,
|
187
251
|
confidence: Optional[ConfidenceLevel] = None,
|
188
252
|
attribution: Optional[Attribution] = None,
|
189
253
|
extracted: Optional[bool] = False,
|
190
|
-
evidence: Optional[List[EvidenceReference]] =
|
191
|
-
media: Optional[List[SourceReference]] =
|
192
|
-
identifiers: Optional[List[Identifier]] = [],
|
254
|
+
evidence: Optional[List[EvidenceReference]] = None,
|
255
|
+
media: Optional[List[SourceReference]] = None,
|
256
|
+
#identifiers: Optional[List[Identifier]] = [],
|
257
|
+
identifiers: Optional[IdentifierList] = None,
|
193
258
|
type: Optional[EventType] = None,
|
194
259
|
date: Optional[Date] = None,
|
195
260
|
place: Optional[PlaceReference] = None,
|
@@ -203,12 +268,22 @@ class Event(Subject):
|
|
203
268
|
|
204
269
|
@property
|
205
270
|
def _as_dict_(self):
|
206
|
-
|
271
|
+
from .Serialization import Serialization
|
272
|
+
type_as_dict = super()._as_dict_
|
273
|
+
type_as_dict.update({
|
274
|
+
'type': self.type.value if self.type else None,
|
275
|
+
'date': self.date,
|
276
|
+
'place': self.place._as_dict_ if self.place else None,
|
277
|
+
'roles': [role._as_dict_ for role in self.roles],
|
278
|
+
})
|
279
|
+
|
280
|
+
return Serialization.serialize_dict(type_as_dict)
|
207
281
|
|
208
282
|
@classmethod
|
209
283
|
def _from_json_(cls, data: dict):
|
210
284
|
"""
|
211
285
|
Create a Person instance from a JSON-dict (already parsed).
|
212
286
|
"""
|
213
|
-
type_as_dict = Serialization.get_class_fields('Event')
|
214
|
-
|
287
|
+
#type_as_dict = Serialization.get_class_fields('Event')
|
288
|
+
from .Serialization import Serialization
|
289
|
+
return Serialization.deserialize(data, Event)
|
@@ -0,0 +1,183 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, Iterator, Literal
|
4
|
+
|
5
|
+
"""
|
6
|
+
======================================================================
|
7
|
+
Project: Gedcom-X
|
8
|
+
File: ExtensibleEnum.py
|
9
|
+
Author: David J. Cartwright
|
10
|
+
Purpose: Create a class that can act like an enum but be extended by the user at runtime.
|
11
|
+
|
12
|
+
Created: 2025-08-25
|
13
|
+
Updated:
|
14
|
+
- YYYY-MM-DD: <change description>
|
15
|
+
|
16
|
+
======================================================================
|
17
|
+
"""
|
18
|
+
|
19
|
+
@dataclass(frozen=True, slots=True)
|
20
|
+
class _EnumItem:
|
21
|
+
"""
|
22
|
+
A single registered member of an :class:`ExtensibleEnum`.
|
23
|
+
|
24
|
+
Each `_EnumItem` represents one (name, value) pair that belongs
|
25
|
+
to a particular `ExtensibleEnum` subclass. Items are immutable
|
26
|
+
once created.
|
27
|
+
|
28
|
+
Attributes
|
29
|
+
----------
|
30
|
+
owner : type
|
31
|
+
The subclass of :class:`ExtensibleEnum` that owns this member
|
32
|
+
(e.g., `Color`).
|
33
|
+
name : str
|
34
|
+
The symbolic name of the member (e.g., `"RED"`).
|
35
|
+
value : Any
|
36
|
+
The underlying value associated with the member (e.g., `"r"`).
|
37
|
+
|
38
|
+
Notes
|
39
|
+
-----
|
40
|
+
- Equality is determined by object identity (not overridden).
|
41
|
+
- Instances are hashable by default since the dataclass is frozen.
|
42
|
+
- The `__repr__` and `__str__` provide user-friendly string forms.
|
43
|
+
|
44
|
+
Examples
|
45
|
+
--------
|
46
|
+
>>> class Color(ExtensibleEnum): ...
|
47
|
+
>>> red = Color.register("RED", "r")
|
48
|
+
>>> repr(red)
|
49
|
+
'Color.RED'
|
50
|
+
>>> str(red)
|
51
|
+
'RED'
|
52
|
+
>>> red.value
|
53
|
+
'r'
|
54
|
+
"""
|
55
|
+
owner: type
|
56
|
+
name: str
|
57
|
+
value: Any
|
58
|
+
def __repr__(self) -> str: # print(...) shows "Color.RED"
|
59
|
+
return f"{self.owner.__name__}.{self.name}"
|
60
|
+
def __str__(self) -> str:
|
61
|
+
return self.name
|
62
|
+
|
63
|
+
class _ExtEnumMeta(type):
|
64
|
+
def __iter__(cls) -> Iterator[_EnumItem]:
|
65
|
+
return iter(cls._members.values())
|
66
|
+
def __contains__(cls, item: object) -> bool:
|
67
|
+
return item in cls._members.values()
|
68
|
+
# Support Color('RED') / Color(2)
|
69
|
+
def __call__(cls, arg: Any, /, *, by: Literal["auto","name","value"]="auto") -> _EnumItem:
|
70
|
+
if isinstance(arg, _EnumItem):
|
71
|
+
if arg.owner is cls:
|
72
|
+
return arg
|
73
|
+
raise TypeError(f"{arg!r} is not a member of {cls.__name__}")
|
74
|
+
if by == "name":
|
75
|
+
return cls.get(str(arg))
|
76
|
+
if by == "value":
|
77
|
+
return cls.from_value(arg)
|
78
|
+
if isinstance(arg, str) and arg in cls._members:
|
79
|
+
return cls.get(arg)
|
80
|
+
return cls.from_value(arg)
|
81
|
+
|
82
|
+
class ExtensibleEnum(metaclass=_ExtEnumMeta):
|
83
|
+
"""
|
84
|
+
A lightweight, **runtime-extensible**, enum-like base class.
|
85
|
+
|
86
|
+
Subclass this to create an enum whose members can be registered at runtime.
|
87
|
+
Registered members are exposed as class attributes (e.g., `Color.RED`) and
|
88
|
+
can be retrieved by name (`Color.get("RED")`) or by value
|
89
|
+
(`Color.from_value("r")`). Square-bracket lookup (`Color["RED"]`) is also
|
90
|
+
supported via ``__class_getitem__``.
|
91
|
+
|
92
|
+
This is useful when:
|
93
|
+
- The full set of enum values is not known until runtime (plugins, config).
|
94
|
+
- You need attribute-style access (`Color.RED`) but want to add members
|
95
|
+
dynamically and/or validate uniqueness of names/values.
|
96
|
+
|
97
|
+
Notes
|
98
|
+
-----
|
99
|
+
- **Uniqueness:** Names and values are unique within a subclass.
|
100
|
+
- **Per-subclass registry:** Each subclass has its own member registry.
|
101
|
+
- **Thread safety:** Registration is **not** thread-safe. If multiple threads
|
102
|
+
may register members, wrap `register()` calls in your own lock.
|
103
|
+
- **Immutability:** Once registered, a member’s `name` and `value` are fixed.
|
104
|
+
Re-registering the same `name` with the *same* `value` returns the existing
|
105
|
+
item; a different value raises an error.
|
106
|
+
|
107
|
+
Examples
|
108
|
+
--------
|
109
|
+
Define an extensible enum and register members:
|
110
|
+
|
111
|
+
>>> class Color(ExtensibleEnum):
|
112
|
+
... pass
|
113
|
+
...
|
114
|
+
>>> Color.register("RED", "r")
|
115
|
+
_EnumItem(owner=Color, name='RED', value='r')
|
116
|
+
>>> Color.register("GREEN", "g")
|
117
|
+
_EnumItem(owner=Color, name='GREEN', value='g')
|
118
|
+
|
119
|
+
Access members:
|
120
|
+
|
121
|
+
>>> Color.RED is Color.get("RED")
|
122
|
+
True
|
123
|
+
>>> Color["GREEN"] is Color.get("GREEN")
|
124
|
+
True
|
125
|
+
>>> Color.from_value("g") is Color.GREEN
|
126
|
+
True
|
127
|
+
>>> Color.names()
|
128
|
+
['RED', 'GREEN']
|
129
|
+
|
130
|
+
Error cases:
|
131
|
+
|
132
|
+
>>> Color.register("RED", "different") # doctest: +IGNORE_EXCEPTION_DETAIL
|
133
|
+
ValueError: name 'RED' already used with different value 'r'
|
134
|
+
>>> Color.get("BLUE") # doctest: +IGNORE_EXCEPTION_DETAIL
|
135
|
+
KeyError: Color has no member named 'BLUE'
|
136
|
+
>>> Color.from_value("b") # doctest: +IGNORE_EXCEPTION_DETAIL
|
137
|
+
KeyError: Color has no member with value 'b'
|
138
|
+
"""
|
139
|
+
"""Runtime-extensible enum-like base."""
|
140
|
+
_members: Dict[str, _EnumItem] = {}
|
141
|
+
|
142
|
+
def __init_subclass__(cls, **kw):
|
143
|
+
super().__init_subclass__(**kw)
|
144
|
+
cls._members = {} # fresh registry per subclass
|
145
|
+
|
146
|
+
@classmethod
|
147
|
+
def __class_getitem__(cls, key: str) -> _EnumItem: # Color['RED']
|
148
|
+
return cls.get(key)
|
149
|
+
|
150
|
+
@classmethod
|
151
|
+
def register(cls, name: str, value: Any) -> _EnumItem:
|
152
|
+
if not isinstance(name, str) or not name.isidentifier():
|
153
|
+
raise ValueError("name must be a valid identifier")
|
154
|
+
if name in cls._members:
|
155
|
+
item = cls._members[name]
|
156
|
+
if item.value != value:
|
157
|
+
raise ValueError(f"name {name!r} already used with different value {item.value!r}")
|
158
|
+
return item
|
159
|
+
if any(m.value == value for m in cls._members.values()):
|
160
|
+
raise ValueError(f"value {value!r} already used")
|
161
|
+
item = _EnumItem(owner=cls, name=name, value=value)
|
162
|
+
cls._members[name] = item
|
163
|
+
setattr(cls, name, item) # enables Color.RED attribute
|
164
|
+
return item
|
165
|
+
|
166
|
+
@classmethod
|
167
|
+
def names(cls) -> list[str]:
|
168
|
+
return list(cls._members.keys())
|
169
|
+
|
170
|
+
@classmethod
|
171
|
+
def get(cls, name: str) -> _EnumItem:
|
172
|
+
try:
|
173
|
+
return cls._members[name]
|
174
|
+
except KeyError as e:
|
175
|
+
raise KeyError(f"{cls.__name__} has no member named {name!r}") from e
|
176
|
+
|
177
|
+
@classmethod
|
178
|
+
def from_value(cls, value: Any) -> _EnumItem:
|
179
|
+
for m in cls._members.values():
|
180
|
+
if m.value == value:
|
181
|
+
return m
|
182
|
+
raise KeyError(f"{cls.__name__} has no member with value {value!r}")
|
183
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
from .rs10 import rsLink
|
@@ -0,0 +1 @@
|
|
1
|
+
from .rsLink import rsLink
|
@@ -0,0 +1,116 @@
|
|
1
|
+
'''
|
2
|
+
The "Link" Data Type
|
3
|
+
The Link data type defines a representation of an available transition from one application state to another.
|
4
|
+
The base definition of a link is provided by RFC 5988.
|
5
|
+
|
6
|
+
Instances of Link can be reasonably expected as extension elements to any GEDCOM X data type,
|
7
|
+
except data types that are defined by the GEDCOM X Conceptual Model to explicitly restrict extension properties.
|
8
|
+
'''
|
9
|
+
|
10
|
+
'''
|
11
|
+
8/25/25, 0.5.5, built serialization, type checking in init for href
|
12
|
+
'''
|
13
|
+
|
14
|
+
from typing import List, Optional
|
15
|
+
from ...URI import URI
|
16
|
+
from ...Exceptions import GedcomClassAttributeError
|
17
|
+
|
18
|
+
|
19
|
+
class rsLink():
|
20
|
+
"""A link description object. RS Extension to GedcomX by FamilySearch.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
rel (str): Link relation identifier. Required.
|
24
|
+
href (str, optional): Link target URI. If omitted, provide `template`.
|
25
|
+
template (str, optional): URI Template (see RFC 6570). If omitted, provide `href`.
|
26
|
+
type (str, optional): Media type(s) of the linked resource (RFC 2616 §3.7).
|
27
|
+
accept (str, optional): Acceptable media type(s) for updating the linked resource (RFC 2616 §3.7).
|
28
|
+
allow (str, optional): Allowable HTTP methods to transition to the linked resource (RFC 2616 §14.7).
|
29
|
+
hreflang (str, optional): Language of the linked resource (e.g., BCP-47 tag).
|
30
|
+
title (str, optional): Human-readable label for the link.
|
31
|
+
|
32
|
+
Raises:
|
33
|
+
ValueError: If neither `href` nor `template` is provided.
|
34
|
+
"""
|
35
|
+
|
36
|
+
"""Attribution Information for a Genealogy, Conclusion, Subject and child classes
|
37
|
+
|
38
|
+
Args:
|
39
|
+
contributor (Agent, optional): Contributor to object being attributed.
|
40
|
+
modified (timestamp, optional): timestamp for when this record was modified.
|
41
|
+
changeMessage (str, optional): Birth date (YYYY-MM-DD).
|
42
|
+
creator (Agent, optional): Creator of object being attributed.
|
43
|
+
created (timestamp, optional): timestamp for when this record was created
|
44
|
+
|
45
|
+
Raises:
|
46
|
+
|
47
|
+
"""
|
48
|
+
identifier = "http://gedcomx.org/v1/Link"
|
49
|
+
|
50
|
+
def __init__(self,rel: Optional[URI] = None,
|
51
|
+
href: Optional[URI] = None,
|
52
|
+
template: Optional[str] = None,
|
53
|
+
type: Optional[str] = None,
|
54
|
+
accept: Optional[str] = None,
|
55
|
+
allow: Optional[str] = None,
|
56
|
+
hreflang: Optional[str] = None,
|
57
|
+
title: Optional[str] = None) -> None:
|
58
|
+
|
59
|
+
self.rel = rel
|
60
|
+
self.href = href if isinstance(href,URI) else URI.from_url(href) if isinstance(href,str) else None
|
61
|
+
self.template = template
|
62
|
+
self.type = type
|
63
|
+
self.accept = accept
|
64
|
+
self.allow = allow
|
65
|
+
self.hreflang = hreflang
|
66
|
+
self.title = title
|
67
|
+
|
68
|
+
if self.href is None and self.template is None:
|
69
|
+
raise GedcomClassAttributeError("href or template are required")
|
70
|
+
|
71
|
+
@property
|
72
|
+
def _as_dict_(self):
|
73
|
+
from ...Serialization import Serialization
|
74
|
+
type_as_dict = {
|
75
|
+
"rel": self.rel._as_dict_ if self.rel else None,
|
76
|
+
"href": self.href._as_dict_ if self.href else None,
|
77
|
+
"template": self.template, # RFC 6570 template if used
|
78
|
+
"type": self.type, # media type (note: shadows built-in 'type')
|
79
|
+
"accept": self.accept,
|
80
|
+
"allow": self.allow,
|
81
|
+
"hreflang": self.hreflang,
|
82
|
+
"title": self.title,
|
83
|
+
}
|
84
|
+
return Serialization.serialize_dict(type_as_dict)
|
85
|
+
|
86
|
+
@property
|
87
|
+
def json(self):
|
88
|
+
import json
|
89
|
+
return json.dumps(self._as_dict_)
|
90
|
+
|
91
|
+
class _rsLinkList():
|
92
|
+
def __init__(self) -> None:
|
93
|
+
self.links = {}
|
94
|
+
|
95
|
+
def add(self,link: rsLink):
|
96
|
+
if link and isinstance(link,rsLink):
|
97
|
+
if link.rel in self.links.keys():
|
98
|
+
self.links[link.rel].append(link.href)
|
99
|
+
else:
|
100
|
+
self.links[link.rel] = [link.href]
|
101
|
+
|
102
|
+
|
103
|
+
@classmethod
|
104
|
+
def _from_json_(cls,data: dict):
|
105
|
+
|
106
|
+
link_list = _rsLinkList()
|
107
|
+
for rel in data.keys():
|
108
|
+
link_list.add(rsLink(rel,data[rel]))
|
109
|
+
return link_list
|
110
|
+
|
111
|
+
@property
|
112
|
+
def _as_dict_(self) -> dict:
|
113
|
+
return self.links
|
114
|
+
|
115
|
+
|
116
|
+
|
gedcomx/Fact.py
CHANGED
@@ -12,7 +12,7 @@ from .Date import Date
|
|
12
12
|
from .Note import Note
|
13
13
|
from .PlaceReference import PlaceReference
|
14
14
|
from .SourceReference import SourceReference
|
15
|
-
|
15
|
+
|
16
16
|
from .Resource import Resource
|
17
17
|
|
18
18
|
from .Conclusion import Conclusion
|
@@ -367,7 +367,6 @@ class FactType(Enum):
|
|
367
367
|
for word in words:
|
368
368
|
matches = difflib.get_close_matches(word, keywords_to_fact_type.keys(), n=1, cutoff=0.8)
|
369
369
|
if matches:
|
370
|
-
print(f"Guessed '{description} to be of fact type: {keywords_to_fact_type[matches[0]]}")
|
371
370
|
return keywords_to_fact_type[matches[0]]
|
372
371
|
return None
|
373
372
|
|
@@ -397,7 +396,7 @@ class Fact(Conclusion):
|
|
397
396
|
|
398
397
|
def __init__(self,
|
399
398
|
id: Optional[str] = None,
|
400
|
-
lang: str =
|
399
|
+
lang: Optional[str] = None,
|
401
400
|
sources: Optional[List[SourceReference]] = [],
|
402
401
|
analysis: Optional[Resource | Document] = None,
|
403
402
|
notes: Optional[List[Note]] = [],
|
@@ -429,24 +428,28 @@ class Fact(Conclusion):
|
|
429
428
|
|
430
429
|
@property
|
431
430
|
def _as_dict_(self):
|
432
|
-
|
431
|
+
from .Serialization import Serialization
|
432
|
+
type_as_dcit = super()._as_dict_
|
433
433
|
# Only add Relationship-specific fields
|
434
|
-
|
435
|
-
'type'
|
436
|
-
|
437
|
-
'
|
438
|
-
|
439
|
-
'
|
440
|
-
|
434
|
+
if self.type:
|
435
|
+
type_as_dcit['type'] = getattr(self.type, 'value', self.type)
|
436
|
+
if self.date:
|
437
|
+
type_as_dcit['date'] = self.date._as_dict_
|
438
|
+
if self.place:
|
439
|
+
type_as_dcit['place'] = self.place._as_dict_
|
440
|
+
if self.value:
|
441
|
+
type_as_dcit['value'] = self.value
|
442
|
+
if self.qualifiers:
|
443
|
+
type_as_dcit['qualifiers'] = [getattr(q, 'value', q) for q in self.qualifiers]
|
441
444
|
|
442
|
-
return Serialization.serialize_dict(
|
445
|
+
return Serialization.serialize_dict(type_as_dcit)
|
443
446
|
|
444
447
|
@classmethod
|
445
448
|
def _from_json_(cls, data: Dict[str, Any]) -> 'Fact':
|
446
449
|
|
447
450
|
# Extract fields, no trailing commas!
|
448
451
|
id_ = data.get('id')
|
449
|
-
lang = data.get('lang',
|
452
|
+
lang = data.get('lang', None)
|
450
453
|
sources = [SourceReference._from_json_(s) for s in data.get('sources',[])]
|
451
454
|
analysis = (Resource._from_json_(data['analysis'])
|
452
455
|
if data.get('analysis') else None)
|