gedcom-x 0.5.6__py3-none-any.whl → 0.5.8__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.
Files changed (64) hide show
  1. {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.8.dist-info}/METADATA +1 -1
  2. gedcom_x-0.5.8.dist-info/RECORD +56 -0
  3. gedcomx/Extensions/__init__.py +1 -0
  4. gedcomx/Extensions/rs10/__init__.py +1 -0
  5. gedcomx/Extensions/rs10/rsLink.py +116 -0
  6. gedcomx/TopLevelTypeCollection.py +1 -1
  7. gedcomx/__init__.py +43 -41
  8. gedcomx/{Address.py → address.py} +13 -13
  9. gedcomx/{Agent.py → agent.py} +52 -24
  10. gedcomx/{Attribution.py → attribution.py} +36 -9
  11. gedcomx/{Conclusion.py → conclusion.py} +49 -21
  12. gedcomx/converter.py +1049 -0
  13. gedcomx/coverage.py +55 -0
  14. gedcomx/{Date.py → date.py} +11 -4
  15. gedcomx/{Document.py → document.py} +27 -8
  16. gedcomx/{Event.py → event.py} +102 -27
  17. gedcomx/{EvidenceReference.py → evidence_reference.py} +2 -2
  18. gedcomx/{Fact.py → fact.py} +45 -34
  19. gedcomx/{Gedcom5x.py → gedcom5x.py} +78 -61
  20. gedcomx/gedcom7/Exceptions.py +9 -0
  21. gedcomx/gedcom7/Gedcom7.py +160 -0
  22. gedcomx/gedcom7/GedcomStructure.py +94 -0
  23. gedcomx/gedcom7/Specification.py +347 -0
  24. gedcomx/gedcom7/__init__.py +26 -0
  25. gedcomx/gedcom7/g7interop.py +205 -0
  26. gedcomx/gedcom7/logger.py +19 -0
  27. gedcomx/gedcomx.py +501 -0
  28. gedcomx/{Gender.py → gender.py} +29 -17
  29. gedcomx/group.py +63 -0
  30. gedcomx/{Identifier.py → identifier.py} +13 -16
  31. gedcomx/{LoggingHub.py → logging_hub.py} +21 -0
  32. gedcomx/{Mutations.py → mutations.py} +50 -26
  33. gedcomx/name.py +396 -0
  34. gedcomx/{Note.py → note.py} +17 -10
  35. gedcomx/{OnlineAccount.py → online_account.py} +1 -1
  36. gedcomx/{Person.py → person.py} +52 -29
  37. gedcomx/place_description.py +123 -0
  38. gedcomx/place_reference.py +62 -0
  39. gedcomx/qualifier.py +54 -0
  40. gedcomx/{Relationship.py → relationship.py} +33 -13
  41. gedcomx/resource.py +85 -0
  42. gedcomx/serialization.py +815 -0
  43. gedcomx/{SourceDescription.py → source_description.py} +144 -85
  44. gedcomx/{SourceReference.py → source_reference.py} +15 -14
  45. gedcomx/{Subject.py → subject.py} +30 -28
  46. gedcomx/{GedcomX.py → translation.py} +283 -446
  47. gedcomx/{URI.py → uri.py} +42 -26
  48. gedcom_x-0.5.6.dist-info/RECORD +0 -45
  49. gedcomx/Coverage.py +0 -36
  50. gedcomx/Group.py +0 -37
  51. gedcomx/Name.py +0 -276
  52. gedcomx/PlaceDescription.py +0 -70
  53. gedcomx/PlaceReference.py +0 -30
  54. gedcomx/Qualifier.py +0 -27
  55. gedcomx/Resource.py +0 -75
  56. gedcomx/Serialization.py +0 -401
  57. gedcomx/Translation.py +0 -219
  58. {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.8.dist-info}/WHEEL +0 -0
  59. {gedcom_x-0.5.6.dist-info → gedcom_x-0.5.8.dist-info}/top_level.txt +0 -0
  60. /gedcomx/{Exceptions.py → exceptions.py} +0 -0
  61. /gedcomx/{ExtensibleEnum.py → extensible_enum.py} +0 -0
  62. /gedcomx/{Gedcom.py → gedcom.py} +0 -0
  63. /gedcomx/{SourceCitation.py → source_citation.py} +0 -0
  64. /gedcomx/{TextValue.py → textvalue.py} +0 -0
