gedcom-x 0.5.6__py3-none-any.whl → 0.5.7__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.
gedcomx/Person.py CHANGED
@@ -1,22 +1,38 @@
1
- from enum import Enum
2
1
  from typing import List, Optional
3
2
  from urllib.parse import urljoin
4
3
 
4
+ """
5
+ ======================================================================
6
+ Project: Gedcom-X
7
+ File: Person.py
8
+ Author: David J. Cartwright
9
+ Purpose: Python Object representation of GedcomX Person Type
10
+
11
+ Created: 2025-08-25
12
+ Updated:
13
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
14
+
15
+ ======================================================================
16
+ """
17
+
18
+ """
19
+ ======================================================================
20
+ GEDCOM Module Types
21
+ ======================================================================
22
+ """
5
23
  from .Attribution import Attribution
6
24
  from .Conclusion import ConfidenceLevel
7
25
  from .Date import Date
8
26
  from .EvidenceReference import EvidenceReference
27
+ from .Extensions.rs10.rsLink import _rsLinkList
9
28
  from .Fact import Fact, FactType
10
29
  from .Gender import Gender, GenderType
11
30
  from .Identifier import IdentifierList
12
31
  from .Name import Name, QuickName
13
32
  from .Note import Note
33
+ from .Resource import Resource
14
34
  from .SourceReference import SourceReference
15
- from .Serialization import Serialization
16
35
  from .Subject import Subject
17
- from .Resource import Resource
18
- from collections.abc import Sized
19
- from .Extensions.rs10.rsLink import _rsLinkList
20
36
 
21
37
  class Person(Subject):
22
38
  """A person in the system.
@@ -40,7 +56,7 @@ class Person(Subject):
40
56
  notes: Optional[List[Note]] = None,
41
57
  confidence: Optional[ConfidenceLevel] = None,
42
58
  attribution: Optional[Attribution] = None,
43
- extracted: bool = None,
59
+ extracted: Optional[bool] = None,
44
60
  evidence: Optional[List[EvidenceReference]] = None,
45
61
  media: Optional[List[SourceReference]] = None,
46
62
  identifiers: Optional[IdentifierList] = None,
@@ -102,7 +118,7 @@ class Person(Subject):
102
118
  "ascendancyNumber": "1",
103
119
  "deathDate": "from 2001 to 2005",
104
120
  "descendancyNumber": "1",
105
- "gender": self.gender.type,
121
+ "gender": self.gender.type if self.gender else 'Unknown',
106
122
  "lifespan": "-2005",
107
123
  "name": self.names[0].nameForms[0].fullText
108
124
  }
@@ -111,17 +127,20 @@ class Person(Subject):
111
127
 
112
128
  @property
113
129
  def _as_dict_(self):
114
- type_as_dict = super()._as_dict_ # Start with base class fields
115
- # Only add Relationship-specific fields
116
- type_as_dict.update({
117
- 'private': self.private,
118
- 'living': self.living,
119
- 'gender': self.gender._as_dict_ if self.gender else None,
120
- 'names': [name._as_dict_ for name in self.names],
121
- 'facts': [fact._as_dict_ for fact in self.facts],
122
- 'uri': self.uri._as_dict_ if self.uri else None
123
-
124
- })
130
+ from .Serialization import Serialization
131
+ type_as_dict = super()._as_dict_
132
+ if self.private is not None:
133
+ type_as_dict['private'] = self.private
134
+ if self.living is not None:
135
+ type_as_dict['living'] = self.living
136
+ if self.gender:
137
+ type_as_dict['gender'] = self.gender._as_dict_
138
+ if self.names:
139
+ type_as_dict['names'] = [n._as_dict_ for n in self.names if n]
140
+ if self.facts:
141
+ type_as_dict['facts'] = [f._as_dict_ for f in self.facts if f]
142
+ if self.uri:
143
+ type_as_dict['uri'] = self.uri._as_dict_
125
144
 
126
145
  return Serialization.serialize_dict(type_as_dict)
127
146
 
@@ -130,10 +149,12 @@ class Person(Subject):
130
149
  """
