gedcom-x 0.5.10__py3-none-any.whl → 0.5.11__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gedcom-x
3
- Version: 0.5.10
3
+ Version: 0.5.11
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,57 @@
1
+ gedcomx/TopLevelTypeCollection.py,sha256=p99i-O5LXiXe3GlC6jWuz4nH1TAcKxOLbe0VRxWbSFY,1495
2
+ gedcomx/Zip.py,sha256=lBxcv-Vip45884EHj56wZJJ5I36Q38UuHUidDxQBoS8,14
3
+ gedcomx/__init__.py,sha256=p9oSZV2SxOwWLK69ukr5eQnVw761qkr4wGpBxtcxMJ8,1880
4
+ gedcomx/address.py,sha256=gXQ6xAVNZ1ywM8NhsSIIaOE_CVUsRrYIj452NnZOic8,8138
5
+ gedcomx/agent.py,sha256=Dox8o2YbuEYf8iYnwJ_r_3LL8WJp_5Np3idRKcXZKyE,13037
6
+ gedcomx/attribution.py,sha256=AF3N3sfenHLBJQ52uvGs05oIrZtY1bddYjgZqe5rRCY,4673
7
+ gedcomx/conclusion.py,sha256=B5_lR7bPQ6VT_08uLNLNuiJkgWwoDz61zBCm_mi7Z6I,11377
8
+ gedcomx/converter.py,sha256=H7SUoSdsP0m0AK0BSDOnavJW1uMjdJ-zEHThMRVUCTI,60641
9
+ gedcomx/coverage.py,sha256=59jY_L1-Y20HDoMODd1FfRHF4v3rXMDbAIf51qa7txM,2293
10
+ gedcomx/date.py,sha256=mI7wKIK1AqFebw8zl5dXTLCPLe41lHW2GdJ5QdhQrXs,3649
11
+ gedcomx/document.py,sha256=IsYhqmzOcmYPVaRmqiyt8evWYMCS7J0LUyLDAD0vPuQ,4655
12
+ gedcomx/event.py,sha256=uuujR7XBFro2HqlZ123aMECOicZH-5HlHuPd6Qr_ki4,15519
13
+ gedcomx/evidence_reference.py,sha256=PEOFo3CKGOjPUt42n9EL0EdEFtrof_msWhzRVq7Q3_E,1478
14
+ gedcomx/exceptions.py,sha256=0OdPM3euhBMgX8o61ZwPuKeN8zPuSuuDcSBFflVGFqk,587
15
+ gedcomx/extensible.py,sha256=6-VaJmOax8BoShKMKxKpJEyZf7aILqA8SMEqbRgP5yM,2659
16
+ gedcomx/extensible_enum.py,sha256=DftCZLMBNul3C9hwh-rf0GE3SVdvylvyd5mt7bX_l6o,6535
17
+ gedcomx/fact.py,sha256=_YrdxfL_FdiL5SmXcAjMhLZOfYi4VP_W8IEI7VTGOhc,25169
18
+ gedcomx/gedcom.py,sha256=A8GQ670E6_knfP37kOGHdVl_cC9RXQoc4Dk6D_qlnuQ,2059
19
+ gedcomx/gedcom5x.py,sha256=AjML2hXX5RG3mdc93o_3Q1KHoTI3ecoOB5CTGKqAfGo,24092
20
+ gedcomx/gedcomx.py,sha256=964jNy72flnWNV9xBH44OyQthZarCmwge_8vagB3xd0,19419
21
+ gedcomx/gender.py,sha256=11Pvi6VpclfWKmeAhzaxwt1KlkKxqhfKBChFTkeknCg,3338
22
+ gedcomx/group.py,sha256=H44ie6tV1h-TYgEeNJhHhJLtw0xB2O9lrCCqv7IJ0sg,3105
23
+ gedcomx/identifier.py,sha256=P9pKWh7eFAkFN7C-i63hb7nmMMvYvW7EGmShLqEtBCU,9694
24
+ gedcomx/logging_hub.py,sha256=tU1fi2MUgY5_b_RwfssB3WTmNSVfvET1EBb-LrHmqWk,13528
25
+ gedcomx/mutations.py,sha256=ugQgfQh0o9r8FIB_dz5s6KqPyIDMHn2YSP7DvAHsDv0,7236
26
+ gedcomx/name.py,sha256=Gaa9d9OoMXb6x0Xn4t57uKEaGMENSgqzk3SlVolWwoQ,19712
27
+ gedcomx/note.py,sha256=R6wWVtyAXFIdQMg5Jqyu9WfT3IBeLLpomX_hEKWSiHk,4265
28
+ gedcomx/online_account.py,sha256=lSHtynqo1_eNlziJ8-QqbO1BVMCPHjohBgOOFPkJbao,1189
29
+ gedcomx/person.py,sha256=kDh7nmNE7_WkQZXIMYbRRF85JNShEvFyJZR1YhSbZjU,9090
30
+ gedcomx/place_description.py,sha256=IRyDvl_HWYtP-diEowwE9WNpRz3yblugSg5BYwIes3s,7664
31
+ gedcomx/place_reference.py,sha256=9pyvQmo5NTzwmzvRo9cgWpWrrQ0q9oikAVGN5xSTH2w,3039
32
+ gedcomx/qualifier.py,sha256=bXlqopQHju-hSUuk_SFH66S1Ou5YfSuxiITzpIXkcPA,2404
33
+ gedcomx/relationship.py,sha256=maTCMOxRKVEF3dIzZ7qQaKZ2XmfqyjfwzYdNO1HjCDI,5972
34
+ gedcomx/resource.py,sha256=B0Ey-RSGWthx8apWIN_5vHEMlxfj4s2nYY81Gob5LCw,4245
35
+ gedcomx/schemas.py,sha256=gTc4RL348GtTeR9O8_qrztAGgfZZ9PGUwLEYzl0MZyE,20666
36
+ gedcomx/serialization.py,sha256=JqnLq1Tyq3Kn9i1DceLXHES4ENSSLXWVw6y_ItQLZRM,31865
37
+ gedcomx/source_citation.py,sha256=w6Ha9WTzLCzHELyTlYkwRJ176H6a8-sPZpFM3AK5-9w,1792
38
+ gedcomx/source_description.py,sha256=rC1ItCuLj2Mm4JHm7oug1RkqJbse7XJG-VHvSlNbocU,19358
39
+ gedcomx/source_reference.py,sha256=zutj_M4asKbGJUf_C9F6ZTxoe_gDlnHvBY6NGuiWl_E,7614
40
+ gedcomx/subject.py,sha256=2LbZ_xHuirKENRJlJFRMsGFIiJWypRt21cWGRmFGc9c,5054
41
+ gedcomx/textvalue.py,sha256=ChjlNzXB1DpiuHko85_KHzVmIxNBBgoP7c6AXVowVBk,3660
42
+ gedcomx/translation.py,sha256=4REL68WaWqcC5f3XcBCZ1dn0gtaZftQJTCkvl1Sf-NA,61395
43
+ gedcomx/uri.py,sha256=frFAHgKN6IX_-kS6btNpbxGu9jeaTD7laEkAopPzp5M,9765
44
+ gedcomx/Extensions/__init__.py,sha256=MQzi_whzlxiLiknUNh10hG8OVrNqJE38l6n-AwCssx8,24
45
+ gedcomx/Extensions/rs10/__init__.py,sha256=nSHoZiD8hsCAyE-KyRTuWSLqSJSFh12kSz7hqilAMps,26
46
+ gedcomx/Extensions/rs10/rsLink.py,sha256=JhfaHBOK_rl1tPWmD5a8KYgfWl-nEjBpAO1pk4Z8osY,6056
47
+ gedcomx/gedcom7/Exceptions.py,sha256=xeKr4x8b7r8pOqJ9yMpsCVTyxPeOlREDGgKoM5rX4U0,149
48
+ gedcomx/gedcom7/GedcomStructure.py,sha256=45p6o01j_bmgCasfprDnGTVy-gyKM_8BuvAakLqCl3c,3431
49
+ gedcomx/gedcom7/__init__.py,sha256=izlM3__nzpZ32qLU8cqqyIpGQ4cA9vlQh3_RXxgMCg4,682
50
+ gedcomx/gedcom7/g7interop.py,sha256=hSzwqeok2n7xziEvN2QiJY7bVCWrOnZIZWXubnkrv7w,9945
51
+ gedcomx/gedcom7/gedcom7.py,sha256=oAp94vmkGtyqqI8-3uA1t5bepxLovMSi3r9rZFlZ0Lg,5620
52
+ gedcomx/gedcom7/logger.py,sha256=QM1SySyh91UEhs90d2DMhH-s9qGF8XS8I8gr1eOcmfw,617
53
+ gedcomx/gedcom7/specification.py,sha256=H6l4IW4UX7YRKoNH6t84REFeGl52kEyRnn6a9f21gbg,288703
54
+ gedcom_x-0.5.11.dist-info/METADATA,sha256=vOaxtFRW521ZXaucmUhih5LBt_tUezBCX-ZfuB7A06k,4333
55
+ gedcom_x-0.5.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ gedcom_x-0.5.11.dist-info/top_level.txt,sha256=smVBF4nxSU-mzCd6idtRYTbYjPICMMi8pTqewEmqF8Y,8
57
+ gedcom_x-0.5.11.dist-info/RECORD,,
@@ -20,6 +20,7 @@ GEDCOM Module Types
20
20
  """
21
21
  from ...exceptions import GedcomClassAttributeError
22
22
  from ...logging_hub import hub, logging
23
+ from ...schemas import schema_class
23
24
  from ...uri import URI
24
25
  """
