gedcom-x 0.5.2__tar.gz → 0.5.5__tar.gz
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.2 → gedcom_x-0.5.5}/PKG-INFO +1 -1
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcom_x.egg-info/PKG-INFO +1 -1
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcom_x.egg-info/SOURCES.txt +4 -3
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Address.py +2 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Agent.py +9 -2
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Attribution.py +10 -46
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Conclusion.py +85 -21
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Coverage.py +10 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Date.py +2 -7
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Document.py +27 -6
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Event.py +20 -1
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Exceptions.py +6 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Fact.py +7 -8
- gedcom_x-0.5.5/gedcomx/Gedcom.py +53 -0
- gedcom_x-0.5.2/gedcomx/Gedcom.py → gedcom_x-0.5.5/gedcomx/Gedcom5x.py +226 -87
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/GedcomX.py +37 -22
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Gender.py +6 -40
- gedcom_x-0.5.5/gedcomx/Identifier.py +226 -0
- gedcom_x-0.5.5/gedcomx/Mutations.py +228 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Name.py +6 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Person.py +49 -90
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/PlaceDescription.py +23 -14
- gedcom_x-0.5.5/gedcomx/PlaceReference.py +30 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Relationship.py +23 -54
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Resource.py +17 -3
- gedcom_x-0.5.5/gedcomx/Serialization.py +401 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/SourceDescription.py +6 -9
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/SourceReference.py +20 -86
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Subject.py +4 -4
- gedcom_x-0.5.5/gedcomx/Translation.py +219 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/URI.py +1 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/__init__.py +7 -1
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/pyproject.toml +1 -1
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/setup.py +1 -1
- gedcom_x-0.5.2/gedcomx/Identifier.py +0 -172
- gedcom_x-0.5.2/gedcomx/PlaceReference.py +0 -33
- gedcom_x-0.5.2/gedcomx/Serialization.py +0 -80
- gedcom_x-0.5.2/gedcomx/_Links.py +0 -37
- gedcom_x-0.5.2/gedcomx/g7interop.py +0 -205
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcom_x.egg-info/dependency_links.txt +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcom_x.egg-info/top_level.txt +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/EvidenceReference.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Group.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Logging.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Note.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/OnlineAccount.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Qualifier.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/SourceCitation.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/TextValue.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/TopLevelTypeCollection.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/gedcomx/Zip.py +0 -0
- {gedcom_x-0.5.2 → gedcom_x-0.5.5}/setup.cfg +0 -0
@@ -16,11 +16,13 @@ gedcomx/EvidenceReference.py
|
|
16
16
|
gedcomx/Exceptions.py
|
17
17
|
gedcomx/Fact.py
|
18
18
|
gedcomx/Gedcom.py
|
19
|
+
gedcomx/Gedcom5x.py
|
19
20
|
gedcomx/GedcomX.py
|
20
21
|
gedcomx/Gender.py
|
21
22
|
gedcomx/Group.py
|
22
23
|
gedcomx/Identifier.py
|
23
24
|
gedcomx/Logging.py
|
25
|
+
gedcomx/Mutations.py
|
24
26
|
gedcomx/Name.py
|
25
27
|
gedcomx/Note.py
|
26
28
|
gedcomx/OnlineAccount.py
|
@@ -37,8 +39,7 @@ gedcomx/SourceReference.py
|
|
37
39
|
gedcomx/Subject.py
|
38
40
|
gedcomx/TextValue.py
|
39
41
|
gedcomx/TopLevelTypeCollection.py
|
42
|
+
gedcomx/Translation.py
|
40
43
|
gedcomx/URI.py
|
41
44
|
gedcomx/Zip.py
|
42
|
-
gedcomx/
|
43
|
-
gedcomx/__init__.py
|
44
|
-
gedcomx/g7interop.py
|
45
|
+
gedcomx/__init__.py
|
@@ -139,6 +139,14 @@ class Agent:
|
|
139
139
|
}
|
140
140
|
return Serialization.serialize_dict(type_as_dict)
|
141
141
|
|
142
|
+
@classmethod
|
143
|
+
def _from_json_(cls, data: dict):
|
144
|
+
"""
|
145
|
+
Create a Person instance from a JSON-dict (already parsed).
|
146
|
+
"""
|
147
|
+
type_as_dict = Serialization.get_class_fields('Agent')
|
148
|
+
return Serialization.deserialize(data, type_as_dict)
|
149
|
+
|
142
150
|
def __str__(self):
|
143
151
|
"""
|
144
152
|
Return a human-readable string representation of the Agent.
|
@@ -179,8 +187,7 @@ class Agent:
|
|
179
187
|
self.uri == other.uri
|
180
188
|
)
|
181
189
|
'''
|
182
|
-
|
183
|
-
print(self)
|
190
|
+
|
184
191
|
self_names = {n.value for n in self.names if hasattr(n, "value")}
|
185
192
|
other_names = {n.value for n in other.names if hasattr(n, "value")}
|
186
193
|
if self_names & other_names: # intersection not empty
|
@@ -10,11 +10,11 @@ class Attribution:
|
|
10
10
|
"""Attribution Information for a Genealogy, Conclusion, Subject and child classes
|
11
11
|
|
12
12
|
Args:
|
13
|
-
contributor (Agent): Contributor to object being attributed.
|
14
|
-
modified (timestamp): timestamp for when this record was modified.
|
15
|
-
changeMessage (str): Birth date (YYYY-MM-DD).
|
13
|
+
contributor (Agent, optional): Contributor to object being attributed.
|
14
|
+
modified (timestamp, optional): timestamp for when this record was modified.
|
15
|
+
changeMessage (str, optional): Birth date (YYYY-MM-DD).
|
16
16
|
creator (Agent, optional): Creator of object being attributed.
|
17
|
-
created (timestamp): timestamp for when this record was created
|
17
|
+
created (timestamp, optional): timestamp for when this record was created
|
18
18
|
|
19
19
|
Raises:
|
20
20
|
|
@@ -56,45 +56,9 @@ class Attribution:
|
|
56
56
|
"""
|
57
57
|
# contributor
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
# creator
|
66
|
-
creat = None
|
67
|
-
if 'creator' in data:
|
68
|
-
raw = data['creator']
|
69
|
-
if isinstance(raw, dict):
|
70
|
-
creat = Resource._from_json_(raw)
|
71
|
-
elif isinstance(raw, str):
|
72
|
-
creat = Resource(uri=raw)
|
73
|
-
|
74
|
-
# parse created date
|
75
|
-
raw_created = data.get('created')
|
76
|
-
if isinstance(raw_created, (int, float)):
|
77
|
-
created_dt = datetime.fromtimestamp(raw_created / 1000.0)
|
78
|
-
elif isinstance(raw_created, str):
|
79
|
-
created_dt = datetime.fromisoformat(raw_created)
|
80
|
-
else:
|
81
|
-
created_dt = None
|
82
|
-
|
83
|
-
# parse modified date
|
84
|
-
raw_modified = data.get('modified')
|
85
|
-
if isinstance(raw_modified, (int, float)):
|
86
|
-
modified_dt = datetime.fromtimestamp(raw_modified / 1000.0)
|
87
|
-
elif isinstance(raw_modified, str):
|
88
|
-
modified_dt = datetime.fromisoformat(raw_modified)
|
89
|
-
else:
|
90
|
-
modified_dt = None
|
91
|
-
|
92
|
-
change_msg = data.get('changeMessage')
|
93
|
-
|
94
|
-
return cls(
|
95
|
-
contributor=contrib,
|
96
|
-
created=created_dt,
|
97
|
-
creator=creat,
|
98
|
-
modified=modified_dt,
|
99
|
-
changeMessage=change_msg
|
100
|
-
)
|
59
|
+
"""
|
60
|
+
Create a Person instance from a JSON-dict (already parsed).
|
61
|
+
"""
|
62
|
+
from .Serialization import Serialization
|
63
|
+
|
64
|
+
return Serialization.deserialize(data, Attribution)
|
@@ -11,7 +11,7 @@ from .Qualifier import Qualifier
|
|
11
11
|
from .Serialization import Serialization
|
12
12
|
from .SourceReference import SourceReference
|
13
13
|
from .Resource import Resource, URI
|
14
|
-
from .
|
14
|
+
from .Extensions.rs10.rsLink import _rsLinkList, rsLink
|
15
15
|
|
16
16
|
from collections.abc import Sized
|
17
17
|
|
@@ -19,15 +19,72 @@ class ConfidenceLevel(Qualifier):
|
|
19
19
|
High = "http://gedcomx.org/High"
|
20
20
|
Medium = "http://gedcomx.org/Medium"
|
21
21
|
Low = "http://gedcomx.org/Low"
|
22
|
-
|
22
|
+
|
23
|
+
_NAME_TO_URI = {
|
24
|
+
"high": High,
|
25
|
+
"medium": Medium,
|
26
|
+
"low": Low,
|
27
|
+
}
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def _from_json_(cls, data):
|
31
|
+
"""
|
32
|
+
Accepts:
|
33
|
+
- "High" | "Medium" | "Low"
|
34
|
+
- "http://gedcomx.org/High" | ".../Medium" | ".../Low"
|
35
|
+
- {"type": "..."} or {"value": "..."} or {"confidence": "..."} or {"level": "..."} or {"uri": "..."}
|
36
|
+
- existing ConfidenceLevel instance
|
37
|
+
Returns:
|
38
|
+
ConfidenceLevel instance with .value set to the canonical URI.
|
39
|
+
"""
|
40
|
+
if data is None:
|
41
|
+
return None
|
42
|
+
|
43
|
+
if isinstance(data, cls):
|
44
|
+
return data
|
45
|
+
|
46
|
+
# Extract token from dicts or use the raw scalar
|
47
|
+
if isinstance(data, dict):
|
48
|
+
token = (
|
49
|
+
data.get("confidence")
|
50
|
+
or data.get("type")
|
51
|
+
or data.get("value")
|
52
|
+
or data.get("level")
|
53
|
+
or data.get("uri")
|
54
|
+
)
|
55
|
+
else:
|
56
|
+
token = data
|
57
|
+
|
58
|
+
if token is None:
|
59
|
+
return None
|
60
|
+
|
61
|
+
token_str = str(token).strip()
|
62
|
+
|
63
|
+
# Normalize to canonical URI
|
64
|
+
if token_str.lower() in cls._NAME_TO_URI:
|
65
|
+
uri = cls._NAME_TO_URI[token_str.lower()]
|
66
|
+
elif token_str in (cls.High, cls.Medium, cls.Low):
|
67
|
+
uri = token_str
|
68
|
+
else:
|
69
|
+
raise ValueError(f"Unknown ConfidenceLevel: {token!r}")
|
70
|
+
|
71
|
+
# Create a ConfidenceLevel instance without invoking Qualifier.__init__
|
72
|
+
obj = cls.__new__(cls)
|
73
|
+
# store the canonical URI on the instance; used by description and (optionally) serialization
|
74
|
+
obj.value = uri
|
75
|
+
return obj
|
76
|
+
|
23
77
|
@property
|
24
78
|
def description(self):
|
25
79
|
descriptions = {
|
26
|
-
|
27
|
-
|
28
|
-
|
80
|
+
self.High: "The contributor has a high degree of confidence that the assertion is true.",
|
81
|
+
self.Medium: "The contributor has a medium degree of confidence that the assertion is true.",
|
82
|
+
self.Low: "The contributor has a low degree of confidence that the assertion is true."
|
29
83
|
}
|
30
|
-
|
84
|
+
# Works whether the instance holds .value or (edge-case) if `self` is compared directly
|
85
|
+
key = getattr(self, "value", self)
|
86
|
+
return descriptions.get(key, "No description available.")
|
87
|
+
|
31
88
|
|
32
89
|
class Conclusion:
|
33
90
|
"""
|
@@ -53,13 +110,7 @@ class Conclusion:
|
|
53
110
|
uri (Resource, optional): A URI reference for the conclusion. Defaults to a
|
54
111
|
URI with the fragment set to the `id`.
|
55
112
|
links (_LinkList, optional): A list of links associated with the conclusion.
|
56
|
-
Defaults to an empty `_LinkList`.
|
57
|
-
|
58
|
-
Methods:
|
59
|
-
add_note(note_to_add): Adds a note if it is not a duplicate and does not
|
60
|
-
exceed the maximum allowed notes.
|
61
|
-
add_source(source_to_add): Adds a source reference if it is not already present.
|
62
|
-
add_link(link): Adds a link to the `_LinkList`.
|
113
|
+
Defaults to an empty `_LinkList`.
|
63
114
|
"""
|
64
115
|
identifier = 'http://gedcomx.org/v1/Conclusion'
|
65
116
|
version = 'http://gedcomx.org/conceptual-model/v1'
|
@@ -75,20 +126,20 @@ class Conclusion:
|
|
75
126
|
return short_uuid
|
76
127
|
|
77
128
|
def __init__(self,
|
78
|
-
id: Optional[str],
|
129
|
+
id: Optional[str] = None,
|
79
130
|
lang: Optional[str] = 'en',
|
80
131
|
sources: Optional[List[SourceReference]] = None,
|
81
132
|
analysis: Optional[object | Resource] = None,
|
82
|
-
notes: Optional[List[Note]] =
|
133
|
+
notes: Optional[List[Note]] = None,
|
83
134
|
confidence: Optional[ConfidenceLevel] = None,
|
84
135
|
attribution: Optional[Attribution] = None,
|
85
136
|
uri: Optional[Resource] = None,
|
86
137
|
_max_note_count: int = 20,
|
87
|
-
links: Optional[
|
138
|
+
links: Optional[_rsLinkList] = None) -> None:
|
88
139
|
|
89
140
|
self._id_generator = Conclusion.default_id_generator
|
90
141
|
|
91
|
-
self.id = id if id else
|
142
|
+
self.id = id if id else None
|
92
143
|
self.lang = lang
|
93
144
|
self.sources = sources if sources else []
|
94
145
|
self.analysis = analysis
|
@@ -96,8 +147,8 @@ class Conclusion:
|
|
96
147
|
self.confidence = confidence
|
97
148
|
self.attribution = attribution
|
98
149
|
self.max_note_count = _max_note_count
|
99
|
-
self.uri = uri if uri else URI(fragment=id)
|
100
|
-
self.links = links if links else
|
150
|
+
self.uri = uri if uri else URI(fragment=id if id else self.id)
|
151
|
+
self.links = links if links else _rsLinkList() #NOTE This is not in specification, following FS format
|
101
152
|
|
102
153
|
def add_note(self,note_to_add: Note):
|
103
154
|
if self.notes and len(self.notes) >= self.max_note_count:
|
@@ -118,9 +169,22 @@ class Conclusion:
|
|
118
169
|
else:
|
119
170
|
raise ValueError()
|
120
171
|
|
121
|
-
def add_link(self,link:
|
122
|
-
|
172
|
+
def add_link(self,link: rsLink) -> bool:
|
173
|
+
"""
|
174
|
+
Adds a link to the Conclusion link list.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
link (rsLink): The link to be added.
|
178
|
+
|
179
|
+
Returns:
|
180
|
+
bool: The return value. True for success, False otherwise.
|
181
|
+
|
182
|
+
Note: Duplicate checking not impimented at this level
|
183
|
+
"""
|
184
|
+
if link and isinstance(link,rsLink):
|
123
185
|
self.links.add(link)
|
186
|
+
return True
|
187
|
+
return False
|
124
188
|
|
125
189
|
@property
|
126
190
|
def _as_dict_(self):
|
@@ -2,6 +2,7 @@ from typing import Optional
|
|
2
2
|
|
3
3
|
from .Date import Date
|
4
4
|
from .PlaceReference import PlaceReference
|
5
|
+
from .Serialization import Serialization
|
5
6
|
|
6
7
|
class Coverage:
|
7
8
|
identifier = 'http://gedcomx.org/v1/Coverage'
|
@@ -13,6 +14,15 @@ class Coverage:
|
|
13
14
|
|
14
15
|
# ...existing code...
|
15
16
|
|
17
|
+
@property
|
18
|
+
def _as_dict_(self):
|
19
|
+
type_as_dict = {
|
20
|
+
'spatial': self.spatial if self.spatial else None,
|
21
|
+
'temporal': self. temporal if self.temporal else None
|
22
|
+
}
|
23
|
+
|
24
|
+
return Serialization.serialize_dict(type_as_dict)
|
25
|
+
|
16
26
|
@classmethod
|
17
27
|
def _from_json_(cls, data: dict):
|
18
28
|
"""
|
@@ -21,7 +21,8 @@ class Date:
|
|
21
21
|
|
22
22
|
self.normalized: DateNormalization | None = normalized if normalized else None
|
23
23
|
|
24
|
-
|
24
|
+
@property
|
25
|
+
def _as_dict_(self):
|
25
26
|
return {'original': self.orginal,
|
26
27
|
'formal': self.formal}
|
27
28
|
|
@@ -33,12 +34,6 @@ class Date:
|
|
33
34
|
return Date(original=original,formal=formal)
|
34
35
|
|
35
36
|
|
36
|
-
Date._to_dict_ = lambda self: {
|
37
|
-
'original': self.orginal,
|
38
|
-
'formal': self.formal}
|
39
|
-
|
40
|
-
|
41
|
-
|
42
37
|
|
43
38
|
def date_to_timestamp(date_str: str, assume_utc_if_naive: bool = True, print_definition: bool = True):
|
44
39
|
"""
|
@@ -1,13 +1,13 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import Optional, List
|
3
3
|
|
4
|
-
from
|
5
|
-
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from gedcomx.Resource import Resource
|
4
|
+
from .Attribution import Attribution
|
5
|
+
from .Note import Note
|
6
|
+
from .SourceReference import SourceReference
|
7
|
+
from .Resource import Resource
|
9
8
|
|
10
9
|
from .Conclusion import Conclusion
|
10
|
+
from .Serialization import Serialization
|
11
11
|
|
12
12
|
class DocumentType(Enum):
|
13
13
|
Abstract = "http://gedcomx.org/Abstract"
|
@@ -49,4 +49,25 @@ class Document(Conclusion):
|
|
49
49
|
self.type = type
|
50
50
|
self.extracted = extracted
|
51
51
|
self.textType = textType
|
52
|
-
self.text = text
|
52
|
+
self.text = text
|
53
|
+
|
54
|
+
@property
|
55
|
+
def _as_dict(self):
|
56
|
+
type_as_dict = super()._as_dict_
|
57
|
+
if self.type:
|
58
|
+
type_as_dict['type'] = self.type.value
|
59
|
+
if self.extracted is not None:
|
60
|
+
type_as_dict['extracted'] = self.extracted
|
61
|
+
if self.textType:
|
62
|
+
type_as_dict['textType'] = self.textType.value
|
63
|
+
if self.text:
|
64
|
+
type_as_dict['text'] = self.text
|
65
|
+
return Serialization.serialize_dict(type_as_dict)
|
66
|
+
|
67
|
+
@classmethod
|
68
|
+
def _from_json_(cls, data: dict):
|
69
|
+
"""
|
70
|
+
Create a Person instance from a JSON-dict (already parsed).
|
71
|
+
"""
|
72
|
+
type_as_dict = Serialization.get_class_fields('Document')
|
73
|
+
return Serialization.deserialize(data, type_as_dict)
|
@@ -9,6 +9,7 @@ from .Conclusion import Conclusion, ConfidenceLevel
|
|
9
9
|
from .Date import Date
|
10
10
|
from .Note import Note
|
11
11
|
from .PlaceReference import PlaceReference
|
12
|
+
from .Serialization import Serialization
|
12
13
|
from .SourceReference import SourceReference
|
13
14
|
from .Subject import Subject
|
14
15
|
from .Resource import Resource
|
@@ -84,6 +85,7 @@ class EventType(Enum):
|
|
84
85
|
Naturalization = "http://gedcomx.org/Naturalization"
|
85
86
|
Ordination = "http://gedcomx.org/Ordination"
|
86
87
|
Retirement = "http://gedcomx.org/Retirement"
|
88
|
+
MarriageSettlment = 'https://gedcom.io/terms/v7/MARS'
|
87
89
|
|
88
90
|
@property
|
89
91
|
def description(self):
|
@@ -192,4 +194,21 @@ class Event(Subject):
|
|
192
194
|
date: Optional[Date] = None,
|
193
195
|
place: Optional[PlaceReference] = None,
|
194
196
|
roles: Optional[List[EventRole]] = []) -> None:
|
195
|
-
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
197
|
+
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
|
198
|
+
|
199
|
+
self.type = type if type and isinstance(type, EventType) else None
|
200
|
+
self.date = date if date and isinstance(date, Date) else None
|
201
|
+
self.place = place if place and isinstance(place, PlaceReference) else None
|
202
|
+
self.roles = roles if roles and isinstance(roles, list) else []
|
203
|
+
|
204
|
+
@property
|
205
|
+
def _as_dict_(self):
|
206
|
+
raise NotImplementedError("Not implemented yet")
|
207
|
+
|
208
|
+
@classmethod
|
209
|
+
def _from_json_(cls, data: dict):
|
210
|
+
"""
|
211
|
+
Create a Person instance from a JSON-dict (already parsed).
|
212
|
+
"""
|
213
|
+
type_as_dict = Serialization.get_class_fields('Event')
|
214
|
+
return Serialization.deserialize(data, type_as_dict)
|
@@ -3,6 +3,12 @@
|
|
3
3
|
class GedcomXError(Exception):
|
4
4
|
"""Base for all app-specific errors."""
|
5
5
|
|
6
|
+
class GedcomClassAttributeError(GedcomXError):
|
7
|
+
def __init__(self, *args: object) -> None:
|
8
|
+
msg = f"This class need more information to be created: {args}"
|
9
|
+
super().__init__(msg)
|
10
|
+
|
11
|
+
|
6
12
|
class TagConversionError(GedcomXError):
|
7
13
|
def __init__(self, record,levelstack):
|
8
14
|
msg = f"Cannot convert: #{record.line} TAG: {record.tag} {record.xref if record.xref else ''} Value:{record.value} STACK: {type(levelstack[record.level-1]).__name__}"
|
@@ -22,7 +22,7 @@ from enum import Enum
|
|
22
22
|
|
23
23
|
from collections.abc import Sized
|
24
24
|
|
25
|
-
from .
|
25
|
+
from .Extensions.rs10.rsLink import rsLink, _rsLinkList
|
26
26
|
|
27
27
|
|
28
28
|
class FactType(Enum):
|
@@ -401,14 +401,14 @@ class Fact(Conclusion):
|
|
401
401
|
sources: Optional[List[SourceReference]] = [],
|
402
402
|
analysis: Optional[Resource | Document] = None,
|
403
403
|
notes: Optional[List[Note]] = [],
|
404
|
-
confidence: ConfidenceLevel = None,
|
405
|
-
attribution: Attribution = None,
|
406
|
-
type: FactType = None,
|
404
|
+
confidence: Optional[ConfidenceLevel] = None,
|
405
|
+
attribution: Optional[Attribution] = None,
|
406
|
+
type: Optional[FactType] = None,
|
407
407
|
date: Optional[Date] = None,
|
408
408
|
place: Optional[PlaceReference] = None,
|
409
409
|
value: Optional[str] = None,
|
410
410
|
qualifiers: Optional[List[FactQualifier]] = None,
|
411
|
-
links: Optional[
|
411
|
+
links: Optional[_rsLinkList] = None):
|
412
412
|
super().__init__(id, lang, sources, analysis, notes, confidence, attribution, links=links)
|
413
413
|
self.type = type
|
414
414
|
self.date = date
|
@@ -433,12 +433,11 @@ class Fact(Conclusion):
|
|
433
433
|
# Only add Relationship-specific fields
|
434
434
|
fact_dict.update( {
|
435
435
|
'type': self.type.value if self.type else None,
|
436
|
-
'date': self.date.
|
436
|
+
'date': self.date._as_dict_ if self.date else None,
|
437
437
|
'place': self.place._as_dict_ if self.place else None,
|
438
438
|
'value': self.value,
|
439
439
|
'qualifiers': [q.value for q in self.qualifiers] if self.qualifiers else []
|
440
440
|
})
|
441
|
-
|
442
441
|
|
443
442
|
return Serialization.serialize_dict(fact_dict)
|
444
443
|
|
@@ -463,7 +462,7 @@ class Fact(Conclusion):
|
|
463
462
|
if data.get('place') else None)
|
464
463
|
value = data.get('value')
|
465
464
|
qualifiers = [Qualifier._from_json_(q) for q in data.get('qualifiers', [])]
|
466
|
-
links =
|
465
|
+
links = _rsLinkList._from_json_(data.get('links')) if data.get('links') else None
|
467
466
|
|
468
467
|
return cls(
|
469
468
|
id=id_,
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import re
|
2
|
+
|
3
|
+
class Gedcom():
|
4
|
+
def __init__(self) -> None:
|
5
|
+
pass
|
6
|
+
|
7
|
+
@staticmethod
|
8
|
+
def read_gedcom_version(filepath: str) -> str | None:
|
9
|
+
"""
|
10
|
+
Reads only the HEAD section of a GEDCOM file and returns the GEDCOM standard version.
|
11
|
+
Looks specifically for HEAD → GEDC → VERS.
|
12
|
+
|
13
|
+
Returns:
|
14
|
+
str: GEDCOM version (e.g., "5.5.1" or "7.0.0"), or None if not found.
|
15
|
+
"""
|
16
|
+
version = None
|
17
|
+
inside_head = False
|
18
|
+
inside_gedc = False
|
19
|
+
|
20
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
21
|
+
for line in f:
|
22
|
+
parts = line.strip().split(maxsplit=2)
|
23
|
+
if not parts:
|
24
|
+
continue
|
25
|
+
|
26
|
+
level = int(parts[0])
|
27
|
+
tag = parts[1] if len(parts) > 1 else ""
|
28
|
+
value = parts[2] if len(parts) > 2 else None
|
29
|
+
|
30
|
+
# Enter HEAD
|
31
|
+
if level == 0 and tag == "HEAD":
|
32
|
+
inside_head = True
|
33
|
+
continue
|
34
|
+
|
35
|
+
# Leave HEAD block
|
36
|
+
if inside_head and level == 0:
|
37
|
+
break
|
38
|
+
|
39
|
+
# Inside HEAD, look for GEDC
|
40
|
+
if inside_head and level == 1 and tag == "GEDC":
|
41
|
+
inside_gedc = True
|
42
|
+
continue
|
43
|
+
|
44
|
+
# If we drop back to level 1 (but not GEDC), stop looking inside GEDC
|
45
|
+
if inside_gedc and level == 1:
|
46
|
+
inside_gedc = False
|
47
|
+
|
48
|
+
# Inside GEDC, look for VERS
|
49
|
+
if inside_gedc and tag == "VERS":
|
50
|
+
version = value
|
51
|
+
break
|
52
|
+
|
53
|
+
return version
|