131
150
  Create a Person instance from a JSON-dict (already parsed).
132
151
  """
152
+ from .Serialization import Serialization
133
153
  return Serialization.deserialize(data, Person)
134
154
 
135
155
  @classmethod
136
156
  def from_familysearch(cls, pid: str, token: str, *, base_url: Optional[str] = None):
157
+ from .Serialization import Serialization
137
158
  """
138
159
  Fetch a single person by PID from FamilySearch and return a Person.
139
160
  - pid: e.g. "KPHP-4B4"
@@ -1,24 +1,65 @@
1
1
  from typing import List, Optional
2
2
 
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: PlaceDescription.py
7
+ Author: David J. Cartwright
8
+ Purpose: Python Object representation of GedcomX PlaceDescription Type
9
+
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
13
+
14
+ ======================================================================
15
+ """
16
+
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
3
22
  from .Attribution import Attribution
4
23
  from .Conclusion import ConfidenceLevel
5
24
  from .Date import Date
6
25
  from .EvidenceReference import EvidenceReference
7
26
  from .Identifier import IdentifierList
8
27
  from .Note import Note
9
- from .SourceReference import SourceReference
10
- from .TextValue import TextValue
11
28
  from .Resource import Resource
12
- from .Serialization import Serialization
29
+ from .SourceReference import SourceReference
13
30
  from .Subject import Subject
31
+ from .TextValue import TextValue
14
32
  from .URI import URI
15
33
 
16
34
  class PlaceDescription(Subject):
35
+ """PlaceDescription describes the details of a place in terms of
36
+ its name and possibly its type, time period, and/or a geospatial description
37
+ functioning as a description of a place as a snapshot in time.
38
+
39
+ Encapsulates textual names, geospatial coordinates, jurisdictional context,
40
+ temporal coverage, and related resources (media, sources, evidence, etc.).
41
+
42
+
43
+ Attributes:
44
+ names (Optional[List[TextValue]]): Human-readable names or labels for
45
+ the place (e.g., “Boston, Suffolk, Massachusetts, United States”).
46
+ type (Optional[str]): A place type identifier (e.g., a URI). **TODO:**
47
+ replace with an enumeration when finalized.
48
+ place (Optional[URI]): Canonical identifier (URI) for the place.
49
+ jurisdiction (Optional[Resource|PlaceDescription]): The governing or
50
+ containing jurisdiction of this place (e.g., county for a town).
51
+ latitude (Optional[float]): Latitude in decimal degrees (WGS84).
52
+ longitude (Optional[float]): Longitude in decimal degrees (WGS84).
53
+ temporalDescription (Optional[Date]): Temporal coverage/validity window
54
+ for this description (e.g., when a jurisdictional boundary applied).
55
+ spatialDescription (Optional[Resource]): A resource describing spatial
56
+ geometry or a link to an external gazetteer/shape definition.
57
+ """
17
58
  identifier = "http://gedcomx.org/v1/PlaceDescription"
18
59
  version = 'http://gedcomx.org/conceptual-model/v1'
19
60
 
20
61
  def __init__(self, id: Optional[str] =None,
21
- lang: str = 'en',
62
+ lang: Optional[str] = None,
22
63
  sources: Optional[List[SourceReference]] = None,
23
64
  analysis: Optional[Resource] = None,
24
65
  notes: Optional[List[Note]] =None,
@@ -29,9 +70,9 @@ class PlaceDescription(Subject):
29
70
  media: Optional[List[SourceReference]] = None,
30
71
  identifiers: Optional[IdentifierList] = None,
31
72
  names: Optional[List[TextValue]] = None,
32
- type: Optional[str] = None,
73
+ type: Optional[str] = None, #TODO This needs to be an enumerated value, work out details
33
74
  place: Optional[URI] = None,
34
- jurisdiction: Optional["Resource | PlaceDescription"] = None, # PlaceDescription
75
+ jurisdiction: Optional["Resource | PlaceDescription"] = None,
35
76
  latitude: Optional[float] = None,
36
77
  longitude: Optional[float] = None,
37
78
  temporalDescription: Optional[Date] = None,
@@ -42,24 +83,33 @@ class PlaceDescription(Subject):
42
83
  self.type = type
43
84
  self.place = place
44
85
  self.jurisdiction = jurisdiction
45
- self.latitide = latitude
46
- self.longitute = longitude
86
+ self.latitude = latitude
87
+ self.longitude = longitude
47
88
  self.temporalDescription = temporalDescription
48
- self.spacialDescription = spatialDescription
89
+ self.spatialDescription = spatialDescription
49
90
 
50
91
  @property
51
92
  def _as_dict_(self):
93
+ from .Serialization import Serialization
52
94
  type_as_dict = super()._as_dict_
53
- type_as_dict.update({
54
- "names": [n for n in self.names] if self.names else None,
55
- "type": self.type if self.type else None,
56
- "place": self.place._as_dict_ if self.place else None,
57
- "jurisdiction": self.jurisdiction._as_dict_ if self.jurisdiction else None,
58
- "latitude": float(self.latitide) if self.latitide else None,
59
- "longitude": float(self.longitute) if self.longitute else None,
60
- "temporalDescription": self.temporalDescription if self.temporalDescription else None,
61
- "spatialDescription": self.spacialDescription._as_dict_ if self.spacialDescription else None
62
- })
95
+
96
+ if self.names:
97
+ type_as_dict["names"] = [n._as_dict_ for n in self.names if n]
98
+ if self.type:
99
+ type_as_dict["type"] = self.type #TODO
100
+ if self.place:
101
+ type_as_dict["place"] = self.place._as_dict_
102
+ if self.jurisdiction:
103
+ type_as_dict["jurisdiction"] = self.jurisdiction._as_dict_
104
+ if self.latitude is not None: # include 0.0; exclude only None
105
+ type_as_dict["latitude"] = float(self.latitude)
106
+ if self.longitude is not None: # include 0.0; exclude only None
107
+ type_as_dict["longitude"] = float(self.longitude)
108
+ if self.temporalDescription:
109
+ type_as_dict["temporalDescription"] = self.temporalDescription._as_dict_
110
+ if self.spatialDescription:
111
+ type_as_dict["spatialDescription"] = self.spatialDescription._as_dict_
112
+
63
113
  return Serialization.serialize_dict(type_as_dict)
64
114
 
65
115
  @classmethod
@@ -67,4 +117,5 @@ class PlaceDescription(Subject):
67
117
  """
