gedcom-x 0.5.5__py3-none-any.whl → 0.5.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gedcomx/Coverage.py CHANGED
@@ -2,7 +2,7 @@ from typing import Optional
2
2
 
3
3
  from .Date import Date
4
4
  from .PlaceReference import PlaceReference
5
- from .Serialization import Serialization
5
+
6
6
 
7
7
  class Coverage:
8
8
  identifier = 'http://gedcomx.org/v1/Coverage'
@@ -16,11 +16,12 @@ class Coverage:
16
16
 
17
17
  @property
18
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
-
19
+ from .Serialization import Serialization
20
+ type_as_dict = {}
21
+ if self.spatial:
22
+ type_as_dict['spatial'] = getattr(self.spatial, '_as_dict_', self.spatial)
23
+ if self.temporal: # (fixed: no space after the dot)
24
+ type_as_dict['temporal'] = getattr(self.temporal, '_as_dict_', self.temporal)
24
25
  return Serialization.serialize_dict(type_as_dict)
25
26
 
26
27
  @classmethod
gedcomx/Date.py CHANGED
@@ -4,6 +4,8 @@ from dateutil import parser
4
4
  import time
5
5
 
6
6
 
7
+
8
+
7
9
  class DateFormat:
8
10
  def __init__(self) -> None:
9
11
  pass
@@ -16,18 +18,23 @@ class Date:
16
18
  version = 'http://gedcomx.org/conceptual-model/v1'
17
19
 
18
20
  def __init__(self, original: Optional[str],normalized: Optional[DateNormalization] = None ,formal: Optional[str | DateFormat] = None) -> None:
19
- self.orginal = original
21
+ self.original = original
20
22
  self.formal = formal
21
23
 
22
24
  self.normalized: DateNormalization | None = normalized if normalized else None
23
25
 
24
26
  @property
25
27
  def _as_dict_(self):
26
- return {'original': self.orginal,
27
- 'formal': self.formal}
28
+ from .Serialization import Serialization
29
+ type_as_dict = {}
30
+ if self.original:
31
+ type_as_dict['original'] = self.original
32
+ if self.formal:
33
+ type_as_dict['formal'] = self.formal
34
+ return Serialization.serialize_dict(type_as_dict)
28
35
 
29
36
  @classmethod
30
- def _from_json_(obj,data):
37
+ def _from_json_(cls,data):
31
38
  original = data.get('original',None)
32
39
  formal = data.get('formal',None)
33
40
 
gedcomx/Document.py CHANGED
@@ -7,7 +7,7 @@ from .SourceReference import SourceReference
7
7
  from .Resource import Resource
8
8
 
9
9
  from .Conclusion import Conclusion
10
- from .Serialization import Serialization
10
+
11
11
 
12
12
  class DocumentType(Enum):
13
13
  Abstract = "http://gedcomx.org/Abstract"
@@ -53,6 +53,7 @@ class Document(Conclusion):
53
53
 
54
54
  @property
55
55
  def _as_dict(self):
56
+ from .Serialization import Serialization
56
57
  type_as_dict = super()._as_dict_
57
58
  if self.type:
58
59
  type_as_dict['type'] = self.type.value
gedcomx/Event.py CHANGED
@@ -1,18 +1,37 @@
1
1
  from enum import Enum
2
2
  from typing import List, Optional
3
3
 
4
- from gedcomx.EvidenceReference import EvidenceReference
5
- from gedcomx.Identifier import Identifier
4
+ """
5
+ ======================================================================
6
+ Project: Gedcom-X
7
+ File: Event.py
8
+ Author: David J. Cartwright
9
+ Purpose: Python Object representation of GedcomX Event Type, EventType, EventRole, EventRoleType Types
6
10
 
11
+ Created: 2025-08-25
12
+ Updated:
13
+ - 2025-08-31: fixed mutible [] in init, replaced List[Identifer] with IdentifierList
14
+
15
+ ======================================================================
16
+ """
17
+
18
+ """
19
+ ======================================================================
20
+ GEDCOM Module Type Imports
21
+ ======================================================================
22
+ """
7
23
  from .Attribution import Attribution
8
- from .Conclusion import Conclusion, ConfidenceLevel
24
+ from .Conclusion import ConfidenceLevel, Conclusion
9
25
  from .Date import Date
26
+ from .EvidenceReference import EvidenceReference
27
+ from .Identifier import IdentifierList
10
28
  from .Note import Note
