gedcom-x 0.5.6__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.6.dist-info → gedcom_x-0.5.7.dist-info}/METADATA +1 -1
- 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/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 +78 -61
- gedcomx/GedcomX.py +182 -1039
- gedcomx/Gender.py +7 -9
- gedcomx/Identifier.py +9 -12
- gedcomx/LoggingHub.py +21 -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 +3 -2
- gedcom_x-0.5.6.dist-info/RECORD +0 -45
- {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.7.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.7.dist-info}/top_level.txt +0 -0
gedcomx/SourceDescription.py
CHANGED
@@ -1,24 +1,44 @@
|
|
1
|
-
import base64
|
2
|
-
import uuid
|
3
1
|
import warnings
|
4
2
|
|
5
3
|
from enum import Enum
|
6
4
|
from typing import List, Optional, Dict, Any
|
7
5
|
|
6
|
+
from typing import Optional, TYPE_CHECKING
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from .Document import Document
|
9
|
+
|
10
|
+
"""
|
11
|
+
======================================================================
|
12
|
+
Project: Gedcom-X
|
13
|
+
File: SourceDescription.py
|
14
|
+
Author: David J. Cartwright
|
15
|
+
Purpose:
|
16
|
+
|
17
|
+
Created: 2025-07-25
|
18
|
+
Updated:
|
19
|
+
- 2025-08-31: _as_dict_ refactored to ignore empty fields, changed id creation to make_uid()
|
20
|
+
|
21
|
+
|
22
|
+
======================================================================
|
23
|
+
"""
|
24
|
+
|
25
|
+
"""
|
26
|
+
======================================================================
|
27
|
+
GEDCOM Module Types
|
28
|
+
======================================================================
|
29
|
+
"""
|
8
30
|
from .Agent import Agent
|
9
31
|
from .Attribution import Attribution
|
10
32
|
from .Coverage import Coverage
|
11
33
|
from .Date import Date
|
12
|
-
from .Identifier import Identifier, IdentifierList
|
34
|
+
from .Identifier import Identifier, IdentifierList, make_uid
|
13
35
|
from .Note import Note
|
36
|
+
from .Resource import Resource
|
14
37
|
from .SourceCitation import SourceCitation
|
15
38
|
from .SourceReference import SourceReference
|
16
39
|
from .TextValue import TextValue
|
17
|
-
from .Resource import Resource, get_resource_as_dict
|
18
|
-
from .Serialization import Serialization
|
19
40
|
from .URI import URI
|
20
|
-
|
21
|
-
from collections.abc import Sized
|
41
|
+
#=====================================================================
|
22
42
|
|
23
43
|
class ResourceType(Enum):
|
24
44
|
Collection = "http://gedcomx.org/Collection"
|
@@ -38,41 +58,66 @@ class ResourceType(Enum):
|
|
38
58
|
return descriptions.get(self, "No description available.")
|
39
59
|
|
40
60
|
class SourceDescription:
|
41
|
-
"""
|
42
|
-
|
43
|
-
http://gedcomx.org/v1/SourceDescription
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
"""Description of a genealogical information source.
|
62
|
+
|
63
|
+
See: http://gedcomx.org/v1/SourceDescription
|
64
|
+
|
65
|
+
Args:
|
66
|
+
id (str | None): Unique identifier for this `SourceDescription`.
|
67
|
+
resourceType (ResourceType | None): Type/category of the resource being
|
68
|
+
described (e.g., digital artifact, physical artifact).
|
69
|
+
citations (list[SourceCitation] | None): Citations that reference or
|
70
|
+
justify this source description.
|
71
|
+
mediaType (str | None): IANA media (MIME) type of the resource
|
72
|
+
(e.g., ``"application/pdf"``).
|
73
|
+
about (URI | None): Canonical URI that the description is about.
|
74
|
+
mediator (Resource | None): The mediator resource (if any) involved in
|
75
|
+
providing access to the source.
|
76
|
+
publisher (Resource | Agent | None): Publisher of the resource.
|
77
|
+
authors (list[Resource] | None): Authors/creators of the resource.
|
78
|
+
sources (list[SourceReference] | None): Other sources this description
|
79
|
+
derives from or references.
|
80
|
+
analysis (Resource | None): Analysis document associated with the
|
81
|
+
resource (often a `Document`; kept generic to avoid circular imports).
|
82
|
+
componentOf (SourceReference | None): Reference to a parent/containing
|
83
|
+
source (this is a component/child of that source).
|
84
|
+
titles (list[TextValue] | None): One or more titles for the resource.
|
85
|
+
notes (list[Note] | None): Human-authored notes about the resource.
|
86
|
+
attribution (Attribution | None): Attribution metadata for who supplied
|
87
|
+
or curated this description.
|
88
|
+
rights (list[Resource] | None): Rights statements or licenses.
|
89
|
+
coverage (list[Coverage] | None): Spatial/temporal coverage of the
|
90
|
+
source’s content.
|
91
|
+
descriptions (list[TextValue] | None): Short textual summaries or
|
92
|
+
descriptions.
|
93
|
+
identifiers (IdentifierList | None): Alternative identifiers for the
|
94
|
+
resource (DOI, ARK, call numbers, etc.).
|
95
|
+
created (Date | None): Creation date of the resource.
|
96
|
+
modified (Date | None): Last modified date of the resource.
|
97
|
+
published (Date | None): Publication/release date of the resource.
|
98
|
+
repository (Agent | None): Repository or agency that holds the resource.
|
99
|
+
max_note_count (int): Maximum number of notes to retain/emit. Defaults to 20.
|
100
|
+
|
101
|
+
Raises:
|
102
|
+
ValueError: If `id` is not a valid UUID.
|
103
|
+
|
104
|
+
Attributes:
|
105
|
+
identifier (str): Gedcom-X specification identifier for this type.
|
64
106
|
"""
|
65
107
|
|
108
|
+
identifier = "http://gedcomx.org/v1/SourceDescription"
|
109
|
+
version = 'http://gedcomx.org/conceptual-model/v1'
|
110
|
+
|
66
111
|
def __init__(self, id: Optional[str] = None,
|
67
112
|
resourceType: Optional[ResourceType] = None,
|
68
113
|
citations: Optional[List[SourceCitation]] = [],
|
69
114
|
mediaType: Optional[str] = None,
|
70
115
|
about: Optional[URI] = None,
|
71
|
-
mediator: Optional[Resource] = None,
|
72
|
-
publisher: Optional[Resource
|
116
|
+
mediator: Optional[Agent|Resource] = None,
|
117
|
+
publisher: Optional[Agent|Resource] = None,
|
73
118
|
authors: Optional[List[Resource]] = None,
|
74
119
|
sources: Optional[List[SourceReference]] = None, # SourceReference
|
75
|
-
analysis: Optional[Resource] = None, #
|
120
|
+
analysis: Optional["Document|Resource"] = None, #TODO add type checker so its a document
|
76
121
|
componentOf: Optional[SourceReference] = None, # SourceReference
|
77
122
|
titles: Optional[List[TextValue]] = None,
|
78
123
|
notes: Optional[List[Note]] = None,
|
@@ -87,7 +132,7 @@ class SourceDescription:
|
|
87
132
|
repository: Optional[Agent] = None,
|
88
133
|
max_note_count: int = 20):
|
89
134
|
|
90
|
-
self.id = id if id else
|
135
|
+
self.id = id if id else make_uid()
|
91
136
|
self.resourceType = resourceType
|
92
137
|
self.citations = citations or []
|
93
138
|
self.mediaType = mediaType
|
@@ -111,7 +156,7 @@ class SourceDescription:
|
|
111
156
|
self.repository = repository
|
112
157
|
self.max_note_count = max_note_count
|
113
158
|
|
114
|
-
self.uri = URI(fragment=id)
|
159
|
+
self.uri = URI(fragment=id) if id else None #TODO Should i take care of this in the collections?
|
115
160
|
|
116
161
|
@property
|
117
162
|
def publisher(self) -> Resource | Agent | None:
|
@@ -150,8 +195,7 @@ class SourceDescription:
|
|
150
195
|
return False
|
151
196
|
self.notes.append(note_to_add)
|
152
197
|
|
153
|
-
def add_source(self, source_to_add:
|
154
|
-
#from .SourceReference import SourceReference
|
198
|
+
def add_source(self, source_to_add: SourceReference):
|
155
199
|
if source_to_add and isinstance(object,SourceReference):
|
156
200
|
for current_source in self.sources:
|
157
201
|
if current_source == source_to_add:
|
@@ -167,44 +211,59 @@ class SourceDescription:
|
|
167
211
|
self.titles.append(title_to_add)
|
168
212
|
else:
|
169
213
|
raise ValueError(f"Cannot add title of type {type(title_to_add)}")
|
170
|
-
|
171
|
-
@staticmethod
|
172
|
-
def default_id_generator():
|
173
|
-
# Generate a standard UUID
|
174
|
-
standard_uuid = uuid.uuid4()
|
175
|
-
# Convert UUID to bytes
|
176
|
-
uuid_bytes = standard_uuid.bytes
|
177
|
-
# Encode bytes to a Base64 string
|
178
|
-
short_uuid = base64.urlsafe_b64encode(uuid_bytes).rstrip(b'=').decode('utf-8')
|
179
|
-
return 'SD-' + str(short_uuid)
|
180
|
-
|
214
|
+
|
181
215
|
@property
|
182
216
|
def _as_dict_(self) -> Dict[str, Any]:
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
'
|
188
|
-
|
189
|
-
'
|
190
|
-
|
191
|
-
'
|
192
|
-
|
193
|
-
'
|
194
|
-
|
195
|
-
'
|
196
|
-
|
197
|
-
'
|
198
|
-
|
199
|
-
'
|
200
|
-
|
201
|
-
'
|
202
|
-
|
203
|
-
'
|
204
|
-
|
205
|
-
'
|
206
|
-
|
207
|
-
|
217
|
+
from .Serialization import Serialization
|
218
|
+
type_as_dict = {}
|
219
|
+
|
220
|
+
if self.id:
|
221
|
+
type_as_dict['id'] = self.id
|
222
|
+
if self.about:
|
223
|
+
type_as_dict['about'] = self.about._as_dict_
|
224
|
+
if self.resourceType:
|
225
|
+
type_as_dict['resourceType'] = getattr(self.resourceType, 'value', self.resourceType)
|
226
|
+
if self.citations:
|
227
|
+
type_as_dict['citations'] = [c._as_dict_ for c in self.citations if c]
|
228
|
+
if self.mediaType:
|
229
|
+
type_as_dict['mediaType'] = self.mediaType
|
230
|
+
if self.mediator:
|
231
|
+
type_as_dict['mediator'] = self.mediator._as_dict_
|
232
|
+
if self.publisher:
|
233
|
+
type_as_dict['publisher'] = self.publisher._as_dict_ #TODO Resource this
|
234
|
+
if self.authors:
|
235
|
+
type_as_dict['authors'] = [a._as_dict_ for a in self.authors if a]
|
236
|
+
if self.sources:
|
237
|
+
type_as_dict['sources'] = [s._as_dict_ for s in self.sources if s]
|
238
|
+
if self.analysis:
|
239
|
+
type_as_dict['analysis'] = self.analysis._as_dict_
|
240
|
+
if self.componentOf:
|
241
|
+
type_as_dict['componentOf'] = self.componentOf._as_dict_
|
242
|
+
if self.titles:
|
243
|
+
type_as_dict['titles'] = [t._as_dict_ for t in self.titles if t]
|
244
|
+
if self.notes:
|
245
|
+
type_as_dict['notes'] = [n._as_dict_ for n in self.notes if n]
|
246
|
+
if self.attribution:
|
247
|
+
type_as_dict['attribution'] = self.attribution._as_dict_
|
248
|
+
if self.rights:
|
249
|
+
type_as_dict['rights'] = [r._as_dict_ for r in self.rights if r]
|
250
|
+
if self.coverage:
|
251
|
+
type_as_dict['coverage'] = [c._as_dict_ for c in self.coverage if c]
|
252
|
+
if self.descriptions:
|
253
|
+
type_as_dict['descriptions'] = [d for d in self.descriptions if d]
|
254
|
+
if self.identifiers:
|
255
|
+
type_as_dict['identifiers'] = self.identifiers._as_dict_
|
256
|
+
if self.created is not None:
|
257
|
+
type_as_dict['created'] = self.created
|
258
|
+
if self.modified is not None:
|
259
|
+
type_as_dict['modified'] = self.modified
|
260
|
+
if self.published is not None:
|
261
|
+
type_as_dict['published'] = self.published
|
262
|
+
if self.repository:
|
263
|
+
type_as_dict['repository'] = self.repository._as_dict_ #TODO Resource this
|
264
|
+
if self.uri and self.uri.value:
|
265
|
+
type_as_dict['uri'] = self.uri.value
|
266
|
+
|
208
267
|
|
209
268
|
return Serialization.serialize_dict(type_as_dict)
|
210
269
|
|
@@ -222,7 +281,7 @@ class SourceDescription:
|
|
222
281
|
about = URI._from_json_(data['about']) if data.get('about') else None
|
223
282
|
mediator = Resource._from_json_(data['mediator']) if data.get('mediator') else None
|
224
283
|
publisher = Resource._from_json_(data['publisher']) if data.get('publisher') else None
|
225
|
-
authors = [Resource._from_json_(a) for a in data.get('authors', [])]
|
284
|
+
authors = [Resource._from_json_(a) for a in data.get('authors', [])] if data.get('authors') else None
|
226
285
|
sources = [SourceReference._from_json_(s) for s in data.get('sources', [])]
|
227
286
|
analysis = Resource._from_json_(data['analysis']) if data.get('analysis') else None
|
228
287
|
component_of = SourceReference._from_json_(data['componentOf']) if data.get('componentOf') else None
|
gedcomx/SourceReference.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import List, Optional, TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from .SourceDescription import SourceDescription
|
2
6
|
|
3
7
|
from .Attribution import Attribution
|
4
8
|
from .Qualifier import Qualifier
|
5
9
|
|
6
10
|
from .Resource import Resource
|
7
|
-
|
11
|
+
|
8
12
|
from .URI import URI
|
9
13
|
|
10
14
|
from collections.abc import Sized
|
@@ -48,7 +52,7 @@ class SourceReference:
|
|
48
52
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
49
53
|
|
50
54
|
def __init__(self,
|
51
|
-
description: URI |
|
55
|
+
description: URI | SourceDescription | None = None,
|
52
56
|
descriptionId: Optional[str] = None,
|
53
57
|
attribution: Optional[Attribution] = None,
|
54
58
|
qualifiers: Optional[List[Qualifier]] = None
|
@@ -81,18 +85,15 @@ class SourceReference:
|
|
81
85
|
|
82
86
|
@property
|
83
87
|
def _as_dict_(self):
|
84
|
-
|
88
|
+
from .Serialization import Serialization
|
85
89
|
type_as_dict = {
|
86
90
|
'description':self.description._as_dict_ if self.description else None,
|
87
91
|
'descriptionId': self.descriptionId.replace("\n"," ").replace("\r"," ") if self.descriptionId else None,
|
88
92
|
'attribution': self.attribution._as_dict_ if self.attribution else None,
|
89
|
-
'qualifiers':[qualifier.
|
93
|
+
'qualifiers':[qualifier.__as_dict__ for qualifier in self.qualifiers] if (self.qualifiers and len(self.qualifiers) > 0) else None
|
90
94
|
}
|
91
95
|
return Serialization.serialize_dict(type_as_dict)
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
+
|
96
97
|
@classmethod
|
97
98
|
def _from_json_(cls, data: dict):
|
98
99
|
"""
|
gedcomx/Subject.py
CHANGED
@@ -6,7 +6,7 @@ from .Conclusion import Conclusion, ConfidenceLevel
|
|
6
6
|
from .EvidenceReference import EvidenceReference
|
7
7
|
from .Identifier import Identifier, IdentifierList
|
8
8
|
from .Note import Note
|
9
|
-
|
9
|
+
|
10
10
|
from .SourceReference import SourceReference
|
11
11
|
from .Resource import Resource
|
12
12
|
from .Extensions.rs10.rsLink import _rsLinkList
|
@@ -45,31 +45,15 @@ class Subject(Conclusion):
|
|
45
45
|
|
46
46
|
@property
|
47
47
|
def _as_dict_(self):
|
48
|
-
|
49
|
-
if isinstance(value, (str, int, float, bool, type(None))):
|
50
|
-
return value
|
51
|
-
elif isinstance(value, dict):
|
52
|
-
return {k: _serialize(v) for k, v in value.items()}
|
53
|
-
elif isinstance(value, (list, tuple, set)):
|
54
|
-
return [_serialize(v) for v in value]
|
55
|
-
elif hasattr(value, "_as_dict_"):
|
56
|
-
return value._as_dict_
|
57
|
-
else:
|
58
|
-
return str(value) # fallback for unknown objects
|
48
|
+
from .Serialization import Serialization
|
59
49
|
|
60
|
-
|
50
|
+
type_as_dict = super()._as_dict_ # Start with base class fields
|
61
51
|
# Only add Relationship-specific fields
|
62
|
-
|
52
|
+
type_as_dict.update({
|
63
53
|
"extracted": self.extracted,
|
64
54
|
"evidence": [evidence_ref for evidence_ref in self.evidence] if self.evidence else None,
|
65
55
|
"media": [media for media in self.media] if self.media else None,
|
66
56
|
"identifiers": self.identifiers._as_dict_ if self.identifiers else None
|
67
57
|
|
68
58
|
})
|
69
|
-
|
70
|
-
# Serialize and exclude None values
|
71
|
-
for key, value in subject_fields.items():
|
72
|
-
if value is not None:
|
73
|
-
subject_fields[key] = _serialize(value)
|
74
|
-
|
75
|
-
return subject_fields
|
59
|
+
return Serialization.serialize_dict(type_as_dict)
|