gedcom-x 0.5__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.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: gedcom-x
3
+ Version: 0.5
4
+ Summary: Python implimentation of gedcom-x standard
5
+ Author-email: "David J. Cartwright" <davidcartwright@hotmail.com>
6
+ License: MIT
7
+ Keywords: gedcom,gedcomx,ged
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Utilities
14
+ Classifier: Topic :: Software Development :: User Interfaces
15
+ Requires-Python: >=3.6
16
+ Description-Content-Type: text/markdown
17
+ Dynamic: requires-python
@@ -0,0 +1,37 @@
1
+ gedcomx/Address.py,sha256=gAi9vfSLdTBAsNpCNIV9mdOaB9ewoPpW-7Xrt6JYsPc,3672
2
+ gedcomx/Agent.py,sha256=lp_--92eEo1df0nBqV2pJJafv_Zfzfz_bNj-UlJIjJk,3478
3
+ gedcomx/Attribution.py,sha256=yVctLjdgDWp4d-uOOxHXsCyJvvGLyzkY-x2ysiYv4yc,4367
4
+ gedcomx/Conclusion.py,sha256=hymaHcHk2JayGAGpxOlJvxzxnDjPh2q67eXI9TQk50c,5240
5
+ gedcomx/Coverage.py,sha256=FuVsLDAhzS4HjNHKOHCjwQcvnxd4Uf_EeF3QcDEbems,912
6
+ gedcomx/Date.py,sha256=A21KaTl44dLnMuB2hAcZ7QbqmgaQC27LoNPl4uI3i48,741
7
+ gedcomx/Document.py,sha256=F04hRAddU5OmwAeULbXJRRAPh5CJIzeaaxFK6TI48d0,1850
8
+ gedcomx/Event.py,sha256=qZNGEYdS3jmKI0fnw1DM5zETTw7Du07A_1tr3sc02-w,9538
9
+ gedcomx/EvidenceReference.py,sha256=8LNyfnjLmXt4RbOHpRyw-VHyD2CCm33pPYuYqWIaxtg,333
10
+ gedcomx/Fact.py,sha256=7UVDiimuF8LJG7ASqVdCLKed1YWqb4YhAANI0KJLwdA,23682
11
+ gedcomx/Gedcom.py,sha256=Dn0I8SInN1K_Q_K2CAzZYX1Zer3TwGaNlw5J4rJj688,12692
12
+ gedcomx/GedcomX.py,sha256=bWNeBPxFGaVeTIbHgyEzrefk6rwq-XzI3VOkxsrp5aw,50225
13
+ gedcomx/Gender.py,sha256=98__1lMIdGLfMmUSh5jrqQ0SwQzN1xedgMiW9Gpabko,1720
14
+ gedcomx/Group.py,sha256=ayp3wZEnR8vSuPwe-KfeH8MM0uQ3HJH8NGRnHDPV58M,1586
15
+ gedcomx/Identifier.py,sha256=aFXJQNyWvfnIzUt7ALrRTtwQjSjuToCY7qLBFFVmRC8,3398
16
+ gedcomx/Name.py,sha256=cQkWDjhuF62aoRdGUBXZy6kfvXAmUz7AxWDbnZ-bWIA,11650
17
+ gedcomx/Note.py,sha256=PLElHAQ3XFwETL71UgGkmP4ir5s9nBo3X83jwrRBnTc,2132
18
+ gedcomx/OnlineAccount.py,sha256=dm9HbWWf1Xekn8grDJtWChDOR-hxrIxmIRFiPRx2SQQ,276
19
+ gedcomx/Person.py,sha256=B2zqHN8kYq4K8uRIayRxYOi6Dp7fiiv9EKd-pNDxG6U,7357
20
+ gedcomx/PlaceDescription.py,sha256=EEBWAuxHQr1Kx4PoUOOsu26g4I5ICV1emx1mQXEU1vM,1971
21
+ gedcomx/PlaceReference.py,sha256=N7kU8pjtmsru5tzD3cdyXkYlaRb7ncueyCqj9fihi6U,929
22
+ gedcomx/Qualifier.py,sha256=mZ_GWT3gca8g9nWidlXIRTN5dEtf_rmnLHYl1jJkQYs,728
23
+ gedcomx/Relationship.py,sha256=MVuIzHxw6jTFfOx-HK4VhnMxystZ_tWA3dv1cHJJQjo,4599
24
+ gedcomx/Serialization.py,sha256=LYH3HuSwTLEmM8DQyU0MTrIUBIThGy7uG45AfzDaCrA,1277
25
+ gedcomx/SourceCitation.py,sha256=WyeXHS2HbqxHCmVvcffr5xNTK8xxy68yaY_wX8seJuY,622
26
+ gedcomx/SourceDescription.py,sha256=dOg7-omwHVEI2-NaRNPh8ciXKwJ1ooZKDd3iwBzkiP0,11884
27
+ gedcomx/SourceReference.py,sha256=OpD1hQH_xA-BGw7Lt17wU9u1L98V2FBtxlNQgJrMXrc,7252
28
+ gedcomx/Subject.py,sha256=jGDYgTDIaQrECC9DtQBS2k76ik-yr39ZVDyjhQKCem8,3116
29
+ gedcomx/TextValue.py,sha256=znGDzzb-pNCaatwS7kxcUT1g_csswhnD5zebkLKmdV8,1098
30
+ gedcomx/TopLevelTypeCollection.py,sha256=nvTO6GwFwEZk9jX4fVqhy75ygsshomNb20tnlExKqyY,1495
31
+ gedcomx/URI.py,sha256=OUSCFA7oOtJ7Ya4mTJFoJawq0bZt8BDg8masb9XX-cY,2143
32
+ gedcomx/_Resource.py,sha256=JqtbinRXpt6dLUXUtqGCZ2WmcYmGRzqXmjaw8YPpnYE,242
33
+ gedcomx/__init__.py,sha256=brX-pkFLY5owEk1CKnArX5qXlpTu9PTu0mG_VG12ox4,1362
34
+ gedcom_x-0.5.dist-info/METADATA,sha256=m_ewdjTzhhU2JSxpZvKT2LI_Dopam9Kbdt2X_GVPtiA,631
35
+ gedcom_x-0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ gedcom_x-0.5.dist-info/top_level.txt,sha256=smVBF4nxSU-mzCd6idtRYTbYjPICMMi8pTqewEmqF8Y,8
37
+ gedcom_x-0.5.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ gedcomx
gedcomx/Address.py ADDED
@@ -0,0 +1,100 @@
1
+ from typing import Optional
2
+ import json
3
+
4
+ class Address:
5
+ identifier = "http://gedcomx.org/v1/Address"
6
+ version = 'http://gedcomx.org/conceptual-model/v1'
7
+
8
+ def __init__(self, value: Optional[str] = None,
9
+ city: Optional[str] = None,
10
+ country: Optional[str] = None,
11
+ postalCode: Optional[str] = None,
12
+ stateOrProvince: Optional[str] = None,
13
+ street: Optional[str] = None,
14
+ street2: Optional[str] = None,
15
+ street3: Optional[str] = None,
16
+ street4: Optional[str] = None,
17
+ street5: Optional[str] = None,
18
+ street6: Optional[str] = None):
19
+
20
+ self._value = value #TODO impliment a parser for date strings.
21
+ self.city = city
22
+ self.country = country
23
+ self.postalCode = postalCode
24
+ self.stateOrProvince = stateOrProvince
25
+ self.street = street
26
+ self.street2 = street2
27
+ self.street3 = street3
28
+ self.street4 = street4
29
+ self.street5 = street5
30
+ self.street6 = street6
31
+
32
+ @property
33
+ def value(self) -> Optional[str]:
34
+ if self._value:
35
+ return self._value
36
+ return ', '.join(filter(None, [
37
+ self._street, self._street2, self._street3,
38
+ self._street4, self._street5, self._street6,
39
+ self._city, self._stateOrProvince,
40
+ self._postalCode, self._country
41
+ ]))
42
+
43
+
44
+ def __eq__(self, other):
45
+ if not isinstance(other, self.__class__):
46
+ return False
47
+
48
+ return (
49
+ self.value == other.value and
50
+ self.city == other.city and
51
+ self.country == other.country and
52
+ self.postalCode == other.postalCode and
53
+ self.stateOrProvince == other.stateOrProvince and
54
+ self.street == other.street and
55
+ self.street2 == other.street2 and
56
+ self.street3 == other.street3 and
57
+ self.street4 == other.street4 and
58
+ self.street5 == other.street5 and
59
+ self.street6 == other.street6
60
+ )
61
+
62
+ def __str__(self) -> str:
63
+ # Combine non-empty address components into a formatted string
64
+ parts = [
65
+ self._value,
66
+ self.street,
67
+ self.street2,
68
+ self.street3,
69
+ self.street4,
70
+ self.street5,
71
+ self.street6,
72
+ self.city,
73
+ self.stateOrProvince,
74
+ self.postalCode,
75
+ self.country
76
+ ]
77
+
78
+ # Filter out any parts that are None or empty strings
79
+ filtered_parts = [str(part) for part in parts if part]
80
+
81
+ # Join the remaining parts with a comma and space
82
+ return ', '.join(filtered_parts)
83
+
84
+ @property
85
+ def __as_dict__(self):
86
+ return {
87
+ #"value": self._value if self._value else None,
88
+ "city": self.city if self.city else None,
89
+ "country": self.country if self.country else None,
90
+ "postalCode": self.postalCode if self.postalCode else None,
91
+ "stateOrProvince": self.stateOrProvince if self.stateOrProvince else None,
92
+ "street": self.street if self.street else None,
93
+ "street2": self.street2 if self.street2 else None,
94
+ "street3": self.street3 if self.street3 else None,
95
+ "street4": self.street4 if self.street4 else None,
96
+ "street5": self.street5 if self.street5 else None,
97
+ "street6": self.street6 if self.street6 else None
98
+ }
99
+
100
+
gedcomx/Agent.py ADDED
@@ -0,0 +1,83 @@
1
+ import base64
2
+ import uuid
3
+
4
+ from typing import List, Optional
5
+
6
+ from .Address import Address
7
+ from .Identifier import Identifier
8
+ from .OnlineAccount import OnlineAccount
9
+ from .TextValue import TextValue
10
+ from .URI import URI
11
+
12
+ class Agent:
13
+ @staticmethod
14
+ def default_id_generator():
15
+ # Generate a standard UUID
16
+ standard_uuid = uuid.uuid4()
17
+ # Convert UUID to bytes
18
+ uuid_bytes = standard_uuid.bytes
19
+ # Encode bytes to a Base64 string
20
+ short_uuid = base64.urlsafe_b64encode(uuid_bytes).rstrip(b'=').decode('utf-8')
21
+ return short_uuid
22
+
23
+ def __init__(self, id: Optional[str] = None,
24
+ identifiers: Optional[List[Identifier]] = [],
25
+ names: Optional[List[TextValue]] = [],
26
+ homepage: Optional[URI] = None,
27
+ openid: Optional[URI] = None,
28
+ accounts: Optional[List[OnlineAccount]] = [],
29
+ emails: Optional[List[URI]] = [],
30
+ phones: Optional[List[URI]] = [],
31
+ addresses: Optional[List[Address]] = [],
32
+ person: Optional[object] | Optional[URI] = None, # should be of Type 'Person', 'object' to avoid circular imports
33
+ uri: URI = None):
34
+
35
+ self._id_generator = Agent.default_id_generator
36
+
37
+ self.id = id if id else self._id_generator()
38
+ self.identifiers = identifiers
39
+ self.names = names
40
+ self.homepage = homepage
41
+ self.openid = openid
42
+ self.accounts = accounts
43
+ self.emails = emails
44
+ self.phones = phones
45
+ self.addresses = addresses
46
+
47
+ self._uri = URI(fragment="agent:"+ self.id)
48
+
49
+ def _append_to_name(self, text_to_append: str):
50
+ self.names[0].value = self.names[0].value + text_to_append
51
+
52
+ def add_address(self, address_to_add: Address):
53
+ if address_to_add and isinstance(address_to_add, Address):
54
+ for current_address in self.addresses:
55
+ if address_to_add == current_address:
56
+ return False
57
+ self.addresses.append(address_to_add)
58
+ else:
59
+ raise ValueError(f"address must be of type Address, not {type(address_to_add)}")
60
+
61
+ def add_name(self, name_to_add: TextValue):
62
+ if isinstance(name_to_add,str): name_to_add = TextValue(value=name_to_add)
63
+ if name_to_add and isinstance(name_to_add,TextValue):
64
+ for current_name in self.names:
65
+ if name_to_add == current_name:
66
+ return
67
+ self.names.append(name_to_add)
68
+ else:
69
+ raise ValueError(f'name must be of type str or TextValue, recived {type(name_to_add)}')
70
+
71
+ @property
72
+ def __as_dict__(self):
73
+ return {
74
+ "id": self.id if self.id else None,
75
+ "identifiers": [identifier.__as_dict__() for identifier in self.identifiers],
76
+ "names": [name._prop_dict() for name in self.names],
77
+ "homepage": self.homepage if self.homepage else None,
78
+ "openid": self.openid if self.openid else None,
79
+ "accounts": self.accounts if self.accounts else None,
80
+ "emails": self.emails if self.emails else None,
81
+ "phones": self.phones if self.phones else None,
82
+ "addresses": [address.__as_dict__ for address in self.addresses]
83
+ }
gedcomx/Attribution.py ADDED
@@ -0,0 +1,116 @@
1
+ from datetime import datetime
2
+ from typing import Optional, Dict, Any
3
+
4
+ from .URI import URI
5
+
6
+ class Attribution:
7
+ identifier = 'http://gedcomx.org/v1/Attribution'
8
+ version = 'http://gedcomx.org/conceptual-model/v1'
9
+
10
+ def __init__(self,contributor: Optional[URI],
11
+ modified: Optional[datetime],
12
+ changeMessage: Optional[str],
13
+ creator: Optional[URI],
14
+ created: Optional[datetime]) -> None:
15
+
16
+
17
+ from .Agent import Agent
18
+ self._contributor_object = None
19
+ self.modified = modified
20
+ self.changeMessage = changeMessage
21
+ self.creator = creator
22
+ self.created = created
23
+
24
+
25
+ if isinstance(contributor,URI):
26
+ # TODO DEAL WITH URI <------------------------------------------------------------------------------------------------------------------
27
+ self._contributor_object = contributor
28
+ elif isinstance(contributor,Agent):
29
+ self._contributor_object = contributor
30
+ if hasattr(contributor,'_uri'):
31
+ self.contributor = contributor._uri
32
+ else:
33
+ assert False
34
+ self.description = URI(object=description)
35
+ description._uri = self.description
36
+ description._object = description
37
+ else:
38
+ raise ValueError(f"'description' must be of type 'SourceDescription' or 'URI', type: {type(contributor)} was provided, with value: {contributor}")
39
+
40
+ @property
41
+ def _as_dict_(self) -> Dict[str, Any]:
42
+ """
43
+ Serialize Attribution to a JSON-ready dict, skipping None values.
44
+ """
45
+ def _fmt_dt(dt: datetime) -> str:
46
+ # ISO 8601 format
47
+ return dt.isoformat()
48
+
49
+ data: Dict[str, Any] = {}
50
+ if self._contributor_object:
51
+ data['contributor'] = (self._contributor_object._as_dict_
52
+ if hasattr(self._contributor_object, '_as_dict_') else
53
+ None)
54
+ if self.created:
55
+ data['created'] = _fmt_dt(self.created)
56
+ if self.creator:
57
+ data['creator'] = (self.creator._prop_dict()
58
+ if hasattr(self.creator, '_prop_dict') else
59
+ self.creator._prop_dict())
60
+ if self.modified:
61
+ data['modified'] = _fmt_dt(self.modified)
62
+ if self.changeMessage is not None:
63
+ data['changeMessage'] = self.changeMessage
64
+ return data
65
+
66
+ @classmethod
67
+ def _from_json_(cls, data: Dict[str, Any]) -> 'Attribution':
68
+ """
69
+ Construct Attribution from a dict (as parsed from JSON).
70
+ Handles 'created' and 'modified' as ISO strings or epoch ms ints.
71
+ """
72
+ # contributor
73
+ contrib = None
74
+ if 'contributor' in data:
75
+ raw = data['contributor']
76
+ if isinstance(raw, dict):
77
+ contrib = URI._from_json_(raw)
78
+ elif isinstance(raw, str):
79
+ contrib = URI(uri=raw)
80
+
81
+ # creator
82
+ creat = None
83
+ if 'creator' in data:
84
+ raw = data['creator']
85
+ if isinstance(raw, dict):
86
+ creat = URI._from_json_(raw)
87
+ elif isinstance(raw, str):
88
+ creat = URI(uri=raw)
89
+
90
+ # parse created date
91
+ raw_created = data.get('created')
92
+ if isinstance(raw_created, (int, float)):
93
+ created_dt = datetime.fromtimestamp(raw_created / 1000.0)
94
+ elif isinstance(raw_created, str):
95
+ created_dt = datetime.fromisoformat(raw_created)
96
+ else:
97
+ created_dt = None
98
+
99
+ # parse modified date
100
+ raw_modified = data.get('modified')
101
+ if isinstance(raw_modified, (int, float)):
102
+ modified_dt = datetime.fromtimestamp(raw_modified / 1000.0)
103
+ elif isinstance(raw_modified, str):
104
+ modified_dt = datetime.fromisoformat(raw_modified)
105
+ else:
106
+ modified_dt = None
107
+
108
+ change_msg = data.get('changeMessage')
109
+
110
+ return cls(
111
+ contributor=contrib,
112
+ created=created_dt,
113
+ creator=creat,
114
+ modified=modified_dt,
115
+ changeMessage=change_msg
116
+ )
gedcomx/Conclusion.py ADDED
@@ -0,0 +1,137 @@
1
+ import base64
2
+ import uuid
3
+ import warnings
4
+
5
+ from typing import List, Optional
6
+
7
+ from .Attribution import Attribution
8
+ from .Note import Note
9
+ from .Qualifier import Qualifier
10
+ from .SourceReference import SourceReference
11
+ from .URI import URI
12
+
13
+ class ConfidenceLevel(Qualifier):
14
+ High = "http://gedcomx.org/High"
15
+ Medium = "http://gedcomx.org/Medium"
16
+ Low = "http://gedcomx.org/Low"
17
+
18
+ @property
19
+ def description(self):
20
+ descriptions = {
21
+ ConfidenceLevel.High: "The contributor has a high degree of confidence that the assertion is true.",
22
+ ConfidenceLevel.Medium: "The contributor has a medium degree of confidence that the assertion is true.",
23
+ ConfidenceLevel.Low: "The contributor has a low degree of confidence that the assertion is true."
24
+ }
25
+ return descriptions.get(self, "No description available.")
26
+
27
+ class Conclusion:
28
+ identifier = 'http://gedcomx.org/v1/Conclusion'
29
+ version = 'http://gedcomx.org/conceptual-model/v1'
30
+
31
+ @staticmethod
32
+ def default_id_generator():
33
+ # Generate a standard UUID
34
+ standard_uuid = uuid.uuid4()
35
+ # Convert UUID to bytes
36
+ uuid_bytes = standard_uuid.bytes
37
+ # Encode bytes to a Base64 string
38
+ short_uuid = base64.urlsafe_b64encode(uuid_bytes).rstrip(b'=').decode('utf-8')
39
+ return short_uuid
40
+
41
+ def __init__(self,
42
+ id: Optional[str],
43
+ lang: Optional[str] = 'en',
44
+ sources: Optional[List[SourceReference]] = [],
45
+ analysis: Optional[URI] = None,
46
+ notes: Optional[List[Note]] = [],
47
+ confidence: Optional[ConfidenceLevel] = None,
48
+ attribution: Optional[Attribution] = None,
49
+ uri: Optional[URI] = None,
50
+ max_note_count: int = 20) -> None:
51
+
52
+ self._id_generator = Conclusion.default_id_generator
53
+
54
+ self.id = id if id else self._id_generator()
55
+ self.lang = lang
56
+ self.sources = sources
57
+ self.analysis = analysis
58
+ self.notes = notes
59
+ self.confidence = confidence
60
+ self.attribution = attribution
61
+ self.max_note_count = max_note_count
62
+ self._uri = uri if uri else URI(fragment=id)
63
+
64
+ def add_note(self,note_to_add: Note):
65
+ if len(self.notes) >= self.max_note_count:
66
+ warnings.warn(f"Max not count of {self.max_note_count} reached for id: {self.id}")
67
+ return False
68
+ if note_to_add and isinstance(note_to_add,Note):
69
+ for existing in self.notes:
70
+ if note_to_add == existing:
71
+ return False
72
+ self.notes.append(note_to_add)
73
+
74
+ def add_source(self, source_to_add: SourceReference):
75
+ if source_to_add and isinstance(source_to_add,SourceReference):
76
+ for current_source in self.sources:
77
+ if source_to_add == current_source:
78
+ return
79
+ self.sources.append(source_to_add)
80
+ else:
81
+ raise ValueError()
82
+
83
+ '''
84
+ def _as_dict_(self):
85
+ return {
86
+ 'id':self.id,
87
+ 'lang':self.lang,
88
+ 'sources': [source._prop_dict() for source in self.sources] if self.sources else None,
89
+ 'analysis': self.analysis._uri if self.analysis else None,
90
+ 'notes':"Add notes here",
91
+ 'confidence':self.confidence
92
+ }
93
+ '''
94
+
95
+ @property
96
+ def _as_dict_(self):
97
+ def _serialize(value):
98
+ if isinstance(value, (str, int, float, bool, type(None))):
99
+ return value
100
+ elif isinstance(value, dict):
101
+ return {k: _serialize(v) for k, v in value.items()}
102
+ elif isinstance(value, (list, tuple, set)):
103
+ return [_serialize(v) for v in value]
104
+ elif hasattr(value, "_as_dict_"):
105
+ return value._as_dict_
106
+ else:
107
+ return str(value) # fallback for unknown objects
108
+
109
+ # Only add Relationship-specific fields
110
+ conclusion_fields = {
111
+ 'id':self.id,
112
+ 'lang':self.lang,
113
+ 'sources': [source for source in self.sources] if self.sources else None,
114
+ 'analysis': self.analysis._uri if self.analysis else None,
115
+ 'notes': [note for note in self.notes] if self.notes else None,
116
+ 'confidence':self.confidence
117
+ }
118
+
119
+ # Serialize and exclude None values
120
+ for key, value in conclusion_fields.items():
121
+ if value is not None:
122
+ conclusion_fields[key] = _serialize(value)
123
+ return conclusion_fields
124
+
125
+ def __eq__(self, other):
126
+ if not isinstance(other, self.__class__):
127
+ return False
128
+
129
+ return (
130
+ self.id == other.id and
131
+ self.lang == other.lang and
132
+ self.sources == other.sources and
133
+ self.analysis == other.analysis and
134
+ self.notes == other.notes and
135
+ self.confidence == other.confidence and
136
+ self.attribution == other.attribution
137
+ )
gedcomx/Coverage.py ADDED
@@ -0,0 +1,26 @@
1
+ from typing import Optional
2
+
3
+ from .Date import Date
4
+ from .PlaceReference import PlaceReference
5
+
6
+ class Coverage:
7
+ identifier = 'http://gedcomx.org/v1/Coverage'
8
+ version = 'http://gedcomx.org/conceptual-model/v1'
9
+
10
+ def __init__(self,spatial: Optional[PlaceReference], temporal: Optional[Date]) -> None:
11
+ self.spatial = spatial
12
+ self.temporal = temporal
13
+
14
+ # ...existing code...
15
+
16
+ @classmethod
17
+ def _from_json_(cls, data: dict):
18
+ """
19
+ Create a Coverage instance from a JSON-dict (already parsed).
20
+ """
21
+ from .PlaceReference import PlaceReference
22
+ from .Date import Date
23
+
24
+ spatial = PlaceReference._from_json_(data.get('spatial')) if data.get('spatial') else None
25
+ temporal = Date._from_json_(data.get('temporal')) if data.get('temporal') else None
26
+ return cls(spatial=spatial, temporal=temporal)
gedcomx/Date.py ADDED
@@ -0,0 +1,29 @@
1
+ from typing import Optional
2
+
3
+
4
+ class DateFormat:
5
+ def __init__(self) -> None:
6
+ pass
7
+
8
+
9
+ class Date:
10
+ identifier = 'http://gedcomx.org/v1/Date'
11
+ version = 'http://gedcomx.org/conceptual-model/v1'
12
+
13
+ def __init__(self, original: Optional[str],formal: Optional[str | DateFormat] = None) -> None:
14
+ self.orginal = original
15
+ self.formal = formal
16
+
17
+ def _prop_dict(self):
18
+ return {'original': self.orginal,
19
+ 'formal': self.formal}
20
+
21
+ # Date
22
+ Date._from_json_ = classmethod(lambda cls, data: Date(
23
+ original=data.get('original'),
24
+ formal=data.get('formal')
25
+ ))
26
+
27
+ Date._to_dict_ = lambda self: {
28
+ 'original': self.orginal,
29
+ 'formal': self.formal}
gedcomx/Document.py ADDED
@@ -0,0 +1,42 @@
1
+ from enum import Enum
2
+ from typing import Optional
3
+
4
+ from gedcomx.Attribution import Attribution
5
+ from gedcomx.Conclusion import ConfidenceLevel
6
+ from gedcomx.Note import Note
7
+ from gedcomx.SourceReference import SourceReference
8
+ from gedcomx.URI import URI
9
+
10
+ from .Conclusion import Conclusion
11
+
12
+ class DocumentType(Enum):
13
+ Abstract = "http://gedcomx.org/Abstract"
14
+ Transcription = "http://gedcomx.org/Transcription"
15
+ Translation = "http://gedcomx.org/Translation"
16
+ Analysis = "http://gedcomx.org/Analysis"
17
+
18
+ @property
19
+ def description(self):
20
+ descriptions = {
21
+ DocumentType.Abstract: "The document is an abstract of a record or document.",
22
+ DocumentType.Transcription: "The document is a transcription of a record or document.",
23
+ DocumentType.Translation: "The document is a translation of a record or document.",
24
+ DocumentType.Analysis: "The document is an analysis done by a researcher; a genealogical proof statement is an example of one kind of analysis document."
25
+ }
26
+ return descriptions.get(self, "No description available.")
27
+
28
+ class TextType(Enum):
29
+ plain = 'plain'
30
+ xhtml = 'xhtml'
31
+
32
+ class Document(Conclusion):
33
+ identifier = 'http://gedcomx.org/v1/Document'
34
+ version = 'http://gedcomx.org/conceptual-model/v1'
35
+
36
+ def __init__(self, id: str | None, lang: str | None, sources: SourceReference | None, analysis: URI | None, notes: Note | None, confidence: ConfidenceLevel | None, attribution: Attribution | None,
37
+ type: Optional[DocumentType],
38
+ extracted: Optional[bool], # Default to False
39
+ textType: Optional[TextType],
40
+ text: str,
41
+ ) -> None:
42
+ super().__init__(id, lang, sources, analysis, notes, confidence, attribution)