gedcomx/gedcomx.py ADDED
@@ -0,0 +1,501 @@
1
+ DEBUG = False
2
+
3
+ import json
4
+ import random
5
+ import string
6
+
7
+ from typing import Any, Dict, Optional
8
+
9
+ """
10
+ ======================================================================
11
+ Project: Gedcom-X
12
+ File: GedcomX.py
13
+ Author: David J. Cartwright
14
+ Purpose: Object for working with Gedcom-X Data
15
+
16
+ Created: 2025-07-25
17
+ Updated:
18
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data,
19
+ id_index functionality, will be used for resolution of Resources
20
+
21
+ ======================================================================
22
+ """
23
+
24
+ """
25
+ ======================================================================
26
+ GEDCOM Module Types
27
+ ======================================================================
28
+ """
29
+ from .agent import Agent
30
+ from .attribution import Attribution
31
+ from .document import Document
32
+ from .event import Event
33
+ from .group import Group
34
+ from .identifier import make_uid
35
+ from .Logging import get_logger
36
+ from .person import Person
37
+ from .place_description import PlaceDescription
38
+ from .relationship import Relationship, RelationshipType
39
+ from .resource import Resource
40
+ from .source_description import ResourceType, SourceDescription
41
+ from .textvalue import TextValue
42
+ from .uri import URI
43
+ #=====================================================================
44
+
45
+
46
+ def TypeCollection(item_type):
47
+ """
48
+ Factory that creates a typed, indexable collection for a specific model class.
49
+
50
+ The returned object behaves like:
51
+ - a container (append/remove, __len__, __getitem__)
52
+ - an iterator (for item in collection)
53
+ - a simple query helper via __call__(**field_equals)
54
+ - a small in-memory index on id, name (see note), and uri
55
+
56
+ Parameters
57
+ ----------
58
+ item_type : type
59
+ The class/type that items in this collection must be instances of.
60
+
61
+ Returns
62
+ -------
63
+ Collection
64
+ A new, empty collection instance specialized for `item_type`.
65
+
66
+ Notes
67
+ -----
68
+ - Name indexing is currently disabled (see TODO in `_update_indexes`).
69
+ - The collection auto-assigns/normalizes an item's `uri.path` based on `item_type`.
70
+ """
71
+ class Collection:
72
+ def __init__(self):
73
+ self._items = []
74
+ self._id_index = {}
75
+ self._name_index = {}
76
+ self._uri_index = {}
77
+ self.uri = URI(path=f'/{item_type.__name__}s/')
78
+
79
+ def __iter__(self):
80
+ self._index = 0
81
+ return self
82
+
83
+ def __next__(self):
84
+ if self._index < len(self._items):
85
+ result = self._items[self._index]
86
+ self._index += 1
87
+ return result
88
+ else:
89
+ raise StopIteration
90
+
91
+ @property
92
+ def item_type(self):
93
+ return item_type
94
+
95
+ def _update_indexes(self, item):
96
+ # Update the id index
97
+ if hasattr(item, 'id'):
98
+ self._id_index[item.id] = item
99
+
100
+ try:
101
+ if hasattr(item, 'uri'):
102
+ self._uri_index[item.uri.value] = item
103
+ except AttributeError as e:
104
+ print(f"type{item}")
105
+ assert False
106
+
107
+ # Update the name index
108
+ ''' #TODO Fix name handling on persons
109
+ if hasattr(item, 'names'):
110
+ names = getattr(item, 'names')
111
+ for name in names:
112
+ print(name._as_dict_)
113
+ name_value = name.value if isinstance(name, TextValue) else name
114
+ if name_value in self._name_index:
115
+ self._name_index[name_value].append(item)
116
+ else:
117
+ self._name_index[name_value] = [item]
118
+ '''
119
+ @property
120
+ def id_index(self):
121
+ return self._id_index
122
+
123
+ def _remove_from_indexes(self, item):
124
+ # Remove from the id index
125
+ if hasattr(item, 'id'):
126
+ if item.id in self._id_index:
127
+ del self._id_index[item.id]
128
+
129
+ # Remove from the name index
130
+ if hasattr(item, 'names'):
131
+ names = getattr(item, 'names')
132
+ for name in names:
133
+ name_value = name.value if isinstance(name, TextValue) else name
134
+ if name_value in self._name_index:
135
+ if item in self._name_index[name_value]:
136
+ self._name_index[name_value].remove(item)
137
+ if not self._name_index[name_value]:
138
+ del self._name_index[name_value]
139
+
140
+ def byName(self, sname: str | None):
141
+ # Use the name index for fast lookup
142
+ if sname:
143
+ sname = sname.strip()
144
+ return self._name_index.get(sname, [])
145
+ return []
146
+
147
+ def byId(self, id):
148
+ # Use the id index for fast lookup
149
+ return self._id_index.get(id, None)
150
+
151
+ def byUri(self, uri):
152
+ # Use the id index for fast lookup
153
+ return self._uri_index.get(uri.value, None)
154
+
155
+ def append(self, item):
156
+ if not isinstance(item, item_type):
157
+ raise TypeError(f"Expected item of type {item_type.__name__}, got {type(item).__name__}")
158
+ if item.uri:
159
+ item.uri.path = f'{str(item_type.__name__)}s' if (item.uri.path is None or item.uri.path == "") else item.uri.path
160
+ else:
161
+ item.uri = URI(path=f'/{item_type.__name__}s/',fragment=item.id)
162
+
163
+ self._items.append(item)
164
+ self._update_indexes(item)
165
+
166
+ def remove(self, item):
167
+ if item not in self._items:
168
+ raise ValueError("Item not found in the collection.")
169
+ self._items.remove(item)
170
+ self._remove_from_indexes(item)
171
+
172
+ def __repr__(self):
173
+ return f"Collection({self._items!r})"
174
+
175
+ def list(self):
176
+ for item in self._items:
177
+ print(item)
178
+
179
+ def __call__(self, **kwargs):
180
+ results = []
181
+ for item in self._items:
182
+ match = True
183
+ for key, value in kwargs.items():
184
+ if not hasattr(item, key) or getattr(item, key) != value:
185
+ match = False
186
+ break
187
+ if match:
188
+ results.append(item)
189
+ return results
190
+
191
+ def __len__(self):
192
+ return len(self._items)
193
+
194
+ def __getitem__(self, index):
195
+ return self._items[index]
196
+
197
+ @property
198
+ def _items_as_dict(self) -> dict:
199
+ return {f'{str(item_type.__name__)}s': [item._as_dict_ for item in self._items]}
200
+
201
+ @property
202
+ def _as_dict_(self):
203
+ return {f'{str(item_type.__name__).lower()}s': [item._as_dict_ for item in self._items]}
204
+
205
+ @property
206
+ def json(self) -> str:
207
+
208
+ return json.dumps(self._as_dict_, indent=4)
209
+
210
+ return Collection()
211
+
212
+ class GedcomX:
213
+ """
214
+ Main GedcomX Object representing a Genealogy. Stores collections of Top Level Gedcom-X Types.
215
+ complies with GEDCOM X Conceptual Model V1 (http://gedcomx.org/conceptual-model/v1)
216
+
217
+ Parameters
218
+ ----------
219
+ id : str
220
+ Unique identifier for this Genealogy.
221
+ attribution : Attribution Object
222
+ Attribution information for the Genealogy
223
+ filepath : str
224
+ Not Implimented.
225
+ description : str
226
+ Description of the Genealogy: ex. 'My Family Tree'
227
+
228
+ Raises
229
+ ------
230
+ ValueError
231
+ If `id` is not a valid UUID.
232
+ """
233
+ version = 'http://gedcomx.org/conceptual-model/v1'
234
+
235
+ def __init__(self, id: Optional[str] = None,
236
+ attribution: Optional[Attribution] = None,
237
+ filepath: Optional[str] = None,
238
+ description: Optional[str] = None) -> None:
239
+
240
+ self.id = id
241
+ self.attribution = attribution
242
+ self._filepath = None
243
+
244
+ self.description = description
245
+ self.source_descriptions = TypeCollection(SourceDescription)
246
+ self.persons = TypeCollection(Person)
247
+ self.relationships = TypeCollection(Relationship)
248
+ self.agents = TypeCollection(Agent)
249
+ self.events = TypeCollection(Event)
250
+ self.documents = TypeCollection(Document)
251
+ self.places = TypeCollection(PlaceDescription)
252
+ self.groups = TypeCollection(Group)
253
+
254
+ self.relationship_table = {}
255
+
256
+ self.default_id_generator = make_uid
257
+
258
+ @property
259
+ def contents(self):
260
+ return {
261
+ "source_descriptions": len(self.source_descriptions),
262
+ "persons": len(self.persons),
263
+ "relationships": len(self.relationships),
264
+ "agents": len(self.agents),
265
+ "events": len(self.events),
266
+ "documents": len(self.documents),
267
+ "places": len(self.places),
268
+ "groups": len(self.groups),
269
+ }
270
+
271
+ def add(self,gedcomx_type_object):
272
+ if gedcomx_type_object:
273
+ if isinstance(gedcomx_type_object,Person):
274
+ self.add_person(gedcomx_type_object)
275
+ elif isinstance(gedcomx_type_object,SourceDescription):
276
+ self.add_source_description(gedcomx_type_object)
277
+ elif isinstance(gedcomx_type_object,Agent):
278
+ self.add_agent(gedcomx_type_object)
279
+ elif isinstance(gedcomx_type_object,PlaceDescription):
280
+ self.add_place_description(gedcomx_type_object)
281
+ elif isinstance(gedcomx_type_object,Event):
282
+ self.add_event(gedcomx_type_object)
283
+ elif isinstance(gedcomx_type_object,Relationship):
284
+ self.add_relationship(gedcomx_type_object)
285
+ else:
286
+ raise ValueError(f"I do not know how to add an Object of type {type(gedcomx_type_object)}")
287
+ else:
288
+ Warning("Tried to add a None type to the Geneology")
289
+
290
+ def add_source_description(self,sourceDescription: SourceDescription):
291
+ if sourceDescription and isinstance(sourceDescription,SourceDescription):
292
+ if sourceDescription.id is None:
293
+ sourceDescription.id =self.default_id_generator()
294
+ self.source_descriptions.append(item=sourceDescription)
295
+ self.lastSourceDescriptionAdded = sourceDescription
296
+ else:
297
+ raise ValueError(f"When adding a SourceDescription, value must be of type SourceDescription, type {type(sourceDescription)} was provided")
298
+
299
+ def add_person(self,person: Person):
300
+ """Add a Person object to the Genealogy
301
+
302
+ Args:
303
+ person: Person Object
304
+
305
+ Returns:
306
+ None
307
+
308
+ Raises:
309
+ ValueError: If `person` is not of type Person.
310
+ """
311
+ if person and isinstance(person,Person):
312
+ if person.id is None:
313
+ person.id =self.make_id()
314
+ self.persons.append(item=person)
315
+ else:
316
+ raise ValueError(f'person must be a Person Object not type: {type(person)}')
317
+
318
+ def add_relationship(self,relationship: Relationship):
319
+ if relationship and isinstance(relationship,Relationship):
320
+ if isinstance(relationship.person1,Resource) and isinstance(relationship.person2,Resource):
321
+ print("Adding unresolved Relationship")
322
+ self.relationships.append(relationship)
323
+ return
324
+ elif isinstance(relationship.person1,Person) and isinstance(relationship.person2,Person):
325
+
326
+ if relationship.person1:
327
+ if relationship.person1.id is None:
328
+ relationship.person1.id = self.make_id()
329
+ if not self.persons.byId(relationship.person1.id):
330
+ self.persons.append(relationship.person1)
331
+ if relationship.person1.id not in self.relationship_table:
332
+ self.relationship_table[relationship.person1.id] = []
333
+ self.relationship_table[relationship.person1.id].append(relationship)
334
+ relationship.person1._add_relationship(relationship)
335
+ else:
336
+ pass
337
+
338
+ if relationship.person2:
339
+ if relationship.person2.id is None:
340
+ relationship.person2.id = self.make_id() #TODO
341
+ if not self.persons.byId(relationship.person2.id):
342
+ self.persons.append(relationship.person2)
343
+ if relationship.person2.id not in self.relationship_table:
344
+ self.relationship_table[relationship.person2.id] = []
345
+ self.relationship_table[relationship.person2.id].append(relationship)
346
+ relationship.person2._add_relationship(relationship)
347
+ else:
348
+ pass
349
+
350
+ self.relationships.append(relationship)
351
+ else:
352
+ raise ValueError()
353
+
354
+ def add_place_description(self,placeDescription: PlaceDescription):
355
+ if placeDescription and isinstance(placeDescription,PlaceDescription):
356
+ if placeDescription.id is None:
357
+ Warning("PlaceDescription has no id")
358
+ self.places.append(placeDescription)
359
+
360
+ def add_agent(self,agent: Agent):
361
+ """Add a Agent object to the Genealogy
362
+
363
+ Args:
364
+ agent: Agent Object
365
+
366
+ Returns:
367
+ None
368
+
369
+ Raises:
370
+ ValueError: If `agent` is not of type Agent.
371
+ """
372
+ if agent and isinstance(agent,Agent):
373
+ if agent in self.agents:
374
+ return
375
+ if agent.id is None:
376
+ agent.id = make_uid()
377
+ if self.agents.byId(agent.id):
378
+ pass #TODO Deal with duplicates
379
+ #raise ValueError
380
+ self.agents.append(agent)
381
+
382
+ def add_event(self,event_to_add: Event):
383
+ if event_to_add and isinstance(event_to_add,Event):
384
+ if event_to_add.id is None: event_to_add.id = make_uid()
385
+ for current_event in self.events:
386
+ if event_to_add == current_event:
387
+ print("DUPLICATE EVENT")
388
+ print(event_to_add._as_dict_)
389
+ print(current_event._as_dict_)
390
+ return
391
+ self.events.append(event_to_add)
392
+ else:
393
+ raise ValueError
394
+
395
+ def get_person_by_id(self,id: str):
396
+ filtered = [person for person in self.persons if getattr(person, 'id') == id]
397
+ if filtered: return filtered[0]
398
+ return None
399
+
400
+ def source(self,id: str):
401
+ filtered = [source for source in self.source_descriptions if getattr(source, 'id') == id]
402
+ if filtered: return filtered[0]
403
+ return None
404
+
405
+ @property
406
+ def id_index(self):
407
+ combined = {**self.source_descriptions.id_index,
408
+ **self.persons.id_index,
409
+ **self.relationships.id_index,
410
+ **self.agents.id_index,
411
+ **self.events.id_index,
412
+ **self.documents.id_index,
413
+ **self.places.id_index,
414
+ **self.groups.id_index
415
+ }
416
+ for i in combined.keys():
417
+ combined[i] = str(type(combined[i]).__name__)
418
+ return combined
419
+
420
+ @property
421
+ def _as_dict(self) -> dict[str, Any]:
422
+ type_as_dict: Dict[str, Any] = {}
423
+
424
+ if self.persons and len(self.persons) > 0:
425
+ type_as_dict["persons"] = [person._as_dict_ for person in self.persons]
426
+
427
+ if self.source_descriptions:
428
+ type_as_dict["sourceDescriptions"] = [
429
+ sd._as_dict_ for sd in self.source_descriptions
430
+ ]
431
+
432
+ if self.relationships:
433
+ type_as_dict["relationships"] = [
434
+ rel._as_dict_ for rel in self.relationships
435
+ ]
436
+
437
+ if self.agents:
438
+ type_as_dict["agents"] = [agent._as_dict_ for agent in self.agents]
439
+
440
+ if self.events:
441
+ type_as_dict["events"] = [event._as_dict_ for event in self.events]
442
+
443
+ if self.places:
444
+ type_as_dict["places"] = [place._as_dict_ for place in self.places]
445
+
446
+ if self.documents:
447
+ type_as_dict["documents"] = [doc._as_dict_ for doc in self.documents]
448
+
449
+ return type_as_dict
450
+
451
+ @property
452
+ def json(self):
453
+ """
454
+ JSON Representation of the GedcomX Genealogy.
455
+
456
+ Returns:
457
+ str: JSON Representation of the GedcomX Genealogy in the GEDCOM X JSON Serialization Format
458
+ """
459
+ gedcomx_json = {
460
+ 'persons': [person._as_dict_ for person in self.persons],
461
+ 'sourceDescriptions' : [sourceDescription._as_dict_ for sourceDescription in self.source_descriptions],
462
+ 'relationships': [relationship._as_dict_ for relationship in self.relationships],
463
+ 'agents': [agent._as_dict_ for agent in self.agents],
464
+ 'events': [event._as_dict_ for event in self.events],
465
+ 'places': [place._as_dict_ for place in self.places],
466
+ 'documents': [document._as_dict_ for document in self.documents],
467
+ }
468
+ return json.dumps(gedcomx_json, indent=4)
469
+
470
+ @staticmethod
471
+ def from_json(data: dict):
472
+ from .serialization import Serialization
473
+ gx = GedcomX()
474
+
475
+ source_descriptions = data.get('sourceDescriptions', [])
476
+ for source in source_descriptions:
477
+ gx.add_source_description(Serialization.deserialize(source,SourceDescription))
478
+
479
+ persons = data.get('persons', [])
480
+ for person in persons:
481
+ gx.add_person(Serialization.deserialize(person,Person))
482
+
483
+ relationships = data.get('relationships', [])
484
+ for relationship in relationships:
485
+ gx.add_relationship(Serialization.deserialize(relationship,Relationship))
486
+
487
+ agents = data.get('agents', [])
488
+ for agent in agents:
489
+ gx.add_agent(Serialization.deserialize(agent,Agent))
490
+
491
+ events = data.get('events', [])
492
+ for event in events:
493
+ gx.add_event(Serialization.deserialize(event,Event))
494
+
495
+ return gx
496
+
497
+ @staticmethod
498
+ def make_id(length: int = 12) -> str:
499
+ """Generate a random alphanumeric ID of given length."""
500
+ alphabet = string.ascii_letters + string.digits
501
+ return ''.join(random.choices(alphabet, k=length))
@@ -1,17 +1,31 @@
1
1
  from enum import Enum