25
26
  ======================================================================
@@ -31,7 +32,7 @@ serial_log = "gedcomx.serialization"
31
32
  deserial_log = "gedcomx.deserialization"
32
33
  #=====================================================================
33
34
 
34
-
35
+ @schema_class()
35
36
  class rsLink():
36
37
  """A link description object. RS Extension to GedcomX by FamilySearch.
37
38
 
gedcomx/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .schemas import SCHEMA
2
+ from .subject import Subject
2
3
  from .agent import Agent
3
4
  from .address import Address
4
5
  from .attribution import Attribution
@@ -24,7 +25,6 @@ from .gedcomx import GedcomX
24
25
  from .gender import Gender, GenderType
25
26
  from .group import Group, GroupRole
26
27
  from .identifier import Identifier, IdentifierType, IdentifierList
27
- from .Logging import get_logger
28
28
  from .name import Name, NameForm, NamePart, NamePartType, NameType, NamePartQualifier
29
29
  from .note import Note
30
30
  from .online_account import OnlineAccount
@@ -38,10 +38,12 @@ from .source_citation import SourceCitation
38
38
  from .source_description import SourceDescription
39
39
  from .source_description import ResourceType
40
40
  from .source_reference import SourceReference
41
- from .subject import Subject
41
+
42
42
  from .textvalue import TextValue
43
43
  from .resource import Resource
44
+ SCHEMA.set_resource_class(Resource)
44
45
  from .uri import URI
46
+ SCHEMA.set_uri_class(URI)
45
47
 
46
48
 
47
49
  from .Extensions.rs10.rsLink import rsLink
@@ -49,5 +51,5 @@ from .Extensions.rs10.rsLink import rsLink
49
51
  from .gedcom7.gedcom7 import Gedcom7, GedcomStructure
50
52
  from .translation import g7toXtable
51
53
 
52
-
54
+ SCHEMA.normalize_all()
53
55
 
gedcomx/address.py CHANGED
@@ -10,6 +10,7 @@ from typing import Any, Dict, Optional, List
10
10
  Created: 2025-08-25
11
11
  Updated:
12
12
  - 2025-09-03: _from_json_ refactoring
13
+ - 2025-09-09: added schema_class
13
14
 
14
15
  ======================================================================
15
16
  """
