gedcom-x 0.5.7__py3-none-any.whl → 0.5.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/METADATA +1 -1
- gedcom_x-0.5.9.dist-info/RECORD +56 -0
- gedcomx/Extensions/rs10/rsLink.py +110 -60
- gedcomx/TopLevelTypeCollection.py +1 -1
- gedcomx/__init__.py +43 -42
- gedcomx/address.py +217 -0
- gedcomx/{Agent.py → agent.py} +107 -34
- gedcomx/attribution.py +115 -0
- gedcomx/{Conclusion.py → conclusion.py} +120 -51
- gedcomx/{Converter.py → converter.py} +261 -116
- gedcomx/coverage.py +64 -0
- gedcomx/{Date.py → date.py} +43 -9
- gedcomx/{Document.py → document.py} +60 -12
- gedcomx/{Event.py → event.py} +88 -31
- gedcomx/evidence_reference.py +20 -0
- gedcomx/{Fact.py → fact.py} +81 -74
- gedcomx/{Gedcom.py → gedcom.py} +10 -0
- gedcomx/{Gedcom5x.py → gedcom5x.py} +31 -21
- gedcomx/gedcom7/Exceptions.py +9 -0
- gedcomx/gedcom7/GedcomStructure.py +94 -0
- gedcomx/gedcom7/Specification.py +347 -0
- gedcomx/gedcom7/__init__.py +26 -0
- gedcomx/gedcom7/g7interop.py +205 -0
- gedcomx/gedcom7/gedcom7.py +160 -0
- gedcomx/gedcom7/logger.py +19 -0
- gedcomx/{GedcomX.py → gedcomx.py} +109 -106
- gedcomx/gender.py +91 -0
- gedcomx/group.py +72 -0
- gedcomx/{Identifier.py → identifier.py} +48 -21
- gedcomx/{LoggingHub.py → logging_hub.py} +19 -0
- gedcomx/{Mutations.py → mutations.py} +59 -30
- gedcomx/{Name.py → name.py} +88 -47
- gedcomx/note.py +105 -0
- gedcomx/online_account.py +19 -0
- gedcomx/{Person.py → person.py} +61 -41
- gedcomx/{PlaceDescription.py → place_description.py} +71 -23
- gedcomx/{PlaceReference.py → place_reference.py} +32 -10
- gedcomx/{Qualifier.py → qualifier.py} +20 -4
- gedcomx/relationship.py +156 -0
- gedcomx/resource.py +112 -0
- gedcomx/serialization.py +794 -0
- gedcomx/source_citation.py +37 -0
- gedcomx/source_description.py +401 -0
- gedcomx/{SourceReference.py → source_reference.py} +56 -21
- gedcomx/subject.py +122 -0
- gedcomx/textvalue.py +89 -0
- gedcomx/{Translation.py → translation.py} +4 -4
- gedcomx/uri.py +273 -0
- gedcom_x-0.5.7.dist-info/RECORD +0 -49
- gedcomx/Address.py +0 -131
- gedcomx/Attribution.py +0 -91
- gedcomx/Coverage.py +0 -37
- gedcomx/EvidenceReference.py +0 -11
- gedcomx/Gender.py +0 -65
- gedcomx/Group.py +0 -37
- gedcomx/Note.py +0 -73
- gedcomx/OnlineAccount.py +0 -10
- gedcomx/Relationship.py +0 -97
- gedcomx/Resource.py +0 -85
- gedcomx/Serialization.py +0 -816
- gedcomx/SourceCitation.py +0 -25
- gedcomx/SourceDescription.py +0 -314
- gedcomx/Subject.py +0 -59
- gedcomx/TextValue.py +0 -35
- gedcomx/URI.py +0 -105
- {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/top_level.txt +0 -0
- /gedcomx/{Exceptions.py → exceptions.py} +0 -0
- /gedcomx/{ExtensibleEnum.py → extensible_enum.py} +0 -0
@@ -1,6 +1,37 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
|
2
|
+
"""
|
3
|
+
======================================================================
|
4
|
+
Project: Gedcom-X
|
5
|
+
File: mutations.py
|
6
|
+
Author: David J. Cartwright
|
7
|
+
Purpose: Objects used to convert TAGs/Structues/Types from GEDCOM Versions
|
8
|
+
when simple parsing will not work. (complex or ambiguous structures)
|
9
|
+
|
10
|
+
Created: 2025-08-25
|
11
|
+
Updated:
|
12
|
+
- 2025-08-31: cleaned up imports and documentation
|
13
|
+
- 2025-09-01: filename PEP8 standard, imports changed accordingly
|
14
|
+
|
15
|
+
======================================================================
|
16
|
+
"""
|
17
|
+
|
18
|
+
"""
|
19
|
+
======================================================================
|
20
|
+
GEDCOM Module Types
|
21
|
+
======================================================================
|
22
|
+
"""
|
23
|
+
from .gedcom5x import Gedcom5xRecord
|
24
|
+
from .fact import Fact, FactType
|
25
|
+
from .event import Event, EventType
|
26
|
+
from .logging_hub import hub, logging
|
27
|
+
"""
|
28
|
+
======================================================================
|
29
|
+
Logging
|
30
|
+
======================================================================
|
31
|
+
"""
|
32
|
+
log = logging.getLogger("gedcomx")
|
33
|
+
serial_log = "gedcomx.serialization"
|
34
|
+
#=====================================================================
|
4
35
|
|
5
36
|
fact_event_table = {
|
6
37
|
# Person Fact / Event Types
|
@@ -178,33 +209,34 @@ fact_event_table = {
|
|
178
209
|
}
|
179
210
|
|
180
211
|
class GedcomXObject:
|
181
|
-
def __init__(self,record: Gedcom5xRecord
|
182
|
-
self.
|
183
|
-
self.
|
184
|
-
self.
|
212
|
+
def __init__(self,record: Gedcom5xRecord) -> None:
|
213
|
+
self.record = record
|
214
|
+
self.created_with_tag: str | None = record.tag if record and isinstance(record, Gedcom5xRecord) else None
|
215
|
+
self.created_at_level: int | None = record.level if record and isinstance(record, Gedcom5xRecord) else None
|
216
|
+
self.created_at_line_number: int | None = record.line if record and isinstance(record, Gedcom5xRecord) else None
|
185
217
|
|
186
218
|
class GedcomXSourceOrDocument(GedcomXObject):
|
187
|
-
def __init__(self,record: Gedcom5xRecord
|
219
|
+
def __init__(self,record: Gedcom5xRecord) -> None:
|
188
220
|
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
|
221
|
+
self.title: str | None = None
|
222
|
+
self.citation: str | None = None
|
223
|
+
self.page: str | None = None
|
224
|
+
self.contributor: str | None = None
|
225
|
+
self.publisher: str | None = None
|
226
|
+
self.rights: str | None = None
|
227
|
+
self.url: str | None = None
|
228
|
+
self.medium: str | None = None
|
229
|
+
self.type: str | None = None
|
230
|
+
self.format: str | None = None
|
231
|
+
self.created: str | None = None
|
232
|
+
self.modified: str | None = None
|
233
|
+
self.language: str | None = None
|
234
|
+
self.relation: str | None = None
|
235
|
+
self.identifier: str | None = None
|
236
|
+
self.description: str | None = None
|
205
237
|
|
206
238
|
class GedcomXEventOrFact(GedcomXObject):
|
207
|
-
def __new__(cls,record: Gedcom5xRecord
|
239
|
+
def __new__(cls,record: Gedcom5xRecord, object_stack: dict | None = None) -> object:
|
208
240
|
super().__init__(record)
|
209
241
|
if record.tag in fact_event_table.keys():
|
210
242
|
|
@@ -219,10 +251,7 @@ class GedcomXEventOrFact(GedcomXObject):
|
|
219
251
|
raise ValueError(f"{record.tag} not found in map")
|
220
252
|
|
221
253
|
class GedcomXRelationshipBuilder(GedcomXObject):
|
222
|
-
def
|
223
|
-
|
224
|
-
last_relationship_data = object_stack.get('lastrelationshipdata',None)
|
225
|
-
if not isinstance(last_relationship_data,dict):
|
226
|
-
last_relationship_data = None
|
254
|
+
def __init__(self, record: Gedcom5xRecord) -> None:
|
255
|
+
super().__init__(record)
|
227
256
|
|
228
257
|
|
gedcomx/{Name.py → name.py}
RENAMED
@@ -1,6 +1,5 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import List,Optional
|
3
|
-
from typing_extensions import Self
|
4
3
|
|
5
4
|
"""
|
6
5
|
======================================================================
|
@@ -12,6 +11,7 @@ from typing_extensions import Self
|
|
12
11
|
Created: 2025-08-25
|
13
12
|
Updated:
|
14
13
|
- 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
|
14
|
+
- 2025-09-03: _from_json_ refactor
|
15
15
|
|
16
16
|
======================================================================
|
17
17
|
"""
|
@@ -22,13 +22,23 @@ GEDCOM Module Types
|
|
22
22
|
======================================================================
|
23
23
|
"""
|
24
24
|
#======================================================================
|
25
|
-
from .
|
26
|
-
from .
|
27
|
-
from .
|
28
|
-
from .
|
29
|
-
from .
|
30
|
-
from .
|
31
|
-
|
25
|
+
from .attribution import Attribution
|
26
|
+
from .conclusion import Conclusion, ConfidenceLevel
|
27
|
+
from .date import Date
|
28
|
+
from .document import Document
|
29
|
+
from .Extensions.rs10.rsLink import _rsLinks
|
30
|
+
from .note import Note
|
31
|
+
from .resource import Resource
|
32
|
+
from .source_reference import SourceReference
|
33
|
+
from .logging_hub import hub, logging
|
34
|
+
"""
|
35
|
+
======================================================================
|
36
|
+
Logging
|
37
|
+
======================================================================
|
38
|
+
"""
|
39
|
+
log = logging.getLogger("gedcomx")
|
40
|
+
serial_log = "gedcomx.serialization"
|
41
|
+
#=====================================================================
|
32
42
|
|
33
43
|
|
34
44
|
class NameType(Enum):
|
@@ -39,6 +49,7 @@ class NameType(Enum):
|
|
39
49
|
AdoptiveName = "http://gedcomx.org/AdoptiveName"
|
40
50
|
FormalName = "http://gedcomx.org/FormalName"
|
41
51
|
ReligiousName = "http://gedcomx.org/ReligiousName"
|
52
|
+
Other = "other"
|
42
53
|
|
43
54
|
@property
|
44
55
|
def description(self):
|
@@ -145,7 +156,7 @@ class NamePart:
|
|
145
156
|
|
146
157
|
@property
|
147
158
|
def _as_dict_(self):
|
148
|
-
from .
|
159
|
+
from .serialization import Serialization
|
149
160
|
type_as_dict = {}
|
150
161
|
if self.type:
|
151
162
|
type_as_dict['type'] = self.type.value
|
@@ -153,14 +164,28 @@ class NamePart:
|
|
153
164
|
type_as_dict['value'] = self.value
|
154
165
|
if self.qualifiers:
|
155
166
|
type_as_dict['qualifiers'] = [q.value for q in self.qualifiers]
|
156
|
-
|
167
|
+
return type_as_dict if type_as_dict != {} else None
|
157
168
|
|
158
169
|
@classmethod
|
159
|
-
def _from_json_(cls,data):
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
170
|
+
def _from_json_(cls, data: dict, context=None) -> "NamePart":
|
171
|
+
if not isinstance(data, dict):
|
172
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
|
173
|
+
|
174
|
+
name_part = {}
|
175
|
+
|
176
|
+
# Enum / type
|
177
|
+
if (typ := data.get("type")) is not None:
|
178
|
+
name_part["type"] = NamePartType(typ)
|
179
|
+
|
180
|
+
# String value
|
181
|
+
if (val := data.get("value")) is not None:
|
182
|
+
name_part["value"] = val
|
183
|
+
|
184
|
+
# List of qualifiers
|
185
|
+
if (quals := data.get("qualifiers")) is not None:
|
186
|
+
name_part["qualifiers"] = [NamePartQualifier(q) for q in quals]
|
187
|
+
|
188
|
+
return cls(**name_part)
|
164
189
|
|
165
190
|
def __eq__(self, other):
|
166
191
|
if not isinstance(other, NamePart):
|
@@ -226,24 +251,36 @@ class NameForm:
|
|
226
251
|
|
227
252
|
@property
|
228
253
|
def _as_dict_(self):
|
229
|
-
from .
|
254
|
+
from .serialization import Serialization
|
230
255
|
type_as_dict = {}
|
231
256
|
if self.lang:
|
232
257
|
type_as_dict['lang'] = self.lang
|
233
258
|
if self.fullText:
|
234
259
|
type_as_dict['fullText'] = self.fullText
|
235
260
|
if self.parts:
|
236
|
-
type_as_dict['parts'] = [part._as_dict_ for part in self.parts if part]
|
261
|
+
type_as_dict['parts'] = [part._as_dict_ for part in self.parts if part is not None]
|
262
|
+
return type_as_dict if type_as_dict != {} else None
|
237
263
|
return Serialization.serialize_dict(type_as_dict)
|
238
264
|
|
239
265
|
@classmethod
|
240
|
-
def _from_json_(cls, data: dict) -> "NameForm":
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
266
|
+
def _from_json_(cls, data: dict, context=None) -> "NameForm":
|
267
|
+
if not isinstance(data, dict):
|
268
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
|
269
|
+
|
270
|
+
name_form = {}
|
271
|
+
|
272
|
+
# Scalars
|
273
|
+
if (lang := data.get("lang")) is not None:
|
274
|
+
name_form["lang"] = lang
|
275
|
+
|
276
|
+
if (full := data.get("fullText")) is not None:
|
277
|
+
name_form["fullText"] = full
|
278
|
+
|
279
|
+
# List of parts
|
280
|
+
if (parts := data.get("parts")) is not None:
|
281
|
+
name_form["parts"] = [NamePart._from_json_(p, context) for p in parts if p]
|
282
|
+
|
283
|
+
return cls(**name_form)
|
247
284
|
|
248
285
|
def _fulltext_parts(self):
|
249
286
|
pass
|
@@ -309,29 +346,29 @@ class Name(Conclusion):
|
|
309
346
|
def __init__(self, id: Optional[str] = None,
|
310
347
|
lang: Optional[str] = None,
|
311
348
|
sources: Optional[List[SourceReference]] = None,
|
312
|
-
analysis: Resource = None,
|
349
|
+
analysis: Optional[Document |Resource] = None,
|
313
350
|
notes: Optional[List[Note]] = None,
|
314
351
|
confidence: Optional[ConfidenceLevel] = None,
|
315
352
|
attribution: Optional[Attribution] = None,
|
316
353
|
type: Optional[NameType] = None,
|
317
354
|
nameForms: Optional[List[NameForm]]= None,
|
318
|
-
date: Optional[Date] = None
|
319
|
-
|
355
|
+
date: Optional[Date] = None,
|
356
|
+
links: Optional[_rsLinks] = None) -> None:
|
357
|
+
super().__init__(id, lang, sources, analysis, notes, confidence, attribution,links=links)
|
320
358
|
self.type = type
|
321
359
|
self.nameForms = nameForms if nameForms else []
|
322
360
|
self.date = date
|
323
361
|
|
324
|
-
def _add_name_part(self,
|
325
|
-
if
|
362
|
+
def _add_name_part(self, namepart: NamePart):
|
363
|
+
if namepart and isinstance(namepart, NamePart):
|
326
364
|
for current_namepart in self.nameForms[0].parts:
|
327
|
-
if
|
365
|
+
if namepart == current_namepart:
|
328
366
|
return False
|
329
|
-
self.nameForms[0].parts.append(
|
367
|
+
self.nameForms[0].parts.append(namepart)
|
330
368
|
|
331
369
|
@property
|
332
370
|
def _as_dict_(self):
|
333
|
-
|
334
|
-
type_as_dict = super()._as_dict_
|
371
|
+
type_as_dict = super()._as_dict_ or {}
|
335
372
|
if self.type:
|
336
373
|
type_as_dict['type'] = getattr(self.type, 'value', self.type)
|
337
374
|
if self.nameForms:
|
@@ -339,23 +376,27 @@ class Name(Conclusion):
|
|
339
376
|
if self.date:
|
340
377
|
type_as_dict['date'] = self.date._as_dict_
|
341
378
|
|
342
|
-
return
|
379
|
+
return type_as_dict if type_as_dict != {} else None
|
380
|
+
|
343
381
|
|
344
382
|
@classmethod
|
345
|
-
def _from_json_(cls, data: dict) -> "Name":
|
383
|
+
def _from_json_(cls, data: dict,context = None) -> "Name":
|
346
384
|
"""Build a Name from JSON-like dict."""
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
)
|
385
|
+
name = Conclusion._dict_from_json_(data)
|
386
|
+
|
387
|
+
# Enum
|
388
|
+
if (typ := data.get("type")) is not None:
|
389
|
+
name["type"] = NameType(typ)
|
390
|
+
|
391
|
+
# List
|
392
|
+
if (forms := data.get("nameForms")) is not None:
|
393
|
+
name["nameForms"] = [NameForm._from_json_(f, context) for f in forms]
|
394
|
+
|
395
|
+
# Object
|
396
|
+
if (date := data.get("date")) is not None:
|
397
|
+
name["date"] = Date._from_json_(date, context)
|
398
|
+
|
399
|
+
return cls(**name)
|
359
400
|
|
360
401
|
def __str__(self) -> str:
|
361
402
|
"""Return a human-readable string for the Name-like object."""
|
gedcomx/note.py
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
from typing import Any, Optional
|
2
|
+
"""
|
3
|
+
======================================================================
|
4
|
+
Project: Gedcom-X
|
5
|
+
File: note.py
|
6
|
+
Author: David J. Cartwright
|
7
|
+
Purpose: Python Object representation of GedcomX Name, NameType, NameForm, NamePart Types
|
8
|
+
|
9
|
+
Created: 2025-08-25
|
10
|
+
Updated:
|
11
|
+
- 2025-09-03: _from_json_ refactor
|
12
|
+
|
13
|
+
======================================================================
|
14
|
+
"""
|
15
|
+
|
16
|
+
"""
|
17
|
+
======================================================================
|
18
|
+
GEDCOM Module Types
|
19
|
+
======================================================================
|
20
|
+
"""
|
21
|
+
from .attribution import Attribution
|
22
|
+
from .logging_hub import hub, logging
|
23
|
+
"""
|
24
|
+
======================================================================
|
25
|
+
Logging
|
26
|
+
======================================================================
|
27
|
+
"""
|
28
|
+
log = logging.getLogger("gedcomx")
|
29
|
+
serial_log = "gedcomx.serialization"
|
30
|
+
#=====================================================================
|
31
|
+
|
32
|
+
class Note:
|
33
|
+
identifier = 'http://gedcomx.org/v1/Note'
|
34
|
+
version = 'http://gedcomx.org/conceptual-model/v1'
|
35
|
+
|
36
|
+
def __init__(self,lang: Optional[str] = 'en', subject: Optional[str] = None, text: Optional[str] = None, attribution: Optional[Attribution] = None) -> None:
|
37
|
+
self.lang = lang
|
38
|
+
self.subject = subject
|
39
|
+
self.text = text
|
40
|
+
self.attribution = attribution
|
41
|
+
|
42
|
+
def append(self, text_to_add: str):
|
43
|
+
if text_to_add and isinstance(text_to_add, str):
|
44
|
+
if self.text:
|
45
|
+
self.text = self.text + text_to_add
|
46
|
+
else:
|
47
|
+
self.text = text_to_add
|
48
|
+
else:
|
49
|
+
return #TODO
|
50
|
+
raise ValueError("The text to add must be a non-empty string.")
|
51
|
+
|
52
|
+
@property
|
53
|
+
def _as_dict_(self):
|
54
|
+
from .serialization import Serialization
|
55
|
+
type_as_dict = {}
|
56
|
+
if self.lang:
|
57
|
+
type_as_dict["lang"] = self.lang
|
58
|
+
if self.subject:
|
59
|
+
type_as_dict["subject"] = self.subject
|
60
|
+
if self.text:
|
61
|
+
type_as_dict["text"] = self.text
|
62
|
+
if self.attribution:
|
63
|
+
# If attribution exposes `_as_dict_` as a property, use it; otherwise include as-is
|
64
|
+
type_as_dict["attribution"] = getattr(self.attribution, "_as_dict_", self.attribution)
|
65
|
+
return type_as_dict if type_as_dict != {} else None
|
66
|
+
return Serialization.serialize_dict(type_as_dict)
|
67
|
+
|
68
|
+
def __eq__(self, other):
|
69
|
+
if not isinstance(other, Note):
|
70
|
+
return NotImplemented
|
71
|
+
|
72
|
+
def safe_str(val):
|
73
|
+
return val.strip() if isinstance(val, str) else ''
|
74
|
+
|
75
|
+
return (
|
76
|
+
#safe_str(self.lang) == safe_str(other.lang) and
|
77
|
+
#safe_str(self.subject) == safe_str(other.subject) and
|
78
|
+
safe_str(self.text) == safe_str(other.text) #and
|
79
|
+
# self.attribution == other.attribution # Assumes Attribution defines __eq__
|
80
|
+
)
|
81
|
+
|
82
|
+
@classmethod
|
83
|
+
def _from_json_(cls, data: Any, context=None) -> "Note":
|
84
|
+
# Allow shorthand: "some note text"
|
85
|
+
#if isinstance(data, str):
|
86
|
+
# return cls(text=data)
|
87
|
+
|
88
|
+
if not isinstance(data, dict):
|
89
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
|
90
|
+
|
91
|
+
obj: dict[str, Any] = {}
|
92
|
+
|
93
|
+
# Scalars
|
94
|
+
if (lang := data.get("lang")) is not None:
|
95
|
+
obj["lang"] = lang
|
96
|
+
if (subject := data.get("subject")) is not None:
|
97
|
+
obj["subject"] = subject
|
98
|
+
if (text := data.get("text")) is not None:
|
99
|
+
obj["text"] = text
|
100
|
+
|
101
|
+
# Object
|
102
|
+
if (attr := data.get("attribution")) is not None:
|
103
|
+
obj["attribution"] = Attribution._from_json_(attr, context)
|
104
|
+
|
105
|
+
return cls(**obj)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from .resource import Resource
|
4
|
+
from .logging_hub import hub, logging
|
5
|
+
"""
|
6
|
+
======================================================================
|
7
|
+
Logging
|
8
|
+
======================================================================
|
9
|
+
"""
|
10
|
+
log = logging.getLogger("gedcomx")
|
11
|
+
serial_log = "gedcomx.serialization"
|
12
|
+
#=====================================================================
|
13
|
+
|
14
|
+
class OnlineAccount:
|
15
|
+
identifier = 'http://gedcomx.org/v1/OnlineAccount'
|
16
|
+
version = 'http://gedcomx.org/conceptual-model/v1'
|
17
|
+
|
18
|
+
def __init__(self, serviceHomepage: Resource, accountName: str) -> None:
|
19
|
+
pass
|
gedcomx/{Person.py → person.py}
RENAMED
@@ -11,6 +11,7 @@ from urllib.parse import urljoin
|
|
11
11
|
Created: 2025-08-25
|
12
12
|
Updated:
|
13
13
|
- 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
|
14
|
+
- 2025-09-03: _from_json_ refactor
|
14
15
|
|
15
16
|
======================================================================
|
16
17
|
"""
|
@@ -20,19 +21,32 @@ from urllib.parse import urljoin
|
|
20
21
|
GEDCOM Module Types
|
21
22
|
======================================================================
|
22
23
|
"""
|
23
|
-
from .
|
24
|
-
from .
|
25
|
-
from .
|
26
|
-
from .
|
27
|
-
from .Extensions.rs10.rsLink import
|
28
|
-
from .
|
29
|
-
from .
|
30
|
-
from .
|
31
|
-
from .
|
32
|
-
from .
|
33
|
-
from .
|
34
|
-
from .
|
35
|
-
from .
|
24
|
+
from .attribution import Attribution
|
25
|
+
from .conclusion import ConfidenceLevel
|
26
|
+
from .date import Date
|
27
|
+
from .evidence_reference import EvidenceReference
|
28
|
+
from .Extensions.rs10.rsLink import _rsLinks
|
29
|
+
from .fact import Fact, FactType
|
30
|
+
from .gender import Gender, GenderType
|
31
|
+
from .identifier import IdentifierList
|
32
|
+
from .logging_hub import hub, logging
|
33
|
+
from .name import Name, QuickName
|
34
|
+
from .note import Note
|
35
|
+
from .resource import Resource
|
36
|
+
from .source_reference import SourceReference
|
37
|
+
from .subject import Subject
|
38
|
+
from .logging_hub import hub, logging
|
39
|
+
"""
|
40
|
+
======================================================================
|
41
|
+
Logging
|
42
|
+
======================================================================
|
43
|
+
"""
|
44
|
+
log = logging.getLogger("gedcomx")
|
45
|
+
serial_log = "gedcomx.serialization"
|
46
|
+
deserial_log = "degedcomx.serialization"
|
47
|
+
#=====================================================================
|
48
|
+
|
49
|
+
|
36
50
|
|
37
51
|
class Person(Subject):
|
38
52
|
"""A person in the system.
|
@@ -60,12 +74,12 @@ class Person(Subject):
|
|
60
74
|
evidence: Optional[List[EvidenceReference]] = None,
|
61
75
|
media: Optional[List[SourceReference]] = None,
|
62
76
|
identifiers: Optional[IdentifierList] = None,
|
63
|
-
private: Optional[bool] =
|
77
|
+
private: Optional[bool] = None,
|
64
78
|
gender: Optional[Gender] = Gender(type=GenderType.Unknown),
|
65
79
|
names: Optional[List[Name]] = None,
|
66
80
|
facts: Optional[List[Fact]] = None,
|
67
|
-
living: Optional[bool] =
|
68
|
-
links: Optional[
|
81
|
+
living: Optional[bool] = None,
|
82
|
+
links: Optional[_rsLinks] = None,
|
69
83
|
uri: Optional[Resource] = None) -> None:
|
70
84
|
# Call superclass initializer if needed
|
71
85
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links,uri=uri)
|
@@ -93,6 +107,7 @@ class Person(Subject):
|
|
93
107
|
return False
|
94
108
|
self.facts.append(fact_to_add)
|
95
109
|
return True
|
110
|
+
return False
|
96
111
|
|
97
112
|
def add_name(self, name_to_add: Name) -> bool:
|
98
113
|
if len(self.names) > 5:
|
@@ -105,9 +120,10 @@ class Person(Subject):
|
|
105
120
|
return False
|
106
121
|
self.names.append(name_to_add)
|
107
122
|
return True
|
123
|
+
return False
|
108
124
|
|
109
125
|
def _add_relationship(self, relationship_to_add: object):
|
110
|
-
from .
|
126
|
+
from .relationship import Relationship
|
111
127
|
if isinstance(relationship_to_add,Relationship):
|
112
128
|
self._relationships.append(relationship_to_add)
|
113
129
|
else:
|
@@ -127,34 +143,38 @@ class Person(Subject):
|
|
127
143
|
|
128
144
|
@property
|
129
145
|
def _as_dict_(self):
|
130
|
-
from .
|
131
|
-
|
132
|
-
|
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_
|
144
|
-
|
145
|
-
return Serialization.serialize_dict(type_as_dict)
|
146
|
+
from .serialization import Serialization
|
147
|
+
return Serialization.serialize(self)
|
148
|
+
|
146
149
|
|
147
150
|
@classmethod
|
148
|
-
def _from_json_(cls, data: dict):
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
151
|
+
def _from_json_(cls, data: dict, context = None) -> "Person":
|
152
|
+
def _to_bool(v):
|
153
|
+
if isinstance(v, str):
|
154
|
+
return v.strip().lower() in ("1", "true", "yes", "y", "t")
|
155
|
+
return bool(v)
|
156
|
+
with hub.use(deserial_log):
|
157
|
+
log.debug(f"Deserializing a Person")
|
158
|
+
person_data: dict = Subject._dict_from_json_(data,context)
|
159
|
+
if (private := data.get("private")) is not None:
|
160
|
+
person_data["private"] = _to_bool(private)
|
161
|
+
if (living := data.get("living")) is not None:
|
162
|
+
person_data["living"] = _to_bool(living)
|
163
|
+
if (gender := data.get("gender")) is not None:
|
164
|
+
person_data["gender"] = Gender._from_json_(gender, context)
|
165
|
+
if (names := data.get("names")) is not None:
|
166
|
+
person_data["names"] = [Name._from_json_(n, context) for n in names]
|
167
|
+
if (facts := data.get("facts")) is not None:
|
168
|
+
person_data["facts"] = [Fact._from_json_(f, context) for f in facts]
|
169
|
+
|
170
|
+
diff = data.keys() - person_data.keys()
|
171
|
+
log.debug(f"Desserialization found keys {diff} that are not in deserialization list")
|
172
|
+
|
173
|
+
return cls(**person_data)
|
154
174
|
|
155
175
|
@classmethod
|
156
176
|
def from_familysearch(cls, pid: str, token: str, *, base_url: Optional[str] = None):
|
157
|
-
from .
|
177
|
+
from .serialization import Serialization
|
158
178
|
"""
|
159
179
|
Fetch a single person by PID from FamilySearch and return a Person.
|
160
180
|
- pid: e.g. "KPHP-4B4"
|
@@ -184,7 +204,7 @@ class Person(Subject):
|
|
184
204
|
raise ValueError(f"FamilySearch returned no person for PID {pid}")
|
185
205
|
|
186
206
|
# Keep your existing deserialization helper
|
187
|
-
return
|
207
|
+
return Person._from_json_(person_json)
|
188
208
|
|
189
209
|
class QuickPerson:
|
190
210
|
"""A GedcomX Person Data Type created with basic information.
|