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
@@ -1,11 +1,25 @@
1
1
  from __future__ import annotations
2
2
  from dataclasses import dataclass, field
3
- from typing import Mapping, Optional, Union, Iterable, Tuple
3
+ from typing import Mapping, Sequence, Tuple, Union, Iterable
4
4
  from urllib.parse import urlsplit, urlunsplit, urlencode, parse_qsl, SplitResult
5
5
 
6
6
  _DEFAULT_SCHEME = "gedcomx"
7
7
 
8
- @dataclass(frozen=False, slots=True)
8
+ # ---------- typing helpers for urlencode (Option A) ----------
9
+ QuerySeq = Sequence[Tuple[str, str]]
10
+ QueryMap = Mapping[str, str]
11
+ QueryLike = Union[str, QueryMap, QuerySeq]
12
+
13
+ def _encode_query(q: QueryLike) -> str:
14
+ """Return a properly encoded query string."""
15
+ if isinstance(q, str):
16
+ return q
17
+ if isinstance(q, Mapping):
18
+ return urlencode(q, doseq=True) # mapping is fine
19
+ return urlencode(list(q), doseq=True) # coerce iterable to a sequence
20
+
21
+
22
+ @dataclass(slots=True)
9
23
  class URI:
10
24
  scheme: str = field(default=_DEFAULT_SCHEME)
11
25
  authority: str = field(default="")
@@ -15,29 +29,33 @@ class URI:
15
29
 
16
30
  # ---------- constructors ----------
17
31
  @classmethod
18
- def from_url(cls, url: str, *, default_scheme: str = _DEFAULT_SCHEME) -> "URI":
32
+ def from_url(cls, url: str, *, default_scheme: str = _DEFAULT_SCHEME) -> URI:
19
33
  s = urlsplit(url)
20
34
  scheme = s.scheme or default_scheme
21
35
  return cls(scheme=scheme, authority=s.netloc, path=s.path, query=s.query, fragment=s.fragment)
22
36
 
23
37
  @classmethod
24
- def parse(cls, value: str) -> "URI":
38
+ def parse(cls, value: str) -> URI:
25
39
  return cls.from_url(value)
26
40
 
27
41
  @classmethod
28
42
  def from_parts(
29
43
  cls,
30
44
  *,
31
- scheme: Optional[str] = None,
45
+ scheme: str | None = None,
32
46
  authority: str = "",
33
47
  path: str = "",
34
- query: Union[str, Mapping[str, str], Iterable[Tuple[str, str]]] = "",
35
- fragment: str = ""
36
- ) -> "URI":
37
- q = query if isinstance(query, str) else urlencode(query, doseq=True)
48
+ query: QueryLike = "",
49
+ fragment: str = "",
50
+ ) -> URI:
51
+ q = _encode_query(query)
38
52
  return cls(scheme=scheme or _DEFAULT_SCHEME, authority=authority, path=path, query=q, fragment=fragment)
39
53
 
40
54
  # ---------- views ----------
55
+ @property
56
+ def uri(self) -> str:
57
+ return str(self)
58
+
41
59
  @property
42
60
  def value(self) -> str:
43
61
  return str(self)
@@ -49,9 +67,7 @@ class URI:
49
67
  return urlunsplit(self.split())
50
68
 
51
69
  @property
52
- def _as_dict_(self) -> dict:
53
- # Keeps a simple, explicit structure
54
- return urlunsplit(self.split())
70
+ def _as_dict_(self) -> dict[str, object]:
55
71
  return {
56
72
  "scheme": self.scheme,
57
73
  "authority": self.authority,
@@ -61,30 +77,29 @@ class URI:
61
77
  "value": str(self),
62
78
  }
63
79
 
64
- # Accepts {'resource': '...'} or a plain string, mirroring your original
80
+ # Accepts {'resource': '...'} or a plain string
65
81
  @classmethod
66
- def from_jsonish(cls, data: Union[str, Mapping[str, str]]) -> "URI":
82
+ def from_jsonish(cls, data: str | Mapping[str, object]) -> URI:
67
83
  if isinstance(data, str):
68
84
  return cls.from_url(data)
69
85
  if isinstance(data, Mapping):
70
86
  raw = data.get("resource") or data.get("value") or ""
71
- if raw:
87
+ if isinstance(raw, str) and raw:
72
88
  return cls.from_url(raw)
73
89
  raise ValueError(f"Cannot build URI from: {data!r}")