2
2
  from typing import List, Optional
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: gender.py
7
+ Author: David J. Cartwright
8
+ Purpose:
3
9
 
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.Resource import Resource
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-08-31:
13
+
14
+ ======================================================================
15
+ """
9
16
 
10
- from .Conclusion import Conclusion
11
- from .Qualifier import Qualifier
12
- from .Serialization import Serialization
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
22
+ from .attribution import Attribution
23
+ from .conclusion import ConfidenceLevel, Conclusion
24
+ from .note import Note
25
+ from .resource import Resource
26
+ from .source_reference import SourceReference
27
+ #=====================================================================
13
28
 
14
- from collections.abc import Sized
15
29
 
16
30
  class GenderType(Enum):
17
31
  Male = "http://gedcomx.org/Male"
@@ -35,7 +49,7 @@ class Gender(Conclusion):
35
49
 
36
50
  def __init__(self,
37
51
  id: Optional[str] = None,
38
- lang: Optional[str] = 'en',
52
+ lang: Optional[str] = None,
39
53
  sources: Optional[List[SourceReference]] = None,
40
54
  analysis: Optional[Resource] = None,
41
55
  notes: Optional[List[Note]] = None,
@@ -48,19 +62,17 @@ class Gender(Conclusion):
48
62
 
49
63
  @property
50
64
  def _as_dict_(self):
65
+ from .serialization import Serialization
66
+ type_as_dict = super()._as_dict_
67
+ if self.type:
68
+ type_as_dict['type'] = self.type.value if self.type else None
51
69
 
52
-
53
- type_as_dict = super()._as_dict_ # Start with base class fields
54
- # Only add Relationship-specific fields
55
- type_as_dict.update({
56
- 'type':self.type.value if self.type else None
57
-
58
- })
59
70
 
60
71
  return Serialization.serialize_dict(type_as_dict)
61
72
 
62
73
  @classmethod
63
74
  def _from_json_(cls,data):
75
+ from .serialization import Serialization
64
76
 
65
77
  return Serialization.deserialize(data, Gender)
66
78
 
gedcomx/group.py ADDED
@@ -0,0 +1,63 @@
1
+ from enum import Enum
2
+ from typing import List, Optional
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: group.py
7
+ Author: David J. Cartwright
8
+ Purpose:
9
+
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-09-01: Updating basic structure, identify TODO s
13
+
14
+ ======================================================================
15
+ """
16
+
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
22
+ from .attribution import Attribution
23
+ from .conclusion import ConfidenceLevel
24
+ from .document import Document
25
+ from .date import Date
26
+ from .evidence_reference import EvidenceReference
27
+ from .identifier import Identifier
28
+ from .note import Note
29
+ from .place_reference import PlaceReference
30
+ from .source_reference import SourceReference
31
+ from .resource import Resource
32
+
33
+ from .textvalue import TextValue
34
+ from .subject import Subject
35
+
36
+ class GroupRoleType(Enum): #TODO Impliment
37
+ def __init__(self) -> None:
38
+ super().__init__()
39
+
40
+ class GroupRole: #TODO Impliment
41
+ identifier = 'http://gedcomx.org/v1/GroupRole'
42
+ version = 'http://gedcomx.org/conceptual-model/v1'
43
+
44
+ def __init__(self, person: Resource,type: Optional[Enum], date: Optional[Date],details: Optional[str]) -> None:
45
+ pass
46
+
47
+ class Group(Subject): #TODO Impliment
48
+ identifier = 'http://gedcomx.org/v1/Group'
49
+ version = 'http://gedcomx.org/conceptual-model/v1'
50
+
51
+ def __init__(self,
52
+ id: str | None, lang: str | None,
53
+ sources: List[SourceReference] | None,
54
+ analysis: Document | Resource | None, notes: List[Note] | None, confidence: ConfidenceLevel | None, attribution: Attribution | None, extracted: bool | None, evidence: List[EvidenceReference] | None, media: List[SourceReference] | None, identifiers: List[Identifier] | None,
55
+ names: List[TextValue],
56
+ date: Optional[Date],
57
+ place: Optional[PlaceReference],
58
+ roles: Optional[List[GroupRole]]) -> None:
59
+ super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
60
+ self.names = names if names else []
61
+ self.date = date
62
+ self.place = place
63
+ self.roles = roles if roles else []