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,15 +1,16 @@
|
|
1
|
-
from typing import List, Optional
|
1
|
+
from typing import Any, Dict, List, Optional
|
2
2
|
|
3
3
|
"""
|
4
4
|
======================================================================
|
5
5
|
Project: Gedcom-X
|
6
|
-
File:
|
6
|
+
File: place_description.py
|
7
7
|
Author: David J. Cartwright
|
8
|
-
Purpose:
|
8
|
+
Purpose:
|
9
9
|
|
10
10
|
Created: 2025-08-25
|
11
11
|
Updated:
|
12
|
-
- 2025-
|
12
|
+
- 2025-09-01: filename PEP8 standard
|
13
|
+
- 2025-09-03: _from_json_ refactored
|
13
14
|
|
14
15
|
======================================================================
|
15
16
|
"""
|
@@ -19,17 +20,28 @@ from typing import List, Optional
|
|
19
20
|
GEDCOM Module Types
|
20
21
|
======================================================================
|
21
22
|
"""
|
22
|
-
from .
|
23
|
-
from .
|
24
|
-
from .
|
25
|
-
from .
|
26
|
-
from .
|
27
|
-
from .
|
28
|
-
from .
|
29
|
-
from .
|
30
|
-
from .
|
31
|
-
from .
|
32
|
-
from .
|
23
|
+
from .attribution import Attribution
|
24
|
+
from .conclusion import ConfidenceLevel
|
25
|
+
from .date import Date
|
26
|
+
from .evidence_reference import EvidenceReference
|
27
|
+
from .Extensions.rs10.rsLink import _rsLinks #new
|
28
|
+
from .identifier import IdentifierList
|
29
|
+
from .note import Note
|
30
|
+
from .resource import Resource
|
31
|
+
from .source_reference import SourceReference
|
32
|
+
from .subject import Subject
|
33
|
+
from .textvalue import TextValue
|
34
|
+
from .uri import URI
|
35
|
+
from .logging_hub import hub, logging
|
36
|
+
"""
|
37
|
+
======================================================================
|
38
|
+
Logging
|
39
|
+
======================================================================
|
40
|
+
"""
|
41
|
+
log = logging.getLogger("gedcomx")
|
42
|
+
serial_log = "gedcomx.serialization"
|
43
|
+
#=====================================================================
|
44
|
+
|
33
45
|
|
34
46
|
class PlaceDescription(Subject):
|
35
47
|
"""PlaceDescription describes the details of a place in terms of
|
@@ -76,9 +88,11 @@ class PlaceDescription(Subject):
|
|
76
88
|
latitude: Optional[float] = None,
|
77
89
|
longitude: Optional[float] = None,
|
78
90
|
temporalDescription: Optional[Date] = None,
|
79
|
-
spatialDescription: Optional[Resource] = None,
|
91
|
+
spatialDescription: Optional[Resource] = None,
|
92
|
+
links: Optional[_rsLinks] = None
|
93
|
+
) -> None:
|
80
94
|
|
81
|
-
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
95
|
+
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links)
|
82
96
|
self.names = names
|
83
97
|
self.type = type
|
84
98
|
self.place = place
|
@@ -90,8 +104,8 @@ class PlaceDescription(Subject):
|
|
90
104
|
|
91
105
|
@property
|
92
106
|
def _as_dict_(self):
|
93
|
-
from .
|
94
|
-
type_as_dict = super()._as_dict_
|
107
|
+
from .serialization import Serialization
|
108
|
+
type_as_dict = super()._as_dict_ or {}
|
95
109
|
|
96
110
|
if self.names:
|
97
111
|
type_as_dict["names"] = [n._as_dict_ for n in self.names if n]
|
@@ -110,12 +124,46 @@ class PlaceDescription(Subject):
|
|
110
124
|
if self.spatialDescription:
|
111
125
|
type_as_dict["spatialDescription"] = self.spatialDescription._as_dict_
|
112
126
|
|
127
|
+
return type_as_dict if type_as_dict != {} else None
|
113
128
|
return Serialization.serialize_dict(type_as_dict)
|
114
129
|
|
115
130
|
@classmethod
|
116
|
-
def _from_json_(cls, data:
|
131
|
+
def _from_json_(cls, data: Any, context: Any = None) -> "PlaceDescription":
|
117
132
|
"""
|
118
133
|
Create a PlaceDescription instance from a JSON-dict (already parsed).
|
119
|
-
"""
|
120
|
-
|
121
|
-
|
134
|
+
"""
|
135
|
+
if not isinstance(data, dict):
|
136
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
|
137
|
+
|
138
|
+
person_data: Dict[str, Any] = Subject._dict_from_json_(data,context)
|
139
|
+
|
140
|
+
# names (allow both list and a single 'name' alias)
|
141
|
+
if (names := data.get("names")) is not None:
|
142
|
+
person_data["names"] = [TextValue._from_json_(n, context) for n in names]
|
143
|
+
|
144
|
+
# type (string for now; promote to enum later)
|
145
|
+
if (typ := data.get("type")) is not None:
|
146
|
+
person_data["type"] = typ
|
147
|
+
|
148
|
+
# place: URI (accept string or dict)
|
149
|
+
if (pl := data.get("place")) is not None:
|
150
|
+
person_data["place"] = URI(pl)
|
151
|
+
|
152
|
+
# jurisdiction: Resource | PlaceDescription
|
153
|
+
if (jur := data.get("jurisdiction")) is not None:
|
154
|
+
person_data["jurisdiction"] = Resource._from_json_(jur, context)
|
155
|
+
|
156
|
+
# coordinates
|
157
|
+
if (lat := data.get("latitude")) is not None:
|
158
|
+
person_data["latitude"] = float(lat)
|
159
|
+
if (lon := data.get("longitude")) is not None:
|
160
|
+
person_data["longitude"] = float(lon)
|
161
|
+
|
162
|
+
# temporal / spatial descriptions
|
163
|
+
if (td := data.get("temporalDescription")) is not None:
|
164
|
+
person_data["temporalDescription"] = Date._from_json_(td, context)
|
165
|
+
|
166
|
+
if (sd := data.get("spatialDescription")) is not None:
|
167
|
+
person_data["spatialDescription"] = Resource._from_json_(sd, context)
|
168
|
+
|
169
|
+
return cls(**person_data)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Optional, TYPE_CHECKING
|
3
3
|
if TYPE_CHECKING:
|
4
|
-
from .
|
4
|
+
from .place_description import PlaceDescription
|
5
5
|
|
6
6
|
"""
|
7
7
|
======================================================================
|
@@ -13,6 +13,7 @@ if TYPE_CHECKING:
|
|
13
13
|
Created: 2025-08-25
|
14
14
|
Updated:
|
15
15
|
- 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
|
16
|
+
- 2025-09-03: _from_json refactored
|
16
17
|
|
17
18
|
======================================================================
|
18
19
|
"""
|
@@ -22,7 +23,17 @@ if TYPE_CHECKING:
|
|
22
23
|
GEDCOM Module Types
|
23
24
|
======================================================================
|
24
25
|
"""
|
25
|
-
from .
|
26
|
+
from .resource import Resource
|
27
|
+
from .logging_hub import hub, logging
|
28
|
+
from .uri import URI
|
29
|
+
"""
|
30
|
+
======================================================================
|
31
|
+
Logging
|
32
|
+
======================================================================
|
33
|
+
"""
|
34
|
+
log = logging.getLogger("gedcomx")
|
35
|
+
serial_log = "gedcomx.serialization"
|
36
|
+
#=====================================================================
|
26
37
|
|
27
38
|
class PlaceReference:
|
28
39
|
"""defines a reference to a PlaceDescription.
|
@@ -39,24 +50,35 @@ class PlaceReference:
|
|
39
50
|
|
40
51
|
def __init__(self,
|
41
52
|
original: Optional[str] = None,
|
42
|
-
description: Optional["
|
53
|
+
description: Optional["URI | PlaceDescription"] = None) -> None:
|
43
54
|
self.original = original
|
44
|
-
self.description = description
|
55
|
+
self.description = description # descriptionRef
|
45
56
|
|
46
57
|
@property
|
47
58
|
def _as_dict_(self):
|
48
|
-
|
59
|
+
|
49
60
|
type_as_dict = {}
|
50
61
|
if self.original:
|
51
62
|
type_as_dict['original'] = self.original
|
52
63
|
if self.description:
|
53
|
-
type_as_dict['description'] = self.description._as_dict_
|
54
|
-
return
|
64
|
+
type_as_dict['description'] = URI(target=self.description)._as_dict_
|
65
|
+
return type_as_dict if type_as_dict != {} else None
|
66
|
+
|
55
67
|
|
56
68
|
@classmethod
|
57
|
-
def _from_json_(cls, data):
|
58
|
-
|
59
|
-
|
69
|
+
def _from_json_(cls, data, context=None) -> "PlaceReference":
|
70
|
+
if not isinstance(data, dict):
|
71
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
|
72
|
+
|
73
|
+
place_reference_data = {}
|
74
|
+
|
75
|
+
# Scalars
|
76
|
+
if (orig := data.get("original")) is not None:
|
77
|
+
place_reference_data["original"] = orig
|
78
|
+
if (desc := data.get("description")) is not None:
|
79
|
+
place_reference_data["description"] = URI._from_json_(desc, context)
|
80
|
+
|
81
|
+
return cls(**place_reference_data)
|
60
82
|
|
61
83
|
|
62
84
|
|
@@ -11,9 +11,19 @@ from typing import Optional
|
|
11
11
|
Updated:
|
12
12
|
- 2025-08-31: _as_dict_ to only create entries in dict for fields that
|
13
13
|
hold data, updated _from_json
|
14
|
+
- 2025-09-03: _from_json_ refactor
|
14
15
|
|
15
16
|
======================================================================
|
16
17
|
"""
|
18
|
+
from .logging_hub import hub, logging
|
19
|
+
"""
|
20
|
+
======================================================================
|
21
|
+
Logging
|
22
|
+
======================================================================
|
23
|
+
"""
|
24
|
+
log = logging.getLogger("gedcomx")
|
25
|
+
serial_log = "gedcomx.serialization"
|
26
|
+
#=====================================================================
|
17
27
|
|
18
28
|
class Qualifier:
|
19
29
|
"""defines the data structure used to supply additional details, annotations,
|
@@ -37,7 +47,7 @@ class Qualifier:
|
|
37
47
|
|
38
48
|
@property
|
39
49
|
def __as_dict__(self):
|
40
|
-
from .
|
50
|
+
from .serialization import Serialization
|
41
51
|
|
42
52
|
type_as_dict = {}
|
43
53
|
if self.name:
|
@@ -45,10 +55,16 @@ class Qualifier:
|
|
45
55
|
if self.value:
|
46
56
|
type_as_dict["value"] = self.value
|
47
57
|
|
58
|
+
return type_as_dict if type_as_dict != {} else None
|
48
59
|
return Serialization.serialize_dict(type_as_dict)
|
49
60
|
|
50
61
|
@classmethod
|
51
|
-
def
|
52
|
-
|
53
|
-
|
62
|
+
def _from_json_(cls, data: dict, context=None) -> "Qualifier":
|
63
|
+
if not isinstance(data, dict):
|
64
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
|
65
|
+
|
66
|
+
name = data.get("name")
|
67
|
+
value = data.get("value")
|
68
|
+
|
69
|
+
return cls(name=name, value=value)
|
54
70
|
|
gedcomx/relationship.py
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from typing import Any, Dict, Optional, List
|
3
|
+
"""
|
4
|
+
======================================================================
|
5
|
+
Project: Gedcom-X
|
6
|
+
File: relationship.py
|
7
|
+
Author: David J. Cartwright
|
8
|
+
Purpose:
|
9
|
+
|
10
|
+
Created: 2025-08-25
|
11
|
+
Updated:
|
12
|
+
- 2025-09-31: filename PEP8 standard
|
13
|
+
- 2025-09-03: _from_json_ refactor
|
14
|
+
|
15
|
+
======================================================================
|
16
|
+
"""
|
17
|
+
|
18
|
+
"""
|
19
|
+
======================================================================
|
20
|
+
GEDCOM Module Types
|
21
|
+
======================================================================
|
22
|
+
"""
|
23
|
+
from .attribution import Attribution
|
24
|
+
from .conclusion import ConfidenceLevel
|
25
|
+
from .evidence_reference import EvidenceReference
|
26
|
+
from .Extensions.rs10.rsLink import _rsLinks #new
|
27
|
+
from .fact import Fact
|
28
|
+
from .identifier import Identifier, make_uid
|
29
|
+
from .note import Note
|
30
|
+
from .person import Person
|
31
|
+
from .resource import Resource
|
32
|
+
from .source_reference import SourceReference
|
33
|
+
from .subject import Subject
|
34
|
+
from .logging_hub import hub, logging
|
35
|
+
"""
|
36
|
+
======================================================================
|
37
|
+
Logging
|
38
|
+
======================================================================
|
39
|
+
"""
|
40
|
+
log = logging.getLogger("gedcomx")
|
41
|
+
serial_log = "gedcomx.serialization"
|
42
|
+
#=====================================================================
|
43
|
+
|
44
|
+
|
45
|
+
class RelationshipType(Enum):
|
46
|
+
Couple = "http://gedcomx.org/Couple"
|
47
|
+
ParentChild = "http://gedcomx.org/ParentChild"
|
48
|
+
|
49
|
+
@property
|
50
|
+
def description(self):
|
51
|
+
descriptions = {
|
52
|
+
RelationshipType.Couple: "A relationship of a pair of persons.",
|
53
|
+
RelationshipType.ParentChild: "A relationship from a parent to a child."
|
54
|
+
}
|
55
|
+
return descriptions.get(self, "No description available.")
|
56
|
+
|
57
|
+
class Relationship(Subject):
|
58
|
+
"""Represents a relationship between two Person(s)
|
59
|
+
|
60
|
+
Args:
|
61
|
+
type (RelationshipType): Type of relationship
|
62
|
+
person1 (Person) = First Person in Relationship
|
63
|
+
person2 (Person): Second Person in Relationship
|
64
|
+
|
65
|
+
Raises:
|
66
|
+
|
67
|
+
"""
|
68
|
+
identifier = 'http://gedcomx.org/v1/Relationship'
|
69
|
+
version = 'http://gedcomx.org/conceptual-model/v1'
|
70
|
+
|
71
|
+
def __init__(self,
|
72
|
+
person1: Optional[Person | Resource] = None,
|
73
|
+
person2: Optional[Person | Resource] = None,
|
74
|
+
facts: Optional[List[Fact]] = None,
|
75
|
+
id: Optional[str] = None,
|
76
|
+
lang: Optional[str] = None,
|
77
|
+
sources: Optional[List[SourceReference]] = None,
|
78
|
+
analysis: Optional[Resource] = None,
|
79
|
+
notes: Optional[List[Note]] = None,
|
80
|
+
confidence: Optional[ConfidenceLevel] = None,
|
81
|
+
attribution: Optional[Attribution] = None,
|
82
|
+
extracted: Optional[bool] = None,
|
83
|
+
evidence: Optional[List[EvidenceReference]] = None,
|
84
|
+
media: Optional[List[SourceReference]] = None,
|
85
|
+
identifiers: Optional[List[Identifier]] = None,
|
86
|
+
type: Optional[RelationshipType] = None,
|
87
|
+
links: Optional[_rsLinks] = None,
|
88
|
+
) -> None:
|
89
|
+
|
90
|
+
# Call superclass initializer if required
|
91
|
+
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers,links=links)
|
92
|
+
|
93
|
+
self.id = id if id else make_uid()
|
94
|
+
self.type = type
|
95
|
+
self.person1 = person1
|
96
|
+
self.person2 = person2
|
97
|
+
self.facts = facts if facts else []
|
98
|
+
|
99
|
+
def add_fact(self,fact: Fact):
|
100
|
+
if (fact is not None) and isinstance(fact,Fact):
|
101
|
+
for existing_fact in self.facts:
|
102
|
+
if fact == existing_fact:
|
103
|
+
return
|
104
|
+
self.facts.append(fact)
|
105
|
+
else:
|
106
|
+
raise TypeError(f"Expected type 'Fact' recieved type {type(fact)}")
|
107
|
+
|
108
|
+
@property
|
109
|
+
def _as_dict_(self):
|
110
|
+
from .serialization import Serialization
|
111
|
+
return Serialization.serialize(self)
|
112
|
+
|
113
|
+
type_as_dict = (super()._as_dict_ or {}).copy()
|
114
|
+
|
115
|
+
extras = {
|
116
|
+
"type": getattr(self.type, "value", None),
|
117
|
+
"person1": Resource(target=self.person1)._as_dict_ if self.person1 else None,
|
118
|
+
"person2": Resource(target=self.person2)._as_dict_ if self.person2 else None,
|
119
|
+
"facts": [f._as_dict_ for f in self.facts if f] if getattr(self, "facts", None) else None,
|
120
|
+
}
|
121
|
+
|
122
|
+
# only keep non-empty values
|
123
|
+
type_as_dict.update({k: v for k, v in extras.items() if v not in (None, [], {}, ())})
|
124
|
+
|
125
|
+
return type_as_dict or None
|
126
|
+
|
127
|
+
@classmethod
|
128
|
+
def _from_json_(cls, data: Dict[str, Any], context: Any = None) -> "Relationship":
|
129
|
+
"""
|
130
|
+
Create a Person instance from a JSON-dict (already parsed).
|
131
|
+
"""
|
132
|
+
if not isinstance(data, dict):
|
133
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)}")
|
134
|
+
|
135
|
+
relationship_data: Dict[str, Any] = {}
|
136
|
+
relationship_data = Subject._dict_from_json_(data,context)
|
137
|
+
|
138
|
+
if (id_ := data.get("id")) is not None:
|
139
|
+
relationship_data["id"] = id_
|
140
|
+
|
141
|
+
if (type_ := data.get("type")) is not None:
|
142
|
+
relationship_data["type"] = RelationshipType(type_)
|
143
|
+
|
144
|
+
# person1 / person2
|
145
|
+
if (p1 := data.get("person1")) is not None:
|
146
|
+
relationship_data["person1"] = Resource._from_json_(p1,context)
|
147
|
+
|
148
|
+
if (p2 := data.get("person2")) is not None:
|
149
|
+
relationship_data["person2"] = Resource._from_json_(p2,context)
|
150
|
+
|
151
|
+
# facts
|
152
|
+
if (facts := data.get("facts")) is not None:
|
153
|
+
relationship_data["facts"] = [Fact._from_json_(f, context) for f in facts]
|
154
|
+
|
155
|
+
return cls(**relationship_data)
|
156
|
+
|
gedcomx/resource.py
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
"""
|
4
|
+
======================================================================
|
5
|
+
Project: Gedcom-X
|
6
|
+
File: Resource.py
|
7
|
+
Author: David J. Cartwright
|
8
|
+
Purpose: References TopLevel Types for Serialization
|
9
|
+
|
10
|
+
Created: 2025-08-25
|
11
|
+
Updated:
|
12
|
+
- 2025-08-31: working on target=Resource and deserialization issues
|
13
|
+
- 2025-09-03: _from_json_ refactor, arguments changed
|
14
|
+
|
15
|
+
======================================================================
|
16
|
+
"""
|
17
|
+
|
18
|
+
"""
|
19
|
+
======================================================================
|
20
|
+
GEDCOM Module Types
|
21
|
+
======================================================================
|
22
|
+
"""
|
23
|
+
|
24
|
+
from .uri import URI
|
25
|
+
from .logging_hub import hub, logging
|
26
|
+
"""
|
27
|
+
======================================================================
|
28
|
+
Logging
|
29
|
+
======================================================================
|
30
|
+
"""
|
31
|
+
log = logging.getLogger("gedcomx")
|
32
|
+
serial_log = "gedcomx.serialization"
|
33
|
+
#=====================================================================
|
34
|
+
|
35
|
+
class Resource:
|
36
|
+
"""
|
37
|
+
Class used to track and resolve URIs and references between datastores.
|
38
|
+
|
39
|
+
Parameters
|
40
|
+
----------
|
41
|
+
|
42
|
+
Raises
|
43
|
+
------
|
44
|
+
ValueError
|
45
|
+
If `id` is not a valid UUID.
|
46
|
+
"""
|
47
|
+
# TODO, Deal with a resouce being passed, as it may be unresolved.
|
48
|
+
def __init__(self,resource: URI | None = None,resourceId: str | None = None, target: object = None) -> None:
|
49
|
+
|
50
|
+
#if (resource is None) and (target is None): #TODO
|
51
|
+
# raise ValueError('Resource object must point to something.')
|
52
|
+
|
53
|
+
self.resource = resource
|
54
|
+
self.resourceId = resourceId
|
55
|
+
self.Id = None
|
56
|
+
|
57
|
+
self.type = None
|
58
|
+
self.resolved = False
|
59
|
+
self.target: object = target
|
60
|
+
self.remote: bool | None = None # is the resource pointed to persitent on a remote datastore?
|
61
|
+
|
62
|
+
if target:
|
63
|
+
if isinstance(target,Resource):
|
64
|
+
self.resource = target.resource
|
65
|
+
self.resourceId = target.resourceId
|
66
|
+
self.target = target.target
|
67
|
+
else:
|
68
|
+
log.debug(f"Target of type: {type(target)}, {target}")
|
69
|
+
if hasattr(target,'uri'):
|
70
|
+
self.resource = target.uri
|
71
|
+
else:
|
72
|
+
self.resourceId = URI(fragment=target.id)
|
73
|
+
self.resourceId = target.id
|
74
|
+
|
75
|
+
@property
|
76
|
+
def uri(self):
|
77
|
+
return self.resource
|
78
|
+
|
79
|
+
@property
|
80
|
+
def _as_dict_(self):
|
81
|
+
from .serialization import Serialization
|
82
|
+
return Serialization.serialize(self)
|
83
|
+
typ_as_dict = {}
|
84
|
+
if self.resource is not None:
|
85
|
+
typ_as_dict["resource"] = self.resource
|
86
|
+
if self.resourceId is not None:
|
87
|
+
typ_as_dict["resourceId"] = self.resourceId,
|
88
|
+
|
89
|
+
return typ_as_dict or None
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
@classmethod
|
94
|
+
def _from_json_(cls, data: dict, context=None) -> "Resource":
|
95
|
+
if not isinstance(data, dict):
|
96
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict, got {type(data)} {data}")
|
97
|
+
resource = {}
|
98
|
+
|
99
|
+
# Scalars
|
100
|
+
if (res := data.get("resource")) is not None:
|
101
|
+
resource["resource"] = res
|
102
|
+
|
103
|
+
return cls(**resource)
|
104
|
+
|
105
|
+
def __repr__(self) -> str:
|
106
|
+
return f"Resource(resource={self.resource}, resourceId={self.resourceId}, target={self.target})"
|
107
|
+
|
108
|
+
def __str__(self) -> str:
|
109
|
+
return f"resource={self.resource}{f', resourceId={self.resourceId}' if self.resourceId else ''}"
|
110
|
+
|
111
|
+
|
112
|
+
|