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/Name.py
CHANGED
@@ -2,13 +2,33 @@ from enum import Enum
|
|
2
2
|
from typing import List,Optional
|
3
3
|
from typing_extensions import Self
|
4
4
|
|
5
|
+
"""
|
6
|
+
======================================================================
|
7
|
+
Project: Gedcom-X
|
8
|
+
File: Name.py
|
9
|
+
Author: David J. Cartwright
|
10
|
+
Purpose: Python Object representation of GedcomX Name, NameType, NameForm, NamePart Types
|
11
|
+
|
12
|
+
Created: 2025-08-25
|
13
|
+
Updated:
|
14
|
+
- 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
|
15
|
+
|
16
|
+
======================================================================
|
17
|
+
"""
|
18
|
+
|
19
|
+
"""
|
20
|
+
======================================================================
|
21
|
+
GEDCOM Module Types
|
22
|
+
======================================================================
|
23
|
+
"""
|
24
|
+
#======================================================================
|
5
25
|
from .Attribution import Attribution
|
6
26
|
from .Conclusion import Conclusion, ConfidenceLevel
|
7
27
|
from .Date import Date
|
8
28
|
from .Note import Note
|
9
|
-
from .Serialization import Serialization
|
10
|
-
from .SourceReference import SourceReference
|
11
29
|
from .Resource import Resource
|
30
|
+
from .SourceReference import SourceReference
|
31
|
+
#======================================================================
|
12
32
|
|
13
33
|
|
14
34
|
class NameType(Enum):
|
@@ -90,22 +110,57 @@ class NamePartType(Enum):
|
|
90
110
|
return descriptions.get(self, "No description available.")
|
91
111
|
|
92
112
|
class NamePart:
|
113
|
+
"""Used to model a portion of a full name
|
114
|
+
including the terms that make up that portion. Some name parts may have qualifiers
|
115
|
+
to provide additional semantic meaning to the name part (e.g., "given name" or "surname").
|
116
|
+
|
117
|
+
Args:
|
118
|
+
type (NamePartType | None): Classification of this component of the name
|
119
|
+
(e.g., ``Given``, ``Surname``, ``Prefix``, ``Suffix``, ``Title``, ``Particle``).
|
120
|
+
value (str): The textual value for this part, without surrounding
|
121
|
+
punctuation (e.g., ``"John"``, ``"van"``, ``"III"``).
|
122
|
+
qualifiers (list[NamePartQualifier] | None): Optional qualifiers that refine
|
123
|
+
the meaning or usage of this part (e.g., language/script variants, initials).
|
124
|
+
|
125
|
+
Examples:
|
126
|
+
>>> from gedcomx.Name import *
|
127
|
+
>>> typ = NamePartType.Given
|
128
|
+
>>> given = "Moses"
|
129
|
+
>>> q = NamePartQualifier.Primary
|
130
|
+
>>> name = NamePart(type=typ,value=given,qualifiers=[q])
|
131
|
+
>>> print(name)
|
132
|
+
NamePart(type=Given, value='Moses', qualifiers=1)
|
133
|
+
|
134
|
+
"""
|
93
135
|
identifier = 'http://gedcomx.org/v1/NamePart'
|
94
136
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
95
137
|
|
96
138
|
def __init__(self,
|
97
139
|
type: Optional[NamePartType] = None,
|
98
140
|
value: Optional[str] = None,
|
99
|
-
qualifiers: Optional[List[NamePartQualifier]] =
|
141
|
+
qualifiers: Optional[List[NamePartQualifier]] = None) -> None:
|
100
142
|
self.type = type
|
101
143
|
self.value = value
|
102
|
-
self.qualifiers = qualifiers
|
144
|
+
self.qualifiers = qualifiers if qualifiers else []
|
103
145
|
|
146
|
+
@property
|
104
147
|
def _as_dict_(self):
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
148
|
+
from .Serialization import Serialization
|
149
|
+
type_as_dict = {}
|
150
|
+
if self.type:
|
151
|
+
type_as_dict['type'] = self.type.value
|
152
|
+
if self.value:
|
153
|
+
type_as_dict['value'] = self.value
|
154
|
+
if self.qualifiers:
|
155
|
+
type_as_dict['qualifiers'] = [q.value for q in self.qualifiers]
|
156
|
+
Serialization.serialize_dict(type_as_dict)
|
157
|
+
|
158
|
+
@classmethod
|
159
|
+
def _from_json_(cls,data):
|
160
|
+
name_part =NamePart(type=NamePartType(data['type']) if 'type' in data else None,
|
161
|
+
value=data.get('value'),
|
162
|
+
qualifiers=[NamePartQualifier(q) for q in data.get('qualifiers')])
|
163
|
+
return name_part
|
109
164
|
|
110
165
|
def __eq__(self, other):
|
111
166
|
if not isinstance(other, NamePart):
|
@@ -114,26 +169,114 @@ class NamePart:
|
|
114
169
|
self.value == other.value and
|
115
170
|
self.qualifiers == other.qualifiers)
|
116
171
|
|
172
|
+
def __str__(self) -> str:
|
173
|
+
parts = []
|
174
|
+
if self.type is not None:
|
175
|
+
parts.append(f"type={getattr(self.type, 'name', str(self.type))}")
|
176
|
+
if self.value is not None:
|
177
|
+
parts.append(f"value={self.value!r}")
|
178
|
+
if self.qualifiers:
|
179
|
+
parts.append(f"qualifiers={len(self.qualifiers)}")
|
180
|
+
return f"NamePart({', '.join(parts)})" if parts else "NamePart()"
|
181
|
+
|
182
|
+
def __repr__(self) -> str:
|
183
|
+
if self.type is not None:
|
184
|
+
tcls = self.type.__class__.__name__
|
185
|
+
tname = getattr(self.type, "name", str(self.type))
|
186
|
+
tval = getattr(self.type, "value", self.type)
|
187
|
+
type_repr = f"<{tcls}.{tname}: {tval!r}>"
|
188
|
+
else:
|
189
|
+
type_repr = "None"
|
190
|
+
return (
|
191
|
+
f"{self.__class__.__name__}("
|
192
|
+
f"type={type_repr}, "
|
193
|
+
f"value={self.value!r}, "
|
194
|
+
f"qualifiers={self.qualifiers!r})"
|
195
|
+
)
|
196
|
+
|
117
197
|
class NameForm:
|
198
|
+
"""A representation of a name (a "name form")
|
199
|
+
within a given cultural context, such as a given language and script.
|
200
|
+
As names are captured (both in records or in applications), the terms
|
201
|
+
in the name are sometimes classified by type. For example, a certificate
|
202
|
+
of death might prompt for **"given name(s)"** and **"surname"**. The parts
|
203
|
+
list can be used to represent the terms in the name that have been classified.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
lang (str | None): BCP-47 language tag for this name form (e.g., "en").
|
207
|
+
fullText (str | None): The full, unparsed name string as written in the source.
|
208
|
+
If provided, the name SHOULD be rendered as it would normally be spoken in
|
209
|
+
the applicable cultural context.
|
210
|
+
parts (list[NamePart] | None): Ordered structured components of the name
|
211
|
+
(e.g., Given, Surname). If provided, ``fullText`` may be omitted. If
|
212
|
+
provided, the list SHOULD be ordered such that the parts are in the order
|
213
|
+
they would normally be spoken in the applicable cultural context.
|
214
|
+
|
215
|
+
"""
|
118
216
|
identifier = 'http://gedcomx.org/v1/NameForm'
|
119
217
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
120
218
|
|
121
|
-
def __init__(self, lang: Optional[str] =
|
219
|
+
def __init__(self, lang: Optional[str] = None,
|
220
|
+
fullText: Optional[str] = None,
|
221
|
+
parts: Optional[List[NamePart]] = None) -> None:
|
222
|
+
|
122
223
|
self.lang = lang
|
123
224
|
self.fullText = fullText
|
124
|
-
self.parts = parts
|
225
|
+
self.parts = parts if parts else []
|
125
226
|
|
126
227
|
@property
|
127
228
|
def _as_dict_(self):
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
229
|
+
from .Serialization import Serialization
|
230
|
+
type_as_dict = {}
|
231
|
+
if self.lang:
|
232
|
+
type_as_dict['lang'] = self.lang
|
233
|
+
if self.fullText:
|
234
|
+
type_as_dict['fullText'] = self.fullText
|
235
|
+
if self.parts:
|
236
|
+
type_as_dict['parts'] = [part._as_dict_ for part in self.parts if part]
|
237
|
+
return Serialization.serialize_dict(type_as_dict)
|
238
|
+
|
239
|
+
@classmethod
|
240
|
+
def _from_json_(cls, data: dict) -> "NameForm":
|
241
|
+
"""Build a NameForm from JSON-like dict."""
|
242
|
+
return cls(
|
243
|
+
lang=data.get("lang", "en"),
|
244
|
+
fullText=data.get("fullText"),
|
245
|
+
parts=[NamePart._from_json_(p) for p in ensure_list(data.get("parts"))],
|
246
|
+
)
|
132
247
|
|
133
248
|
def _fulltext_parts(self):
|
134
249
|
pass
|
135
250
|
|
136
251
|
class Name(Conclusion):
|
252
|
+
"""**Defines a name of a person.**
|
253
|
+
|
254
|
+
A Name is intended to represent a single variant of a person's name.
|
255
|
+
This means that nicknames, spelling variations, or other names
|
256
|
+
(often distinguishable by a name type) should be modeled with
|
257
|
+
separate instances of Name.
|
258
|
+
|
259
|
+
.. admonition:: Advanced details
|
260
|
+
:class: toggle
|
261
|
+
|
262
|
+
The name forms of a name contain alternate representations of the name.
|
263
|
+
A Name MUST contain at least one name form, presumably a representation
|
264
|
+
of the name that is considered proper and well formed in the person's native,
|
265
|
+
historical cultural context. Other name forms MAY be included, which can be
|
266
|
+
used to represent this name in contexts where the native name form is not easily
|
267
|
+
recognized and interpreted. Alternate forms are more likely in situations where
|
268
|
+
conclusions are being analyzed across cultural context boundaries that have both
|
269
|
+
language and writing script differences.
|
270
|
+
|
271
|
+
|
272
|
+
Attributes:
|
273
|
+
type (Optional[:class:`~gedcomx.NameType`]): Classification of the name
|
274
|
+
(e.g., BirthName, AlsoKnownAs).
|
275
|
+
nameForms (List[:class:`~gedcomx.NameForm`]): One or more structured
|
276
|
+
representations of the name (full text and parts).
|
277
|
+
date (Optional[:class:`~gedcomx.Date`]): Date context for this name
|
278
|
+
(e.g., when the name was used or recorded).
|
279
|
+
"""
|
137
280
|
identifier = 'http://gedcomx.org/v1/Name'
|
138
281
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
139
282
|
|
@@ -163,8 +306,8 @@ class Name(Conclusion):
|
|
163
306
|
name = Name()
|
164
307
|
return name
|
165
308
|
|
166
|
-
def __init__(self, id: str = None,
|
167
|
-
lang: str =
|
309
|
+
def __init__(self, id: Optional[str] = None,
|
310
|
+
lang: Optional[str] = None,
|
168
311
|
sources: Optional[List[SourceReference]] = None,
|
169
312
|
analysis: Resource = None,
|
170
313
|
notes: Optional[List[Note]] = None,
|
@@ -187,39 +330,52 @@ class Name(Conclusion):
|
|
187
330
|
|
188
331
|
@property
|
189
332
|
def _as_dict_(self):
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
return value._as_dict_
|
199
|
-
else:
|
200
|
-
return str(value) # fallback for unknown objects
|
201
|
-
|
202
|
-
name_as_dict = super()._as_dict_
|
203
|
-
|
204
|
-
name_as_dict.update( {
|
205
|
-
'type':self.type.value if self.type else None,
|
206
|
-
'nameForms': [nameForm._as_dict_ for nameForm in self.nameForms],
|
207
|
-
'date': self.date._as_dict_ if self.date else None})
|
333
|
+
from .Serialization import Serialization
|
334
|
+
type_as_dict = super()._as_dict_
|
335
|
+
if self.type:
|
336
|
+
type_as_dict['type'] = getattr(self.type, 'value', self.type)
|
337
|
+
if self.nameForms:
|
338
|
+
type_as_dict['nameForms'] = [nf._as_dict_ for nf in self.nameForms if nf]
|
339
|
+
if self.date:
|
340
|
+
type_as_dict['date'] = self.date._as_dict_
|
208
341
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
342
|
+
return Serialization.serialize_dict(type_as_dict)
|
343
|
+
|
344
|
+
@classmethod
|
345
|
+
def _from_json_(cls, data: dict) -> "Name":
|
346
|
+
"""Build a Name from JSON-like dict."""
|
347
|
+
return cls(
|
348
|
+
id=data.get("id"),
|
349
|
+
lang=data.get("lang", "en"),
|
350
|
+
sources=[SourceReference._from_json_(s) for s in ensure_list(data.get("sources"))],
|
351
|
+
analysis=Resource._from_json_(data["analysis"]) if data.get("analysis") else None,
|
352
|
+
notes=[Note._from_json_(n) for n in ensure_list(data.get("notes"))],
|
353
|
+
confidence=ConfidenceLevel._from_json_(data["confidence"]) if data.get("confidence") else None,
|
354
|
+
attribution=Attribution._from_json_(data["attribution"]) if data.get("attribution") else None,
|
355
|
+
type=NameType(data["type"]) if data.get("type") else None,
|
356
|
+
nameForms=[NameForm._from_json_(nf) for nf in ensure_list(data.get("nameForms"))],
|
357
|
+
date=Date._from_json_(data["date"]) if data.get("date") else None,
|
358
|
+
)
|
221
359
|
|
222
|
-
|
360
|
+
def __str__(self) -> str:
|
361
|
+
"""Return a human-readable string for the Name-like object."""
|
362
|
+
return f"Name(id={self.id}, type={self.type}, forms={len(self.nameForms)}, date={self.date})"
|
363
|
+
|
364
|
+
def __repr__(self) -> str:
|
365
|
+
"""Return an unambiguous string representation of the Name-like object."""
|
366
|
+
return (
|
367
|
+
f"{self.__class__.__name__}("
|
368
|
+
f"id={self.id!r}, "
|
369
|
+
f"lang={self.lang!r}, "
|
370
|
+
f"sources={self.sources!r}, "
|
371
|
+
f"analysis={self.analysis!r}, "
|
372
|
+
f"notes={self.notes!r}, "
|
373
|
+
f"confidence={self.confidence!r}, "
|
374
|
+
f"attribution={self.attribution!r}, "
|
375
|
+
f"type={self.type!r}, "
|
376
|
+
f"nameForms={self.nameForms!r}, "
|
377
|
+
f"date={self.date!r})"
|
378
|
+
)
|
223
379
|
|
224
380
|
class QuickName():
|
225
381
|
def __new__(cls,name: str) -> Name:
|
@@ -231,46 +387,10 @@ def ensure_list(val):
|
|
231
387
|
return []
|
232
388
|
return val if isinstance(val, list) else [val]
|
233
389
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
))
|
239
|
-
|
240
|
-
NamePart._to_dict_ = lambda self: {
|
241
|
-
'type': self.type.value if self.type else None,
|
242
|
-
'value': self.value,
|
243
|
-
'qualifiers': [q.value for q in self.qualifiers] if self.qualifiers else []
|
244
|
-
}
|
245
|
-
|
246
|
-
|
247
|
-
# NameForm
|
248
|
-
NameForm._from_json_ = classmethod(lambda cls, data: NameForm(
|
249
|
-
lang=data.get('lang', 'en'),
|
250
|
-
fullText=data.get('fullText'),
|
251
|
-
parts=[NamePart._from_json_(p) for p in ensure_list(data.get('parts'))]
|
252
|
-
))
|
253
|
-
|
254
|
-
NameForm._to_dict_ = lambda self: {
|
255
|
-
'lang': self.lang,
|
256
|
-
'fullText': self.fullText,
|
257
|
-
'parts': [p._to_dict_() for p in self.parts] if self.parts else []
|
258
|
-
}
|
259
|
-
|
260
|
-
|
261
|
-
# Name
|
262
|
-
Name._from_json_ = classmethod(lambda cls, data: cls(
|
263
|
-
id=data.get('id'),
|
264
|
-
lang=data.get('lang', 'en'),
|
265
|
-
sources=[SourceReference._from_json_(s) for s in ensure_list(data.get('sources'))],
|
266
|
-
analysis=Resource._from_json_(data['analysis']) if data.get('analysis') else None,
|
267
|
-
notes=[Note._from_json_(n) for n in ensure_list(data.get('notes'))],
|
268
|
-
confidence=ConfidenceLevel._from_json_(data['confidence']) if data.get('confidence') else None,
|
269
|
-
attribution=Attribution._from_json_(data['attribution']) if data.get('attribution') else None,
|
270
|
-
type=NameType(data['type']) if data.get('type') else None,
|
271
|
-
nameForms=[NameForm._from_json_(nf) for nf in ensure_list(data.get('nameForms'))],
|
272
|
-
date=Date._from_json_(data['date']) if data.get('date') else None
|
273
|
-
))
|
390
|
+
|
391
|
+
|
392
|
+
|
393
|
+
|
274
394
|
|
275
395
|
|
276
396
|
|
gedcomx/Note.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
3
|
from .Attribution import Attribution
|
4
|
-
from .Serialization import Serialization
|
5
4
|
|
6
5
|
class Note:
|
7
6
|
identifier = 'http://gedcomx.org/v1/Note'
|
@@ -15,20 +14,28 @@ class Note:
|
|
15
14
|
|
16
15
|
def append(self, text_to_add: str):
|
17
16
|
if text_to_add and isinstance(text_to_add, str):
|
18
|
-
|
17
|
+
if self.text:
|
18
|
+
self.text = self.text + text_to_add
|
19
|
+
else:
|
20
|
+
self.text = text_to_add
|
19
21
|
else:
|
20
22
|
return #TODO
|
21
23
|
raise ValueError("The text to add must be a non-empty string.")
|
22
24
|
|
23
25
|
@property
|
24
26
|
def _as_dict_(self):
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
"
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
from .Serialization import Serialization
|
28
|
+
type_as_dict = {}
|
29
|
+
if self.lang:
|
30
|
+
type_as_dict["lang"] = self.lang
|
31
|
+
if self.subject:
|
32
|
+
type_as_dict["subject"] = self.subject
|
33
|
+
if self.text:
|
34
|
+
type_as_dict["text"] = self.text
|
35
|
+
if self.attribution:
|
36
|
+
# If attribution exposes `_as_dict_` as a property, use it; otherwise include as-is
|
37
|
+
type_as_dict["attribution"] = getattr(self.attribution, "_as_dict_", self.attribution)
|
38
|
+
return Serialization.serialize_dict(type_as_dict)
|
32
39
|
|
33
40
|
def __eq__(self, other):
|
34
41
|
if not isinstance(other, Note):
|
gedcomx/Person.py
CHANGED
@@ -1,22 +1,38 @@
|
|
1
|
-
from enum import Enum
|
2
1
|
from typing import List, Optional
|
3
2
|
from urllib.parse import urljoin
|
4
3
|
|
4
|
+
"""
|
5
|
+
======================================================================
|
6
|
+
Project: Gedcom-X
|
7
|
+
File: Person.py
|
8
|
+
Author: David J. Cartwright
|
9
|
+
Purpose: Python Object representation of GedcomX Person Type
|
10
|
+
|
11
|
+
Created: 2025-08-25
|
12
|
+
Updated:
|
13
|
+
- 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
|
14
|
+
|
15
|
+
======================================================================
|
16
|
+
"""
|
17
|
+
|
18
|
+
"""
|
19
|
+
======================================================================
|
20
|
+
GEDCOM Module Types
|
21
|
+
======================================================================
|
22
|
+
"""
|
5
23
|
from .Attribution import Attribution
|
6
24
|
from .Conclusion import ConfidenceLevel
|
7
25
|
from .Date import Date
|
8
26
|
from .EvidenceReference import EvidenceReference
|
27
|
+
from .Extensions.rs10.rsLink import _rsLinkList
|
9
28
|
from .Fact import Fact, FactType
|
10
29
|
from .Gender import Gender, GenderType
|
11
30
|
from .Identifier import IdentifierList
|
12
31
|
from .Name import Name, QuickName
|
13
32
|
from .Note import Note
|
33
|
+
from .Resource import Resource
|
14
34
|
from .SourceReference import SourceReference
|
15
|
-
from .Serialization import Serialization
|
16
35
|
from .Subject import Subject
|
17
|
-
from .Resource import Resource
|
18
|
-
from collections.abc import Sized
|
19
|
-
from .Extensions.rs10.rsLink import _rsLinkList
|
20
36
|
|
21
37
|
class Person(Subject):
|
22
38
|
"""A person in the system.
|
@@ -40,7 +56,7 @@ class Person(Subject):
|
|
40
56
|
notes: Optional[List[Note]] = None,
|
41
57
|
confidence: Optional[ConfidenceLevel] = None,
|
42
58
|
attribution: Optional[Attribution] = None,
|
43
|
-
extracted: bool = None,
|
59
|
+
extracted: Optional[bool] = None,
|
44
60
|
evidence: Optional[List[EvidenceReference]] = None,
|
45
61
|
media: Optional[List[SourceReference]] = None,
|
46
62
|
identifiers: Optional[IdentifierList] = None,
|
@@ -102,7 +118,7 @@ class Person(Subject):
|
|
102
118
|
"ascendancyNumber": "1",
|
103
119
|
"deathDate": "from 2001 to 2005",
|
104
120
|
"descendancyNumber": "1",
|
105
|
-
"gender": self.gender.type,
|
121
|
+
"gender": self.gender.type if self.gender else 'Unknown',
|
106
122
|
"lifespan": "-2005",
|
107
123
|
"name": self.names[0].nameForms[0].fullText
|
108
124
|
}
|
@@ -111,17 +127,20 @@ class Person(Subject):
|
|
111
127
|
|
112
128
|
@property
|
113
129
|
def _as_dict_(self):
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
'private'
|
118
|
-
|
119
|
-
'
|
120
|
-
|
121
|
-
'
|
122
|
-
|
123
|
-
|
124
|
-
|
130
|
+
from .Serialization import Serialization
|
131
|
+
type_as_dict = super()._as_dict_
|
132
|
+
if self.private is not None:
|
133
|
+
type_as_dict['private'] = self.private
|
134
|
+
if self.living is not None:
|
135
|
+
type_as_dict['living'] = self.living
|
136
|
+
if self.gender:
|
137
|
+
type_as_dict['gender'] = self.gender._as_dict_
|
138
|
+
if self.names:
|
139
|
+
type_as_dict['names'] = [n._as_dict_ for n in self.names if n]
|
140
|
+
if self.facts:
|
141
|
+
type_as_dict['facts'] = [f._as_dict_ for f in self.facts if f]
|
142
|
+
if self.uri:
|
143
|
+
type_as_dict['uri'] = self.uri._as_dict_
|
125
144
|
|
126
145
|
return Serialization.serialize_dict(type_as_dict)
|
127
146
|
|
@@ -130,10 +149,12 @@ class Person(Subject):
|
|
130
149
|
"""
|
131
150
|
Create a Person instance from a JSON-dict (already parsed).
|
132
151
|
"""
|
152
|
+
from .Serialization import Serialization
|
133
153
|
return Serialization.deserialize(data, Person)
|
134
154
|
|
135
155
|
@classmethod
|
136
156
|
def from_familysearch(cls, pid: str, token: str, *, base_url: Optional[str] = None):
|
157
|
+
from .Serialization import Serialization
|
137
158
|
"""
|
138
159
|
Fetch a single person by PID from FamilySearch and return a Person.
|
139
160
|
- pid: e.g. "KPHP-4B4"
|
gedcomx/PlaceDescription.py
CHANGED
@@ -1,24 +1,65 @@
|
|
1
1
|
from typing import List, Optional
|
2
2
|
|
3
|
+
"""
|
4
|
+
======================================================================
|
5
|
+
Project: Gedcom-X
|
6
|
+
File: PlaceDescription.py
|
7
|
+
Author: David J. Cartwright
|
8
|
+
Purpose: Python Object representation of GedcomX PlaceDescription Type
|
9
|
+
|
10
|
+
Created: 2025-08-25
|
11
|
+
Updated:
|
12
|
+
- 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
|
13
|
+
|
14
|
+
======================================================================
|
15
|
+
"""
|
16
|
+
|
17
|
+
"""
|
18
|
+
======================================================================
|
19
|
+
GEDCOM Module Types
|
20
|
+
======================================================================
|
21
|
+
"""
|
3
22
|
from .Attribution import Attribution
|
4
23
|
from .Conclusion import ConfidenceLevel
|
5
24
|
from .Date import Date
|
6
25
|
from .EvidenceReference import EvidenceReference
|
7
26
|
from .Identifier import IdentifierList
|
8
27
|
from .Note import Note
|
9
|
-
from .SourceReference import SourceReference
|
10
|
-
from .TextValue import TextValue
|
11
28
|
from .Resource import Resource
|
12
|
-
from .
|
29
|
+
from .SourceReference import SourceReference
|
13
30
|
from .Subject import Subject
|
31
|
+
from .TextValue import TextValue
|
14
32
|
from .URI import URI
|
15
33
|
|
16
34
|
class PlaceDescription(Subject):
|
35
|
+
"""PlaceDescription describes the details of a place in terms of
|
36
|
+
its name and possibly its type, time period, and/or a geospatial description
|
37
|
+
functioning as a description of a place as a snapshot in time.
|
38
|
+
|
39
|
+
Encapsulates textual names, geospatial coordinates, jurisdictional context,
|
40
|
+
temporal coverage, and related resources (media, sources, evidence, etc.).
|
41
|
+
|
42
|
+
|
43
|
+
Attributes:
|
44
|
+
names (Optional[List[TextValue]]): Human-readable names or labels for
|
45
|
+
the place (e.g., “Boston, Suffolk, Massachusetts, United States”).
|
46
|
+
type (Optional[str]): A place type identifier (e.g., a URI). **TODO:**
|
47
|
+
replace with an enumeration when finalized.
|
48
|
+
place (Optional[URI]): Canonical identifier (URI) for the place.
|
49
|
+
jurisdiction (Optional[Resource|PlaceDescription]): The governing or
|
50
|
+
containing jurisdiction of this place (e.g., county for a town).
|
51
|
+
latitude (Optional[float]): Latitude in decimal degrees (WGS84).
|
52
|
+
longitude (Optional[float]): Longitude in decimal degrees (WGS84).
|
53
|
+
temporalDescription (Optional[Date]): Temporal coverage/validity window
|
54
|
+
for this description (e.g., when a jurisdictional boundary applied).
|
55
|
+
spatialDescription (Optional[Resource]): A resource describing spatial
|
56
|
+
geometry or a link to an external gazetteer/shape definition.
|
57
|
+
"""
|
17
58
|
identifier = "http://gedcomx.org/v1/PlaceDescription"
|
18
59
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
19
60
|
|
20
61
|
def __init__(self, id: Optional[str] =None,
|
21
|
-
lang: str =
|
62
|
+
lang: Optional[str] = None,
|
22
63
|
sources: Optional[List[SourceReference]] = None,
|
23
64
|
analysis: Optional[Resource] = None,
|
24
65
|
notes: Optional[List[Note]] =None,
|
@@ -29,9 +70,9 @@ class PlaceDescription(Subject):
|
|
29
70
|
media: Optional[List[SourceReference]] = None,
|
30
71
|
identifiers: Optional[IdentifierList] = None,
|
31
72
|
names: Optional[List[TextValue]] = None,
|
32
|
-
type: Optional[str] = None,
|
73
|
+
type: Optional[str] = None, #TODO This needs to be an enumerated value, work out details
|
33
74
|
place: Optional[URI] = None,
|
34
|
-
jurisdiction: Optional["Resource | PlaceDescription"] = None,
|
75
|
+
jurisdiction: Optional["Resource | PlaceDescription"] = None,
|
35
76
|
latitude: Optional[float] = None,
|
36
77
|
longitude: Optional[float] = None,
|
37
78
|
temporalDescription: Optional[Date] = None,
|
@@ -42,24 +83,33 @@ class PlaceDescription(Subject):
|
|
42
83
|
self.type = type
|
43
84
|
self.place = place
|
44
85
|
self.jurisdiction = jurisdiction
|
45
|
-
self.
|
46
|
-
self.
|
86
|
+
self.latitude = latitude
|
87
|
+
self.longitude = longitude
|
47
88
|
self.temporalDescription = temporalDescription
|
48
|
-
self.
|
89
|
+
self.spatialDescription = spatialDescription
|
49
90
|
|
50
91
|
@property
|
51
92
|
def _as_dict_(self):
|
93
|
+
from .Serialization import Serialization
|
52
94
|
type_as_dict = super()._as_dict_
|
53
|
-
|
54
|
-
|
55
|
-
"
|
56
|
-
|
57
|
-
"
|
58
|
-
|
59
|
-
"
|
60
|
-
|
61
|
-
"
|
62
|
-
|
95
|
+
|
96
|
+
if self.names:
|
97
|
+
type_as_dict["names"] = [n._as_dict_ for n in self.names if n]
|
98
|
+
if self.type:
|
99
|
+
type_as_dict["type"] = self.type #TODO
|
100
|
+
if self.place:
|
101
|
+
type_as_dict["place"] = self.place._as_dict_
|
102
|
+
if self.jurisdiction:
|
103
|
+
type_as_dict["jurisdiction"] = self.jurisdiction._as_dict_
|
104
|
+
if self.latitude is not None: # include 0.0; exclude only None
|
105
|
+
type_as_dict["latitude"] = float(self.latitude)
|
106
|
+
if self.longitude is not None: # include 0.0; exclude only None
|
107
|
+
type_as_dict["longitude"] = float(self.longitude)
|
108
|
+
if self.temporalDescription:
|
109
|
+
type_as_dict["temporalDescription"] = self.temporalDescription._as_dict_
|
110
|
+
if self.spatialDescription:
|
111
|
+
type_as_dict["spatialDescription"] = self.spatialDescription._as_dict_
|
112
|
+
|
63
113
|
return Serialization.serialize_dict(type_as_dict)
|
64
114
|
|
65
115
|
@classmethod
|
@@ -67,4 +117,5 @@ class PlaceDescription(Subject):
|
|
67
117
|
"""
|
68
118
|
Create a PlaceDescription instance from a JSON-dict (already parsed).
|
69
119
|
"""
|
120
|
+
from .Serialization import Serialization
|
70
121
|
return Serialization.deserialize(data, PlaceDescription)
|