68
118
  Create a PlaceDescription instance from a JSON-dict (already parsed).
69
119
  """
120
+ from .Serialization import Serialization
70
121
  return Serialization.deserialize(data, PlaceDescription)
gedcomx/PlaceReference.py CHANGED
@@ -1,29 +1,61 @@
1
- from typing import Optional
1
+ from __future__ import annotations
2
+ from typing import Optional, TYPE_CHECKING
3
+ if TYPE_CHECKING:
4
+ from .PlaceDescription import PlaceDescription
2
5
 
6
+ """
7
+ ======================================================================
8
+ Project: Gedcom-X
9
+ File: PlaceReference.py
10
+ Author: David J. Cartwright
11
+ Purpose: Python Object representation of GedcomX PlaceReference Type
12
+
13
+ Created: 2025-08-25
14
+ Updated:
15
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
16
+
17
+ ======================================================================
18
+ """
19
+
20
+ """
21
+ ======================================================================
22
+ GEDCOM Module Types
23
+ ======================================================================
24
+ """
3
25
  from .Resource import Resource
4
- from .Serialization import Serialization
5
26
 
6
27
  class PlaceReference:
28
+ """defines a reference to a PlaceDescription.
29
+
30
+
31
+ Attributes:
32
+ original (Optional[str]): The unnormalized, user- or source-provided place text.
33
+ Keep punctuation and ordering exactly as recorded in the source.
34
+ description (Optional[Resource|PlaceDescription]): A :class:`gedcomx.PlaceDescription` Object or pointer to it. (URI/:class:`~Resource`)
35
+
36
+ """
7
37
  identifier = 'http://gedcomx.org/v1/PlaceReference'
8
38
  version = 'http://gedcomx.org/conceptual-model/v1'
9
39
 
10
40
  def __init__(self,
11
41
  original: Optional[str] = None,
12
- description: Optional[Resource] = None) -> None:
42
+ description: Optional["Resource | PlaceDescription"] = None) -> None:
13
43
  self.original = original
14
44
  self.description = description
15
45
 
16
46
  @property
17
47
  def _as_dict_(self):
18
- type_as_dict = {
19
- 'original': self.original,
20
- 'description': self.description._as_dict_ if self.description else None
21
- }
48
+ from .Serialization import Serialization
49
+ type_as_dict = {}
50
+ if self.original:
51
+ type_as_dict['original'] = self.original
52
+ if self.description:
53
+ type_as_dict['description'] = self.description._as_dict_
22
54
  return Serialization.serialize_dict(type_as_dict)
23
55
 
24
56
  @classmethod
25
57
  def _from_json_(cls, data):
26
-
58
+ from .Serialization import Serialization
27
59
  return Serialization.deserialize(data, PlaceReference)
28
60
 
29
61
 
gedcomx/Qualifier.py CHANGED
@@ -1,6 +1,33 @@
1
1
  from typing import Optional
2
2
 
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: Qualifier.py
7
+ Author: David J. Cartwright
8
+ Purpose: Python Object representation of GedcomX Qualifier Type
9
+
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that
13
+ hold data, updated _from_json
14
+
15
+ ======================================================================
16
+ """
17
+
3
18
  class Qualifier:
19
+ """defines the data structure used to supply additional details, annotations,
20
+ tags, or other qualifying data to a specific data element.
21
+
22
+
23
+ Attributes:
24
+ name str: The name of the Qualifier. *It is RECOMMENDED that the qualifier
25
+ name resolve to an element of a constrained vocabulary.*
26
+
27
+ value (Optional[str]): The value of the Qualifier. *If provided, the name
28
+ MAY give the semantic meaning of the value.*
29
+
30
+ """
4
31
  identifier = 'http://gedcomx.org/v1/Qualifier'
5
32
  version = 'http://gedcomx.org/conceptual-model/v1'
6
33
 
@@ -10,18 +37,18 @@ class Qualifier:
10
37
 
11
38
  @property
12
39
  def __as_dict__(self):
13
- from .Serialization import serialize_to_dict
14
-
15
- data = {
16
- "name":self.name if self.name else None,
17
- "value":self.value if self.value else None
18
- }
40
+ from .Serialization import Serialization
19
41
 
20
- return serialize_to_dict(data,False)
42
+ type_as_dict = {}
43
+ if self.name:
44
+ type_as_dict["name"] = self.name
45
+ if self.value:
46
+ type_as_dict["value"] = self.value
47
+
48
+ return Serialization.serialize_dict(type_as_dict)
21
49
 
22
- # Qualifier
23
- Qualifier._from_json_ = classmethod(lambda cls, data: Qualifier(
24
- name=data.get('name'),
25
- value=data.get('value')
26
- ))
50
+ @classmethod
51
+ def _from_json(cls,data):
52
+ qualifier = Qualifier(name=data.get('name',"ERROR: This Qualifier require a 'name' but has none."),value=data.get('value'))
53
+ return qualifier
27
54
 
gedcomx/Relationship.py CHANGED
@@ -8,7 +8,7 @@ from .Fact import Fact
8
8
  from .Identifier import Identifier
9
9
  from .Note import Note
10
10
  from .Person import Person
11
- from .Serialization import Serialization
11
+
12
12
  from .SourceReference import SourceReference
13
13
  from .Resource import Resource
14
14
 
@@ -77,11 +77,12 @@ class Relationship(Subject):
77
77
 