@@ -20,6 +21,7 @@ GEDCOM Module Types
20
21
  ======================================================================
21
22
  """
22
23
  from .logging_hub import hub, logging
24
+ from .schemas import schema_class
23
25
  """
24
26
  ======================================================================
25
27
  Logging
@@ -29,6 +31,7 @@ log = logging.getLogger("gedcomx")
29
31
  serial_log = "gedcomx.serialization"
30
32
  #=====================================================================
31
33
 
34
+ @schema_class()
32
35
  class Address:
33
36
  """A GedcomX Address Data Type
34
37
  A GedcomX Address Data Type.
gedcomx/agent.py CHANGED
@@ -1,7 +1,8 @@
1
- import base64
2
- import uuid
1
+ from __future__ import annotations
3
2
 
4
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
4
+ if TYPE_CHECKING:
5
+ from .person import Person
5
6
  """
6
7
  ======================================================================
7
8
  Project: Gedcom-X
@@ -12,6 +13,7 @@ from typing import Any, Dict, List, Optional
12
13
  Created: 2025-08-25
13
14
  Updated:
14
15
  - 2025-09-03: _from_json_ refactor
16
+ - 2025-09-09: added schema_class
15
17
 
16
18
  ======================================================================
17
19
  """
@@ -25,6 +27,7 @@ from .address import Address
25
27
  from .identifier import Identifier, IdentifierList, make_uid
26
28
  from .online_account import OnlineAccount
27
29
  from .resource import Resource
30
+ from .schemas import schema_class
28
31
  from .textvalue import TextValue
29
32
  from .uri import URI
30
33
  from .logging_hub import hub, logging
@@ -37,7 +40,7 @@ log = logging.getLogger("gedcomx")
37
40
  serial_log = "gedcomx.serialization"
38
41
  #=====================================================================
39
42
 
40
-
43
+ @schema_class(toplevel=True)
41
44
  class Agent:
42
45
  """A GedcomX Agent Data Type.
