gedcom-x 0.5.1__py3-none-any.whl → 0.5.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gedcom_x-0.5.1.dist-info → gedcom_x-0.5.5.dist-info}/METADATA +1 -1
- gedcom_x-0.5.5.dist-info/RECORD +43 -0
- gedcomx/Address.py +42 -11
- gedcomx/Agent.py +136 -23
- gedcomx/Attribution.py +39 -91
- gedcomx/Conclusion.py +132 -53
- gedcomx/Coverage.py +10 -0
- gedcomx/Date.py +47 -11
- gedcomx/Document.py +43 -12
- gedcomx/Event.py +24 -5
- gedcomx/EvidenceReference.py +2 -2
- gedcomx/Exceptions.py +16 -0
- gedcomx/Fact.py +73 -50
- gedcomx/Gedcom.py +40 -333
- gedcomx/Gedcom5x.py +558 -0
- gedcomx/GedcomX.py +439 -194
- gedcomx/Gender.py +27 -8
- gedcomx/Group.py +3 -3
- gedcomx/Identifier.py +192 -55
- gedcomx/Logging.py +19 -0
- gedcomx/Mutations.py +228 -0
- gedcomx/Name.py +73 -38
- gedcomx/Note.py +5 -4
- gedcomx/OnlineAccount.py +2 -2
- gedcomx/Person.py +106 -92
- gedcomx/PlaceDescription.py +39 -16
- gedcomx/PlaceReference.py +14 -15
- gedcomx/Relationship.py +35 -56
- gedcomx/Resource.py +75 -0
- gedcomx/Serialization.py +394 -30
- gedcomx/SourceCitation.py +6 -1
- gedcomx/SourceDescription.py +89 -75
- gedcomx/SourceReference.py +33 -88
- gedcomx/Subject.py +12 -10
- gedcomx/TextValue.py +2 -1
- gedcomx/Translation.py +219 -0
- gedcomx/URI.py +96 -61
- gedcomx/Zip.py +1 -0
- gedcomx/__init__.py +11 -3
- gedcom_x-0.5.1.dist-info/RECORD +0 -37
- gedcomx/_Resource.py +0 -11
- {gedcom_x-0.5.1.dist-info → gedcom_x-0.5.5.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.1.dist-info → gedcom_x-0.5.5.dist-info}/top_level.txt +0 -0
gedcomx/Name.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import List,Optional
|
3
|
+
from typing_extensions import Self
|
3
4
|
|
4
5
|
from .Attribution import Attribution
|
5
6
|
from .Conclusion import Conclusion, ConfidenceLevel
|
6
7
|
from .Date import Date
|
7
8
|
from .Note import Note
|
9
|
+
from .Serialization import Serialization
|
8
10
|
from .SourceReference import SourceReference
|
9
|
-
from .
|
11
|
+
from .Resource import Resource
|
10
12
|
|
11
13
|
|
12
14
|
class NameType(Enum):
|
@@ -99,10 +101,11 @@ class NamePart:
|
|
99
101
|
self.value = value
|
100
102
|
self.qualifiers = qualifiers
|
101
103
|
|
102
|
-
def
|
103
|
-
return
|
104
|
-
|
105
|
-
|
104
|
+
def _as_dict_(self):
|
105
|
+
return Serialization.serialize_dict(
|
106
|
+
{'type': self.type.value if self.type else None,
|
107
|
+
'value': self.value if self.value else None,
|
108
|
+
'qualifiers': [qualifier.value for qualifier in self.qualifiers] if self.qualifiers else None})
|
106
109
|
|
107
110
|
def __eq__(self, other):
|
108
111
|
if not isinstance(other, NamePart):
|
@@ -120,10 +123,12 @@ class NameForm:
|
|
120
123
|
self.fullText = fullText
|
121
124
|
self.parts = parts
|
122
125
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
126
|
+
@property
|
127
|
+
def _as_dict_(self):
|
128
|
+
return Serialization.serialize_dict(
|
129
|
+
{'lang': self.lang,
|
130
|
+
'fullText': self.fullText,
|
131
|
+
'parts': [part._as_dict_() for part in self.parts] if self.parts else None})
|
127
132
|
|
128
133
|
def _fulltext_parts(self):
|
129
134
|
pass
|
@@ -137,37 +142,40 @@ class Name(Conclusion):
|
|
137
142
|
"""
|
138
143
|
Takes a string and returns a GedcomX Name Object
|
139
144
|
"""
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
# Assign val1 and val2 based on the split
|
144
|
-
given = parts[0] if len(parts) > 1 else ""
|
145
|
-
surname = parts[1] if len(parts) > 1 else parts[0]
|
145
|
+
if text:
|
146
|
+
text = text.replace("/","")
|
147
|
+
parts = text.rsplit(' ', 1)
|
146
148
|
|
147
|
-
|
148
|
-
|
149
|
-
|
149
|
+
# Assign val1 and val2 based on the split
|
150
|
+
given = parts[0] if len(parts) > 1 else ""
|
151
|
+
surname = parts[1] if len(parts) > 1 else parts[0]
|
152
|
+
|
153
|
+
# Remove any '/' characters from both val1 and val2
|
154
|
+
#given = given.replace('/', '')
|
155
|
+
#surname = surname.replace('/', '')
|
150
156
|
|
151
|
-
|
152
|
-
|
157
|
+
given_name_part = NamePart(type = NamePartType.Given, value=given)
|
158
|
+
surname_part = NamePart(type = NamePartType.Surname, value=surname)
|
153
159
|
|
154
|
-
|
155
|
-
|
160
|
+
name_form = NameForm(fullText=text,parts=[given_name_part,surname_part])
|
161
|
+
name = Name(type=NameType.BirthName,nameForms=[name_form])
|
162
|
+
else:
|
163
|
+
name = Name()
|
156
164
|
return name
|
157
165
|
|
158
166
|
def __init__(self, id: str = None,
|
159
167
|
lang: str = 'en',
|
160
|
-
sources: Optional[List[SourceReference]] =
|
161
|
-
analysis:
|
162
|
-
notes: Optional[List[Note]] =
|
163
|
-
confidence: ConfidenceLevel = None,
|
164
|
-
attribution: Attribution = None,
|
168
|
+
sources: Optional[List[SourceReference]] = None,
|
169
|
+
analysis: Resource = None,
|
170
|
+
notes: Optional[List[Note]] = None,
|
171
|
+
confidence: Optional[ConfidenceLevel] = None,
|
172
|
+
attribution: Optional[Attribution] = None,
|
165
173
|
type: Optional[NameType] = None,
|
166
|
-
nameForms: NameForm
|
174
|
+
nameForms: Optional[List[NameForm]]= None,
|
167
175
|
date: Optional[Date] = None) -> None:
|
168
176
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution)
|
169
177
|
self.type = type
|
170
|
-
self.nameForms = nameForms
|
178
|
+
self.nameForms = nameForms if nameForms else []
|
171
179
|
self.date = date
|
172
180
|
|
173
181
|
def _add_name_part(self, namepart_to_add: NamePart):
|
@@ -179,13 +187,45 @@ class Name(Conclusion):
|
|
179
187
|
|
180
188
|
@property
|
181
189
|
def _as_dict_(self):
|
190
|
+
def _serialize(value):
|
191
|
+
if isinstance(value, (str, int, float, bool, type(None))):
|
192
|
+
return value
|
193
|
+
elif isinstance(value, dict):
|
194
|
+
return {k: _serialize(v) for k, v in value.items()}
|
195
|
+
elif isinstance(value, (list, tuple, set)):
|
196
|
+
return [_serialize(v) for v in value]
|
197
|
+
elif hasattr(value, "_as_dict_"):
|
198
|
+
return value._as_dict_
|
199
|
+
else:
|
200
|
+
return str(value) # fallback for unknown objects
|
201
|
+
|
182
202
|
name_as_dict = super()._as_dict_
|
203
|
+
|
183
204
|
name_as_dict.update( {
|
184
205
|
'type':self.type.value if self.type else None,
|
185
|
-
'nameForms': [nameForm.
|
186
|
-
'date': self.date.
|
206
|
+
'nameForms': [nameForm._as_dict_ for nameForm in self.nameForms],
|
207
|
+
'date': self.date._as_dict_ if self.date else None})
|
208
|
+
|
209
|
+
#del name_as_dict['id']
|
210
|
+
# Serialize and exclude None values
|
211
|
+
for key, value in name_as_dict.items():
|
212
|
+
if value is not None:
|
213
|
+
name_as_dict[key] = _serialize(value)
|
214
|
+
|
215
|
+
# 3) merge and filter out None *at the top level*
|
216
|
+
return {
|
217
|
+
k: v
|
218
|
+
for k, v in name_as_dict.items()
|
219
|
+
if v is not None
|
220
|
+
}
|
221
|
+
|
187
222
|
return name_as_dict
|
188
223
|
|
224
|
+
class QuickName():
|
225
|
+
def __new__(cls,name: str) -> Name:
|
226
|
+
obj = Name(nameForms=[NameForm(fullText=name)])
|
227
|
+
return obj
|
228
|
+
|
189
229
|
def ensure_list(val):
|
190
230
|
if val is None:
|
191
231
|
return []
|
@@ -223,7 +263,7 @@ Name._from_json_ = classmethod(lambda cls, data: cls(
|
|
223
263
|
id=data.get('id'),
|
224
264
|
lang=data.get('lang', 'en'),
|
225
265
|
sources=[SourceReference._from_json_(s) for s in ensure_list(data.get('sources'))],
|
226
|
-
analysis=
|
266
|
+
analysis=Resource._from_json_(data['analysis']) if data.get('analysis') else None,
|
227
267
|
notes=[Note._from_json_(n) for n in ensure_list(data.get('notes'))],
|
228
268
|
confidence=ConfidenceLevel._from_json_(data['confidence']) if data.get('confidence') else None,
|
229
269
|
attribution=Attribution._from_json_(data['attribution']) if data.get('attribution') else None,
|
@@ -232,10 +272,5 @@ Name._from_json_ = classmethod(lambda cls, data: cls(
|
|
232
272
|
date=Date._from_json_(data['date']) if data.get('date') else None
|
233
273
|
))
|
234
274
|
|
235
|
-
|
236
|
-
**self._as_dict_,
|
237
|
-
'type': self.type.value if self.type else None,
|
238
|
-
'nameForms': [nf._to_dict_() for nf in self.nameForms] if self.nameForms else [],
|
239
|
-
'date': self.date._prop_date() if self.date else None
|
240
|
-
}
|
275
|
+
|
241
276
|
|
gedcomx/Note.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
3
|
from .Attribution import Attribution
|
4
|
+
from .Serialization import Serialization
|
4
5
|
|
5
6
|
class Note:
|
6
7
|
identifier = 'http://gedcomx.org/v1/Note'
|
@@ -10,24 +11,24 @@ class Note:
|
|
10
11
|
self.lang = lang
|
11
12
|
self.subject = subject
|
12
13
|
self.text = text
|
13
|
-
self.attribution = attribution
|
14
|
-
|
15
|
-
|
14
|
+
self.attribution = attribution
|
16
15
|
|
17
16
|
def append(self, text_to_add: str):
|
18
17
|
if text_to_add and isinstance(text_to_add, str):
|
19
18
|
self.text = self.text + text_to_add
|
20
19
|
else:
|
20
|
+
return #TODO
|
21
21
|
raise ValueError("The text to add must be a non-empty string.")
|
22
22
|
|
23
23
|
@property
|
24
24
|
def _as_dict_(self):
|
25
|
-
|
25
|
+
note_dict = {
|
26
26
|
"lang":self.lang if self.lang else None,
|
27
27
|
"subject":self.subject if self.subject else None,
|
28
28
|
"text":self.text if self.text else None,
|
29
29
|
"attribution": self.attribution if self.attribution else None
|
30
30
|
}
|
31
|
+
return Serialization.serialize_dict(note_dict)
|
31
32
|
|
32
33
|
def __eq__(self, other):
|
33
34
|
if not isinstance(other, Note):
|
gedcomx/OnlineAccount.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
|
-
from .
|
3
|
+
from .Resource import Resource
|
4
4
|
|
5
5
|
class OnlineAccount:
|
6
6
|
identifier = 'http://gedcomx.org/v1/OnlineAccount'
|
7
7
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
8
8
|
|
9
|
-
def __init__(self, serviceHomepage:
|
9
|
+
def __init__(self, serviceHomepage: Resource, accountName: str) -> None:
|
10
10
|
pass
|
gedcomx/Person.py
CHANGED
@@ -1,40 +1,58 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import List, Optional
|
3
|
+
from urllib.parse import urljoin
|
3
4
|
|
4
5
|
from .Attribution import Attribution
|
5
6
|
from .Conclusion import ConfidenceLevel
|
7
|
+
from .Date import Date
|
6
8
|
from .EvidenceReference import EvidenceReference
|
7
|
-
from .Fact import Fact
|
9
|
+
from .Fact import Fact, FactType
|
8
10
|
from .Gender import Gender, GenderType
|
9
|
-
from .Identifier import
|
10
|
-
from .Name import Name,
|
11
|
+
from .Identifier import IdentifierList
|
12
|
+
from .Name import Name, QuickName
|
11
13
|
from .Note import Note
|
12
14
|
from .SourceReference import SourceReference
|
15
|
+
from .Serialization import Serialization
|
13
16
|
from .Subject import Subject
|
14
|
-
from .
|
17
|
+
from .Resource import Resource
|
18
|
+
from collections.abc import Sized
|
19
|
+
from .Extensions.rs10.rsLink import _rsLinkList
|
15
20
|
|
16
21
|
class Person(Subject):
|
22
|
+
"""A person in the system.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
id (str): Unique identifier for this person.
|
26
|
+
name (str): Full name.
|
27
|
+
birth (date): Birth date (YYYY-MM-DD).
|
28
|
+
friends (List[Person], optional): List of friends. Defaults to None.
|
29
|
+
|
30
|
+
Raises:
|
31
|
+
ValueError: If `id` is not a valid UUID.
|
32
|
+
"""
|
17
33
|
identifier = 'http://gedcomx.org/v1/Person'
|
18
34
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
19
35
|
|
20
|
-
def __init__(self, id: str = None,
|
36
|
+
def __init__(self, id: str | None = None,
|
21
37
|
lang: str = 'en',
|
22
38
|
sources: Optional[List[SourceReference]] = None,
|
23
|
-
analysis: Optional[
|
39
|
+
analysis: Optional[Resource] = None,
|
24
40
|
notes: Optional[List[Note]] = None,
|
25
41
|
confidence: Optional[ConfidenceLevel] = None,
|
26
42
|
attribution: Optional[Attribution] = None,
|
27
43
|
extracted: bool = None,
|
28
44
|
evidence: Optional[List[EvidenceReference]] = None,
|
29
45
|
media: Optional[List[SourceReference]] = None,
|
30
|
-
identifiers: Optional[
|
46
|
+
identifiers: Optional[IdentifierList] = None,
|
31
47
|
private: Optional[bool] = False,
|
32
48
|
gender: Optional[Gender] = Gender(type=GenderType.Unknown),
|
33
49
|
names: Optional[List[Name]] = None,
|
34
50
|
facts: Optional[List[Fact]] = None,
|
35
|
-
living: Optional[bool] = False
|
51
|
+
living: Optional[bool] = False,
|
52
|
+
links: Optional[_rsLinkList] = None,
|
53
|
+
uri: Optional[Resource] = None) -> None:
|
36
54
|
# Call superclass initializer if needed
|
37
|
-
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
55
|
+
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links,uri=uri)
|
38
56
|
|
39
57
|
# Initialize mutable attributes to empty lists if None
|
40
58
|
self.sources = sources if sources is not None else []
|
@@ -51,8 +69,7 @@ class Person(Subject):
|
|
51
69
|
self.living = living #TODO This is from familysearch API
|
52
70
|
|
53
71
|
self._relationships = []
|
54
|
-
|
55
|
-
|
72
|
+
|
56
73
|
def add_fact(self, fact_to_add: Fact) -> bool:
|
57
74
|
if fact_to_add and isinstance(fact_to_add,Fact):
|
58
75
|
for current_fact in self.facts:
|
@@ -80,99 +97,96 @@ class Person(Subject):
|
|
80
97
|
else:
|
81
98
|
raise ValueError()
|
82
99
|
|
100
|
+
def display(self):
|
101
|
+
display = {
|
102
|
+
"ascendancyNumber": "1",
|
103
|
+
"deathDate": "from 2001 to 2005",
|
104
|
+
"descendancyNumber": "1",
|
105
|
+
"gender": self.gender.type,
|
106
|
+
"lifespan": "-2005",
|
107
|
+
"name": self.names[0].nameForms[0].fullText
|
108
|
+
}
|
109
|
+
|
110
|
+
return display
|
111
|
+
|
83
112
|
@property
|
84
113
|
def _as_dict_(self):
|
85
|
-
|
86
|
-
if isinstance(value, (str, int, float, bool, type(None))):
|
87
|
-
return value
|
88
|
-
elif isinstance(value, dict):
|
89
|
-
return {k: _serialize(v) for k, v in value.items()}
|
90
|
-
elif isinstance(value, (list, tuple, set)):
|
91
|
-
return [_serialize(v) for v in value]
|
92
|
-
elif hasattr(value, "_as_dict_"):
|
93
|
-
return value._as_dict_
|
94
|
-
else:
|
95
|
-
return str(value) # fallback for unknown objects
|
96
|
-
|
97
|
-
subject_fields = super()._as_dict_ # Start with base class fields
|
114
|
+
type_as_dict = super()._as_dict_ # Start with base class fields
|
98
115
|
# Only add Relationship-specific fields
|
99
|
-
|
116
|
+
type_as_dict.update({
|
100
117
|
'private': self.private,
|
101
118
|
'living': self.living,
|
102
|
-
'gender': self.gender.
|
103
|
-
'names': [name for name in self.names],
|
104
|
-
'facts': [fact for fact in self.facts]
|
119
|
+
'gender': self.gender._as_dict_ if self.gender else None,
|
120
|
+
'names': [name._as_dict_ for name in self.names],
|
121
|
+
'facts': [fact._as_dict_ for fact in self.facts],
|
122
|
+
'uri': self.uri._as_dict_ if self.uri else None
|
105
123
|
|
106
124
|
})
|
107
125
|
|
108
|
-
|
109
|
-
|
110
|
-
if value is not None:
|
111
|
-
subject_fields[key] = _serialize(value)
|
112
|
-
|
113
|
-
return subject_fields
|
114
|
-
|
115
|
-
|
126
|
+
return Serialization.serialize_dict(type_as_dict)
|
127
|
+
|
116
128
|
@classmethod
|
117
129
|
def _from_json_(cls, data: dict):
|
118
130
|
"""
|
119
131
|
Create a Person instance from a JSON-dict (already parsed).
|
120
132
|
"""
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
133
|
+
return Serialization.deserialize(data, Person)
|
134
|
+
|
135
|
+
@classmethod
|
136
|
+
def from_familysearch(cls, pid: str, token: str, *, base_url: Optional[str] = None):
|
137
|
+
"""
|
138
|
+
Fetch a single person by PID from FamilySearch and return a Person.
|
139
|
+
- pid: e.g. "KPHP-4B4"
|
140
|
+
- token: OAuth2 access token (Bearer)
|
141
|
+
- base_url: override API base (defaults to settings.FS_API_BASE or prod)
|
142
|
+
"""
|
143
|
+
import requests
|
144
|
+
default_base = "https://apibeta.familysearch.org/platform/"
|
145
|
+
|
146
|
+
base = (base_url or default_base).rstrip("/") + "/"
|
147
|
+
url = urljoin(base, f"tree/persons/{pid}")
|
148
|
+
|
149
|
+
headers = {
|
150
|
+
"Accept": "application/json",
|
151
|
+
"Authorization": f"Bearer {token}",
|
152
|
+
}
|
153
|
+
|
154
|
+
resp = requests.get(url, headers=headers, timeout=(5, 30))
|
155
|
+
resp.raise_for_status()
|
156
|
+
|
157
|
+
payload = resp.json()
|
158
|
+
persons = payload.get("persons") or []
|
159
|
+
|
160
|
+
# Prefer exact match on PID, else first item if present
|
161
|
+
person_json = next((p for p in persons if (p.get("id") == pid)), None) or (persons[0] if persons else None)
|
162
|
+
if not person_json:
|
163
|
+
raise ValueError(f"FamilySearch returned no person for PID {pid}")
|
164
|
+
|
165
|
+
# Keep your existing deserialization helper
|
166
|
+
return Serialization.deserialize(person_json, Person)
|
167
|
+
|
168
|
+
class QuickPerson:
|
169
|
+
"""A GedcomX Person Data Type created with basic information.
|
170
|
+
|
171
|
+
Underlying GedcomX Types are created for you.
|
148
172
|
|
173
|
+
Args:
|
174
|
+
name (str): Full name.
|
175
|
+
birth (date,Optional): Birth date (YYYY-MM-DD).
|
176
|
+
death (date, Optional)
|
177
|
+
|
149
178
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
facts
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
notes = notes,
|
165
|
-
confidence = confidence,
|
166
|
-
attribution = attribution,
|
167
|
-
extracted = extracted,
|
168
|
-
evidence = evidence,
|
169
|
-
media = media,
|
170
|
-
identifiers = identifiers,
|
171
|
-
private = private,
|
172
|
-
gender = gender,
|
173
|
-
names = names,
|
174
|
-
facts = facts,
|
175
|
-
living = living
|
176
|
-
)
|
177
|
-
|
178
|
-
return inst
|
179
|
+
|
180
|
+
Raises:
|
181
|
+
ValueError: If `id` is not a valid UUID.
|
182
|
+
"""
|
183
|
+
def __new__(cls, name: str, dob: Optional[str] = None, dod: Optional[str] = None):
|
184
|
+
# Build facts from args
|
185
|
+
facts = []
|
186
|
+
if dob:
|
187
|
+
facts.append(Fact(type=FactType.Birth, date=Date(original=dob)))
|
188
|
+
if dod:
|
189
|
+
facts.append(Fact(type=FactType.Death, date=Date(original=dod)))
|
190
|
+
|
191
|
+
# Return the different class instance
|
192
|
+
return Person(facts=facts, names=[QuickName(name=name)] if name else None)
|
gedcomx/PlaceDescription.py
CHANGED
@@ -4,36 +4,39 @@ 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"
|
17
18
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
18
19
|
|
19
|
-
def __init__(self, id: str =None,
|
20
|
+
def __init__(self, id: Optional[str] =None,
|
20
21
|
lang: str = 'en',
|
21
|
-
sources: Optional[List[SourceReference]] =
|
22
|
-
analysis:
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
sources: Optional[List[SourceReference]] = None,
|
23
|
+
analysis: Optional[Resource] = None,
|
24
|
+
notes: Optional[List[Note]] =None,
|
25
|
+
confidence: Optional[ConfidenceLevel] = None,
|
26
|
+
attribution: Optional[Attribution] = None,
|
27
|
+
extracted: Optional[bool] = None,
|
28
|
+
evidence: Optional[List[EvidenceReference]] = None,
|
29
|
+
media: Optional[List[SourceReference]] = None,
|
30
|
+
identifiers: Optional[IdentifierList] = None,
|
31
|
+
names: Optional[List[TextValue]] = None,
|
30
32
|
type: Optional[str] = None,
|
31
33
|
place: Optional[URI] = None,
|
32
|
-
jurisdiction: Optional["PlaceDescription"] = None, # PlaceDescription
|
34
|
+
jurisdiction: Optional["Resource | PlaceDescription"] = None, # PlaceDescription
|
33
35
|
latitude: Optional[float] = None,
|
34
36
|
longitude: Optional[float] = None,
|
35
37
|
temporalDescription: Optional[Date] = None,
|
36
|
-
spatialDescription: Optional[
|
38
|
+
spatialDescription: Optional[Resource] = None,) -> None:
|
39
|
+
|
37
40
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
38
41
|
self.names = names
|
39
42
|
self.type = type
|
@@ -44,4 +47,24 @@ class PlaceDescription(Subject):
|
|
44
47
|
self.temporalDescription = temporalDescription
|
45
48
|
self.spacialDescription = spatialDescription
|
46
49
|
|
47
|
-
|
50
|
+
@property
|
51
|
+
def _as_dict_(self):
|
52
|
+
type_as_dict = super()._as_dict_
|
53
|
+
type_as_dict.update({
|
54
|
+
"names": [n for n in self.names] if self.names else None,
|
55
|
+
"type": self.type if self.type else None,
|
56
|
+
"place": self.place._as_dict_ if self.place else None,
|
57
|
+
"jurisdiction": self.jurisdiction._as_dict_ if self.jurisdiction else None,
|
58
|
+
"latitude": float(self.latitide) if self.latitide else None,
|
59
|
+
"longitude": float(self.longitute) if self.longitute else None,
|
60
|
+
"temporalDescription": self.temporalDescription if self.temporalDescription else None,
|
61
|
+
"spatialDescription": self.spacialDescription._as_dict_ if self.spacialDescription else None
|
62
|
+
})
|
63
|
+
return Serialization.serialize_dict(type_as_dict)
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def _from_json_(cls, data: dict):
|
67
|
+
"""
|
68
|
+
Create a PlaceDescription instance from a JSON-dict (already parsed).
|
69
|
+
"""
|
70
|
+
return Serialization.deserialize(data, PlaceDescription)
|
gedcomx/PlaceReference.py
CHANGED
@@ -1,31 +1,30 @@
|
|
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,
|
10
|
+
def __init__(self,
|
11
|
+
original: Optional[str] = None,
|
12
|
+
description: Optional[Resource] = None) -> None:
|
10
13
|
self.original = original
|
11
|
-
self.
|
14
|
+
self.description = description
|
12
15
|
|
13
16
|
@property
|
14
17
|
def _as_dict_(self):
|
15
|
-
|
18
|
+
type_as_dict = {
|
16
19
|
'original': self.original,
|
17
|
-
'
|
20
|
+
'description': self.description._as_dict_ if self.description else None
|
18
21
|
}
|
22
|
+
return Serialization.serialize_dict(type_as_dict)
|
23
|
+
|
24
|
+
@classmethod
|
25
|
+
def _from_json_(cls, data):
|
26
|
+
|
27
|
+
return Serialization.deserialize(data, PlaceReference)
|
19
28
|
|
20
|
-
def ensure_list(val):
|
21
|
-
if val is None:
|
22
|
-
return []
|
23
|
-
return val if isinstance(val, list) else [val]
|
24
29
|
|
25
|
-
# PlaceReference
|
26
|
-
PlaceReference._from_json_ = classmethod(lambda cls, data: PlaceReference(
|
27
|
-
original=data.get('original'),
|
28
|
-
descriptionRef=URI._from_json_(data['description']) if data.get('description') else None
|
29
|
-
))
|
30
30
|
|
31
|
-
|