78
78
  @property
79
79
  def _as_dict_(self):
80
+ from .Serialization import Serialization
80
81
  type_as_dict = super()._as_dict_
81
82
  type_as_dict.update({
82
83
  "type": self.type.value if isinstance(self.type, RelationshipType) else self.type,
83
- "person1": self.person1._as_dict_ if self.person1 else None,
84
- "person2": self.person2._as_dict_ if self.person2 else None,
84
+ "person1": Resource(target=self.person1)._as_dict_ if self.person1 else None,
85
+ "person2": Resource(target=self.person1)._as_dict_ if self.person2 else None,
85
86
  "facts": [fact for fact in self.facts] if self.facts else None
86
87
  })
87
88
  return Serialization.serialize_dict(type_as_dict)
@@ -91,5 +92,6 @@ class Relationship(Subject):
91
92
  """
92
93
  Create a Person instance from a JSON-dict (already parsed).
93
94
  """
95
+ from .Serialization import Serialization
94
96
  return Serialization.deserialize(data, Relationship)
95
97
 
gedcomx/Resource.py CHANGED
@@ -1,5 +1,24 @@
1
- from typing import List, Optional
1
+ from typing import Optional
2
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
+
14
+ ======================================================================
15
+ """
16
+
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
3
22
 
4
23
  from .URI import URI
5
24
 
@@ -15,6 +34,7 @@ class Resource:
15
34
  ValueError
16
35
  If `id` is not a valid UUID.
17
36
  """
37
+ # TODO, Deal with a resouce being passed, as it may be unresolved.
18
38
  def __init__(self,uri: Optional[URI|str] = None, id:Optional[str] = None,top_lvl_object: Optional[object] = None,target= None) -> None:
19
39
 
20
40
  self.resource = URI.from_url(uri.value) if isinstance(uri,URI) else URI.from_url(uri) if isinstance(uri,str) else None
@@ -26,15 +46,27 @@ class Resource:
26
46
  self.remote: bool | None = None # is the resource pointed to persitent on a remote datastore?
27
47
 
28
48
  if target:
29
- self.resource = target._uri
30
- self.Id = target.id
31
- self.type = type(target)
49
+ if isinstance(target,Resource):
50
+ self.resource = target.resource
51
+ self.Id = target.Id
52
+ self.target = target.target
53
+ else:
54
+ self.resource = target.uri
55
+ self.Id = target.id
56
+ self.type = type(target)
32
57
 
58
+ @property
59
+ def uri(self):
60
+ return self.resource
61
+
33
62
  @property
34
63
  def _as_dict_(self):
35
64
  from .Serialization import Serialization
36
- typ_as_dict = {'resource':self.resource.value if self.resource else None,
37
- 'resourceId':self.Id}
65
+ typ_as_dict = {}
66
+ if self.resource:
67
+ typ_as_dict['resource'] = self.resource.value if self.resource else None
68
+ if self.Id:
69
+ typ_as_dict['resourceId'] = self.Id
38
70
  return Serialization.serialize_dict(typ_as_dict)
39
71
 
40
72
  @classmethod
@@ -49,27 +81,5 @@ class Resource:
49
81
  def __str__(self) -> str:
50
82
  return f"{self.resource}{f', id={self.Id}' if self.Id else ''}"
51
83
 
52
- def get_resource_as_dict(value):
53
- """
54
- If value is truthy:
55
- - If it's already a Resource, return it.
56
- - Otherwise, wrap it in Resource using (value._uri, value.id).
57
- Returns None if value is falsy.
58
- """
59
-
60
- if not value:
61
- return None
62
84
 
63
- if isinstance(value, Resource):
64
- return value._as_dict_
65
-
66
- try:
67
- return Resource(
68
- getattr(value, "uri", None),
69
- getattr(value, "id", None)
70
- )._as_dict_
71
- except AttributeError:
72
- print('get_resource_as_dict',type(value),value)
73
- print((f"value: {value} as inproper attributes"))
74
- exit()
75
85