11
29
  from .PlaceReference import PlaceReference
12
- from .Serialization import Serialization
30
+ from .Resource import Resource
13
31
  from .SourceReference import SourceReference
14
32
  from .Subject import Subject
15
- from .Resource import Resource
33
+
34
+ #=====================================================================
16
35
 
17
36
  class EventRoleType(Enum):
18
37
  Principal = "http://gedcomx.org/Principal"
@@ -23,10 +42,10 @@ class EventRoleType(Enum):
23
42
  @property
24
43
  def description(self):
25
44
  descriptions = {
26
- EventRole.Principal: "The person is the principal person of the event. For example, the principal of a birth event is the person that was born.",
27
- EventRole.Participant: "A participant in the event.",
28
- EventRole.Official: "A person officiating the event.",
29
- EventRole.Witness: "A witness of the event."
45
+ EventRoleType.Principal: "The person is the principal person of the event. For example, the principal of a birth event is the person that was born.",
46
+ EventRoleType.Participant: "A participant in the event.",
47
+ EventRoleType.Official: "A person officiating the event.",
48
+ EventRoleType.Witness: "A witness of the event."
30
49
  }
31
50
  return descriptions.get(self, "No description available.")
32
51
 
@@ -36,19 +55,64 @@ class EventRole(Conclusion):
36
55
 
37
56
  def __init__(self,
38
57
  id: Optional[str] = None,
39
- lang: Optional[str] = 'en',
58
+ lang: Optional[str] = None,
40
59
  sources: Optional[List[SourceReference]] = [],
41
60
  analysis: Optional[Resource] = None,
42
61
  notes: Optional[List[Note]] = [],
43
62
  confidence: Optional[ConfidenceLevel] = None,
44
63
  attribution: Optional[Attribution] = None,
45
- person: Resource = None,
64
+ person: Optional[Resource] = None,
46
65
  type: Optional[EventRoleType] = None,
47
66
  details: Optional[str] = None) -> None:
48
67
  super().__init__(id, lang, sources, analysis, notes, confidence, attribution)
49
68
  self.person = person
50
69
  self.type = type
51
70
  self.details = details
71
+
72
+ def __str__(self) -> str:
73
+ parts = []
74
+ if self.type is not None:
75
+ # assume enums expose .name
76
+ parts.append(f"type={getattr(self.type, 'name', str(self.type))}")
77
+ if self.person is not None:
78
+ # assume classes have meaningful __str__
79
+ parts.append(f"person={self.person}")
80
+ if self.details:
81
+ parts.append(f"details={self.details!r}")
82
+ if getattr(self, "id", None):
83
+ parts.append(f"id={self.id!r}")
84
+ return f"EventRole({', '.join(parts)})" if parts else "EventRole()"
85
+
86
+ def __repr__(self) -> str:
87
+ # assume enums expose .name and .value
88
+ if self.type is not None:
89
+ tcls = self.type.__class__.__name__
90
+ tname = getattr(self.type, "name", str(self.type))
91
+ tval = getattr(self.type, "value", self.type)
92
+ type_repr = f"<{tcls}.{tname}: {tval!r}>"
93
+ else:
94
+ type_repr = "None"
95
+ return (
96
+ f"{self.__class__.__name__}("
97
+ f"id={getattr(self, 'id', None)!r}, "
98
+ f"lang={getattr(self, 'lang', None)!r}, "
99
+ f"type={type_repr}, "
100
+ f"person={self.person!r}, "
101
+ f"details={self.details!r})"
102
+ )
103
+
104
+ @property
105
+ def _as_dict_(self):
106
+ from .Serialization import Serialization
107
+ type_as_dict = super()._as_dict_
108
+ if self.person:
109
+ type_as_dict['person'] = Resource(target=self.person)._as_dict_
110
+ if self.type is not None:
111
+ type_as_dict['type'] = getattr(self.type, 'value', self.type)
112
+ if self.details:
113
+ type_as_dict['details'] = self.details
114
+
115
+ return Serialization.serialize_dict(type_as_dict)
52
116
 
53
117
  class EventType(Enum):
54
118
  Adoption = "http://gedcomx.org/Adoption"
@@ -180,16 +244,17 @@ class Event(Subject):
180
244
 