74
90
 
75
91
  # ---------- functional updaters ----------
76
- def with_scheme(self, scheme: str) -> "URI": return self.replace(scheme=scheme)
77
- def with_authority(self, authority: str) -> "URI": return self.replace(authority=authority)
78
- def with_path(self, path: str, *, join: bool = False) -> "URI":
92
+ def with_scheme(self, scheme: str) -> URI: return self.replace(scheme=scheme)
93
+ def with_authority(self, authority: str) -> URI: return self.replace(authority=authority)
94
+ def with_path(self, path: str, *, join: bool = False) -> URI:
79
95
  new_path = (self.path.rstrip("/") + "/" + path.lstrip("/")) if join else path
80
96
  return self.replace(path=new_path)
81
- def with_fragment(self, fragment: str | None) -> "URI":
97
+ def with_fragment(self, fragment: str | None) -> URI:
82
98
  return self.replace(fragment=(fragment or ""))
83
- def without_fragment(self) -> "URI": return self.replace(fragment="")
84
- def with_query(self, query: Union[str, Mapping[str, str], Iterable[Tuple[str, str]]]) -> "URI":
85
- q = query if isinstance(query, str) else urlencode(query, doseq=True)
86
- return self.replace(query=q)
87
- def add_query_params(self, params: Mapping[str, Union[str, Iterable[str]]]) -> "URI":
99
+ def without_fragment(self) -> URI: return self.replace(fragment="")
100
+ def with_query(self, query: QueryLike) -> URI:
101
+ return self.replace(query=_encode_query(query))
102
+ def add_query_params(self, params: Mapping[str, Union[str, Iterable[str]]]) -> URI:
88
103
  existing = parse_qsl(self.query, keep_blank_values=True)
89
104
  for k, v in params.items():
90
105
  if isinstance(v, str):
@@ -95,8 +110,9 @@ class URI:
95
110
  return self.replace(query=urlencode(existing, doseq=True))
96
111
 
97
112
  # ---------- helpers ----------
