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
gedcomx/SourceCitation.py
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
class SourceCitation:
|
4
|
-
identifier = 'http://gedcomx.org/v1/SourceCitation'
|
5
|
-
version = 'http://gedcomx.org/conceptual-model/v1'
|
6
|
-
|
7
|
-
def __init__(self, lang: Optional[str], value: str) -> None:
|
8
|
-
self.lang = lang if lang else 'en'
|
9
|
-
self.value = value
|
10
|
-
|
11
|
-
# ...existing code...
|
12
|
-
|
13
|
-
@classmethod
|
14
|
-
def _from_json_(cls, data: dict):
|
15
|
-
"""
|
16
|
-
Create a SourceCitation instance from a JSON-dict (already parsed).
|
17
|
-
"""
|
18
|
-
lang = data.get('lang', 'en')
|
19
|
-
value = data.get('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}
|
gedcomx/SourceDescription.py
DELETED
@@ -1,314 +0,0 @@
|
|
1
|
-
import warnings
|
2
|
-
|
3
|
-
from enum import Enum
|
4
|
-
from typing import List, Optional, Dict, Any
|
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
|
-
"""
|
30
|
-
from .Agent import Agent
|
31
|
-
from .Attribution import Attribution
|
32
|
-
from .Coverage import Coverage
|
33
|
-
from .Date import Date
|
34
|
-
from .Identifier import Identifier, IdentifierList, make_uid
|
35
|
-
from .Note import Note
|
36
|
-
from .Resource import Resource
|
37
|
-
from .SourceCitation import SourceCitation
|
38
|
-
from .SourceReference import SourceReference
|
39
|
-
from .TextValue import TextValue
|
40
|
-
from .URI import URI
|
41
|
-
#=====================================================================
|
42
|
-
|
43
|
-
class ResourceType(Enum):
|
44
|
-
Collection = "http://gedcomx.org/Collection"
|
45
|
-
PhysicalArtifact = "http://gedcomx.org/PhysicalArtifact"
|
46
|
-
DigitalArtifact = "http://gedcomx.org/DigitalArtifact"
|
47
|
-
Record = "http://gedcomx.org/Record"
|
48
|
-
Person = "http://gedcomx.org/Person"
|
49
|
-
|
50
|
-
@property
|
51
|
-
def description(self):
|
52
|
-
descriptions = {
|
53
|
-
ResourceType.Collection: "A collection of genealogical resources. A collection may contain physical artifacts (such as a collection of books in a library), records (such as the 1940 U.S. Census), or digital artifacts (such as an online genealogical application).",
|
54
|
-
ResourceType.PhysicalArtifact: "A physical artifact, such as a book.",
|
55
|
-
ResourceType.DigitalArtifact: "A digital artifact, such as a digital image of a birth certificate or other record.",
|
56
|
-
ResourceType.Record: "A historical record, such as a census record or a vital record."
|
57
|
-
}
|
58
|
-
return descriptions.get(self, "No description available.")
|
59
|
-
|
60
|
-
class SourceDescription:
|
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.
|
106
|
-
"""
|
107
|
-
|
108
|
-
identifier = "http://gedcomx.org/v1/SourceDescription"
|
109
|
-
version = 'http://gedcomx.org/conceptual-model/v1'
|
110
|
-
|
111
|
-
def __init__(self, id: Optional[str] = None,
|
112
|
-
resourceType: Optional[ResourceType] = None,
|
113
|
-
citations: Optional[List[SourceCitation]] = [],
|
114
|
-
mediaType: Optional[str] = None,
|
115
|
-
about: Optional[URI] = None,
|
116
|
-
mediator: Optional[Agent|Resource] = None,
|
117
|
-
publisher: Optional[Agent|Resource] = None,
|
118
|
-
authors: Optional[List[Resource]] = None,
|
119
|
-
sources: Optional[List[SourceReference]] = None, # SourceReference
|
120
|
-
analysis: Optional["Document|Resource"] = None, #TODO add type checker so its a document
|
121
|
-
componentOf: Optional[SourceReference] = None, # SourceReference
|
122
|
-
titles: Optional[List[TextValue]] = None,
|
123
|
-
notes: Optional[List[Note]] = None,
|
124
|
-
attribution: Optional[Attribution] = None,
|
125
|
-
rights: Optional[List[Resource]] = [],
|
126
|
-
coverage: Optional[List[Coverage]] = None, # Coverage
|
127
|
-
descriptions: Optional[List[TextValue]] = None,
|
128
|
-
identifiers: Optional[IdentifierList] = None,
|
129
|
-
created: Optional[Date] = None,
|
130
|
-
modified: Optional[Date] = None,
|
131
|
-
published: Optional[Date] = None,
|
132
|
-
repository: Optional[Agent] = None,
|
133
|
-
max_note_count: int = 20):
|
134
|
-
|
135
|
-
self.id = id if id else make_uid()
|
136
|
-
self.resourceType = resourceType
|
137
|
-
self.citations = citations or []
|
138
|
-
self.mediaType = mediaType
|
139
|
-
self.about = about
|
140
|
-
self.mediator = mediator
|
141
|
-
self._publisher = publisher
|
142
|
-
self.authors = authors or []
|
143
|
-
self.sources = sources or []
|
144
|
-
self.analysis = analysis
|
145
|
-
self.componentOf = componentOf
|
146
|
-
self.titles = titles or []
|
147
|
-
self.notes = notes or []
|
148
|
-
self.attribution = attribution
|
149
|
-
self.rights = rights or []
|
150
|
-
self.coverage = coverage or []
|
151
|
-
self.descriptions = descriptions or []
|
152
|
-
self.identifiers = identifiers or IdentifierList()
|
153
|
-
self.created = created
|
154
|
-
self.modified = modified
|
155
|
-
self.published = published
|
156
|
-
self.repository = repository
|
157
|
-
self.max_note_count = max_note_count
|
158
|
-
|
159
|
-
self.uri = URI(fragment=id) if id else None #TODO Should i take care of this in the collections?
|
160
|
-
|
161
|
-
@property
|
162
|
-
def publisher(self) -> Resource | Agent | None:
|
163
|
-
return self._publisher
|
164
|
-
|
165
|
-
|
166
|
-
@publisher.setter
|
167
|
-
def publisher(self, value: Resource | Agent):
|
168
|
-
if value is None:
|
169
|
-
self._publisher = None
|
170
|
-
elif isinstance(value,Resource):
|
171
|
-
self._publisher = value
|
172
|
-
elif isinstance(value,Agent):
|
173
|
-
self._publisher = value
|
174
|
-
else:
|
175
|
-
raise ValueError(f"'publisher' must be of type 'URI' or 'Agent', type: {type(value)} was provided")
|
176
|
-
|
177
|
-
def add_description(self, desccription_to_add: TextValue):
|
178
|
-
if desccription_to_add and isinstance(desccription_to_add,TextValue):
|
179
|
-
for current_description in self.descriptions:
|
180
|
-
if desccription_to_add == current_description:
|
181
|
-
return
|
182
|
-
self.descriptions.append(desccription_to_add)
|
183
|
-
|
184
|
-
def add_identifier(self, identifier_to_add: Identifier):
|
185
|
-
if identifier_to_add and isinstance(identifier_to_add,Identifier):
|
186
|
-
self.identifiers.append(identifier_to_add)
|
187
|
-
|
188
|
-
def add_note(self,note_to_add: Note):
|
189
|
-
if len(self.notes) >= self.max_note_count:
|
190
|
-
warnings.warn(f"Max not count of {self.max_note_count} reached for id: {self.id}")
|
191
|
-
return False
|
192
|
-
if note_to_add and isinstance(note_to_add,Note):
|
193
|
-
for existing in self.notes:
|
194
|
-
if note_to_add == existing:
|
195
|
-
return False
|
196
|
-
self.notes.append(note_to_add)
|
197
|
-
|
198
|
-
def add_source(self, source_to_add: SourceReference):
|
199
|
-
if source_to_add and isinstance(object,SourceReference):
|
200
|
-
for current_source in self.sources:
|
201
|
-
if current_source == source_to_add:
|
202
|
-
return
|
203
|
-
self.sources.append(source_to_add)
|
204
|
-
|
205
|
-
def add_title(self, title_to_add: TextValue):
|
206
|
-
if isinstance(title_to_add,str): title_to_add = TextValue(value=title_to_add)
|
207
|
-
if title_to_add and isinstance(title_to_add, TextValue):
|
208
|
-
for current_title in self.titles:
|
209
|
-
if title_to_add == current_title:
|
210
|
-
return False
|
211
|
-
self.titles.append(title_to_add)
|
212
|
-
else:
|
213
|
-
raise ValueError(f"Cannot add title of type {type(title_to_add)}")
|
214
|
-
|
215
|
-
@property
|
216
|
-
def _as_dict_(self) -> Dict[str, Any]:
|
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
|
-
|
267
|
-
|
268
|
-
return Serialization.serialize_dict(type_as_dict)
|
269
|
-
|
270
|
-
|
271
|
-
@classmethod
|
272
|
-
def _from_json_(cls, data: Dict[str, Any]) -> 'SourceDescription':
|
273
|
-
# TODO Hande Resource/URI
|
274
|
-
|
275
|
-
# Basic fields
|
276
|
-
id_ = data.get('id')
|
277
|
-
rt = ResourceType(data['resourceType']) if data.get('resourceType') else None
|
278
|
-
|
279
|
-
# Sub-objects
|
280
|
-
citations = [SourceCitation._from_json_(c) for c in data.get('citations', [])]
|
281
|
-
about = URI._from_json_(data['about']) if data.get('about') else None
|
282
|
-
mediator = Resource._from_json_(data['mediator']) if data.get('mediator') else None
|
283
|
-
publisher = Resource._from_json_(data['publisher']) if data.get('publisher') else None
|
284
|
-
authors = [Resource._from_json_(a) for a in data.get('authors', [])] if data.get('authors') else None
|
285
|
-
sources = [SourceReference._from_json_(s) for s in data.get('sources', [])]
|
286
|
-
analysis = Resource._from_json_(data['analysis']) if data.get('analysis') else None
|
287
|
-
component_of = SourceReference._from_json_(data['componentOf']) if data.get('componentOf') else None
|
288
|
-
titles = [TextValue._from_json_(t) for t in data.get('titles', [])]
|
289
|
-
notes = [Note._from_json_(n) for n in data.get('notes', [])]
|
290
|
-
attribution = Attribution._from_json_(data['attribution']) if data.get('attribution') else None
|
291
|
-
rights = [URI._from_json_(r) for r in data.get('rights', [])]
|
292
|
-
coverage = [Coverage._from_json_(cvg) for cvg in data.get('coverage',[])]
|
293
|
-
descriptions = [TextValue._from_json_(d) for d in data.get('descriptions', [])]
|
294
|
-
identifiers = IdentifierList._from_json_(data.get('identifiers', []))
|
295
|
-
|
296
|
-
created = Date._from_json_(data['created']) if data.get('created') else None
|
297
|
-
modified = data.get('modified',None)
|
298
|
-
published = Date._from_json_(data['published']) if data.get('published') else None
|
299
|
-
repository = Agent._from_json_(data['repository']) if data.get('repository') else None
|
300
|
-
|
301
|
-
return cls(
|
302
|
-
id=id_, resourceType=rt, citations=citations,
|
303
|
-
mediaType=data.get('mediaType'), about=about,
|
304
|
-
mediator=mediator, publisher=publisher,
|
305
|
-
authors=authors, sources=sources,
|
306
|
-
analysis=analysis, componentOf=component_of,
|
307
|
-
titles=titles, notes=notes, attribution=attribution,
|
308
|
-
rights=rights, coverage=coverage,
|
309
|
-
descriptions=descriptions, identifiers=identifiers,
|
310
|
-
created=created, modified=modified,
|
311
|
-
published=published, repository=repository
|
312
|
-
)
|
313
|
-
|
314
|
-
|
gedcomx/Subject.py
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
import warnings
|
2
|
-
from typing import List, Optional
|
3
|
-
|
4
|
-
from .Attribution import Attribution
|
5
|
-
from .Conclusion import Conclusion, ConfidenceLevel
|
6
|
-
from .EvidenceReference import EvidenceReference
|
7
|
-
from .Identifier import Identifier, IdentifierList
|
8
|
-
from .Note import Note
|
9
|
-
|
10
|
-
from .SourceReference import SourceReference
|
11
|
-
from .Resource import Resource
|
12
|
-
from .Extensions.rs10.rsLink import _rsLinkList
|
13
|
-
|
14
|
-
class Subject(Conclusion):
|
15
|
-
identifier = 'http://gedcomx.org/v1/Subject'
|
16
|
-
version = 'http://gedcomx.org/conceptual-model/v1'
|
17
|
-
|
18
|
-
def __init__(self,
|
19
|
-
id: Optional[str],
|
20
|
-
lang: Optional[str] = 'en',
|
21
|
-
sources: Optional[List[SourceReference]] = [],
|
22
|
-
analysis: Optional[Resource] = None,
|
23
|
-
notes: Optional[List[Note]] = [],
|
24
|
-
confidence: Optional[ConfidenceLevel] = None,
|
25
|
-
attribution: Optional[Attribution] = None,
|
26
|
-
extracted: Optional[bool] = None,
|
27
|
-
evidence: Optional[List[EvidenceReference]] = [],
|
28
|
-
media: Optional[List[SourceReference]] = [],
|
29
|
-
identifiers: Optional[IdentifierList] = None,
|
30
|
-
uri: Optional[Resource] = None,
|
31
|
-
links: Optional[_rsLinkList] = None) -> None:
|
32
|
-
super().__init__(id, lang, sources, analysis, notes, confidence, attribution,links=links)
|
33
|
-
self.extracted = extracted
|
34
|
-
self.evidence = evidence
|
35
|
-
self.media = media
|
36
|
-
self.identifiers = identifiers if identifiers else IdentifierList()
|
37
|
-
self.uri = uri
|
38
|
-
|
39
|
-
def add_identifier(self, identifier_to_add: Identifier):
|
40
|
-
if identifier_to_add and isinstance(identifier_to_add,Identifier):
|
41
|
-
for current_identifier in self.identifiers:
|
42
|
-
if identifier_to_add == current_identifier:
|
43
|
-
return
|
44
|
-
self.identifiers.append(identifier_to_add)
|
45
|
-
|
46
|
-
@property
|
47
|
-
def _as_dict_(self):
|
48
|
-
from .Serialization import Serialization
|
49
|
-
|
50
|
-
type_as_dict = super()._as_dict_ # Start with base class fields
|
51
|
-
# Only add Relationship-specific fields
|
52
|
-
type_as_dict.update({
|
53
|
-
"extracted": self.extracted,
|
54
|
-
"evidence": [evidence_ref for evidence_ref in self.evidence] if self.evidence else None,
|
55
|
-
"media": [media for media in self.media] if self.media else None,
|
56
|
-
"identifiers": self.identifiers._as_dict_ if self.identifiers else None
|
57
|
-
|
58
|
-
})
|
59
|
-
return Serialization.serialize_dict(type_as_dict)
|
gedcomx/TextValue.py
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
class TextValue:
|
4
|
-
identifier = 'http://gedcomx.org/v1/TextValue'
|
5
|
-
version = 'http://gedcomx.org/conceptual-model/v1'
|
6
|
-
|
7
|
-
def __init__(self, value: Optional[str] = None, lang: Optional[str] = 'en') -> None:
|
8
|
-
self.lang = lang
|
9
|
-
self.value = value
|
10
|
-
|
11
|
-
def _append_to_value(self, value_to_append):
|
12
|
-
if not isinstance(value_to_append, str):
|
13
|
-
raise ValueError(f"Cannot append object of type {type(value_to_append)}.")
|
14
|
-
self.value += ' ' + value_to_append
|
15
|
-
|
16
|
-
@property
|
17
|
-
def _as_dict_(self):
|
18
|
-
return {
|
19
|
-
"lang":self.lang if self.lang else None,
|
20
|
-
"value":self.value if self.value else None
|
21
|
-
}
|
22
|
-
|
23
|
-
def __str__(self):
|
24
|
-
return f"{self.value} ({self.lang})"
|
25
|
-
|
26
|
-
# ...existing code...
|
27
|
-
|
28
|
-
@classmethod
|
29
|
-
def _from_json_(cls, data: dict):
|
30
|
-
"""
|
31
|
-
Create a TextValue instance from a JSON-dict (already parsed).
|
32
|
-
"""
|
33
|
-
value = data.get('value')
|
34
|
-
lang = data.get('lang', 'en')
|
35
|
-
return cls(value=value, lang=lang)
|
gedcomx/URI.py
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from dataclasses import dataclass, field
|
3
|
-
from typing import Mapping, Optional, Union, Iterable, Tuple
|
4
|
-
from urllib.parse import urlsplit, urlunsplit, urlencode, parse_qsl, SplitResult
|
5
|
-
|
6
|
-
_DEFAULT_SCHEME = "gedcomx"
|
7
|
-
|
8
|
-
@dataclass(frozen=False, slots=True)
|
9
|
-
class URI:
|
10
|
-
scheme: str = field(default=_DEFAULT_SCHEME)
|
11
|
-
authority: str = field(default="")
|
12
|
-
path: str = field(default="")
|
13
|
-
query: str = field(default="")
|
14
|
-
fragment: str = field(default="")
|
15
|
-
|
16
|
-
# ---------- constructors ----------
|
17
|
-
@classmethod
|
18
|
-
def from_url(cls, url: str, *, default_scheme: str = _DEFAULT_SCHEME) -> "URI":
|
19
|
-
s = urlsplit(url)
|
20
|
-
scheme = s.scheme or default_scheme
|
21
|
-
return cls(scheme=scheme, authority=s.netloc, path=s.path, query=s.query, fragment=s.fragment)
|
22
|
-
|
23
|
-
@classmethod
|
24
|
-
def parse(cls, value: str) -> "URI":
|
25
|
-
return cls.from_url(value)
|
26
|
-
|
27
|
-
@classmethod
|
28
|
-
def from_parts(
|
29
|
-
cls,
|
30
|
-
*,
|
31
|
-
scheme: Optional[str] = None,
|
32
|
-
authority: str = "",
|
33
|
-
path: str = "",
|
34
|
-
query: Union[str, Mapping[str, str], Iterable[Tuple[str, str]]] = "",
|
35
|
-
fragment: str = ""
|
36
|
-
) -> "URI":
|
37
|
-
q = query if isinstance(query, str) else urlencode(query, doseq=True)
|
38
|
-
return cls(scheme=scheme or _DEFAULT_SCHEME, authority=authority, path=path, query=q, fragment=fragment)
|
39
|
-
|
40
|
-
# ---------- views ----------
|
41
|
-
@property
|
42
|
-
def value(self) -> str:
|
43
|
-
return str(self)
|
44
|
-
|
45
|
-
def split(self) -> SplitResult:
|
46
|
-
return SplitResult(self.scheme, self.authority, self.path, self.query, self.fragment)
|
47
|
-
|
48
|
-
def __str__(self) -> str:
|
49
|
-
return urlunsplit(self.split())
|
50
|
-
|
51
|
-
@property
|
52
|
-
def _as_dict_(self) -> str:
|
53
|
-
# Keeps a simple, explicit structure
|
54
|
-
return urlunsplit(self.split())
|
55
|
-
return {
|
56
|
-
"scheme": self.scheme,
|
57
|
-
"authority": self.authority,
|
58
|
-
"path": self.path,
|
59
|
-
"query": self.query,
|
60
|
-
"fragment": self.fragment,
|
61
|
-
"value": str(self),
|
62
|
-
}
|
63
|
-
|
64
|
-
# Accepts {'resource': '...'} or a plain string, mirroring your original
|
65
|
-
@classmethod
|
66
|
-
def from_jsonish(cls, data: Union[str, Mapping[str, str]]) -> "URI":
|
67
|
-
if isinstance(data, str):
|
68
|
-
return cls.from_url(data)
|
69
|
-
if isinstance(data, Mapping):
|
70
|
-
raw = data.get("resource") or data.get("value") or ""
|
71
|
-
if raw:
|
72
|
-
return cls.from_url(raw)
|
73
|
-
raise ValueError(f"Cannot build URI from: {data!r}")
|
74
|
-
|
75
|
-
# ---------- functional updaters ----------
|
76
|
-
def with_scheme(self, scheme: str) -> "URI": return self.replace(scheme=scheme)
|
77
|
-
def with_authority(self, authority: str) -> "URI": return self.replace(authority=authority)
|
78
|
-
def with_path(self, path: str, *, join: bool = False) -> "URI":
|
79
|
-
new_path = (self.path.rstrip("/") + "/" + path.lstrip("/")) if join else path
|
80
|
-
return self.replace(path=new_path)
|
81
|
-
def with_fragment(self, fragment: str | None) -> "URI":
|
82
|
-
return self.replace(fragment=(fragment or ""))
|
83
|
-
def without_fragment(self) -> "URI": return self.replace(fragment="")
|
84
|
-
def with_query(self, query: Union[str, Mapping[str, str], Iterable[Tuple[str, str]]]) -> "URI":
|
85
|
-
q = query if isinstance(query, str) else urlencode(query, doseq=True)
|
86
|
-
return self.replace(query=q)
|
87
|
-
def add_query_params(self, params: Mapping[str, Union[str, Iterable[str]]]) -> "URI":
|
88
|
-
existing = parse_qsl(self.query, keep_blank_values=True)
|
89
|
-
for k, v in params.items():
|
90
|
-
if isinstance(v, str):
|
91
|
-
existing.append((k, v))
|
92
|
-
else:
|
93
|
-
for vv in v:
|
94
|
-
existing.append((k, vv))
|
95
|
-
return self.replace(query=urlencode(existing, doseq=True))
|
96
|
-
|
97
|
-
# ---------- helpers ----------
|
98
|
-
def replace(self, **kwargs) -> "URI":
|
99
|
-
return URI(
|
100
|
-
scheme=kwargs.get("scheme", self.scheme or _DEFAULT_SCHEME),
|
101
|
-
authority=kwargs.get("authority", self.authority),
|
102
|
-
path=kwargs.get("path", self.path),
|
103
|
-
query=kwargs.get("query", self.query),
|
104
|
-
fragment=kwargs.get("fragment", self.fragment),
|
105
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|