181
245
  def __init__(self,
182
246
  id: Optional[str] = None,
183
- lang: Optional[str] = 'en',
184
- sources: Optional[List[SourceReference]] = [],
247
+ lang: Optional[str] = None,
248
+ sources: Optional[List[SourceReference]] = None,
185
249
  analysis: Optional[Resource] = None,
186
- notes: Optional[List[Note]] = [],
250
+ notes: Optional[List[Note]] = None,
187
251
  confidence: Optional[ConfidenceLevel] = None,
188
252
  attribution: Optional[Attribution] = None,
189
253
  extracted: Optional[bool] = False,
190
- evidence: Optional[List[EvidenceReference]] = [],
191
- media: Optional[List[SourceReference]] = [],
192
- identifiers: Optional[List[Identifier]] = [],
254
+ evidence: Optional[List[EvidenceReference]] = None,
255
+ media: Optional[List[SourceReference]] = None,
256
+ #identifiers: Optional[List[Identifier]] = [],
257
+ identifiers: Optional[IdentifierList] = None,
193
258
  type: Optional[EventType] = None,
194
259
  date: Optional[Date] = None,
195
260
  place: Optional[PlaceReference] = None,
@@ -203,12 +268,22 @@ class Event(Subject):
203
268
 
204
269
  @property
205
270
  def _as_dict_(self):
206
- raise NotImplementedError("Not implemented yet")
271
+ from .Serialization import Serialization
272
+ type_as_dict = super()._as_dict_
273
+ type_as_dict.update({
274
+ 'type': self.type.value if self.type else None,
275
+ 'date': self.date,
276
+ 'place': self.place._as_dict_ if self.place else None,
277
+ 'roles': [role._as_dict_ for role in self.roles],
278
+ })
279
+
280
+ return Serialization.serialize_dict(type_as_dict)
207
281
 
208
282
  @classmethod
209
283
  def _from_json_(cls, data: dict):
210
284
  """
211
285
  Create a Person instance from a JSON-dict (already parsed).
212
286
  """
213
- type_as_dict = Serialization.get_class_fields('Event')
214
- return Serialization.deserialize(data, type_as_dict)
287
+ #type_as_dict = Serialization.get_class_fields('Event')
288
+ from .Serialization import Serialization
289
+ return Serialization.deserialize(data, Event)
@@ -0,0 +1,183 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, Iterator, Literal
4
+
5
+ """
6
+ ======================================================================
7
+ Project: Gedcom-X
8
+ File: ExtensibleEnum.py
9
+ Author: David J. Cartwright
10
+ Purpose: Create a class that can act like an enum but be extended by the user at runtime.
11
+
12
+ Created: 2025-08-25
13
+ Updated:
14
+ - YYYY-MM-DD: <change description>
15
+
16
+ ======================================================================
17
+ """
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class _EnumItem:
21
+ """
22
+ A single registered member of an :class:`ExtensibleEnum`.
23
+
24
+ Each `_EnumItem` represents one (name, value) pair that belongs
25
+ to a particular `ExtensibleEnum` subclass. Items are immutable
26
+ once created.
27
+
28
+ Attributes
29
+ ----------
30
+ owner : type
31
+ The subclass of :class:`ExtensibleEnum` that owns this member
32
+ (e.g., `Color`).
33
+ name : str
34
+ The symbolic name of the member (e.g., `"RED"`).
35
+ value : Any
36
+ The underlying value associated with the member (e.g., `"r"`).
37
+
38
+ Notes
39
+ -----
40
+ - Equality is determined by object identity (not overridden).
41
+ - Instances are hashable by default since the dataclass is frozen.
42
+ - The `__repr__` and `__str__` provide user-friendly string forms.
43
+
44
+ Examples
45
+ --------
46
+ >>> class Color(ExtensibleEnum): ...
47
+ >>> red = Color.register("RED", "r")
48
+ >>> repr(red)
49
+ 'Color.RED'
50
+ >>> str(red)
51
+ 'RED'
52
+ >>> red.value
53
+ 'r'
54
+ """
55
+ owner: type
56
+ name: str
57
+ value: Any
58
+ def __repr__(self) -> str: # print(...) shows "Color.RED"
59
+ return f"{self.owner.__name__}.{self.name}"
60
+ def __str__(self) -> str:
61
+ return self.name
62
+
63
+ class _ExtEnumMeta(type):
64
+ def __iter__(cls) -> Iterator[_EnumItem]:
65
+ return iter(cls._members.values())
66
+ def __contains__(cls, item: object) -> bool:
67
+ return item in cls._members.values()
68
+ # Support Color('RED') / Color(2)
69
+ def __call__(cls, arg: Any, /, *, by: Literal["auto","name","value"]="auto") -> _EnumItem:
70
+ if isinstance(arg, _EnumItem):
71
+ if arg.owner is cls:
72
+ return arg
73
+ raise TypeError(f"{arg!r} is not a member of {cls.__name__}")
74
+ if by == "name":
75
+ return cls.get(str(arg))
76
+ if by == "value":
77
+ return cls.from_value(arg)
78
+ if isinstance(arg, str) and arg in cls._members:
79
+ return cls.get(arg)
80
+ return cls.from_value(arg)
81
+
82
+ class ExtensibleEnum(metaclass=_ExtEnumMeta):
83
+ """
84
+ A lightweight, **runtime-extensible**, enum-like base class.
85
+
86
+ Subclass this to create an enum whose members can be registered at runtime.
87
+ Registered members are exposed as class attributes (e.g., `Color.RED`) and
88
+ can be retrieved by name (`Color.get("RED")`) or by value
89
+ (`Color.from_value("r")`). Square-bracket lookup (`Color["RED"]`) is also
90
+ supported via ``__class_getitem__``.
91
+
92
+ This is useful when:
93
+ - The full set of enum values is not known until runtime (plugins, config).
94
+ - You need attribute-style access (`Color.RED`) but want to add members
95
+ dynamically and/or validate uniqueness of names/values.
96
+
97
+ Notes
98
+ -----
99
+ - **Uniqueness:** Names and values are unique within a subclass.
100
+ - **Per-subclass registry:** Each subclass has its own member registry.
101
+ - **Thread safety:** Registration is **not** thread-safe. If multiple threads
102
+ may register members, wrap `register()` calls in your own lock.
103
+ - **Immutability:** Once registered, a member’s `name` and `value` are fixed.
104
+ Re-registering the same `name` with the *same* `value` returns the existing
105
+ item; a different value raises an error.
106
+
107
+ Examples
108
+ --------
109
+ Define an extensible enum and register members:
110
+
111
+ >>> class Color(ExtensibleEnum):
112
+ ... pass
113
+ ...
114
+ >>> Color.register("RED", "r")
115
+ _EnumItem(owner=Color, name='RED', value='r')
116
+ >>> Color.register("GREEN", "g")
117
+ _EnumItem(owner=Color, name='GREEN', value='g')
118
+
119
+ Access members:
120
+
121
+ >>> Color.RED is Color.get("RED")
122
+ True
123
+ >>> Color["GREEN"] is Color.get("GREEN")
124
+ True
125
+ >>> Color.from_value("g") is Color.GREEN
126
+ True
127
+ >>> Color.names()
128
+ ['RED', 'GREEN']
129
+
130
+ Error cases:
131
+
132
+ >>> Color.register("RED", "different") # doctest: +IGNORE_EXCEPTION_DETAIL
133
+ ValueError: name 'RED' already used with different value 'r'
134
+ >>> Color.get("BLUE") # doctest: +IGNORE_EXCEPTION_DETAIL
135
+ KeyError: Color has no member named 'BLUE'
136
+ >>> Color.from_value("b") # doctest: +IGNORE_EXCEPTION_DETAIL
137
+ KeyError: Color has no member with value 'b'
138
+ """
139
+ """Runtime-extensible enum-like base."""
140
+ _members: Dict[str, _EnumItem] = {}
141
+
142
+ def __init_subclass__(cls, **kw):
143
+ super().__init_subclass__(**kw)
144
+ cls._members = {} # fresh registry per subclass
145
+
146
+ @classmethod
147
+ def __class_getitem__(cls, key: str) -> _EnumItem: # Color['RED']
148
+ return cls.get(key)
149
+
150
+ @classmethod
151
+ def register(cls, name: str, value: Any) -> _EnumItem:
152
+ if not isinstance(name, str) or not name.isidentifier():
153
+ raise ValueError("name must be a valid identifier")
154
+ if name in cls._members:
155
+ item = cls._members[name]
156
+ if item.value != value:
157
+ raise ValueError(f"name {name!r} already used with different value {item.value!r}")
158
+ return item
159
+ if any(m.value == value for m in cls._members.values()):
160
+ raise ValueError(f"value {value!r} already used")
161
+ item = _EnumItem(owner=cls, name=name, value=value)
162
+ cls._members[name] = item
163
+ setattr(cls, name, item) # enables Color.RED attribute
164
+ return item
165
+
166
+ @classmethod
167
+ def names(cls) -> list[str]:
168
+ return list(cls._members.keys())
169
+
170
+ @classmethod
171
+ def get(cls, name: str) -> _EnumItem:
172
+ try:
173
+ return cls._members[name]
174
+ except KeyError as e:
175
+ raise KeyError(f"{cls.__name__} has no member named {name!r}") from e
176
+
177
+ @classmethod
178
+ def from_value(cls, value: Any) -> _EnumItem:
179
+ for m in cls._members.values():
180
+ if m.value == value:
181
+ return m
182
+ raise KeyError(f"{cls.__name__} has no member with value {value!r}")
183
+
@@ -0,0 +1 @@
1
+ from .rs10 import rsLink
@@ -0,0 +1 @@
1
+ from .rsLink import rsLink
@@ -0,0 +1,116 @@
1
+ '''
2
+ The "Link" Data Type
3
+ The Link data type defines a representation of an available transition from one application state to another.
4
+ The base definition of a link is provided by RFC 5988.
5
+
6
+ Instances of Link can be reasonably expected as extension elements to any GEDCOM X data type,
7
+ except data types that are defined by the GEDCOM X Conceptual Model to explicitly restrict extension properties.
8
+ '''
9
+
10
+ '''
11
+ 8/25/25, 0.5.5, built serialization, type checking in init for href
12
+ '''
13
+
14
+ from typing import List, Optional
15
+ from ...URI import URI
16
+ from ...Exceptions import GedcomClassAttributeError
17
+
18
+
19
+ class rsLink():
20
+ """A link description object. RS Extension to GedcomX by FamilySearch.
21
+
22
+ Args:
23
+ rel (str): Link relation identifier. Required.
24
+ href (str, optional): Link target URI. If omitted, provide `template`.
25
+ template (str, optional): URI Template (see RFC 6570). If omitted, provide `href`.
26
+ type (str, optional): Media type(s) of the linked resource (RFC 2616 §3.7).
27
+ accept (str, optional): Acceptable media type(s) for updating the linked resource (RFC 2616 §3.7).
28
+ allow (str, optional): Allowable HTTP methods to transition to the linked resource (RFC 2616 §14.7).
29
+ hreflang (str, optional): Language of the linked resource (e.g., BCP-47 tag).
30
+ title (str, optional): Human-readable label for the link.
31
+
32
+ Raises:
33
+ ValueError: If neither `href` nor `template` is provided.
34
+ """
35
+
36
+ """Attribution Information for a Genealogy, Conclusion, Subject and child classes
37
+
38
+ Args:
39
+ contributor (Agent, optional): Contributor to object being attributed.
40
+ modified (timestamp, optional): timestamp for when this record was modified.
41
+ changeMessage (str, optional): Birth date (YYYY-MM-DD).
42
+ creator (Agent, optional): Creator of object being attributed.
43
+ created (timestamp, optional): timestamp for when this record was created
44
+
45
+ Raises:
46
+
47
+ """
48
+ identifier = "http://gedcomx.org/v1/Link"
49
+
50
+ def __init__(self,rel: Optional[URI] = None,
51
+ href: Optional[URI] = None,
52
+ template: Optional[str] = None,
53
+ type: Optional[str] = None,
54
+ accept: Optional[str] = None,
55
+ allow: Optional[str] = None,
56
+ hreflang: Optional[str] = None,
57
+ title: Optional[str] = None) -> None:
58
+
59
+ self.rel = rel
60
+ self.href = href if isinstance(href,URI) else URI.from_url(href) if isinstance(href,str) else None
61
+ self.template = template
62
+ self.type = type
63
+ self.accept = accept
64
+ self.allow = allow
65
+ self.hreflang = hreflang
66
+ self.title = title
67
+
68
+ if self.href is None and self.template is None:
69
+ raise GedcomClassAttributeError("href or template are required")
70
+
71
+ @property
72
+ def _as_dict_(self):
73
+ from ...Serialization import Serialization
74
+ type_as_dict = {
75
+ "rel": self.rel._as_dict_ if self.rel else None,
76
+ "href": self.href._as_dict_ if self.href else None,
77
+ "template": self.template, # RFC 6570 template if used
78
+ "type": self.type, # media type (note: shadows built-in 'type')
79
+ "accept": self.accept,
80
+ "allow": self.allow,
81
+ "hreflang": self.hreflang,
82
+ "title": self.title,
83
+ }
84
+ return Serialization.serialize_dict(type_as_dict)
85
+
86
+ @property
87
+ def json(self):
88
+ import json
89
+ return json.dumps(self._as_dict_)
90
+
91
+ class _rsLinkList():
92
+ def __init__(self) -> None:
93
+ self.links = {}
94
+
95
+ def add(self,link: rsLink):
96
+ if link and isinstance(link,rsLink):
97
+ if link.rel in self.links.keys():
98
+ self.links[link.rel].append(link.href)
99
+ else:
100
+ self.links[link.rel] = [link.href]
101
+
102
+
103
+ @classmethod
104
+ def _from_json_(cls,data: dict):
105
+
106
+ link_list = _rsLinkList()
107
+ for rel in data.keys():
108
+ link_list.add(rsLink(rel,data[rel]))
109
+ return link_list
110
+
111
+ @property
112
+ def _as_dict_(self) -> dict:
113
+ return self.links
114
+
115
+
116
+
gedcomx/Fact.py CHANGED
@@ -12,7 +12,7 @@ from .Date import Date
12
12
  from .Note import Note
13
13
  from .PlaceReference import PlaceReference
14
14
  from .SourceReference import SourceReference
15
- from .Serialization import Serialization
15
+
16
16
  from .Resource import Resource
17
17
 
18
18
  from .Conclusion import Conclusion
@@ -367,7 +367,6 @@ class FactType(Enum):
367
367
  for word in words:
368
368
  matches = difflib.get_close_matches(word, keywords_to_fact_type.keys(), n=1, cutoff=0.8)
369
369
  if matches:
370
- print(f"Guessed '{description} to be of fact type: {keywords_to_fact_type[matches[0]]}")
371
370
  return keywords_to_fact_type[matches[0]]
372
371
  return None
373
372
 
@@ -397,7 +396,7 @@ class Fact(Conclusion):
397
396
 
398
397
  def __init__(self,
399
398
  id: Optional[str] = None,
400
- lang: str = 'en',
399
+ lang: Optional[str] = None,
401
400
  sources: Optional[List[SourceReference]] = [],
402
401
  analysis: Optional[Resource | Document] = None,
403
402
  notes: Optional[List[Note]] = [],
@@ -429,24 +428,28 @@ class Fact(Conclusion):
429
428
 
430
429
  @property
431
430
  def _as_dict_(self):
432
- fact_dict = super()._as_dict_
431
+ from .Serialization import Serialization
432
+ type_as_dcit = super()._as_dict_
433
433
  # Only add Relationship-specific fields
434
- fact_dict.update( {
435
- 'type': self.type.value if self.type else None,
436
- 'date': self.date._as_dict_ if self.date else None,
437
- 'place': self.place._as_dict_ if self.place else None,
438
- 'value': self.value,
439
- 'qualifiers': [q.value for q in self.qualifiers] if self.qualifiers else []
440
- })
434
+ if self.type:
435
+ type_as_dcit['type'] = getattr(self.type, 'value', self.type)
436
+ if self.date:
437
+ type_as_dcit['date'] = self.date._as_dict_
438
+ if self.place:
439
+ type_as_dcit['place'] = self.place._as_dict_
440
+ if self.value:
441
+ type_as_dcit['value'] = self.value
442
+ if self.qualifiers:
443
+ type_as_dcit['qualifiers'] = [getattr(q, 'value', q) for q in self.qualifiers]
441
444
 
442
- return Serialization.serialize_dict(fact_dict)
445
+ return Serialization.serialize_dict(type_as_dcit)
443
446
 
444
447
  @classmethod
445
448
  def _from_json_(cls, data: Dict[str, Any]) -> 'Fact':
446
449
 
447
450
  # Extract fields, no trailing commas!
448
451
  id_ = data.get('id')
449
- lang = data.get('lang', 'en')
452
+ lang = data.get('lang', None)
450
453
  sources = [SourceReference._from_json_(s) for s in data.get('sources',[])]
451
454
  analysis = (Resource._from_json_(data['analysis'])
452
455
  if data.get('analysis') else None)