98
- def replace(self, **kwargs) -> "URI":
99
- return URI(
113
+ def replace(self, **kwargs) -> URI:
114
+ cls = type(self)
115
+ return cls(
100
116
  scheme=kwargs.get("scheme", self.scheme or _DEFAULT_SCHEME),
101
117
  authority=kwargs.get("authority", self.authority),
102
118
  path=kwargs.get("path", self.path),
@@ -1,45 +0,0 @@
1
- gedcomx/Address.py,sha256=FDBic6oQB30eKMWM7-6_YVPHaJdPhziU5LybwE5TY5Q,4855
2
- gedcomx/Agent.py,sha256=C-tzfqcjuU22iJ2LjoatKdCbI_i0xskpmZsuj58K6WY,8555
3
- gedcomx/Attribution.py,sha256=2bcl2UNsMPn10kDrA080qFRzxK-KYqZBgmPGNBZ2M4Y,2608
4
- gedcomx/Conclusion.py,sha256=t8n8FuWwzinFTPHp9D2Umu93-4A12I9L5fCx510mfqA,8414
5
- gedcomx/Coverage.py,sha256=BVlB3kWDoOP2j86j67hNJLC4LqoLwqs3N93z0ZbXYTw,1227
6
- gedcomx/Date.py,sha256=cv2UeyOcTHuH2CVHq9EjTnj3FR-r4_OVuPiuc8sbOPc,2130
7
- gedcomx/Document.py,sha256=32nWfDNVWkPK2_I0-hhcNQkrjH3ssdbtw-II3QMMelw,2957
8
- gedcomx/Event.py,sha256=BHhmO2UlxbkSzB5htdMRf2h-WNuJQsWfrSC8tNGQ6Mg,10375
9
- gedcomx/EvidenceReference.py,sha256=WXZpmcKzwb3lYQmGVf-TY2IsbNG3fIInPY1khRjtOSg,348
10
- gedcomx/Exceptions.py,sha256=0OdPM3euhBMgX8o61ZwPuKeN8zPuSuuDcSBFflVGFqk,587
11
- gedcomx/ExtensibleEnum.py,sha256=DftCZLMBNul3C9hwh-rf0GE3SVdvylvyd5mt7bX_l6o,6535
12
- gedcomx/Fact.py,sha256=-Zz321xeFd93riaX4C4i7gIPwpqdArAYZDlWRoXA1Uk,24444
13
- gedcomx/Gedcom.py,sha256=l_BuLBynQacDtpLjhs2Afrabfiqt2Opoo_5_7myI7Z4,1709
14
- gedcomx/Gedcom5x.py,sha256=ZaMqvmDTn684m4uSDRy3uxgKk442K5WZjDy_0s2ga-8,22459
15
- gedcomx/GedcomX.py,sha256=mFviKVvk8dSn4tHPd1hkHBxoTOMNKlBv7gH3Q1n87IU,63666
16
- gedcomx/Gender.py,sha256=AV125vECC5JSgvNJRmotjDEt-A3cFzth6pcSLFwJBEE,2328
17
- gedcomx/Group.py,sha256=VNI_cI_-YnJ9cHzwMmHD8qwDNMe89kEocPuQ67rwStA,1606
18
- gedcomx/Identifier.py,sha256=pJB8DlMF1k5DNR0Lha6rGTTiOtwe0joVLRLQCuoXtIw,8507
19
- gedcomx/Logging.py,sha256=vBDOjawVXc4tCge1laYjy6_2Ves-fnGzG0m6NnLZejE,624
20
- gedcomx/LoggingHub.py,sha256=lOeMP_0hFPydVNuMcUC31FMBRHtRMP02pBMM8TuhsNg,7143
21
- gedcomx/Mutations.py,sha256=idhQsnikCTovmNEGAhDsOe97V0L9AnFTRaufzUPm64w,6298
22
- gedcomx/Name.py,sha256=en_VkGjpZPP_Dbl9HSq2h2JCtLS2NMzmLvis5RdBPeM,12913
23
- gedcomx/Note.py,sha256=cvV4-fCZ0oh2zXfzD-YYoIM_WTox-fk2_bfl3i4CoNc,2251
24
- gedcomx/OnlineAccount.py,sha256=P24o98IXo_8XQoICYZPgALjdzS0q8t2l4DU3Od9KxyI,291
25
- gedcomx/Person.py,sha256=LyGPCZh6rZU_dE3GCJE92x1lLk41c_Edr_Po9smhPLc,7321
26
- gedcomx/PlaceDescription.py,sha256=TRHIkDiErHa_mlx_x8gYCF6UnQ-XgT08uSmYmFNa4u0,3255
27
- gedcomx/PlaceReference.py,sha256=3NyEYq0FaMI8m3l4H6-Ov-n9NUq_m751iYVxofqtv30,870
28
- gedcomx/Qualifier.py,sha256=mZ_GWT3gca8g9nWidlXIRTN5dEtf_rmnLHYl1jJkQYs,728
29
- gedcomx/Relationship.py,sha256=oyT501vR5jvWxqEYy3KvW9egD4CPL5JaAYWJmlNy9Ig,3599
30
- gedcomx/Resource.py,sha256=bZ2dV-OdpvmDRCppyfHJXiarIKL7-OcofwvYRnpOJW0,2324
31
- gedcomx/Serialization.py,sha256=ubtmrvi-nKmVUCK1XGwuLijrPMggNJe--kkCIC-87Ds,18580
32
- gedcomx/SourceCitation.py,sha256=aW-lEb7bT9QU49GiBjJppFMBvtisR6fhVVuXjr5y4vQ,742
33
- gedcomx/SourceDescription.py,sha256=zOkXo-fy-uCl_EGU8B-c6pzhlMk3nYNHn13ln1AkYk0,11914
34
- gedcomx/SourceReference.py,sha256=swVzZ5A0jt5yFYJNJvMnQZtb7d-2YEAQPxp2a4lEY1k,5071
35
- gedcomx/Subject.py,sha256=znee3SxYKk99JLP6KmZHkXs5hIOGu_jeLwxagENNfXs,3298
36
- gedcomx/TextValue.py,sha256=6B0wMxL0nigFNzhXZDhbTONvFGbnM2t2NcDZiZuu4Zw,1112
37
- gedcomx/TopLevelTypeCollection.py,sha256=nvTO6GwFwEZk9jX4fVqhy75ygsshomNb20tnlExKqyY,1495
38
- gedcomx/Translation.py,sha256=NWa1NtTOC_VgQ-EGwWG06zUpddRxdKFSLPP6066JFaA,12485
39
- gedcomx/URI.py,sha256=S2pFsu_VyKgOsRdHIj8pjnh6_tdA3OjAYiIugoEd70k,4271
40
- gedcomx/Zip.py,sha256=lBxcv-Vip45884EHj56wZJJ5I36Q38UuHUidDxQBoS8,14
41
- gedcomx/__init__.py,sha256=O8LkNk9b8BHIN3mf0RowfR9M2HdKYhF8Fgi3qxW2RSs,1660
42
- gedcom_x-0.5.6.dist-info/METADATA,sha256=rNO-ZyEC3th_8iML9XtfJOQcnPJI1uksyWz2MB9DAV8,4332
43
- gedcom_x-0.5.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- gedcom_x-0.5.6.dist-info/top_level.txt,sha256=smVBF4nxSU-mzCd6idtRYTbYjPICMMi8pTqewEmqF8Y,8
45
- gedcom_x-0.5.6.dist-info/RECORD,,
gedcomx/Coverage.py DELETED
@@ -1,36 +0,0 @@
1
- from typing import Optional
2
-
3
- from .Date import Date
4
- from .PlaceReference import PlaceReference
5
- from .Serialization import Serialization
6
-
7
- class Coverage:
8
- identifier = 'http://gedcomx.org/v1/Coverage'
9
- version = 'http://gedcomx.org/conceptual-model/v1'
10
-
11
- def __init__(self,spatial: Optional[PlaceReference], temporal: Optional[Date]) -> None:
12
- self.spatial = spatial
13
- self.temporal = temporal
14
-
15
- # ...existing code...
16
-
17
- @property
18
- def _as_dict_(self):
19
- type_as_dict = {
20
- 'spatial': self.spatial if self.spatial else None,
21
- 'temporal': self. temporal if self.temporal else None
22
- }
23
-
24
- return Serialization.serialize_dict(type_as_dict)
25
-
26
- @classmethod
27
- def _from_json_(cls, data: dict):
28
- """
29
- Create a Coverage instance from a JSON-dict (already parsed).
30
- """
31
- from .PlaceReference import PlaceReference
32
- from .Date import Date
33
-
34
- spatial = PlaceReference._from_json_(data.get('spatial')) if data.get('spatial') else None
35
- temporal = Date._from_json_(data.get('temporal')) if data.get('temporal') else None
36
- return cls(spatial=spatial, temporal=temporal)
gedcomx/Group.py DELETED
@@ -1,37 +0,0 @@
1
- from enum import Enum
2
- from typing import List, Optional
3
-
4
- from .Attribution import Attribution
5
- from .Conclusion import ConfidenceLevel
6
- from .Date import Date
7
- from .EvidenceReference import EvidenceReference
8
- from .Identifier import Identifier
9
- from .Note import Note
10
- from .PlaceReference import PlaceReference
11
- from .SourceReference import SourceReference
12
- from .Resource import Resource
13
-
14
- from .TextValue import TextValue
15
- from .Subject import Subject
16
-
17
- class GroupRoleType(Enum):
18
- def __init__(self) -> None:
19
- super().__init__()
20
-
21
- class GroupRole:
22
- identifier = 'http://gedcomx.org/v1/GroupRole'
23
- version = 'http://gedcomx.org/conceptual-model/v1'
24
-
25
- def __init__(self, person: Resource,type: Optional[Enum], date: Optional[Date],details: Optional[str]) -> None:
26
- pass
27
-
28
- class Group(Subject):
29
- identifier = 'http://gedcomx.org/v1/Group'
30
- version = 'http://gedcomx.org/conceptual-model/v1'
31
-
32
- def __init__(self, id: str | None, lang: str | None, sources: SourceReference | None, analysis: Resource | None, notes: Note | None, confidence: ConfidenceLevel | None, attribution: Attribution | None, extracted: bool | None, evidence: List[EvidenceReference] | None, media: List[SourceReference] | None, identifiers: List[Identifier] | None,
33
- names: TextValue,
34
- date: Optional[Date],
35
- place: Optional[PlaceReference],
36
- roles: Optional[List[GroupRole]]) -> None:
37
- super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
gedcomx/Name.py DELETED
@@ -1,276 +0,0 @@
1
- from enum import Enum
2
- from typing import List,Optional
3
- from typing_extensions import Self
4
-
5
- from .Attribution import Attribution
6
- from .Conclusion import Conclusion, ConfidenceLevel
7
- from .Date import Date
8
- from .Note import Note
9
- from .Serialization import Serialization
10
- from .SourceReference import SourceReference
11
- from .Resource import Resource
12
-
13
-
14
- class NameType(Enum):
15
- BirthName = "http://gedcomx.org/BirthName"
16
- MarriedName = "http://gedcomx.org/MarriedName"
17
- AlsoKnownAs = "http://gedcomx.org/AlsoKnownAs"
18
- Nickname = "http://gedcomx.org/Nickname"
19
- AdoptiveName = "http://gedcomx.org/AdoptiveName"
20
- FormalName = "http://gedcomx.org/FormalName"
21
- ReligiousName = "http://gedcomx.org/ReligiousName"
22
-
23
- @property
24
- def description(self):
25
- descriptions = {
26
- NameType.BirthName: "Name given at birth.",
27
- NameType.MarriedName: "Name accepted at marriage.",
28
- NameType.AlsoKnownAs: "\"Also known as\" name.",
29
- NameType.Nickname: "Nickname.",
30
- NameType.AdoptiveName: "Name given at adoption.",
31
- NameType.FormalName: "A formal name, usually given to distinguish it from a name more commonly used.",
32
- NameType.ReligiousName: "A name given at a religious rite or ceremony."
33
- }
34
- return descriptions.get(self, "No description available.")
35
-
36
- class NamePartQualifier(Enum):
37
- Title = "http://gedcomx.org/Title"
38
- Primary = "http://gedcomx.org/Primary"
39
- Secondary = "http://gedcomx.org/Secondary"
40
- Middle = "http://gedcomx.org/Middle"
41
- Familiar = "http://gedcomx.org/Familiar"
42
- Religious = "http://gedcomx.org/Religious"
43
- Family = "http://gedcomx.org/Family"
44
- Maiden = "http://gedcomx.org/Maiden"
45
- Patronymic = "http://gedcomx.org/Patronymic"
46
- Matronymic = "http://gedcomx.org/Matronymic"
47
- Geographic = "http://gedcomx.org/Geographic"
48
- Occupational = "http://gedcomx.org/Occupational"
49
- Characteristic = "http://gedcomx.org/Characteristic"
50
- Postnom = "http://gedcomx.org/Postnom"
51
- Particle = "http://gedcomx.org/Particle"
52
- RootName = "http://gedcomx.org/RootName"
53
-
54
- @property
55
- def description(self):
56
- descriptions = {
57
- NamePartQualifier.Title: "A designation for honorifics (e.g., Dr., Rev., His Majesty, Haji), ranks (e.g., Colonel, General), positions (e.g., Count, Chief), or other titles (e.g., PhD, MD). Name part qualifiers of type Title SHOULD NOT provide a value.",
58
- NamePartQualifier.Primary: "A designation for the most prominent name among names of that type (e.g., the primary given name). Name part qualifiers of type Primary SHOULD NOT provide a value.",
59
- NamePartQualifier.Secondary: "A designation for a name that is not primary in its importance among names of that type. Name part qualifiers of type Secondary SHOULD NOT provide a value.",
60
- NamePartQualifier.Middle: "Useful for cultures designating a middle name distinct from a given name and surname. Name part qualifiers of type Middle SHOULD NOT provide a value.",
61
- NamePartQualifier.Familiar: "A designation for one's familiar name. Name part qualifiers of type Familiar SHOULD NOT provide a value.",
62
- NamePartQualifier.Religious: "A name given for religious purposes. Name part qualifiers of type Religious SHOULD NOT provide a value.",
63
- NamePartQualifier.Family: "A name that associates a person with a group, such as a clan, tribe, or patriarchal hierarchy. Name part qualifiers of type Family SHOULD NOT provide a value.",
64
- NamePartQualifier.Maiden: "Original surname retained by women after adopting a new surname upon marriage. Name part qualifiers of type Maiden SHOULD NOT provide a value.",
65
- NamePartQualifier.Patronymic: "A name derived from a father or paternal ancestor. Name part qualifiers of type Patronymic SHOULD NOT provide a value.",
66
- NamePartQualifier.Matronymic: "A name derived from a mother or maternal ancestor. Name part qualifiers of type Matronymic SHOULD NOT provide a value.",
67
- NamePartQualifier.Geographic: "A name derived from associated geography. Name part qualifiers of type Geographic SHOULD NOT provide a value.",
68
- NamePartQualifier.Occupational: "A name derived from one's occupation. Name part qualifiers of type Occupational SHOULD NOT provide a value.",
69
- NamePartQualifier.Characteristic: "A name derived from a characteristic. Name part qualifiers of type Characteristic SHOULD NOT provide a value.",
70
- NamePartQualifier.Postnom: "A name mandated by law for populations in specific regions. Name part qualifiers of type Postnom SHOULD NOT provide a value.",
71
- NamePartQualifier.Particle: "A grammatical designation for articles, prepositions, conjunctions, and other words used as name parts. Name part qualifiers of type Particle SHOULD NOT provide a value.",
72
- NamePartQualifier.RootName: "The 'root' of a name part, as distinguished from prefixes or suffixes (e.g., the root of 'Wilkówna' is 'Wilk'). A RootName qualifier MUST provide a value property."
73
- }
74
- return descriptions.get(self, "No description available.")
75
-
76
- class NamePartType(Enum):
77
- Prefix = "http://gedcomx.org/Prefix"
78
- Suffix = "http://gedcomx.org/Suffix"
79
- Given = "http://gedcomx.org/Given"
80
- Surname = "http://gedcomx.org/Surname"
81
-
82
- @property
83
- def description(self):
84
- descriptions = {
85
- NamePartType.Prefix: "A name prefix.",
86
- NamePartType.Suffix: "A name suffix.",
87
- NamePartType.Given: "A given name.",
88
- NamePartType.Surname: "A surname."
89
- }
90
- return descriptions.get(self, "No description available.")
91
-
92
- class NamePart:
93
- identifier = 'http://gedcomx.org/v1/NamePart'
94
- version = 'http://gedcomx.org/conceptual-model/v1'
95
-
96
- def __init__(self,
97
- type: Optional[NamePartType] = None,
98
- value: Optional[str] = None,
99
- qualifiers: Optional[List[NamePartQualifier]] = []) -> None:
100
- self.type = type
101
- self.value = value
102
- self.qualifiers = qualifiers
103
-
104
- def _as_dict_(self):
105
- return Serialization.serialize_dict(
106
- {'type': self.type.value if self.type else None,
107
- 'value': self.value if self.value else None,
108
- 'qualifiers': [qualifier.value for qualifier in self.qualifiers] if self.qualifiers else None})
109
-
110
- def __eq__(self, other):
111
- if not isinstance(other, NamePart):
112
- return NotImplemented
113
- return (self.type == other.type and
114
- self.value == other.value and
115
- self.qualifiers == other.qualifiers)
116
-
117
- class NameForm:
118
- identifier = 'http://gedcomx.org/v1/NameForm'
119
- version = 'http://gedcomx.org/conceptual-model/v1'
120
-
121
- def __init__(self, lang: Optional[str] = 'en', fullText: Optional[str] = None,parts: Optional[List[NamePart]] = []) -> None:
122
- self.lang = lang
123
- self.fullText = fullText
124
- self.parts = parts
125
-
126
- @property
127
- def _as_dict_(self):
128
- return Serialization.serialize_dict(
129
- {'lang': self.lang,
130
- 'fullText': self.fullText,
131
- 'parts': [part._as_dict_() for part in self.parts] if self.parts else None})
132
-
133
- def _fulltext_parts(self):
134
- pass
135
-
136
- class Name(Conclusion):
137
- identifier = 'http://gedcomx.org/v1/Name'
138
- version = 'http://gedcomx.org/conceptual-model/v1'
139
-
140
- @staticmethod
141
- def simple(text: str):
142
- """
143
- Takes a string and returns a GedcomX Name Object
144
- """
145
- if text:
146
- text = text.replace("/","")
147
- parts = text.rsplit(' ', 1)
148
-
149
- # Assign val1 and val2 based on the split
150
- given = parts[0] if len(parts) > 1 else ""
151
- surname = parts[1] if len(parts) > 1 else parts[0]
152
-
153
- # Remove any '/' characters from both val1 and val2
154
- #given = given.replace('/', '')
155
- #surname = surname.replace('/', '')
156
-
157
- given_name_part = NamePart(type = NamePartType.Given, value=given)
158
- surname_part = NamePart(type = NamePartType.Surname, value=surname)
159
-
160
- name_form = NameForm(fullText=text,parts=[given_name_part,surname_part])
161
- name = Name(type=NameType.BirthName,nameForms=[name_form])
162
- else:
163
- name = Name()
164
- return name
165
-
166
- def __init__(self, id: str = None,
167
- lang: str = 'en',
168
- sources: Optional[List[SourceReference]] = None,
169
- analysis: Resource = None,
170
- notes: Optional[List[Note]] = None,
171
- confidence: Optional[ConfidenceLevel] = None,
172
- attribution: Optional[Attribution] = None,
173
- type: Optional[NameType] = None,
174
- nameForms: Optional[List[NameForm]]= None,
175
- date: Optional[Date] = None) -> None:
176
- super().__init__(id, lang, sources, analysis, notes, confidence, attribution)
177
- self.type = type
178
- self.nameForms = nameForms if nameForms else []
179
- self.date = date
180
-
181
- def _add_name_part(self, namepart_to_add: NamePart):
182
- if namepart_to_add and isinstance(namepart_to_add, NamePart):
183
- for current_namepart in self.nameForms[0].parts:
184
- if namepart_to_add == current_namepart:
185
- return False
186
- self.nameForms[0].parts.append(namepart_to_add)
187
-
188
- @property
189
- def _as_dict_(self):
190
- def _serialize(value):
191
- if isinstance(value, (str, int, float, bool, type(None))):
192
- return value
193
- elif isinstance(value, dict):
194
- return {k: _serialize(v) for k, v in value.items()}
195
- elif isinstance(value, (list, tuple, set)):
196
- return [_serialize(v) for v in value]
197
- elif hasattr(value, "_as_dict_"):
198
- return value._as_dict_
199
- else:
200
- return str(value) # fallback for unknown objects
201
-
202
- name_as_dict = super()._as_dict_
203
-
204
- name_as_dict.update( {
205
- 'type':self.type.value if self.type else None,
206
- 'nameForms': [nameForm._as_dict_ for nameForm in self.nameForms],
207
- 'date': self.date._as_dict_ if self.date else None})
208
-
209
- #del name_as_dict['id']
210
- # Serialize and exclude None values
211
- for key, value in name_as_dict.items():
212
- if value is not None:
213
- name_as_dict[key] = _serialize(value)
214
-
215
- # 3) merge and filter out None *at the top level*
216
- return {
217
- k: v
218
- for k, v in name_as_dict.items()
219
- if v is not None
220
- }
221
-
222
- return name_as_dict
223
-
224
- class QuickName():
225
- def __new__(cls,name: str) -> Name:
226
- obj = Name(nameForms=[NameForm(fullText=name)])
227
- return obj
228
-
229
- def ensure_list(val):
230
- if val is None:
231
- return []
232
- return val if isinstance(val, list) else [val]
233
-
234
- NamePart._from_json_ = classmethod(lambda cls, data: NamePart(
235
- type=NamePartType(data['type']) if 'type' in data else None,
236
- value=data.get('value'),
237
- qualifiers=[NamePartQualifier(q) for q in ensure_list(data.get('qualifiers'))]
238
- ))
239
-
240
- NamePart._to_dict_ = lambda self: {
241
- 'type': self.type.value if self.type else None,
242
- 'value': self.value,
243
- 'qualifiers': [q.value for q in self.qualifiers] if self.qualifiers else []
244
- }
245
-
246
-
247
- # NameForm
248
- NameForm._from_json_ = classmethod(lambda cls, data: NameForm(
249
- lang=data.get('lang', 'en'),
250
- fullText=data.get('fullText'),
251
- parts=[NamePart._from_json_(p) for p in ensure_list(data.get('parts'))]
252
- ))
253
-
254
- NameForm._to_dict_ = lambda self: {
255
- 'lang': self.lang,
256
- 'fullText': self.fullText,
257
- 'parts': [p._to_dict_() for p in self.parts] if self.parts else []
258
- }
259
-
260
-
261
- # Name
262
- Name._from_json_ = classmethod(lambda cls, data: cls(
263
- id=data.get('id'),
264
- lang=data.get('lang', 'en'),
265
- sources=[SourceReference._from_json_(s) for s in ensure_list(data.get('sources'))],
266
- analysis=Resource._from_json_(data['analysis']) if data.get('analysis') else None,
267
- notes=[Note._from_json_(n) for n in ensure_list(data.get('notes'))],
268
- confidence=ConfidenceLevel._from_json_(data['confidence']) if data.get('confidence') else None,
269
- attribution=Attribution._from_json_(data['attribution']) if data.get('attribution') else None,
270
- type=NameType(data['type']) if data.get('type') else None,
271
- nameForms=[NameForm._from_json_(nf) for nf in ensure_list(data.get('nameForms'))],
272
- date=Date._from_json_(data['date']) if data.get('date') else None
273
- ))
274
-
275
-
276
-
@@ -1,70 +0,0 @@
1
- from typing import List, Optional
2
-
3
- from .Attribution import Attribution
4
- from .Conclusion import ConfidenceLevel
5
- from .Date import Date
6
- from .EvidenceReference import EvidenceReference
7
- from .Identifier import IdentifierList
8
- from .Note import Note
9
- from .SourceReference import SourceReference
10
- from .TextValue import TextValue
11
- from .Resource import Resource
12
- from .Serialization import Serialization
13
- from .Subject import Subject
14
- from .URI import URI
15
-
16
- class PlaceDescription(Subject):
17
- identifier = "http://gedcomx.org/v1/PlaceDescription"
18
- version = 'http://gedcomx.org/conceptual-model/v1'
19
-
20
- def __init__(self, id: Optional[str] =None,
21
- lang: str = 'en',
22
- sources: Optional[List[SourceReference]] = None,
23
- analysis: Optional[Resource] = None,
24
- notes: Optional[List[Note]] =None,
25
- confidence: Optional[ConfidenceLevel] = None,
26
- attribution: Optional[Attribution] = None,
27
- extracted: Optional[bool] = None,
28
- evidence: Optional[List[EvidenceReference]] = None,
29
- media: Optional[List[SourceReference]] = None,
30
- identifiers: Optional[IdentifierList] = None,
31
- names: Optional[List[TextValue]] = None,
32
- type: Optional[str] = None,
33
- place: Optional[URI] = None,
34
- jurisdiction: Optional["Resource | PlaceDescription"] = None, # PlaceDescription
35
- latitude: Optional[float] = None,
36
- longitude: Optional[float] = None,
37
- temporalDescription: Optional[Date] = None,
38
- spatialDescription: Optional[Resource] = None,) -> None:
39
-
40
- super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
41
- self.names = names
42
- self.type = type
43
- self.place = place
44
- self.jurisdiction = jurisdiction
45
- self.latitide = latitude
46
- self.longitute = longitude
47
- self.temporalDescription = temporalDescription
48
- self.spacialDescription = spatialDescription
49
-
50
- @property
51
- def _as_dict_(self):
52
- 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
- })
63
- return Serialization.serialize_dict(type_as_dict)
64
-
65
- @classmethod
66
- def _from_json_(cls, data: dict):
67
- """
68
- Create a PlaceDescription instance from a JSON-dict (already parsed).
69
- """
70
- return Serialization.deserialize(data, PlaceDescription)
gedcomx/PlaceReference.py DELETED
@@ -1,30 +0,0 @@
1
- from typing import Optional
2
-
3
- from .Resource import Resource
4
- from .Serialization import Serialization
5
-
6
- class PlaceReference:
7
- identifier = 'http://gedcomx.org/v1/PlaceReference'
8
- version = 'http://gedcomx.org/conceptual-model/v1'
9
-
10
- def __init__(self,
11
- original: Optional[str] = None,
12
- description: Optional[Resource] = None) -> None:
13
- self.original = original
14
- self.description = description
15
-
16
- @property
17
- def _as_dict_(self):
18
- type_as_dict = {
19
- 'original': self.original,
20
- 'description': self.description._as_dict_ if self.description else None
21
- }
22
- return Serialization.serialize_dict(type_as_dict)
23
-
24
- @classmethod
25
- def _from_json_(cls, data):
26
-
27
- return Serialization.deserialize(data, PlaceReference)
28
-
29
-
30
-
gedcomx/Qualifier.py DELETED
@@ -1,27 +0,0 @@
1
- from typing import Optional
2
-
3
- class Qualifier:
4
- identifier = 'http://gedcomx.org/v1/Qualifier'
5
- version = 'http://gedcomx.org/conceptual-model/v1'
6
-
7
- def __init__(self, name: str, value: Optional[str]) -> None:
8
- self.name = name
9
- self.value = value
10
-
11
- @property
12
- 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
- }
19
-
20
- return serialize_to_dict(data,False)
21
-
22
- # Qualifier
23
- Qualifier._from_json_ = classmethod(lambda cls, data: Qualifier(
24
- name=data.get('name'),
25
- value=data.get('value')
26
- ))
27
-