gedcom-x 0.5.8__py3-none-any.whl → 0.5.10__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.8.dist-info → gedcom_x-0.5.10.dist-info}/METADATA +1 -1
- gedcom_x-0.5.10.dist-info/RECORD +58 -0
- gedcomx/Extensions/rs10/rsLink.py +109 -59
- gedcomx/__init__.py +4 -1
- gedcomx/address.py +102 -16
- gedcomx/agent.py +81 -24
- gedcomx/attribution.py +52 -28
- gedcomx/conclusion.py +98 -46
- gedcomx/converter.py +209 -79
- gedcomx/coverage.py +10 -1
- gedcomx/date.py +42 -8
- gedcomx/document.py +37 -7
- gedcomx/event.py +77 -20
- gedcomx/evidence_reference.py +9 -0
- gedcomx/extensible.py +86 -0
- gedcomx/fact.py +53 -54
- gedcomx/gedcom.py +10 -0
- gedcomx/gedcom5x.py +30 -20
- gedcomx/gedcom7/GedcomStructure.py +1 -3
- gedcomx/gedcom7/__init__.py +2 -2
- gedcomx/gedcom7/{Gedcom7.py → gedcom7.py} +3 -3
- gedcomx/gedcom7/specification.py +4817 -0
- gedcomx/gedcomx.py +95 -93
- gedcomx/gender.py +21 -9
- gedcomx/group.py +9 -0
- gedcomx/identifier.py +47 -20
- gedcomx/logging_hub.py +19 -0
- gedcomx/mutations.py +10 -5
- gedcomx/name.py +74 -33
- gedcomx/note.py +50 -18
- gedcomx/online_account.py +9 -0
- gedcomx/person.py +46 -27
- gedcomx/place_description.py +54 -8
- gedcomx/place_reference.py +30 -8
- gedcomx/qualifier.py +19 -3
- gedcomx/relationship.py +55 -14
- gedcomx/resource.py +45 -18
- gedcomx/schemas.py +328 -0
- gedcomx/serialization.py +400 -421
- gedcomx/source_citation.py +16 -4
- gedcomx/source_description.py +181 -94
- gedcomx/source_reference.py +51 -16
- gedcomx/subject.py +59 -14
- gedcomx/textvalue.py +66 -12
- gedcomx/translation.py +3 -3
- gedcomx/uri.py +155 -3
- gedcom_x-0.5.8.dist-info/RECORD +0 -56
- gedcomx/gedcom7/Specification.py +0 -347
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/WHEEL +0 -0
- {gedcom_x-0.5.8.dist-info → gedcom_x-0.5.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
gedcomx/Logging.py,sha256=vBDOjawVXc4tCge1laYjy6_2Ves-fnGzG0m6NnLZejE,624
|
2
|
+
gedcomx/TopLevelTypeCollection.py,sha256=p99i-O5LXiXe3GlC6jWuz4nH1TAcKxOLbe0VRxWbSFY,1495
|
3
|
+
gedcomx/Zip.py,sha256=lBxcv-Vip45884EHj56wZJJ5I36Q38UuHUidDxQBoS8,14
|
4
|
+
gedcomx/__init__.py,sha256=iSsS6j_4t0Xdrz7PeE598cFP_oTxxjhMi-_DzdG4k8U,1825
|
5
|
+
gedcomx/address.py,sha256=7tGUPRItlSiUZ6-WaDNj4sbjvU8W_f2kw-I8s_nntTU,8049
|
6
|
+
gedcomx/agent.py,sha256=HxDRB8axhReUmTDtn0XFNemG6znjJQezi-qxO2JRfCg,12803
|
7
|
+
gedcomx/attribution.py,sha256=S648H7it_780WLA0c4dkBSrjeXIMz_YVMk1eJ6wZk6Q,4586
|
8
|
+
gedcomx/conclusion.py,sha256=JNWukegiNsE3SHU9-q3gd9YbxOQunW1Bo5VEg7MAeCs,11169
|
9
|
+
gedcomx/converter.py,sha256=_VzdkjS52AVXrtKUAzgI6VGDaw7Wysa76B1Vt1XnNEs,57637
|
10
|
+
gedcomx/coverage.py,sha256=XmBiTGxuH9WoeXumrgu03EMWtccHe982hFJu2Coxceo,2206
|
11
|
+
gedcomx/date.py,sha256=xiAatY25DXTW8usmhbyx_6rmEae3FJnmR5FriGkCN80,3560
|
12
|
+
gedcomx/document.py,sha256=quaa1N1XcqhMdpTwCzpvnZfRgygK1mRhFPWyX09ZrWw,4555
|
13
|
+
gedcomx/event.py,sha256=7Iogi3eVmR4_-qg6seIo7orAmVS1ENvUQcJO3_tZGlc,15417
|
14
|
+
gedcomx/evidence_reference.py,sha256=vtP0rYpehfuu4Jugf1V9u5V_wgPu-2EPb7mqPLS_9cI,696
|
15
|
+
gedcomx/exceptions.py,sha256=0OdPM3euhBMgX8o61ZwPuKeN8zPuSuuDcSBFflVGFqk,587
|
16
|
+
gedcomx/extensible.py,sha256=6-VaJmOax8BoShKMKxKpJEyZf7aILqA8SMEqbRgP5yM,2659
|
17
|
+
gedcomx/extensible_enum.py,sha256=DftCZLMBNul3C9hwh-rf0GE3SVdvylvyd5mt7bX_l6o,6535
|
18
|
+
gedcomx/fact.py,sha256=bEAUKvLpn7ZAJjyOE57dcvOnvkdzZ63GqF3yb2nGhX0,24972
|
19
|
+
gedcomx/gedcom.py,sha256=A8GQ670E6_knfP37kOGHdVl_cC9RXQoc4Dk6D_qlnuQ,2059
|
20
|
+
gedcomx/gedcom5x.py,sha256=ajjLeTlncrSgw3JSBRo8Ip9aZik2NHnUt1iDhjc9-mE,23645
|
21
|
+
gedcomx/gedcomx.py,sha256=PunMwjHIE4fp7O7sjuNrI8p44_4Y3pI43FQSCh7pyqE,19329
|
22
|
+
gedcomx/gender.py,sha256=d2AEvTSa28sfQ1910X-Ysfi7MNDRMQ35EA7jcBYsLck,3178
|
23
|
+
gedcomx/group.py,sha256=I4vdBBsdMdqAMigkzGg4mRxrCAjvsJJ-bIa_aR6CDhA,2879
|
24
|
+
gedcomx/identifier.py,sha256=hCLfBuHVTVwa2VGD_2NFsiftFkqS3-7W8XMnyjmPZbM,9603
|
25
|
+
gedcomx/logging_hub.py,sha256=DbYGwMdVD_nEj0xpVzHjsMVTi_WjttLLRexiebezfzM,8307
|
26
|
+
gedcomx/mutations.py,sha256=ugQgfQh0o9r8FIB_dz5s6KqPyIDMHn2YSP7DvAHsDv0,7236
|
27
|
+
gedcomx/name.py,sha256=5UpNgFPBfTfzC5yLQDqmeSJ-Fg1T6Nio9vEa6f5kA2Y,19501
|
28
|
+
gedcomx/note.py,sha256=joDgKrDS9pbOEEve9LGopP2Gz48kFKqMeHYR72Z7bA0,3813
|
29
|
+
gedcomx/online_account.py,sha256=EYMW-CepfwppaJu5RiPQOK63YKxnjE8bVOPUNQqbrMM,639
|
30
|
+
gedcomx/person.py,sha256=iUyX_Yl6cmbwYPC91OEfwS4i1csdPrkdcpa5yaGeiZc,9040
|
31
|
+
gedcomx/place_description.py,sha256=vFCVW37tKBosYCGttkXppR7yDXA-L8YVLpixSN-8b4E,7564
|
32
|
+
gedcomx/place_reference.py,sha256=XE-tyRsvfXVK--vdiA_Pvj0ZrUecl72JldbV-c-k_PU,2939
|
33
|
+
gedcomx/qualifier.py,sha256=Lg5D_CRLKxaUHcCGLzXhKmpMkmdOeQso0V-XN3yH2n4,2352
|
34
|
+
gedcomx/relationship.py,sha256=_wA1NujEqQO0HzSfjlfLcl-yCFWBcOsRyxs3klr0HfE,5852
|
35
|
+
gedcomx/resource.py,sha256=ShK3sQOaNI4ZlrIUcepL7_uLd_QX4zMx-A1SU2In_m4,3608
|
36
|
+
gedcomx/schemas.py,sha256=wZ7GISQSoQ6reghE92OsutnTqQ1LXy6JD_XBX55Loe4,12506
|
37
|
+
gedcomx/serialization.py,sha256=Pg_tDO5wUXDgyMqVGPwAlm2WdBBNUA0L-TBaRSWhaNo,30308
|
38
|
+
gedcomx/source_citation.py,sha256=QSh1zHK9DsgjQu6oHPDTCReLVs2HP8YDR-NlLAG-pek,1237
|
39
|
+
gedcomx/source_description.py,sha256=A2R1T26HYqpk8Q4W8BYp7zlvHYqycHkUMX94uQRpwV8,19258
|
40
|
+
gedcomx/source_reference.py,sha256=iakI-vLMXbSMuZLDU2c4J5s-XDanOCUJvV2Mz8uRP_8,6895
|
41
|
+
gedcomx/subject.py,sha256=dgStcWiI3a3VX2QEgUIQNbo8HELD2sFUhYT-VI46ND8,5117
|
42
|
+
gedcomx/textvalue.py,sha256=Y-SDRpHR2gGpWIyaOvTYrM5EbfOUeX7Tnh_7iOITAbk,3135
|
43
|
+
gedcomx/translation.py,sha256=4REL68WaWqcC5f3XcBCZ1dn0gtaZftQJTCkvl1Sf-NA,61395
|
44
|
+
gedcomx/uri.py,sha256=ceCcbbrx99QekivhQ_xfMvkmaUKiW_IF21WKTOFBtJg,9693
|
45
|
+
gedcomx/Extensions/__init__.py,sha256=MQzi_whzlxiLiknUNh10hG8OVrNqJE38l6n-AwCssx8,24
|
46
|
+
gedcomx/Extensions/rs10/__init__.py,sha256=nSHoZiD8hsCAyE-KyRTuWSLqSJSFh12kSz7hqilAMps,26
|
47
|
+
gedcomx/Extensions/rs10/rsLink.py,sha256=IdmcG1pVicxX91ZMu9fUPwKYoXCl0Q60inGwiThDAVE,6004
|
48
|
+
gedcomx/gedcom7/Exceptions.py,sha256=xeKr4x8b7r8pOqJ9yMpsCVTyxPeOlREDGgKoM5rX4U0,149
|
49
|
+
gedcomx/gedcom7/GedcomStructure.py,sha256=45p6o01j_bmgCasfprDnGTVy-gyKM_8BuvAakLqCl3c,3431
|
50
|
+
gedcomx/gedcom7/__init__.py,sha256=izlM3__nzpZ32qLU8cqqyIpGQ4cA9vlQh3_RXxgMCg4,682
|
51
|
+
gedcomx/gedcom7/g7interop.py,sha256=hSzwqeok2n7xziEvN2QiJY7bVCWrOnZIZWXubnkrv7w,9945
|
52
|
+
gedcomx/gedcom7/gedcom7.py,sha256=oAp94vmkGtyqqI8-3uA1t5bepxLovMSi3r9rZFlZ0Lg,5620
|
53
|
+
gedcomx/gedcom7/logger.py,sha256=QM1SySyh91UEhs90d2DMhH-s9qGF8XS8I8gr1eOcmfw,617
|
54
|
+
gedcomx/gedcom7/specification.py,sha256=H6l4IW4UX7YRKoNH6t84REFeGl52kEyRnn6a9f21gbg,288703
|
55
|
+
gedcom_x-0.5.10.dist-info/METADATA,sha256=6sx_S_SMYe8PooiGv8TYwqOSodtyBBDVl9gKbdQes5w,4333
|
56
|
+
gedcom_x-0.5.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
57
|
+
gedcom_x-0.5.10.dist-info/top_level.txt,sha256=smVBF4nxSU-mzCd6idtRYTbYjPICMMi8pTqewEmqF8Y,8
|
58
|
+
gedcom_x-0.5.10.dist-info/RECORD,,
|
@@ -1,19 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Union
|
2
|
+
"""
|
3
|
+
======================================================================
|
4
|
+
Project: Gedcom-X
|
5
|
+
File: rsLink.py
|
6
|
+
Author: David J. Cartwright
|
7
|
+
Purpose: Link type of GedcomX RS (Extension)
|
8
|
+
|
9
|
+
Created: 2025-08-25
|
10
|
+
Updated:
|
11
|
+
-
|
12
|
+
|
13
|
+
======================================================================
|
14
|
+
"""
|
15
|
+
|
16
|
+
"""
|
17
|
+
======================================================================
|
18
|
+
GEDCOM Module Types
|
19
|
+
======================================================================
|
20
|
+
"""
|
16
21
|
from ...exceptions import GedcomClassAttributeError
|
22
|
+
from ...logging_hub import hub, logging
|
23
|
+
from ...uri import URI
|
24
|
+
"""
|
25
|
+
======================================================================
|
26
|
+
Logging
|
27
|
+
======================================================================
|
28
|
+
"""
|
29
|
+
log = logging.getLogger("gedcomx")
|
30
|
+
serial_log = "gedcomx.serialization"
|
31
|
+
deserial_log = "gedcomx.deserialization"
|
32
|
+
#=====================================================================
|
17
33
|
|
18
34
|
|
19
35
|
class rsLink():
|
@@ -47,7 +63,7 @@ class rsLink():
|
|
47
63
|
"""
|
48
64
|
identifier = "http://gedcomx.org/v1/Link"
|
49
65
|
|
50
|
-
def __init__(self,
|
66
|
+
def __init__(self,
|
51
67
|
href: Optional[URI] = None,
|
52
68
|
template: Optional[str] = None,
|
53
69
|
type: Optional[str] = None,
|
@@ -56,7 +72,7 @@ class rsLink():
|
|
56
72
|
hreflang: Optional[str] = None,
|
57
73
|
title: Optional[str] = None) -> None:
|
58
74
|
|
59
|
-
|
75
|
+
|
60
76
|
self.href = href if isinstance(href,URI) else URI.from_url(href) if isinstance(href,str) else None
|
61
77
|
self.template = template
|
62
78
|
self.type = type
|
@@ -65,52 +81,86 @@ class rsLink():
|
|
65
81
|
self.hreflang = hreflang
|
66
82
|
self.title = title
|
67
83
|
|
68
|
-
if self.href is None and self.template is None:
|
84
|
+
if self.href is None: # and self.template is None:
|
69
85
|
raise GedcomClassAttributeError("href or template are required")
|
86
|
+
|
87
|
+
def __str__(self) -> str:
|
88
|
+
def to_text(v):
|
89
|
+
if v is None:
|
90
|
+
return None
|
91
|
+
# unwrap URI-like objects
|
92
|
+
if isinstance(v, URI):
|
93
|
+
return getattr(v, "value", None) or str(v)
|
94
|
+
# normalize strings (skip empty/whitespace-only)
|
95
|
+
if isinstance(v, str):
|
96
|
+
s = v.strip()
|
97
|
+
return s or None
|
98
|
+
return str(v)
|
99
|
+
|
100
|
+
parts = []
|
101
|
+
|
102
|
+
# show href as the primary bit if present
|
103
|
+
href_s = to_text(self.href)
|
104
|
+
if href_s:
|
105
|
+
parts.append(href_s)
|
106
|
+
|
107
|
+
# show other fields as key=value
|
108
|
+
for name in ("template", "type", "accept", "allow", "hreflang", "title"):
|
109
|
+
val = to_text(getattr(self, name, None))
|
110
|
+
if val:
|
111
|
+
parts.append(f"{name}={val}")
|
112
|
+
|
113
|
+
return " | ".join(parts) if parts else self.__class__.__name__
|
114
|
+
|
70
115
|
|
71
|
-
@property
|
72
|
-
def _as_dict_(self):
|
73
|
-
from ...serialization import Serialization
|
74
|
-
type_as_dict = {
|
75
|
-
"rel": self.rel._as_dict_ if self.rel else None,
|
76
|
-
"href": self.href._as_dict_ if self.href else None,
|
77
|
-
"template": self.template, # RFC 6570 template if used
|
78
|
-
"type": self.type, # media type (note: shadows built-in 'type')
|
79
|
-
"accept": self.accept,
|
80
|
-
"allow": self.allow,
|
81
|
-
"hreflang": self.hreflang,
|
82
|
-
"title": self.title,
|
83
|
-
}
|
84
|
-
return Serialization.serialize_dict(type_as_dict)
|
85
116
|
|
86
|
-
@property
|
87
|
-
def json(self):
|
88
|
-
import json
|
89
|
-
return json.dumps(self._as_dict_)
|
90
|
-
|
91
|
-
class _rsLinkList():
|
92
|
-
def __init__(self) -> None:
|
93
|
-
self.links = {}
|
94
|
-
|
95
|
-
def add(self,link: rsLink):
|
96
|
-
if link and isinstance(link,rsLink):
|
97
|
-
if link.rel in self.links.keys():
|
98
|
-
self.links[link.rel].append(link.href)
|
99
|
-
else:
|
100
|
-
self.links[link.rel] = [link.href]
|
101
|
-
|
102
|
-
|
103
117
|
@classmethod
|
104
|
-
def _from_json_(cls,data:
|
118
|
+
def _from_json_(cls, data: Any, context: Any = None) -> "rsLink":
|
119
|
+
"""
|
120
|
+
Build an rsLink from JSON.
|
121
|
+
|
122
|
+
Accepted shapes:
|
123
|
+
- {"rel": "self", "href": "https://..."}
|
124
|
+
- {"rel": {...}, "href": {...}} # URI objects as dicts
|
125
|
+
- {"href": "https://...", "type": "...", ...} # rel optional
|
126
|
+
- {"uri": "https://..."} or {"url": "..."} # href aliases
|
127
|
+
- "https://example.com" # shorthand -> href only
|
128
|
+
|
129
|
+
Note:
|
130
|
+
- `rel` is coerced to a URI if possible.
|
131
|
+
- `href` is coerced to a URI (string/dict supported).
|
132
|
+
- If both `href` and `template` are missing, __init__ will raise.
|
133
|
+
"""
|
134
|
+
# Shorthand: bare string is an href
|
105
135
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
136
|
+
if not isinstance(data, dict):
|
137
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
|
138
|
+
|
139
|
+
print("LINK DATA:",data)
|
140
|
+
# Extract with common aliases
|
141
|
+
rel = data.get("rel")
|
142
|
+
href = data.get("href")
|
143
|
+
|
144
|
+
|
114
145
|
|
146
|
+
return cls(
|
147
|
+
rel=rel,
|
148
|
+
href=href,
|
149
|
+
template=data.get("template"),
|
150
|
+
type=data.get("type"),
|
151
|
+
accept=data.get("accept"),
|
152
|
+
allow=data.get("allow"),
|
153
|
+
hreflang=data.get("hreflang"),
|
154
|
+
title=data.get("title"),
|
155
|
+
)
|
115
156
|
|
116
|
-
|
157
|
+
|
158
|
+
class _rsLinks():
|
159
|
+
|
160
|
+
def __init__(self,
|
161
|
+
person: rsLink | None = None,
|
162
|
+
portrait: rsLink | None= None
|
163
|
+
) -> None:
|
164
|
+
|
165
|
+
self.person = person
|
166
|
+
self.portrait = portrait
|
gedcomx/__init__.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from .schemas import SCHEMA
|
1
2
|
from .agent import Agent
|
2
3
|
from .address import Address
|
3
4
|
from .attribution import Attribution
|
@@ -12,6 +13,8 @@ from .extensible_enum import ExtensibleEnum
|
|
12
13
|
from .event import Event
|
13
14
|
from .event import EventType
|
14
15
|
from .event import EventRole
|
16
|
+
from .extensible import Extensible
|
17
|
+
from. extensible import _ExtraField
|
15
18
|
from .fact import Fact
|
16
19
|
from .fact import FactQualifier
|
17
20
|
from .fact import FactType
|
@@ -43,7 +46,7 @@ from .uri import URI
|
|
43
46
|
|
44
47
|
from .Extensions.rs10.rsLink import rsLink
|
45
48
|
|
46
|
-
from .gedcom7.
|
49
|
+
from .gedcom7.gedcom7 import Gedcom7, GedcomStructure
|
47
50
|
from .translation import g7toXtable
|
48
51
|
|
49
52
|
|
gedcomx/address.py
CHANGED
@@ -1,7 +1,33 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
import json
|
1
|
+
from typing import Any, Dict, Optional, List
|
3
2
|
|
3
|
+
"""
|
4
|
+
======================================================================
|
5
|
+
Project: Gedcom-X
|
6
|
+
File: address.py
|
7
|
+
Author: David J. Cartwright
|
8
|
+
Purpose:
|
4
9
|
|
10
|
+
Created: 2025-08-25
|
11
|
+
Updated:
|
12
|
+
- 2025-09-03: _from_json_ refactoring
|
13
|
+
|
14
|
+
======================================================================
|
15
|
+
"""
|
16
|
+
|
17
|
+
"""
|
18
|
+
======================================================================
|
19
|
+
GEDCOM Module Types
|
20
|
+
======================================================================
|
21
|
+
"""
|
22
|
+
from .logging_hub import hub, logging
|
23
|
+
"""
|
24
|
+
======================================================================
|
25
|
+
Logging
|
26
|
+
======================================================================
|
27
|
+
"""
|
28
|
+
log = logging.getLogger("gedcomx")
|
29
|
+
serial_log = "gedcomx.serialization"
|
30
|
+
#=====================================================================
|
5
31
|
|
6
32
|
class Address:
|
7
33
|
"""A GedcomX Address Data Type
|
@@ -113,19 +139,79 @@ class Address:
|
|
113
139
|
|
114
140
|
@property
|
115
141
|
def _as_dict_(self):
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
142
|
+
with hub.use(serial_log):
|
143
|
+
log.debug(f"Serializing 'Address' with value: '{self.value}'")
|
144
|
+
type_as_dict = {}
|
145
|
+
if self.city: type_as_dict["city"] = self.city
|
146
|
+
if self.country: type_as_dict["country"] = self.country
|
147
|
+
if self.postalCode: type_as_dict["postalCode"] = self.postalCode
|
148
|
+
if self.stateOrProvince: type_as_dict["stateOrProvince"] = self.stateOrProvince
|
149
|
+
if self.street: type_as_dict["street"] = self.street
|
150
|
+
if self.street2: type_as_dict["street2"] = self.street2
|
151
|
+
if self.street3: type_as_dict["street3"] = self.street3
|
152
|
+
if self.street4: type_as_dict["street4"] = self.street4
|
153
|
+
if self.street5: type_as_dict["street5"] = self.street5
|
154
|
+
if self.street6: type_as_dict["street6"] = self.street6
|
155
|
+
log.debug(f"'Address' serialized with fields: '{type_as_dict.keys()}'")
|
156
|
+
if type_as_dict == {} or len(type_as_dict.keys()) == 0: log.warning("serializing and empty 'Address' Object")
|
157
|
+
|
158
|
+
return type_as_dict if type_as_dict != {} else None
|
159
|
+
|
160
|
+
|
161
|
+
@classmethod
|
162
|
+
def _from_json_(cls, data: Any, context: Any = None) -> "Address":
|
163
|
+
"""
|
164
|
+
Build an Address from JSON.
|
165
|
+
Supports:
|
166
|
+
- Shorthand string -> value
|
167
|
+
- Aliases: postal_code/postal -> postalCode; state/province -> stateOrProvince
|
168
|
+
- Line aliases: line1..line6 / address1..address6 / addr1..addr6 -> street..street6
|
169
|
+
- 'lines': [..] list -> street..street6
|
170
|
+
"""
|
171
|
+
if data is None: return None
|
172
|
+
|
173
|
+
if not isinstance(data, dict):
|
174
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
|
175
|
+
|
176
|
+
address_data: Dict[str, Any] = {}
|
177
|
+
|
178
|
+
# Freeform value (accept a few aliases)
|
179
|
+
if (v := data.get("value")) is None:
|
180
|
+
address_data["value"] = str(v)
|
181
|
+
|
182
|
+
# Simple scalars
|
183
|
+
if (city := data.get("city")) is not None:
|
184
|
+
address_data["city"] = city
|
185
|
+
if (country := data.get("country")) is not None:
|
186
|
+
address_data["country"] = country
|
187
|
+
|
188
|
+
# Postal code (aliases)
|
189
|
+
if (postal := data.get("postalCode")) is not None:
|
190
|
+
address_data["postalCode"] = postal
|
191
|
+
|
192
|
+
# State / Province (aliases)
|
193
|
+
if (stateprov := data.get("stateOrProvince")) is not None:
|
194
|
+
address_data["stateOrProvince"] = stateprov
|
195
|
+
|
196
|
+
if data.get("street") is not None:
|
197
|
+
address_data["street"] = data["street"]
|
198
|
+
|
199
|
+
if data.get("street2") is not None:
|
200
|
+
address_data["street2"] = data["street2"]
|
201
|
+
|
202
|
+
if data.get("street3") is not None:
|
203
|
+
address_data["street3"] = data["street3"]
|
204
|
+
|
205
|
+
if data.get("street4") is not None:
|
206
|
+
address_data["street4"] = data["street4"]
|
207
|
+
|
208
|
+
if data.get("street5") is not None:
|
209
|
+
address_data["street5"] = data["street5"]
|
210
|
+
|
211
|
+
if data.get("street6") is not None:
|
212
|
+
address_data["street6"] = data["street6"]
|
213
|
+
|
214
|
+
|
215
|
+
return cls(**address_data)
|
130
216
|
|
131
217
|
|
gedcomx/agent.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import base64
|
2
2
|
import uuid
|
3
3
|
|
4
|
-
from typing import List, Optional
|
4
|
+
from typing import Any, Dict, List, Optional
|
5
5
|
"""
|
6
6
|
======================================================================
|
7
7
|
Project: Gedcom-X
|
@@ -11,7 +11,7 @@ from typing import List, Optional
|
|
11
11
|
|
12
12
|
Created: 2025-08-25
|
13
13
|
Updated:
|
14
|
-
-
|
14
|
+
- 2025-09-03: _from_json_ refactor
|
15
15
|
|
16
16
|
======================================================================
|
17
17
|
"""
|
@@ -22,11 +22,20 @@ GEDCOM Module Type Imports
|
|
22
22
|
======================================================================
|
23
23
|
"""
|
24
24
|
from .address import Address
|
25
|
-
from .identifier import Identifier, IdentifierList
|
25
|
+
from .identifier import Identifier, IdentifierList, make_uid
|
26
26
|
from .online_account import OnlineAccount
|
27
27
|
from .resource import Resource
|
28
28
|
from .textvalue import TextValue
|
29
29
|
from .uri import URI
|
30
|
+
from .logging_hub import hub, logging
|
31
|
+
"""
|
32
|
+
======================================================================
|
33
|
+
Logging
|
34
|
+
======================================================================
|
35
|
+
"""
|
36
|
+
log = logging.getLogger("gedcomx")
|
37
|
+
serial_log = "gedcomx.serialization"
|
38
|
+
#=====================================================================
|
30
39
|
|
31
40
|
|
32
41
|
class Agent:
|
@@ -61,17 +70,7 @@ class Agent:
|
|
61
70
|
attribution (Attribution, optional): Attribution information related to the agent.
|
62
71
|
uri (Resource, optional): A URI reference for this agent.
|
63
72
|
"""
|
64
|
-
|
65
|
-
@staticmethod
|
66
|
-
def default_id_generator():
|
67
|
-
# Generate a standard UUID
|
68
|
-
standard_uuid = uuid.uuid4()
|
69
|
-
# Convert UUID to bytes
|
70
|
-
uuid_bytes = standard_uuid.bytes
|
71
|
-
# Encode bytes to a Base64 string
|
72
|
-
short_uuid = base64.urlsafe_b64encode(uuid_bytes).rstrip(b'=').decode('utf-8')
|
73
|
-
return short_uuid
|
74
|
-
|
73
|
+
|
75
74
|
def __init__(self, id: Optional[str] = None,
|
76
75
|
identifiers: Optional[IdentifierList] = None,
|
77
76
|
names: Optional[List[TextValue]] = [],
|
@@ -86,9 +85,8 @@ class Agent:
|
|
86
85
|
attribution: Optional[object] = None, # Added for compatibility with GEDCOM5/7 Imports
|
87
86
|
uri: Optional[URI | Resource] = None):
|
88
87
|
|
89
|
-
|
90
|
-
|
91
|
-
self.id = id if id else None #TODO self._id_generator()
|
88
|
+
|
89
|
+
self.id = id if id else make_uid()
|
92
90
|
self.identifiers = identifiers or IdentifierList()
|
93
91
|
self.names = names if names else []
|
94
92
|
self.homepage = homepage or None
|
@@ -164,16 +162,75 @@ class Agent:
|
|
164
162
|
type_as_dict["addresses"] = [address._as_dict_ for address in self.addresses if address]
|
165
163
|
if self.xnotes:
|
166
164
|
type_as_dict["notes"] = [note._as_dict_() for note in self.xnotes if note]
|
165
|
+
return type_as_dict if type_as_dict != {} else None
|
167
166
|
return Serialization.serialize_dict(type_as_dict)
|
168
167
|
|
169
168
|
@classmethod
|
170
|
-
def _from_json_(cls, data:
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
169
|
+
def _from_json_(cls, data: Any, context: Any = None) -> "Agent":
|
170
|
+
|
171
|
+
if not isinstance(data, dict):
|
172
|
+
raise TypeError(f"{cls.__name__}._from_json_ expected dict or str, got {type(data)}")
|
173
|
+
|
174
|
+
agent_data: Dict[str, Any] = {}
|
175
|
+
|
176
|
+
# ── Scalars ──────────────────────────────────────────────────────────────
|
177
|
+
if (id_ := data.get("id")) is not None:
|
178
|
+
agent_data["id"] = id_
|
179
|
+
|
180
|
+
# ── Objects ─────────────────────────────────────────────────────────────
|
181
|
+
if (identifiers := data.get("identifiers")) is not None:
|
182
|
+
agent_data["identifiers"] = IdentifierList._from_json_(identifiers, context)
|
183
|
+
|
184
|
+
# homepage / openid / uri: accept string or dict
|
185
|
+
if (homepage := data.get("homepage")) is not None:
|
186
|
+
agent_data["homepage"] = URI.from_url_(homepage) if isinstance(homepage, str) else URI._from_json_(homepage, context)
|
187
|
+
|
188
|
+
if (openid := data.get("openid")) is not None:
|
189
|
+
agent_data["openid"] = URI.from_url_(openid) if isinstance(openid, str) else URI._from_json_(openid, context)
|
190
|
+
|
191
|
+
if (uri := data.get("uri")) is not None:
|
192
|
+
if isinstance(uri, str):
|
193
|
+
agent_data["uri"] = URI.from_url_(uri)
|
194
|
+
else: raise ValueError()
|
195
|
+
|
196
|
+
# person can be a full Person object or a Resource/URI reference
|
197
|
+
if (person := data.get("person")) is not None:
|
198
|
+
if isinstance(person, dict):
|
199
|
+
agent_data["person"] = Resource._from_json_(person, context)
|
200
|
+
else:
|
201
|
+
raise ValueError()
|
202
|
+
|
203
|
+
# attribution (GEDCOM5/7 compatibility): try Attribution if shape matches; otherwise store as-is
|
204
|
+
if (attr := data.get("attribution")) is not None:
|
205
|
+
"""
|
206
|
+
======================================================================
|
207
|
+
GEDCOM Module Type Imports
|
208
|
+
======================================================================
|
209
|
+
"""
|
210
|
+
from .attribution import Attribution
|
211
|
+
#======================================================================
|
212
|
+
if isinstance(attr, dict) and any(k in attr for k in ("contributor", "created", "modified")):
|
213
|
+
agent_data["attribution"] = Attribution._from_json_(attr, context)
|
214
|
+
else:
|
215
|
+
raise ValueError()
|
216
|
+
|
217
|
+
# ── Lists ───────────────────────────────────────────────────────────────
|
218
|
+
if (names := data.get("names")) is not None:
|
219
|
+
agent_data["names"] = [TextValue._from_json_(n, context) if isinstance(n, (dict,)) else TextValue(n) for n in names]
|
220
|
+
|
221
|
+
if (accounts := data.get("accounts")) is not None:
|
222
|
+
agent_data["accounts"] = [OnlineAccount._from_json_(a, context) for a in accounts]
|
223
|
+
|
224
|
+
if (emails := data.get("emails")) is not None:
|
225
|
+
agent_data["emails"] = [URI.from_url_(e) if isinstance(e, str) else URI._from_json_(e, context) for e in emails]
|
226
|
+
|
227
|
+
if (phones := data.get("phones")) is not None:
|
228
|
+
agent_data["phones"] = [URI.from_url_(p) if isinstance(p, str) else URI._from_json_(p, context) for p in phones]
|
229
|
+
|
230
|
+
if (addresses := data.get("addresses")) is not None:
|
231
|
+
agent_data["addresses"] = [Address._from_json_(a, context) for a in addresses]
|
232
|
+
|
233
|
+
return cls(**agent_data)
|
177
234
|
|
178
235
|
def __str__(self):
|
179
236
|
"""
|