43
46
 
@@ -80,10 +83,10 @@ class Agent:
80
83
  emails: Optional[List[URI]] = [],
81
84
  phones: Optional[List[URI]] = [],
82
85
  addresses: Optional[List[Address]] = [],
83
- person: Optional[object] | Optional[Resource] = None, # should be of Type 'Person', 'object' to avoid circular imports
86
+ person: Optional[Union[Resource,Person]] = None, # should be of Type 'Person', 'object' to avoid circular imports
84
87
  #xnotes: Optional[List[Note]] = None,
85
88
  attribution: Optional[object] = None, # Added for compatibility with GEDCOM5/7 Imports
86
- uri: Optional[URI | Resource] = None):
89
+ ):
87
90
 
88
91
 
89
92
  self.id = id if id else make_uid()
@@ -123,6 +126,8 @@ class Agent:
123
126
  for current_name in self.names:
124
127
  if name_to_add == current_name:
125
128
  return
129
+ if name_to_add.value is None or name_to_add == '':
130
+ assert False
126
131
  self.names.append(name_to_add)
127
132
  else:
128
133
  raise ValueError(f'name must be of type str or TextValue, recived {type(name_to_add)}')
gedcomx/attribution.py CHANGED
@@ -13,6 +13,7 @@ from typing import Optional, Dict, Any
13
13
  Updated:
14
14
  - 2025-08-31: fixed _as_dict_ to deal with Resources and ignore empty fields
15
15
  - 2025-09-03: _from_json_ refactor
16
+ - 2025-09-09: added schema_class
16
17
 
17
18
 
18
19
  ======================================================================
@@ -25,6 +26,7 @@ GEDCOM Module Types
25
26
  """
26
27
  from .agent import Agent
27
28
  from .resource import Resource
29
+ from .schemas import schema_class
28
30
  from .logging_hub import hub, logging
29
31
  """
30
32
  ======================================================================
@@ -35,7 +37,7 @@ log = logging.getLogger("gedcomx")
35
37
  serial_log = "gedcomx.serialization"
36
38
  #=====================================================================
37
39
 
38
-
40
+ @schema_class()
39
41
  class Attribution:
40
42
  """Attribution Information for a Genealogy, Conclusion, Subject and child classes
41
43
 
gedcomx/conclusion.py CHANGED
@@ -1,7 +1,7 @@
1
-
1
+ from __future__ import annotations
2
2
  import warnings
3
3
 
4
- from typing import Any, Optional, List
4
+ from typing import Any, Optional, List, Union, TYPE_CHECKING
5
5
 
6
6
  """
7
7
  ======================================================================
@@ -13,6 +13,7 @@ from typing import Any, Optional, List
13
13
  Created: 2025-08-25
14
14
  Updated:
15
15
  - 2025-09-03: _from_json_ refactor
16
+ - 2025-09-09: added schema_class
16
17
 
17
18
  ======================================================================
18
19
  """
@@ -23,11 +24,14 @@ GEDCOM Module Type Imports
23
24
  ======================================================================
24
25
  """
25
26
  from .attribution import Attribution
27
+ if TYPE_CHECKING:
28
+ from .document import Document
26
29
  from .Extensions.rs10.rsLink import _rsLinks, rsLink
27
30
  from .identifier import make_uid
28
31
  from .note import Note
29
32
  from .qualifier import Qualifier
30
33
  from .resource import Resource, URI
34
+ from .schemas import schema_class
31
35
  from .source_reference import SourceReference
32
36
  from .logging_hub import hub, logging
33
37
  """
@@ -112,7 +116,7 @@ class ConfidenceLevel(Qualifier):
112
116
  key = getattr(self, "value", self)
113
117
  return descriptions.get(key, "No description available.")
114
118
 
115
-
119
+ @schema_class()
116
120
  class Conclusion():
117
121
  """
118
122
  Represents a conclusion in the GEDCOM X conceptual model. A conclusion is a
@@ -146,11 +150,10 @@ class Conclusion():
146
150
  id: Optional[str] = None,
147
151
  lang: Optional[str] = None,
148
152
  sources: Optional[List[SourceReference]] = None,
149
- analysis: Optional[object | Resource] = None,
153
+ analysis: Optional[Union[Resource,Document]] = None,
150
154
  notes: Optional[List[Note]] = None,
