gedcom-x 0.5.7__tar.gz → 0.5.9__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.
Files changed (80) hide show
  1. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/PKG-INFO +1 -1
  2. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcom_x.egg-info/PKG-INFO +1 -1
  3. gedcom_x-0.5.9/gedcom_x.egg-info/SOURCES.txt +89 -0
  4. gedcom_x-0.5.9/gedcomx/Extensions/rs10/rsLink.py +166 -0
  5. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcomx/TopLevelTypeCollection.py +1 -1
  6. gedcom_x-0.5.9/gedcomx/__init__.py +50 -0
  7. gedcom_x-0.5.9/gedcomx/address.py +217 -0
  8. gedcom_x-0.5.7/gedcomx/Agent.py → gedcom_x-0.5.9/gedcomx/agent.py +107 -34
  9. gedcom_x-0.5.9/gedcomx/attribution.py +115 -0
  10. gedcom_x-0.5.7/gedcomx/Conclusion.py → gedcom_x-0.5.9/gedcomx/conclusion.py +120 -51
  11. gedcom_x-0.5.7/gedcomx/Converter.py → gedcom_x-0.5.9/gedcomx/converter.py +261 -116
  12. gedcom_x-0.5.9/gedcomx/coverage.py +64 -0
  13. gedcom_x-0.5.7/gedcomx/Date.py → gedcom_x-0.5.9/gedcomx/date.py +43 -9
  14. gedcom_x-0.5.7/gedcomx/Document.py → gedcom_x-0.5.9/gedcomx/document.py +60 -12
  15. gedcom_x-0.5.7/gedcomx/Event.py → gedcom_x-0.5.9/gedcomx/event.py +88 -31
  16. gedcom_x-0.5.9/gedcomx/evidence_reference.py +20 -0
  17. gedcom_x-0.5.7/gedcomx/Fact.py → gedcom_x-0.5.9/gedcomx/fact.py +81 -74
  18. gedcom_x-0.5.7/gedcomx/Gedcom.py → gedcom_x-0.5.9/gedcomx/gedcom.py +10 -0
  19. gedcom_x-0.5.7/gedcomx/Gedcom5x.py → gedcom_x-0.5.9/gedcomx/gedcom5x.py +31 -21
  20. gedcom_x-0.5.9/gedcomx/gedcom7/Exceptions.py +9 -0
  21. gedcom_x-0.5.9/gedcomx/gedcom7/GedcomStructure.py +94 -0
  22. gedcom_x-0.5.9/gedcomx/gedcom7/Specification.py +347 -0
  23. gedcom_x-0.5.9/gedcomx/gedcom7/__init__.py +26 -0
  24. gedcom_x-0.5.9/gedcomx/gedcom7/g7interop.py +205 -0
  25. gedcom_x-0.5.9/gedcomx/gedcom7/gedcom7.py +160 -0
  26. gedcom_x-0.5.9/gedcomx/gedcom7/logger.py +19 -0
  27. gedcom_x-0.5.7/gedcomx/GedcomX.py → gedcom_x-0.5.9/gedcomx/gedcomx.py +109 -106
  28. gedcom_x-0.5.9/gedcomx/gender.py +91 -0
  29. gedcom_x-0.5.9/gedcomx/group.py +72 -0
  30. gedcom_x-0.5.7/gedcomx/Identifier.py → gedcom_x-0.5.9/gedcomx/identifier.py +48 -21
  31. gedcom_x-0.5.7/gedcomx/LoggingHub.py → gedcom_x-0.5.9/gedcomx/logging_hub.py +19 -0
  32. gedcom_x-0.5.7/gedcomx/Mutations.py → gedcom_x-0.5.9/gedcomx/mutations.py +59 -30
  33. gedcom_x-0.5.7/gedcomx/Name.py → gedcom_x-0.5.9/gedcomx/name.py +88 -47
  34. gedcom_x-0.5.9/gedcomx/note.py +105 -0
  35. gedcom_x-0.5.9/gedcomx/online_account.py +19 -0
  36. gedcom_x-0.5.7/gedcomx/Person.py → gedcom_x-0.5.9/gedcomx/person.py +61 -41
  37. gedcom_x-0.5.7/gedcomx/PlaceDescription.py → gedcom_x-0.5.9/gedcomx/place_description.py +71 -23
  38. gedcom_x-0.5.7/gedcomx/PlaceReference.py → gedcom_x-0.5.9/gedcomx/place_reference.py +32 -10
  39. gedcom_x-0.5.7/gedcomx/Qualifier.py → gedcom_x-0.5.9/gedcomx/qualifier.py +20 -4
  40. gedcom_x-0.5.9/gedcomx/relationship.py +156 -0
  41. gedcom_x-0.5.9/gedcomx/resource.py +112 -0
  42. gedcom_x-0.5.9/gedcomx/serialization.py +794 -0
  43. gedcom_x-0.5.9/gedcomx/source_citation.py +37 -0
  44. gedcom_x-0.5.9/gedcomx/source_description.py +401 -0
  45. gedcom_x-0.5.7/gedcomx/SourceReference.py → gedcom_x-0.5.9/gedcomx/source_reference.py +56 -21
  46. gedcom_x-0.5.9/gedcomx/subject.py +122 -0
  47. gedcom_x-0.5.9/gedcomx/textvalue.py +89 -0
  48. gedcom_x-0.5.7/gedcomx/Translation.py → gedcom_x-0.5.9/gedcomx/translation.py +4 -4
  49. gedcom_x-0.5.9/gedcomx/uri.py +273 -0
  50. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/pyproject.toml +2 -2
  51. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/setup.py +1 -1
  52. gedcom_x-0.5.7/gedcom_x.egg-info/SOURCES.txt +0 -52
  53. gedcom_x-0.5.7/gedcomx/Address.py +0 -131
  54. gedcom_x-0.5.7/gedcomx/Attribution.py +0 -91
  55. gedcom_x-0.5.7/gedcomx/Coverage.py +0 -37
  56. gedcom_x-0.5.7/gedcomx/EvidenceReference.py +0 -11
  57. gedcom_x-0.5.7/gedcomx/Extensions/rs10/rsLink.py +0 -116
  58. gedcom_x-0.5.7/gedcomx/Gender.py +0 -65
  59. gedcom_x-0.5.7/gedcomx/Group.py +0 -37
  60. gedcom_x-0.5.7/gedcomx/Note.py +0 -73
  61. gedcom_x-0.5.7/gedcomx/OnlineAccount.py +0 -10
  62. gedcom_x-0.5.7/gedcomx/Relationship.py +0 -97
  63. gedcom_x-0.5.7/gedcomx/Resource.py +0 -85
  64. gedcom_x-0.5.7/gedcomx/Serialization.py +0 -816
  65. gedcom_x-0.5.7/gedcomx/SourceCitation.py +0 -25
  66. gedcom_x-0.5.7/gedcomx/SourceDescription.py +0 -314
  67. gedcom_x-0.5.7/gedcomx/Subject.py +0 -59
  68. gedcom_x-0.5.7/gedcomx/TextValue.py +0 -35
  69. gedcom_x-0.5.7/gedcomx/URI.py +0 -105
  70. gedcom_x-0.5.7/gedcomx/__init__.py +0 -49
  71. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/README.md +0 -0
  72. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcom_x.egg-info/dependency_links.txt +0 -0
  73. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcom_x.egg-info/top_level.txt +0 -0
  74. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcomx/Extensions/__init__.py +0 -0
  75. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcomx/Extensions/rs10/__init__.py +0 -0
  76. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcomx/Logging.py +0 -0
  77. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/gedcomx/Zip.py +0 -0
  78. /gedcom_x-0.5.7/gedcomx/Exceptions.py → /gedcom_x-0.5.9/gedcomx/exceptions.py +0 -0
  79. /gedcom_x-0.5.7/gedcomx/ExtensibleEnum.py → /gedcom_x-0.5.9/gedcomx/extensible_enum.py +0 -0
  80. {gedcom_x-0.5.7 → gedcom_x-0.5.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gedcom-x
3
- Version: 0.5.7
3
+ Version: 0.5.9
4
4
  Summary: Python implimentation of gedcom-x standard
5
5
  Author-email: "David J. Cartwright" <davidcartwright@hotmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gedcom-x
3
- Version: 0.5.7
3
+ Version: 0.5.9
4
4
  Summary: Python implimentation of gedcom-x standard
5
5
  Author-email: "David J. Cartwright" <davidcartwright@hotmail.com>
6
6
  License: MIT
@@ -0,0 +1,89 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ gedcom_x.egg-info/PKG-INFO
5
+ gedcom_x.egg-info/SOURCES.txt
6
+ gedcom_x.egg-info/dependency_links.txt
7
+ gedcom_x.egg-info/top_level.txt
8
+ gedcomx/Address.py
9
+ gedcomx/Agent.py
10
+ gedcomx/Attribution.py
11
+ gedcomx/Conclusion.py
12
+ gedcomx/Converter.py
13
+ gedcomx/Coverage.py
14
+ gedcomx/Date.py
15
+ gedcomx/Document.py
16
+ gedcomx/Event.py
17
+ gedcomx/Exceptions.py
18
+ gedcomx/Fact.py
19
+ gedcomx/Gedcom.py
20
+ gedcomx/Gedcom5x.py
21
+ gedcomx/GedcomX.py
22
+ gedcomx/Gender.py
23
+ gedcomx/Group.py
24
+ gedcomx/Identifier.py
25
+ gedcomx/Logging.py
26
+ gedcomx/Mutations.py
27
+ gedcomx/Name.py
28
+ gedcomx/Note.py
29
+ gedcomx/Person.py
30
+ gedcomx/Qualifier.py
31
+ gedcomx/Relationship.py
32
+ gedcomx/Resource.py
33
+ gedcomx/Serialization.py
34
+ gedcomx/Subject.py
35
+ gedcomx/TextValue.py
36
+ gedcomx/TopLevelTypeCollection.py
37
+ gedcomx/Translation.py
38
+ gedcomx/URI.py
39
+ gedcomx/Zip.py
40
+ gedcomx/__init__.py
41
+ gedcomx/address.py
42
+ gedcomx/agent.py
43
+ gedcomx/attribution.py
44
+ gedcomx/conclusion.py
45
+ gedcomx/converter.py
46
+ gedcomx/coverage.py
47
+ gedcomx/date.py
48
+ gedcomx/document.py
49
+ gedcomx/event.py
50
+ gedcomx/evidence_reference.py
51
+ gedcomx/exceptions.py
52
+ gedcomx/extensible_enum.py
53
+ gedcomx/fact.py
54
+ gedcomx/gedcom.py
55
+ gedcomx/gedcom5x.py
56
+ gedcomx/gedcomx.py
57
+ gedcomx/gender.py
58
+ gedcomx/group.py
59
+ gedcomx/identifier.py
60
+ gedcomx/logging_hub.py
61
+ gedcomx/mutations.py
62
+ gedcomx/name.py
63
+ gedcomx/note.py
64
+ gedcomx/online_account.py
65
+ gedcomx/person.py
66
+ gedcomx/place_description.py
67
+ gedcomx/place_reference.py
68
+ gedcomx/qualifier.py
69
+ gedcomx/relationship.py
70
+ gedcomx/resource.py
71
+ gedcomx/serialization.py
72
+ gedcomx/source_citation.py
73
+ gedcomx/source_description.py
74
+ gedcomx/source_reference.py
75
+ gedcomx/subject.py
76
+ gedcomx/textvalue.py
77
+ gedcomx/translation.py
78
+ gedcomx/uri.py
79
+ gedcomx/Extensions/__init__.py
80
+ gedcomx/Extensions/rs10/__init__.py
81
+ gedcomx/Extensions/rs10/rsLink.py
82
+ gedcomx/gedcom7/Exceptions.py
83
+ gedcomx/gedcom7/Gedcom7.py
84
+ gedcomx/gedcom7/GedcomStructure.py
85
+ gedcomx/gedcom7/Specification.py
86
+ gedcomx/gedcom7/__init__.py
87
+ gedcomx/gedcom7/g7interop.py
88
+ gedcomx/gedcom7/gedcom7.py
89
+ gedcomx/gedcom7/logger.py
@@ -0,0 +1,166 @@
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
+ """
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
+ #=====================================================================
33
+
34
+
35
+ class rsLink():
36
+ """A link description object. RS Extension to GedcomX by FamilySearch.
37
+
38
+ Args:
39
+ rel (str): Link relation identifier. Required.
40
+ href (str, optional): Link target URI. If omitted, provide `template`.
41
+ template (str, optional): URI Template (see RFC 6570). If omitted, provide `href`.
42
+ type (str, optional): Media type(s) of the linked resource (RFC 2616 §3.7).
43
+ accept (str, optional): Acceptable media type(s) for updating the linked resource (RFC 2616 §3.7).
44
+ allow (str, optional): Allowable HTTP methods to transition to the linked resource (RFC 2616 §14.7).
45
+ hreflang (str, optional): Language of the linked resource (e.g., BCP-47 tag).
46
+ title (str, optional): Human-readable label for the link.
47
+
48
+ Raises:
49
+ ValueError: If neither `href` nor `template` is provided.
50
+ """
51
+
52
+ """Attribution Information for a Genealogy, Conclusion, Subject and child classes
53
+
54
+ Args:
55
+ contributor (Agent, optional): Contributor to object being attributed.
56
+ modified (timestamp, optional): timestamp for when this record was modified.
57
+ changeMessage (str, optional): Birth date (YYYY-MM-DD).
58
+ creator (Agent, optional): Creator of object being attributed.
59
+ created (timestamp, optional): timestamp for when this record was created
60
+
61
+ Raises:
62
+
63
+ """
64
+ identifier = "http://gedcomx.org/v1/Link"
65
+
66
+ def __init__(self,
67
+ href: Optional[URI] = None,
68
+ template: Optional[str] = None,
69
+ type: Optional[str] = None,
70
+ accept: Optional[str] = None,
71
+ allow: Optional[str] = None,
72
+ hreflang: Optional[str] = None,
73
+ title: Optional[str] = None) -> None:
74
+
75
+
76
+ self.href = href if isinstance(href,URI) else URI.from_url(href) if isinstance(href,str) else None
77
+ self.template = template
78
+ self.type = type
79
+ self.accept = accept
80
+ self.allow = allow
81
+ self.hreflang = hreflang
82
+ self.title = title
83
+
84
+ if self.href is None: # and self.template is None:
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
+
115
+
116
+
117
+ @classmethod
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
135
+
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
+
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
+ )
156
+
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
@@ -1,4 +1,4 @@
1
- from .TextValue import TextValue
1
+ from .textvalue import TextValue
2
2
 
3
3
  class TopLevelTypeCollection:
4
4
  def __init__(self):
@@ -0,0 +1,50 @@
1
+ from .agent import Agent
2
+ from .address import Address
3
+ from .attribution import Attribution
4
+ from .conclusion import Conclusion
5
+ from .converter import GedcomConverter
6
+ from .coverage import Coverage
7
+ from .date import Date
8
+ from .document import Document
9
+ from .document import DocumentType
10
+ from .evidence_reference import EvidenceReference
11
+ from .extensible_enum import ExtensibleEnum
12
+ from .event import Event
13
+ from .event import EventType
14
+ from .event import EventRole
15
+ from .fact import Fact
16
+ from .fact import FactQualifier
17
+ from .fact import FactType
18
+ from .gedcom import Gedcom
19
+ from .gedcom5x import Gedcom5x, Gedcom5xRecord
20
+ from .gedcomx import GedcomX
21
+ from .gender import Gender, GenderType
22
+ from .group import Group, GroupRole
23
+ from .identifier import Identifier, IdentifierType, IdentifierList
24
+ from .Logging import get_logger
25
+ from .name import Name, NameForm, NamePart, NamePartType, NameType, NamePartQualifier
26
+ from .note import Note
27
+ from .online_account import OnlineAccount
28
+ from .person import Person, QuickPerson
29
+ from .place_description import PlaceDescription
30
+ from .place_reference import PlaceReference
31
+ from .qualifier import Qualifier
32
+ from .relationship import Relationship, RelationshipType
33
+ from .serialization import Serialization
34
+ from .source_citation import SourceCitation
35
+ from .source_description import SourceDescription
36
+ from .source_description import ResourceType
37
+ from .source_reference import SourceReference
38
+ from .subject import Subject
39
+ from .textvalue import TextValue
40
+ from .resource import Resource
41
+ from .uri import URI
42
+
43
+
44
+ from .Extensions.rs10.rsLink import rsLink
45
+
46
+ from .gedcom7.gedcom7 import Gedcom7, GedcomStructure
47
+ from .translation import g7toXtable
48
+
49
+
50
+
@@ -0,0 +1,217 @@
1
+ from typing import Any, Dict, Optional, List
2
+
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: address.py
7
+ Author: David J. Cartwright
8
+ Purpose:
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
+ #=====================================================================
31
+
32
+ class Address:
33
+ """A GedcomX Address Data Type
34
+ A GedcomX Address Data Type.
35
+
36
+ Represents a postal address according to the GedcomX conceptual model.
37
+
38
+ Args:
39
+ value (str, optional): A complete address as a single string.
40
+ city (str, optional): Name of the city or town.
41
+ country (str, optional): Name of the country.
42
+ postalCode (str, optional): Postal or ZIP code.
43
+ stateOrProvince (str, optional): Name of the state, province, or region.
44
+ street (str, optional): First street address line.
45
+ street2 (str, optional): Second street address line.
46
+ street3 (str, optional): Third street address line.
47
+ street4 (str, optional): Fourth street address line.
48
+ street5 (str, optional): Fifth street address line.
49
+ street6 (str, optional): Sixth street address line.
50
+ """
51
+
52
+ identifier = "http://gedcomx.org/v1/Address"
53
+ version = 'http://gedcomx.org/conceptual-model/v1'
54
+
55
+ def __init__(self, value: Optional[str] = None,
56
+ city: Optional[str] = None,
57
+ country: Optional[str] = None,
58
+ postalCode: Optional[str] = None,
59
+ stateOrProvince: Optional[str] = None,
60
+ street: Optional[str] = None,
61
+ street2: Optional[str] = None,
62
+ street3: Optional[str] = None,
63
+ street4: Optional[str] = None,
64
+ street5: Optional[str] = None,
65
+ street6: Optional[str] = None):
66
+
67
+ self._value = value #TODO impliment a parser for date strings.
68
+ self.city = city
69
+ self.country = country
70
+ self.postalCode = postalCode
71
+ self.stateOrProvince = stateOrProvince
72
+ self.street = street
73
+ self.street2 = street2
74
+ self.street3 = street3
75
+ self.street4 = street4
76
+ self.street5 = street5
77
+ self.street6 = street6
78
+
79
+ @property
80
+ def value(self) -> str:
81
+ return ', '.join(filter(None, [
82
+ self.street, self.street2, self.street3,
83
+ self.street4, self.street5, self.street6,
84
+ self.city, self.stateOrProvince,
85
+ self.postalCode, self.country
86
+ ]))
87
+
88
+ @value.setter
89
+ def value(self,value: str):
90
+ self._value = value
91
+ return
92
+ raise NotImplementedError("Parsing of a full address is not implimented.")
93
+
94
+ def _append(self,value):
95
+ if self._value:
96
+ self._value = self._value + ' ' + value
97
+ else:
98
+ self._value = value
99
+
100
+ def __eq__(self, other):
101
+ if not isinstance(other, self.__class__):
102
+ return False
103
+
104
+ return (
105
+ self.value == other.value and
106
+ self.city == other.city and
107
+ self.country == other.country and
108
+ self.postalCode == other.postalCode and
109
+ self.stateOrProvince == other.stateOrProvince and
110
+ self.street == other.street and
111
+ self.street2 == other.street2 and
112
+ self.street3 == other.street3 and
113
+ self.street4 == other.street4 and
114
+ self.street5 == other.street5 and
115
+ self.street6 == other.street6
116
+ )
117
+
118
+ def __str__(self) -> str:
119
+ # Combine non-empty address components into a formatted string
120
+ parts = [
121
+ self._value,
122
+ self.street,
123
+ self.street2,
124
+ self.street3,
125
+ self.street4,
126
+ self.street5,
127
+ self.street6,
128
+ self.city,
129
+ self.stateOrProvince,
130
+ self.postalCode,
131
+ self.country
132
+ ]
133
+
134
+ # Filter out any parts that are None or empty strings
135
+ filtered_parts = [str(part) for part in parts if part]
136
+
137
+ # Join the remaining parts with a comma and space
138
+ return ', '.join(filtered_parts)
139
+
140
+ @property
141
+ def _as_dict_(self):
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)
216
+
217
+