gedcom-x 0.5__py3-none-any.whl → 0.5.2__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.dist-info → gedcom_x-0.5.2.dist-info}/METADATA +1 -1
- gedcom_x-0.5.2.dist-info/RECORD +42 -0
- gedcomx/Address.py +40 -11
- gedcomx/Agent.py +129 -23
- gedcomx/Attribution.py +38 -54
- gedcomx/Conclusion.py +60 -45
- gedcomx/Date.py +49 -8
- gedcomx/Document.py +19 -9
- gedcomx/Event.py +4 -4
- gedcomx/EvidenceReference.py +2 -2
- gedcomx/Exceptions.py +10 -0
- gedcomx/Fact.py +70 -46
- gedcomx/Gedcom.py +111 -37
- gedcomx/GedcomX.py +405 -175
- gedcomx/Gender.py +61 -8
- gedcomx/Group.py +3 -3
- gedcomx/Identifier.py +93 -10
- gedcomx/Logging.py +19 -0
- gedcomx/Name.py +67 -38
- gedcomx/Note.py +5 -4
- gedcomx/OnlineAccount.py +2 -2
- gedcomx/Person.py +88 -33
- gedcomx/PlaceDescription.py +22 -8
- gedcomx/PlaceReference.py +7 -5
- gedcomx/Relationship.py +19 -9
- gedcomx/Resource.py +61 -0
- gedcomx/Serialization.py +44 -1
- gedcomx/SourceCitation.py +6 -1
- gedcomx/SourceDescription.py +89 -72
- gedcomx/SourceReference.py +25 -14
- gedcomx/Subject.py +10 -8
- gedcomx/TextValue.py +2 -1
- gedcomx/URI.py +95 -61
- gedcomx/Zip.py +1 -0
- gedcomx/_Links.py +37 -0
- gedcomx/__init__.py +4 -2
- gedcomx/g7interop.py +205 -0
- gedcom_x-0.5.dist-info/RECORD +0 -37
- gedcomx/_Resource.py +0 -11
- {gedcom_x-0.5.dist-info → gedcom_x-0.5.2.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.dist-info → gedcom_x-0.5.2.dist-info}/top_level.txt +0 -0
gedcomx/Person.py
CHANGED
@@ -3,38 +3,53 @@ from typing import List, Optional
|
|
3
3
|
|
4
4
|
from .Attribution import Attribution
|
5
5
|
from .Conclusion import ConfidenceLevel
|
6
|
+
from .Date import Date
|
6
7
|
from .EvidenceReference import EvidenceReference
|
7
|
-
from .Fact import Fact
|
8
|
+
from .Fact import Fact, FactType
|
8
9
|
from .Gender import Gender, GenderType
|
9
|
-
from .Identifier import
|
10
|
-
from .Name import Name
|
10
|
+
from .Identifier import IdentifierList
|
11
|
+
from .Name import Name
|
11
12
|
from .Note import Note
|
12
13
|
from .SourceReference import SourceReference
|
13
14
|
from .Subject import Subject
|
14
|
-
from .
|
15
|
+
from .Resource import Resource
|
16
|
+
from collections.abc import Sized
|
17
|
+
from ._Links import _LinkList
|
15
18
|
|
16
19
|
class Person(Subject):
|
20
|
+
"""A person in the system.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
id (str): Unique identifier for this person.
|
24
|
+
name (str): Full name.
|
25
|
+
birth (date): Birth date (YYYY-MM-DD).
|
26
|
+
friends (List[Person], optional): List of friends. Defaults to None.
|
27
|
+
|
28
|
+
Raises:
|
29
|
+
ValueError: If `id` is not a valid UUID.
|
30
|
+
"""
|
17
31
|
identifier = 'http://gedcomx.org/v1/Person'
|
18
32
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
19
33
|
|
20
|
-
def __init__(self, id: str = None,
|
34
|
+
def __init__(self, id: str | None = None,
|
21
35
|
lang: str = 'en',
|
22
36
|
sources: Optional[List[SourceReference]] = None,
|
23
|
-
analysis: Optional[
|
37
|
+
analysis: Optional[Resource] = None,
|
24
38
|
notes: Optional[List[Note]] = None,
|
25
39
|
confidence: Optional[ConfidenceLevel] = None,
|
26
40
|
attribution: Optional[Attribution] = None,
|
27
41
|
extracted: bool = None,
|
28
42
|
evidence: Optional[List[EvidenceReference]] = None,
|
29
43
|
media: Optional[List[SourceReference]] = None,
|
30
|
-
identifiers: Optional[
|
44
|
+
identifiers: Optional[IdentifierList] = None,
|
31
45
|
private: Optional[bool] = False,
|
32
46
|
gender: Optional[Gender] = Gender(type=GenderType.Unknown),
|
33
47
|
names: Optional[List[Name]] = None,
|
34
48
|
facts: Optional[List[Fact]] = None,
|
35
|
-
living: Optional[bool] = False
|
49
|
+
living: Optional[bool] = False,
|
50
|
+
links: Optional[_LinkList] = None) -> None:
|
36
51
|
# Call superclass initializer if needed
|
37
|
-
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
52
|
+
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links)
|
38
53
|
|
39
54
|
# Initialize mutable attributes to empty lists if None
|
40
55
|
self.sources = sources if sources is not None else []
|
@@ -51,8 +66,7 @@ class Person(Subject):
|
|
51
66
|
self.living = living #TODO This is from familysearch API
|
52
67
|
|
53
68
|
self._relationships = []
|
54
|
-
|
55
|
-
|
69
|
+
|
56
70
|
def add_fact(self, fact_to_add: Fact) -> bool:
|
57
71
|
if fact_to_add and isinstance(fact_to_add,Fact):
|
58
72
|
for current_fact in self.facts:
|
@@ -80,6 +94,18 @@ class Person(Subject):
|
|
80
94
|
else:
|
81
95
|
raise ValueError()
|
82
96
|
|
97
|
+
def display(self):
|
98
|
+
display = {
|
99
|
+
"ascendancyNumber": "1",
|
100
|
+
"deathDate": "from 2001 to 2005",
|
101
|
+
"descendancyNumber": "1",
|
102
|
+
"gender": self.gender.type,
|
103
|
+
"lifespan": "-2005",
|
104
|
+
"name": self.names[0].nameForms[0].fullText
|
105
|
+
}
|
106
|
+
|
107
|
+
return display
|
108
|
+
|
83
109
|
@property
|
84
110
|
def _as_dict_(self):
|
85
111
|
def _serialize(value):
|
@@ -99,9 +125,10 @@ class Person(Subject):
|
|
99
125
|
subject_fields.update({
|
100
126
|
'private': self.private,
|
101
127
|
'living': self.living,
|
102
|
-
'gender': self.gender.
|
103
|
-
'names': [name for name in self.names],
|
104
|
-
'facts': [fact for fact in self.facts]
|
128
|
+
'gender': self.gender._as_dict_ if self.gender else None,
|
129
|
+
'names': [name._as_dict_ for name in self.names],
|
130
|
+
'facts': [fact for fact in self.facts],
|
131
|
+
'uri': 'uri: ' + self.uri.value
|
105
132
|
|
106
133
|
})
|
107
134
|
|
@@ -110,20 +137,20 @@ class Person(Subject):
|
|
110
137
|
if value is not None:
|
111
138
|
subject_fields[key] = _serialize(value)
|
112
139
|
|
113
|
-
|
114
|
-
|
115
|
-
|
140
|
+
# 3) merge and filter out None *at the top level*
|
141
|
+
return {
|
142
|
+
k: v
|
143
|
+
for k, v in subject_fields.items()
|
144
|
+
if v is not None and not (isinstance(v, Sized) and len(v) == 0)
|
145
|
+
}
|
146
|
+
|
147
|
+
|
116
148
|
@classmethod
|
117
149
|
def _from_json_(cls, data: dict):
|
118
150
|
"""
|
119
151
|
Create a Person instance from a JSON-dict (already parsed).
|
120
152
|
"""
|
121
|
-
|
122
|
-
if value is None:
|
123
|
-
return []
|
124
|
-
if isinstance(value, list):
|
125
|
-
return value
|
126
|
-
return [value] # wrap single item in list
|
153
|
+
|
127
154
|
|
128
155
|
# Basic scalar fields
|
129
156
|
id_ = data.get('id')
|
@@ -134,9 +161,9 @@ class Person(Subject):
|
|
134
161
|
living = data.get('extracted', False)
|
135
162
|
|
136
163
|
# Complex singletons
|
137
|
-
analysis =
|
164
|
+
analysis = Resource._from_json_(data['analysis']) if data.get('analysis') else None
|
138
165
|
attribution = Attribution._from_json_(data['attribution']) if data.get('attribution') else None
|
139
|
-
confidence =
|
166
|
+
confidence = ConfidenceLevel_from_json_(data['confidence']) if data.get('confidence') else None
|
140
167
|
|
141
168
|
# Gender (string or dict depending on your JSON)
|
142
169
|
gender_json = data.get('gender')
|
@@ -147,13 +174,14 @@ class Person(Subject):
|
|
147
174
|
gender = Gender(type=GenderType(gender_json)) if gender_json else Gender(type=GenderType.Unknown)
|
148
175
|
|
149
176
|
|
150
|
-
sources = [SourceReference._from_json_(o) for o in
|
151
|
-
notes = [Note._from_json_(o) for o in
|
152
|
-
evidence = [EvidenceReference._from_json_(o) for o in
|
153
|
-
media = [SourceReference._from_json_(o) for o in
|
154
|
-
identifiers =
|
155
|
-
names = [Name._from_json_(o) for o in
|
156
|
-
facts = [Fact._from_json_(o) for o in
|
177
|
+
sources = [SourceReference._from_json_(o) for o in data.get('sources')] if data.get('sources') else None
|
178
|
+
notes = [Note._from_json_(o) for o in data.get('notes')] if data.get('notes') else None
|
179
|
+
evidence = [EvidenceReference._from_json_(o) for o in data.get('evidence')] if data.get('evidence') else None
|
180
|
+
media = [SourceReference._from_json_(o) for o in data.get('media')] if data.get('media') else None
|
181
|
+
identifiers = IdentifierList._from_json_(data.get('identifiers')) if data.get('identifiers') else None
|
182
|
+
names = [Name._from_json_(o) for o in data.get('names')] if data.get('names') else None
|
183
|
+
facts = [Fact._from_json_(o) for o in data.get('facts')] if data.get('facts') else None
|
184
|
+
links = _LinkList._from_json_(data.get('links')) if data.get('links') else None
|
157
185
|
|
158
186
|
# Build the instance
|
159
187
|
inst = cls(
|
@@ -172,7 +200,34 @@ class Person(Subject):
|
|
172
200
|
gender = gender,
|
173
201
|
names = names,
|
174
202
|
facts = facts,
|
175
|
-
living = living
|
203
|
+
living = living,
|
204
|
+
links = links
|
176
205
|
)
|
177
206
|
|
178
207
|
return inst
|
208
|
+
|
209
|
+
class QuickPerson:
|
210
|
+
"""A GedcomX Person Data Type created with basic information.
|
211
|
+
|
212
|
+
Underlying GedcomX Types are created for you.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
name (str): Full name.
|
216
|
+
birth (date,Optional): Birth date (YYYY-MM-DD).
|
217
|
+
death (date, Optional)
|
218
|
+
|
219
|
+
|
220
|
+
|
221
|
+
Raises:
|
222
|
+
ValueError: If `id` is not a valid UUID.
|
223
|
+
"""
|
224
|
+
def __new__(cls, name: Optional[str], dob: Optional[str], dod: Optional[str]):
|
225
|
+
# Build facts from args
|
226
|
+
facts = []
|
227
|
+
if dob:
|
228
|
+
facts.append(Fact(type=FactType.Birth, date=Date(original=dob)))
|
229
|
+
if dod:
|
230
|
+
facts.append(Fact(type=FactType.Death, date=Date(original=dod)))
|
231
|
+
|
232
|
+
# Return the different class instance
|
233
|
+
return Person(facts=facts, names=[name] if name else None)
|
gedcomx/PlaceDescription.py
CHANGED
@@ -4,13 +4,14 @@ from .Attribution import Attribution
|
|
4
4
|
from .Conclusion import ConfidenceLevel
|
5
5
|
from .Date import Date
|
6
6
|
from .EvidenceReference import EvidenceReference
|
7
|
-
from .Identifier import
|
7
|
+
from .Identifier import IdentifierList
|
8
8
|
from .Note import Note
|
9
9
|
from .SourceReference import SourceReference
|
10
10
|
from .TextValue import TextValue
|
11
|
-
from .
|
12
|
-
|
11
|
+
from .Resource import Resource
|
12
|
+
from .Serialization import Serialization
|
13
13
|
from .Subject import Subject
|
14
|
+
from .URI import URI
|
14
15
|
|
15
16
|
class PlaceDescription(Subject):
|
16
17
|
identifier = "http://gedcomx.org/v1/PlaceDescription"
|
@@ -19,21 +20,21 @@ class PlaceDescription(Subject):
|
|
19
20
|
def __init__(self, id: str =None,
|
20
21
|
lang: str = 'en',
|
21
22
|
sources: Optional[List[SourceReference]] = [],
|
22
|
-
analysis:
|
23
|
+
analysis: Resource = None, notes: Optional[List[Note]] =[],
|
23
24
|
confidence: ConfidenceLevel = None,
|
24
25
|
attribution: Attribution = None,
|
25
26
|
extracted: bool = None,
|
26
27
|
evidence: List[EvidenceReference] = None,
|
27
28
|
media: List[SourceReference] = [],
|
28
|
-
identifiers: List[
|
29
|
+
identifiers: List[IdentifierList] = [],
|
29
30
|
names: List[TextValue] = [],
|
30
31
|
type: Optional[str] = None,
|
31
32
|
place: Optional[URI] = None,
|
32
|
-
jurisdiction: Optional["PlaceDescription"] = None, # PlaceDescription
|
33
|
+
jurisdiction: Optional["Resource | PlaceDescription"] = None, # PlaceDescription
|
33
34
|
latitude: Optional[float] = None,
|
34
35
|
longitude: Optional[float] = None,
|
35
36
|
temporalDescription: Optional[Date] = None,
|
36
|
-
spatialDescription: Optional[
|
37
|
+
spatialDescription: Optional[Resource] = None,) -> None:
|
37
38
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
38
39
|
self.names = names
|
39
40
|
self.type = type
|
@@ -44,4 +45,17 @@ class PlaceDescription(Subject):
|
|
44
45
|
self.temporalDescription = temporalDescription
|
45
46
|
self.spacialDescription = spatialDescription
|
46
47
|
|
47
|
-
|
48
|
+
@property
|
49
|
+
def _as_dict_(self):
|
50
|
+
place_description_dict = super()._as_dict_
|
51
|
+
place_description_dict.update({
|
52
|
+
"names": [n for n in self.names] if self.names else None,
|
53
|
+
"type": self.type if self.type else None,
|
54
|
+
"place": self.place._as_dict_ if self.place else None,
|
55
|
+
"jurisdiction": self.jurisdiction._as_dict_ if self.jurisdiction else None,
|
56
|
+
"latitude": float(self.latitide) if self.latitide else None,
|
57
|
+
"longitude": float(self.longitute) if self.longitute else None,
|
58
|
+
"temporalDescription": self.temporalDescription if self.temporalDescription else None,
|
59
|
+
"spatialDescription": self.spacialDescription._as_dict_ if self.temporalDescription else None
|
60
|
+
})
|
61
|
+
return Serialization.serialize_dict(place_description_dict)
|
gedcomx/PlaceReference.py
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
|
-
from .
|
3
|
+
from .Resource import Resource
|
4
|
+
from .Serialization import Serialization
|
4
5
|
|
5
6
|
class PlaceReference:
|
6
7
|
identifier = 'http://gedcomx.org/v1/PlaceReference'
|
7
8
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
8
9
|
|
9
|
-
def __init__(self, original: Optional[str], descriptionRef: Optional[
|
10
|
+
def __init__(self, original: Optional[str], descriptionRef: Optional[Resource]) -> None:
|
10
11
|
self.original = original
|
11
12
|
self.descriptionRef = descriptionRef
|
12
13
|
|
13
14
|
@property
|
14
15
|
def _as_dict_(self):
|
15
|
-
|
16
|
+
place_reference_dict = {
|
16
17
|
'original': self.original,
|
17
|
-
'descriptionRef': self.descriptionRef._as_dict_ if self.descriptionRef else
|
18
|
+
'descriptionRef': self.descriptionRef._as_dict_ if isinstance(self.descriptionRef,Resource) else Resource(target=self.descriptionRef)._as_dict_
|
18
19
|
}
|
20
|
+
return Serialization.serialize_dict(place_reference_dict)
|
19
21
|
|
20
22
|
def ensure_list(val):
|
21
23
|
if val is None:
|
@@ -25,7 +27,7 @@ def ensure_list(val):
|
|
25
27
|
# PlaceReference
|
26
28
|
PlaceReference._from_json_ = classmethod(lambda cls, data: PlaceReference(
|
27
29
|
original=data.get('original'),
|
28
|
-
descriptionRef=
|
30
|
+
descriptionRef=Resource._from_json_(data['description']) if data.get('description') else None
|
29
31
|
))
|
30
32
|
|
31
33
|
|
gedcomx/Relationship.py
CHANGED
@@ -8,9 +8,9 @@ from .Fact import Fact
|
|
8
8
|
from .Identifier import Identifier
|
9
9
|
from .Note import Note
|
10
10
|
from .Person import Person
|
11
|
-
from .Serialization import
|
11
|
+
from .Serialization import Serialization
|
12
12
|
from .SourceReference import SourceReference
|
13
|
-
from .
|
13
|
+
from .Resource import Resource
|
14
14
|
|
15
15
|
from .Subject import Subject
|
16
16
|
|
@@ -27,6 +27,16 @@ class RelationshipType(Enum):
|
|
27
27
|
return descriptions.get(self, "No description available.")
|
28
28
|
|
29
29
|
class Relationship(Subject):
|
30
|
+
"""Represents a relationship between two Person(s)
|
31
|
+
|
32
|
+
Args:
|
33
|
+
type (RelationshipType): Type of relationship
|
34
|
+
person1 (Person) = First Person in Relationship
|
35
|
+
person2 (Person): Second Person in Relationship
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
ValueError: If `id` is not a valid UUID.
|
39
|
+
"""
|
30
40
|
identifier = 'http://gedcomx.org/v1/Relationship'
|
31
41
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
32
42
|
|
@@ -34,7 +44,7 @@ class Relationship(Subject):
|
|
34
44
|
id: Optional[str] = None,
|
35
45
|
lang: Optional[str] = None,
|
36
46
|
sources: Optional[List[SourceReference]] = None,
|
37
|
-
analysis: Optional[
|
47
|
+
analysis: Optional[Resource] = None,
|
38
48
|
notes: Optional[List[Note]] = None,
|
39
49
|
confidence: Optional[ConfidenceLevel] = None,
|
40
50
|
attribution: Optional[Attribution] = None,
|
@@ -43,8 +53,8 @@ class Relationship(Subject):
|
|
43
53
|
media: Optional[List[SourceReference]] = None,
|
44
54
|
identifiers: Optional[List[Identifier]] = None,
|
45
55
|
type: Optional[RelationshipType] = None,
|
46
|
-
person1: Optional[
|
47
|
-
person2: Optional[
|
56
|
+
person1: Optional[Person | Resource] = None,
|
57
|
+
person2: Optional[Person | Resource] = None,
|
48
58
|
facts: Optional[List[Fact]] = None) -> None:
|
49
59
|
|
50
60
|
# Call superclass initializer if required
|
@@ -68,8 +78,8 @@ class Relationship(Subject):
|
|
68
78
|
def _as_dict_(self):
|
69
79
|
return serialize_to_dict(self, {
|
70
80
|
"type": self.type.value if isinstance(self.type, RelationshipType) else self.type,
|
71
|
-
"person1": self.person1.
|
72
|
-
"person2": self.person2.
|
81
|
+
"person1": self.person1.uri,
|
82
|
+
"person2": self.person2.uri,
|
73
83
|
"facts": [fact for fact in self.facts] if self.facts else None
|
74
84
|
})
|
75
85
|
|
@@ -92,8 +102,8 @@ class Relationship(Subject):
|
|
92
102
|
private = data.get('private', None)
|
93
103
|
|
94
104
|
# Complex singletons (adjust as needed)
|
95
|
-
person1 =
|
96
|
-
person2 =
|
105
|
+
person1 = Resource.from_url(data.get('person1')['resource']) if data.get('person1') else None
|
106
|
+
person2 = Resource.from_url(data.get('person2')['resource']) if data.get('person2') else None
|
97
107
|
facts = [Fact._from_json_(o) for o in ensure_list(data.get('facts'))]
|
98
108
|
sources = [SourceReference._from_json_(o) for o in ensure_list(data.get('sources'))]
|
99
109
|
notes = [Note._from_json_(o) for o in ensure_list(data.get('notes'))]
|
gedcomx/Resource.py
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from .URI import URI
|
4
|
+
|
5
|
+
class Resource:
|
6
|
+
"""
|
7
|
+
Class used to track and resolve URIs and references between datastores.
|
8
|
+
|
9
|
+
Parameters
|
10
|
+
----------
|
11
|
+
|
12
|
+
Raises
|
13
|
+
------
|
14
|
+
ValueError
|
15
|
+
If `id` is not a valid UUID.
|
16
|
+
"""
|
17
|
+
def __init__(self,uri: Optional[URI|str] = None, id:Optional[str] = None,top_lvl_object: Optional[object] = None,target= None) -> None:
|
18
|
+
|
19
|
+
self.resource = URI.from_url(uri.value)
|
20
|
+
self.Id = id
|
21
|
+
|
22
|
+
self.type = None
|
23
|
+
self.resolved = False
|
24
|
+
self.target: object = target
|
25
|
+
self.remote: bool | None = None # is the resource pointed to persitent on a remote datastore?
|
26
|
+
|
27
|
+
if target:
|
28
|
+
self.resource = target._uri
|
29
|
+
self.Id = target.id
|
30
|
+
self.type = type(target)
|
31
|
+
|
32
|
+
@property
|
33
|
+
def _as_dict_(self):
|
34
|
+
return {'resource':self.resource.value,
|
35
|
+
'resourceId':self.Id}
|
36
|
+
|
37
|
+
def get_resource_as_dict(value):
|
38
|
+
"""
|
39
|
+
If value is truthy:
|
40
|
+
- If it's already a Resource, return it.
|
41
|
+
- Otherwise, wrap it in Resource using (value._uri, value.id).
|
42
|
+
Returns None if value is falsy.
|
43
|
+
"""
|
44
|
+
|
45
|
+
if not value:
|
46
|
+
return None
|
47
|
+
|
48
|
+
if isinstance(value, Resource):
|
49
|
+
return value._as_dict_
|
50
|
+
|
51
|
+
try:
|
52
|
+
return Resource(
|
53
|
+
getattr(value, "uri", None),
|
54
|
+
getattr(value, "id", None)
|
55
|
+
)._as_dict_
|
56
|
+
except AttributeError:
|
57
|
+
print('get_resource_as_dict',type(value),value)
|
58
|
+
print((f"value: {value} as inproper attributes"))
|
59
|
+
exit()
|
60
|
+
|
61
|
+
|
gedcomx/Serialization.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
from typing import Dict
|
2
2
|
|
3
|
+
from collections.abc import Sized
|
4
|
+
|
3
5
|
|
4
6
|
def _has_parent_class(obj) -> bool:
|
5
7
|
return hasattr(obj, '__class__') and hasattr(obj.__class__, '__bases__') and len(obj.__class__.__bases__) > 0
|
@@ -34,4 +36,45 @@ def serialize_to_dict(obj,class_values:Dict,ignore_null=True):
|
|
34
36
|
for key in empty_fields:
|
35
37
|
del values_dict[key]
|
36
38
|
|
37
|
-
return values_dict
|
39
|
+
return values_dict
|
40
|
+
|
41
|
+
class Serialization:
|
42
|
+
|
43
|
+
@staticmethod
|
44
|
+
def serialize_dict(dict_to_serialize: dict) -> dict:
|
45
|
+
"""
|
46
|
+
Iterates through the dict, serilaizing all Gedcom Types into a json compatible value
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
dict_to_serialize: dict
|
51
|
+
dict that has been created from any Gedcom Type Object's _as_dict_ property
|
52
|
+
|
53
|
+
Raises
|
54
|
+
------
|
55
|
+
ValueError
|
56
|
+
If `id` is not a valid UUID.
|
57
|
+
"""
|
58
|
+
def _serialize(value):
|
59
|
+
if isinstance(value, (str, int, float, bool, type(None))):
|
60
|
+
return value
|
61
|
+
elif isinstance(value, dict):
|
62
|
+
return {k: _serialize(v) for k, v in value.items()}
|
63
|
+
elif isinstance(value, (list, tuple, set)):
|
64
|
+
return [_serialize(v) for v in value]
|
65
|
+
elif hasattr(value, "_as_dict_"):
|
66
|
+
return value._as_dict_
|
67
|
+
else:
|
68
|
+
return str(value) # fallback for unknown objects
|
69
|
+
|
70
|
+
if dict_to_serialize and isinstance(dict_to_serialize,dict):
|
71
|
+
for key, value in dict_to_serialize.items():
|
72
|
+
if value is not None:
|
73
|
+
dict_to_serialize[key] = _serialize(value)
|
74
|
+
|
75
|
+
return {
|
76
|
+
k: v
|
77
|
+
for k, v in dict_to_serialize.items()
|
78
|
+
if v is not None and not (isinstance(v, Sized) and len(v) == 0)
|
79
|
+
}
|
80
|
+
return {}
|
gedcomx/SourceCitation.py
CHANGED
@@ -17,4 +17,9 @@ class SourceCitation:
|
|
17
17
|
"""
|
18
18
|
lang = data.get('lang', 'en')
|
19
19
|
value = data.get('value')
|
20
|
-
return cls(lang=lang, value=value)
|
20
|
+
return cls(lang=lang, value=value)
|
21
|
+
|
22
|
+
@property
|
23
|
+
def _as_dict_(self):
|
24
|
+
return {'lang':self.lang,
|
25
|
+
'value': self.value}
|