151
155
  confidence: Optional[ConfidenceLevel] = None,
152
156
  attribution: Optional[Attribution] = None,
153
- uri: Optional[Resource] = None,
154
157
  _max_note_count: int = 20,
155
158
  links: Optional[_rsLinks] = None) -> None:
156
159
 
@@ -164,6 +167,7 @@ class Conclusion():
164
167
  self.attribution = attribution
165
168
  self.max_note_count = _max_note_count
166
169
  self.links = links if links else _rsLinks() #NOTE This is not in specification, following FS format
170
+ self.uri = URI(fragment=id) if id else None
167
171
 
168
172
  def add_note(self,note_to_add: Note):
169
173
  if self.notes and len(self.notes) >= self.max_note_count:
gedcomx/converter.py CHANGED
@@ -1,10 +1,11 @@
1
- DEBUG = False
2
1
  import logging
3
2
  import mimetypes
4
3
  import re
5
4
  import xml.etree.ElementTree as ET
6
5
 
7
6
  from typing import Any, Mapping
7
+ from typing import Iterable, Callable, TypeVar, Hashable, List, Optional
8
+
8
9
  import math
9
10
  import shutil
10
11
 
@@ -73,6 +74,8 @@ serial_log = "gedcomx.serialization"
73
74
  convert_log = "gedcomx.convert.GEDCOM5x"
74
75
  #=====================================================================
75
76
 
77
+
78
+
76
79
  hub.start_channel(
77
80
  ChannelConfig(
78
81
  name=convert_log,
@@ -81,16 +84,22 @@ hub.start_channel(
81
84
  rotation="size:10MB:3", # rotate by size, keep 3 backups
82
85
  ))
83
86
 
87
+ T = TypeVar("T")
88
+ K = TypeVar("K", bound=Hashable)
84
89
 
85
90
  class GedcomConverter():
86
91
  def __init__(self) -> None:
87
- self.gedcomx = GedcomX()
92
+ self.gedcomx: GedcomX = GedcomX()
88
93
  self.object_map: dict[Any, Any] = {-1:self.gedcomx}
89
94
  self.missing_handler_count = {}
90
95
 
91
96
  type_name_type = {
92
97
  'aka': NameType.AlsoKnownAs
93
98
  }
99
+
100
+ personal_events = ["BARM", "BASM", "BLES", "CHRA", "CONF", "CENS", "CREM", "EMIG", "GRAD", "NATU", "ORDN", "RETI", "WILL"]
101
+
102
+
94
103
  gedcom_even_to_fact = {
95
104
  # Person Fact Types
96
105
  "ADOP": FactType.Adoption,
@@ -193,24 +202,25 @@ class GedcomConverter():
193
202
 
194
203
  def parse_gedcom5x_record(self,record: Gedcom5xRecord):
195
204
  if record is not None:
196
- with hub.use(convert_log):
197
- handler_name = 'handle_' + record.tag.lower()
198
-
199
- if hasattr(self,handler_name):
205
+ with hub.use(convert_log):
206
+ handler_name = 'handle_' + record.tag.lower()
207
+ if record.tag in self.personal_events:
208
+ self.handle_pevent(record)
209
+ elif hasattr(self,handler_name):
200
210
  log.info(f'Using {handler_name} to pars Record: {record.describe()}')
201
211
  handler = getattr(self,handler_name)
202
212
  handler(record)
203
- if record.tag != 'FAM':
204
- for sub_record in record.subRecords():
205
- log.debug(sub_record.describe())
206
- self.parse_gedcom5x_record(sub_record)
207
213
  else:
208
214
  if record.tag in self.missing_handler_count:
209
215
  self.missing_handler_count[record.tag] += 1
210
216
  else:
211
217
  self.missing_handler_count[record.tag] = 1
212
-
213
218
  log.error(f'Failed Parsing Record: {record.describe()}')
219
+ return
220
+ if record.tag != 'FAM':
221
+ for sub_record in record.subRecords():
222
+ log.debug(sub_record.describe())
223
+ self.parse_gedcom5x_record(sub_record)
214
224
  log.debug(f"{record.tag} with id: {record.xref} has {len(record.subRecords())} subRecords")
215
225
 
216
226
  else:
@@ -436,12 +446,12 @@ class GedcomConverter():
436
446
 
437
447
  def handle_cont(self, record: Gedcom5xRecord):
438
448
  if isinstance(self.object_map[record.level-1], Note):
439
- gxobject = str("\n" + record.value if record.value else '')
449
+ gxobject = str(" " + record.value if record.value else '')
440
450
  self.object_map[record.level-1].append(gxobject)
441
451
  elif isinstance(self.object_map[record.level-1], Agent):
442
- gxobject = str("\n" + record.value if record.value else '')
452
+ gxobject = str(" " + record.value if record.value else '')
443
453
  elif isinstance(self.object_map[record.level-1], Qualifier):
444
- gxobject = str("\n" + record.value if record.value else '')
454
+ gxobject = str(" " + record.value if record.value else '')
445
455
  self.object_map[record.level-1].append(gxobject)
446
456
  elif isinstance(self.object_map[record.level-1], TextValue):
447
457
  #gxobject = TextValue(value="\n" + record.value)
@@ -471,6 +481,12 @@ class GedcomConverter():
471
481
  else:
472
482
  raise ValueError(f"Could not handle '{record.tag}' tag in record {record.describe()}, last stack object {self.object_map[record.level-1]}")
473
483
 
484
+ def handle__crea(self, record: Gedcom5xRecord):
485
+ if isinstance(self.object_map[record.level-1], SourceDescription):
486
+ if record.value is not None:
487
+ self.object_map[record.level-1].created = Date(original=record.value)
488
+ else: raise ValueError('DATE had not value')
489
+
474
490
  def handle_ctry(self, record: Gedcom5xRecord):
475
491
  if isinstance(self.object_map[record.level-1], Address):
476
492
  if record.value is not None:
@@ -526,6 +542,13 @@ class GedcomConverter():
526
542
  else:
527
543
  raise TagConversionError(record=record,levelstack=self.object_map)
528
544
 
545
+ def handle_pevent(self, record: Gedcom5xRecord):
546
+ if record.tag in self.gedcom_even_to_fact.keys():
547
+ if isinstance(self.object_map[record.level-1], Person):
548
+ gxobject = Fact(type=self.gedcom_even_to_fact[record.tag])
549
+ self.object_map[record.level-1].add_fact(gxobject)
550
+ self.object_map[record.level] = gxobject
551
+
529
552
  def handle_even(self, record: Gedcom5xRecord):
530
553
  # TODO If events in a @S, check if only 1 person matches?
531
554
  if record.value and (not record.value.strip() == ''):
@@ -733,8 +756,9 @@ class GedcomConverter():
733
756
 
734
757
  self.object_map[record.level] = gxobject
735
758
  elif isinstance(self.object_map[record.level-1], Agent):
736
-
759
+ print(record.describe())
737
760
  gxobject = TextValue(value=record.value)
761
+ print(gxobject)
738
762
  self.object_map[record.level-1].add_name(gxobject)
739
763
 
740
764
  else:
@@ -808,7 +832,7 @@ class GedcomConverter():
808
832
  def handle_page(self, record: Gedcom5xRecord):
809
833
  if isinstance(self.object_map[record.level-1], SourceReference):
810
834
  #self.object_map[record.level-1].descriptionId = record.value
811
- gx_object = KnownSourceReference(name=KnownSourceReference.Page,value=record.value)
835
+ gx_object = Qualifier(name=KnownSourceReference.Page,value=record.value)
812
836
  self.object_map[record.level-1].add_qualifier(gx_object)
813
837
  self.object_map[record.level] = self.object_map[record.level-1]
814
838
  else:
@@ -854,15 +878,19 @@ class GedcomConverter():
854
878
 
855
879
  def handle_publ(self, record: Gedcom5xRecord):
856
880
  if isinstance(self.object_map[record.level-1], SourceDescription):
857
- if record.value and self.gedcomx.agents.byName(record.value):
858
- gxobject = self.gedcomx.agents.byName(record.value)[0]
881
+ if record.value == None:
882
+ #check for date
883
+ if (date := record['DATE']) is not None:
884
+ self.object_map[record.level-1].published = date
885
+ print("found date")
859
886
  else:
860
- gxobject = Agent(names=[TextValue(record.value)])
861
- self.gedcomx.add_agent(gxobject)
862
- self.object_map[record.level-1].publisher = gxobject
863
-
864
-
865
- self.object_map[record.level] = gxobject
887
+ if record.value and self.gedcomx.agents.byName(record.value):
888
+ gxobject = self.gedcomx.agents.byName(record.value)[0]
889
+ else:
890
+ gxobject = Agent(names=[TextValue(record.value)])
891
+ self.gedcomx.add_agent(gxobject)
892
+ self.object_map[record.level-1].publisher = gxobject
893
+ self.object_map[record.level] = gxobject
866
894
  else:
867
895
  raise TagConversionError(record=record,levelstack=self.object_map)
868
896
 
@@ -900,8 +928,10 @@ class GedcomConverter():
900
928
  def handle_repo(self, record: Gedcom5xRecord):
901
929
 
902
930
  if record.level == 0:
903
- if (gxobject := self.gedcomx.agents.byId(id=record.xref)) is None:
904
- gxobject = Agent(id=record.xref)
931
+ if record.value is not None and self.gedcomx.agents.byName(record.value):
932
+ gxobject = self.gedcomx.agents.byName(record.value)[0]
933
+ else:
934
+ gxobject = Agent(id=record.xref,names = [TextValue(record.value)] if record.value else [])
905
935
  self.gedcomx.add_agent(gxobject)
906
936
 
907
937
  self.object_map[record.level] = gxobject
@@ -1128,6 +1158,34 @@ class GedcomConverter():
1128
1158
  line.append(cell.ljust(cell_width))
1129
1159
  print("".join(line).rstrip())
1130
1160
 
1161
+ def has_duplicates(self,seq) -> bool:
1162
+ """Fast True/False check (works for hashable items)."""
1163
+ return len(seq) != len(set(seq))
1164
+
1165
+ def find_duplicates(self, seq):
1166
+ """Return duplicate items once each, in the order they first repeat."""
1167
+ seen, dups = set(), []
1168
+ for x in seq:
1169
+ if x in seen and x not in dups:
1170
+ dups.append(x)
1171
+ seen.add(x)
1172
+ return dups
1173
+
1174
+ def unique(self, seq: Iterable[T], key: Optional[Callable[[T], K]] = None) -> List[T]:
1175
+ """
1176
+ Return a list with duplicates removed, keeping the first occurrence.
1177
+ If `key` is provided, it’s used to compute a hashable identity for each item.
1178
+ """
1179
+
1180
+ seen: set[K] = set()
1181
+ out: List[T] = []
1182
+ for item in seq:
1183
+ k = item if key is None else key(item)
1184
+ if k not in seen:
1185
+ seen.add(k)
1186
+ out.append(item)
1187
+ return out
1188
+
1131
1189
  def Gedcom5x_GedcomX(self, gedcom5x: Gedcom5x):
1132
1190
  print(f'Parsing GEDCOM Version {gedcom5x.version}')
1133
1191
  individual_ids = set()
@@ -1173,7 +1231,14 @@ class GedcomConverter():
1173
1231
  self.parse_gedcom5x_record(repo)
1174
1232
  for family in gedcom5x.families:
1175
1233
  self.parse_gedcom5x_record(family)
1176
-
1234
+
1235
+ #clean up notes, and TextValue descriptions:
1236
+ for sd in self.gedcomx.sourceDescriptions:
1237
+ log.debug(f"Removing dupliate notes in SourceDescriptions")
1238
+ sd.notes = self.unique(sd.notes,key=lambda n: n._key())
1239
+ log.debug(f"Removing dupliate descriptions in SourceDescriptions")
1240
+ sd.descriptions = self.unique(sd.descriptions,key=lambda n: n._key())
1241
+
1177
1242
  self.print_counts_table(self.missing_handler_count)
1178
1243
 
1179
1244
  return self.gedcomx
gedcomx/coverage.py CHANGED
@@ -9,6 +9,7 @@ from typing import Optional
9
9
  Created: 2025-08-25
10
10
  Updated:
11
11
  - 2025-09-03: _from_json_ refactor
12
+ - 2025-09-09: added schema_class
12
13
 
13
14
  ======================================================================
14
15
  """
@@ -21,6 +22,7 @@ GEDCOM Module Types
21
22
  from .date import Date
22
23
  from .place_reference import PlaceReference
23
24
  from .logging_hub import hub, logging
25
+ from .schemas import schema_class
24
26
  """
25
27
  ======================================================================
26
28
  Logging
@@ -30,7 +32,7 @@ log = logging.getLogger("gedcomx")
30
32
  serial_log = "gedcomx.serialization"
31
33
  #=====================================================================
32
34
 
33
-
35
+ @schema_class()
34
36
  class Coverage:
35
37
  identifier = 'http://gedcomx.org/v1/Coverage'
36
38
  version = 'http://gedcomx.org/conceptual-model/v1'
gedcomx/date.py CHANGED
@@ -11,6 +11,7 @@ from dateutil import parser
11
11
  Created: 2025-08-25
12
12
  Updated:
13
13
  - 2025-09-03: _from_json refactored
14
+ - 2025-09-09: added schema_class
14
15
 
15
16
  ======================================================================
16
17
  """
@@ -21,6 +22,7 @@ GEDCOM Module Types
21
22
  ======================================================================
22
23
  """
23
24
  from .logging_hub import hub, logging
25
+ from .schemas import schema_class
24
26
  """
25
27
  ======================================================================
26
28
  Logging
@@ -38,6 +40,7 @@ class DateFormat:
38
40
  class DateNormalization():
39
41
  pass
40
42
 
43
+ @schema_class()
41
44
  class Date:
42
45
  identifier = 'http://gedcomx.org/v1/Date'
43
46
  version = 'http://gedcomx.org/conceptual-model/v1'
gedcomx/document.py CHANGED
@@ -10,6 +10,7 @@ from typing import Any, Dict, List, Optional
10
10
  Created: 2025-08-25
11
11
  Updated:
12
12
  - 2025-09-03: _from_json_ refactored
13
+ - 2025-09-09: added schema_class
13
14
 
14
15
  ======================================================================
15
16
  """
@@ -23,6 +24,7 @@ from .attribution import Attribution
23
24
  from .conclusion import Conclusion, ConfidenceLevel
24
25
  from .note import Note
25
26
  from .resource import Resource
27
+ from .schemas import schema_class
26
28
  from .source_reference import SourceReference
27
29
  from .logging_hub import hub, logging
28
30
  """
@@ -34,7 +36,7 @@ log = logging.getLogger("gedcomx")
34
36
  serial_log = "gedcomx.serialization"
35
37
  #=====================================================================
36
38
 
37
-
39
+ @schema_class(toplevel=True)
38
40
  class DocumentType(Enum):
39
41
  Abstract = "http://gedcomx.org/Abstract"
40
42
  Transcription = "http://gedcomx.org/Transcription"
gedcomx/event.py CHANGED
@@ -13,6 +13,7 @@ from typing import Any, Dict, List, Optional
13
13
  - 2025-08-31: fixed mutible [] in init, replaced List[Identifer] with IdentifierList
14
14
  - 2025-09-03: _from_json_ refactor
15
15
  - 2025-09-04: changed _as_dict_ to std dict creation and removed call to serailization
16
+ - 2025-09-09: added schema_class
16
17
 
17
18
  ======================================================================
18
19
  """
@@ -30,6 +31,7 @@ from .identifier import IdentifierList
30
31
  from .note import Note
31
32
  from .place_reference import PlaceReference
32
33
  from .resource import Resource
34
+ from .schemas import schema_class
33
35
  from .source_reference import SourceReference
34
36
  from .subject import Subject
35
37
  from .logging_hub import hub, logging
@@ -272,6 +274,7 @@ class EventType(Enum):
272
274
 
273
275
  return None # Default to UNKNOWN if no match is found
274
276
 
277
+ @schema_class(toplevel=True)
275
278
  class Event(Subject):
276
279
  identifier = 'http://gedcomx.org/v1/Event'
277
280
  version = 'http://gedcomx.org/conceptual-model/v1'
@@ -1,7 +1,29 @@
1
- from typing import Optional
1
+ from typing import Optional, TYPE_CHECKING
2
+ """
3
+ ======================================================================
4
+ Project: Gedcom-X
5
+ File: evidence_reference.py
6
+ Author: David J. Cartwright
7
+ Purpose:
8
+
9
+ Created: 2025-08-25
10
+ Updated:
11
+ - 2025-09-09: added schema_class
12
+
13
+
14
+ ======================================================================
15
+ """
2
16
 
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Type Imports
20
+ ======================================================================
21
+ """
3
22
  from .attribution import Attribution
4
23
  from .resource import Resource
24
+ from .schemas import schema_class
25
+ if TYPE_CHECKING:
26
+ from .subject import Subject
5
27
  from .logging_hub import hub, logging
6
28
  """
7
29
  ======================================================================
@@ -12,9 +34,14 @@ log = logging.getLogger("gedcomx")
12
34
  serial_log = "gedcomx.serialization"
13
35
  #=====================================================================
14
36
 
37
+ @schema_class()
15
38
  class EvidenceReference:
16
39
  identifier = 'http://gedcomx.org/v1/EvidenceReference'
17
40
  version = 'http://gedcomx.org/conceptual-model/v1'
18
41
 
19
- def __init__(self, resource: Resource, attribution: Optional[Attribution]) -> None:
20
- pass
42
+ def __init__(self, resource: "Resource | Subject", attribution: Optional[Attribution]) -> None:
43
+ self.resource = resource
44
+ self.attribution: Attribution = attribution
45
+
46
+ def _validate(self):
47
+ raise NotImplemented