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,22 +1,38 @@
1
- from enum import Enum
2
1
  from typing import List, Optional
3
2
  from urllib.parse import urljoin
4
3
 
5
- from .Attribution import Attribution
6
- from .Conclusion import ConfidenceLevel
7
- from .Date import Date
8
- from .EvidenceReference import EvidenceReference
9
- from .Fact import Fact, FactType
10
- from .Gender import Gender, GenderType
11
- from .Identifier import IdentifierList
12
- from .Name import Name, QuickName
13
- from .Note import Note
14
- from .SourceReference import SourceReference
15
- from .Serialization import Serialization
16
- from .Subject import Subject
17
- from .Resource import Resource
18
- from collections.abc import Sized
4
+ """
5
+ ======================================================================
6
+ Project: Gedcom-X
7
+ File: Person.py
8
+ Author: David J. Cartwright
9
+ Purpose: Python Object representation of GedcomX Person Type
10
+
11
+ Created: 2025-08-25
12
+ Updated:
13
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
14
+
15
+ ======================================================================
16
+ """
17
+
18
+ """
19
+ ======================================================================
20
+ GEDCOM Module Types
21
+ ======================================================================
22
+ """
23
+ from .attribution import Attribution
24
+ from .conclusion import ConfidenceLevel
25
+ from .date import Date
26
+ from .evidence_reference import EvidenceReference
19
27
  from .Extensions.rs10.rsLink import _rsLinkList
28
+ from .fact import Fact, FactType
29
+ from .gender import Gender, GenderType
30
+ from .identifier import IdentifierList
31
+ from .name import Name, QuickName
32
+ from .note import Note
33
+ from .resource import Resource
34
+ from .source_reference import SourceReference
35
+ from .subject import Subject
20
36
 
21
37
  class Person(Subject):
22
38
  """A person in the system.
@@ -40,7 +56,7 @@ class Person(Subject):
40
56
  notes: Optional[List[Note]] = None,
41
57
  confidence: Optional[ConfidenceLevel] = None,
42
58
  attribution: Optional[Attribution] = None,
43
- extracted: bool = None,
59
+ extracted: Optional[bool] = None,
44
60
  evidence: Optional[List[EvidenceReference]] = None,
45
61
  media: Optional[List[SourceReference]] = None,
46
62
  identifiers: Optional[IdentifierList] = None,
@@ -77,6 +93,7 @@ class Person(Subject):
77
93
  return False
78
94
  self.facts.append(fact_to_add)
79
95
  return True
96
+ return False
80
97
 
81
98
  def add_name(self, name_to_add: Name) -> bool:
82
99
  if len(self.names) > 5:
@@ -89,9 +106,10 @@ class Person(Subject):
89
106
  return False
90
107
  self.names.append(name_to_add)
91
108
  return True
109
+ return False
92
110
 
93
111
  def _add_relationship(self, relationship_to_add: object):
94
- from .Relationship import Relationship
112
+ from .relationship import Relationship
95
113
  if isinstance(relationship_to_add,Relationship):
96
114
  self._relationships.append(relationship_to_add)
97
115
  else:
@@ -102,7 +120,7 @@ class Person(Subject):
102
120
  "ascendancyNumber": "1",
103
121
  "deathDate": "from 2001 to 2005",
104
122
  "descendancyNumber": "1",
105
- "gender": self.gender.type,
123
+ "gender": self.gender.type if self.gender else 'Unknown',
106
124
  "lifespan": "-2005",
107
125
  "name": self.names[0].nameForms[0].fullText
108
126
  }
@@ -111,17 +129,20 @@ class Person(Subject):
111
129
 
112
130
  @property
113
131
  def _as_dict_(self):
114
- type_as_dict = super()._as_dict_ # Start with base class fields
115
- # Only add Relationship-specific fields
116
- type_as_dict.update({
117
- 'private': self.private,
118
- 'living': self.living,
119
- 'gender': self.gender._as_dict_ if self.gender else None,
120
- 'names': [name._as_dict_ for name in self.names],
121
- 'facts': [fact._as_dict_ for fact in self.facts],
122
- 'uri': self.uri._as_dict_ if self.uri else None
123
-
124
- })
132
+ from .serialization import Serialization
133
+ type_as_dict = super()._as_dict_
134
+ if self.private is not None:
135
+ type_as_dict['private'] = self.private
136
+ if self.living is not None:
137
+ type_as_dict['living'] = self.living
138
+ if self.gender:
139
+ type_as_dict['gender'] = self.gender._as_dict_
140
+ if self.names:
141
+ type_as_dict['names'] = [n._as_dict_ for n in self.names if n]
142
+ if self.facts:
143
+ type_as_dict['facts'] = [f._as_dict_ for f in self.facts if f]
144
+ if self.uri:
145
+ type_as_dict['uri'] = self.uri._as_dict_
125
146
 
126
147
  return Serialization.serialize_dict(type_as_dict)
127
148
 
@@ -130,10 +151,12 @@ class Person(Subject):
130
151
  """
131
152
  Create a Person instance from a JSON-dict (already parsed).
132
153
  """
154
+ from .serialization import Serialization
133
155
  return Serialization.deserialize(data, Person)
134
156
 
135
157
  @classmethod
136
158
  def from_familysearch(cls, pid: str, token: str, *, base_url: Optional[str] = None):
159
+ from .serialization import Serialization
137
160
  """
138
161
  Fetch a single person by PID from FamilySearch and return a Person.
139
162
  - pid: e.g. "KPHP-4B4"
@@ -0,0 +1,123 @@
1
+ from typing import List, Optional
2
+
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: place_description.py
7
+ Author: David J. Cartwright
8
+ Purpose:
9
+
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-09-01: filename PEP8 standard
13
+
14
+ ======================================================================
15
+ """
16
+
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
22
+ from .attribution import Attribution
23
+ from .conclusion import ConfidenceLevel
24
+ from .date import Date
25
+ from .evidence_reference import EvidenceReference
26
+ from .identifier import IdentifierList
27
+ from .note import Note
28
+ from .resource import Resource
29
+ from .source_reference import SourceReference
30
+ from .subject import Subject
31
+ from .textvalue import TextValue
32
+ from .uri import URI
33
+ #=====================================================================
34
+
35
+
36
+ class PlaceDescription(Subject):
37
+ """PlaceDescription describes the details of a place in terms of
38
+ its name and possibly its type, time period, and/or a geospatial description
39
+ functioning as a description of a place as a snapshot in time.
40
+
41
+ Encapsulates textual names, geospatial coordinates, jurisdictional context,
42
+ temporal coverage, and related resources (media, sources, evidence, etc.).
43
+
44
+
45
+ Attributes:
46
+ names (Optional[List[TextValue]]): Human-readable names or labels for
47
+ the place (e.g., “Boston, Suffolk, Massachusetts, United States”).
48
+ type (Optional[str]): A place type identifier (e.g., a URI). **TODO:**
49
+ replace with an enumeration when finalized.
50
+ place (Optional[URI]): Canonical identifier (URI) for the place.
51
+ jurisdiction (Optional[Resource|PlaceDescription]): The governing or
52
+ containing jurisdiction of this place (e.g., county for a town).
53
+ latitude (Optional[float]): Latitude in decimal degrees (WGS84).
54
+ longitude (Optional[float]): Longitude in decimal degrees (WGS84).
55
+ temporalDescription (Optional[Date]): Temporal coverage/validity window
56
+ for this description (e.g., when a jurisdictional boundary applied).
57
+ spatialDescription (Optional[Resource]): A resource describing spatial
58
+ geometry or a link to an external gazetteer/shape definition.
59
+ """
60
+ identifier = "http://gedcomx.org/v1/PlaceDescription"
61
+ version = 'http://gedcomx.org/conceptual-model/v1'
62
+
63
+ def __init__(self, id: Optional[str] =None,
64
+ lang: Optional[str] = None,
65
+ sources: Optional[List[SourceReference]] = None,
66
+ analysis: Optional[Resource] = None,
67
+ notes: Optional[List[Note]] =None,
68
+ confidence: Optional[ConfidenceLevel] = None,
69
+ attribution: Optional[Attribution] = None,
70
+ extracted: Optional[bool] = None,
71
+ evidence: Optional[List[EvidenceReference]] = None,
72
+ media: Optional[List[SourceReference]] = None,
73
+ identifiers: Optional[IdentifierList] = None,
74
+ names: Optional[List[TextValue]] = None,
75
+ type: Optional[str] = None, #TODO This needs to be an enumerated value, work out details
76
+ place: Optional[URI] = None,
77
+ jurisdiction: Optional["Resource | PlaceDescription"] = None,
78
+ latitude: Optional[float] = None,
79
+ longitude: Optional[float] = None,
80
+ temporalDescription: Optional[Date] = None,
81
+ spatialDescription: Optional[Resource] = None,) -> None:
82
+
83
+ super().__init__(id, lang, sources, analysis, notes, confidence, attribution, extracted, evidence, media, identifiers)
84
+ self.names = names
85
+ self.type = type
86
+ self.place = place
87
+ self.jurisdiction = jurisdiction
88
+ self.latitude = latitude
89
+ self.longitude = longitude
90
+ self.temporalDescription = temporalDescription
91
+ self.spatialDescription = spatialDescription
92
+
93
+ @property
94
+ def _as_dict_(self):
95
+ from .serialization import Serialization
96
+ type_as_dict = super()._as_dict_
97
+
98
+ if self.names:
99
+ type_as_dict["names"] = [n._as_dict_ for n in self.names if n]
100
+ if self.type:
101
+ type_as_dict["type"] = self.type #TODO
102
+ if self.place:
103
+ type_as_dict["place"] = self.place._as_dict_
104
+ if self.jurisdiction:
105
+ type_as_dict["jurisdiction"] = self.jurisdiction._as_dict_
106
+ if self.latitude is not None: # include 0.0; exclude only None
107
+ type_as_dict["latitude"] = float(self.latitude)
108
+ if self.longitude is not None: # include 0.0; exclude only None
109
+ type_as_dict["longitude"] = float(self.longitude)
110
+ if self.temporalDescription:
111
+ type_as_dict["temporalDescription"] = self.temporalDescription._as_dict_
112
+ if self.spatialDescription:
113
+ type_as_dict["spatialDescription"] = self.spatialDescription._as_dict_
114
+
115
+ return Serialization.serialize_dict(type_as_dict)
116
+
117
+ @classmethod
118
+ def _from_json_(cls, data: dict):
119
+ """
120
+ Create a PlaceDescription instance from a JSON-dict (already parsed).
121
+ """
122
+ from .serialization import Serialization
123
+ return Serialization.deserialize(data, PlaceDescription)
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, TYPE_CHECKING
3
+ if TYPE_CHECKING:
4
+ from .place_description import PlaceDescription
5
+
6
+ """
7
+ ======================================================================
8
+ Project: Gedcom-X
9
+ File: PlaceReference.py
10
+ Author: David J. Cartwright
11
+ Purpose: Python Object representation of GedcomX PlaceReference Type
12
+
13
+ Created: 2025-08-25
14
+ Updated:
15
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that hold data
16
+
17
+ ======================================================================
18
+ """
19
+
20
+ """
21
+ ======================================================================
22
+ GEDCOM Module Types
23
+ ======================================================================
24
+ """
25
+ from .resource import Resource
26
+
27
+ class PlaceReference:
28
+ """defines a reference to a PlaceDescription.
29
+
30
+
31
+ Attributes:
32
+ original (Optional[str]): The unnormalized, user- or source-provided place text.
33
+ Keep punctuation and ordering exactly as recorded in the source.
34
+ description (Optional[Resource|PlaceDescription]): A :class:`gedcomx.PlaceDescription` Object or pointer to it. (URI/:class:`~Resource`)
35
+
36
+ """
37
+ identifier = 'http://gedcomx.org/v1/PlaceReference'
38
+ version = 'http://gedcomx.org/conceptual-model/v1'
39
+
40
+ def __init__(self,
41
+ original: Optional[str] = None,
42
+ description: Optional["Resource | PlaceDescription"] = None) -> None:
43
+ self.original = original
44
+ self.description = description
45
+
46
+ @property
47
+ def _as_dict_(self):
48
+ from .serialization import Serialization
49
+ type_as_dict = {}
50
+ if self.original:
51
+ type_as_dict['original'] = self.original
52
+ if self.description:
53
+ type_as_dict['description'] = self.description._as_dict_
54
+ return Serialization.serialize_dict(type_as_dict)
55
+
56
+ @classmethod
57
+ def _from_json_(cls, data):
58
+ from .serialization import Serialization
59
+ return Serialization.deserialize(data, PlaceReference)
60
+
61
+
62
+
gedcomx/qualifier.py ADDED
@@ -0,0 +1,54 @@
1
+ from typing import Optional
2
+
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: Qualifier.py
7
+ Author: David J. Cartwright
8
+ Purpose: Python Object representation of GedcomX Qualifier Type
9
+
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-08-31: _as_dict_ to only create entries in dict for fields that
13
+ hold data, updated _from_json
14
+
15
+ ======================================================================
16
+ """
17
+
18
+ class Qualifier:
19
+ """defines the data structure used to supply additional details, annotations,
20
+ tags, or other qualifying data to a specific data element.
21
+
22
+
23
+ Attributes:
24
+ name str: The name of the Qualifier. *It is RECOMMENDED that the qualifier
25
+ name resolve to an element of a constrained vocabulary.*
26
+
27
+ value (Optional[str]): The value of the Qualifier. *If provided, the name
28
+ MAY give the semantic meaning of the value.*
29
+
30
+ """
31
+ identifier = 'http://gedcomx.org/v1/Qualifier'
32
+ version = 'http://gedcomx.org/conceptual-model/v1'
33
+
34
+ def __init__(self, name: str, value: Optional[str]) -> None:
35
+ self.name = name
36
+ self.value = value
37
+
38
+ @property
39
+ def __as_dict__(self):
40
+ from .serialization import Serialization
41
+
42
+ type_as_dict = {}
43
+ if self.name:
44
+ type_as_dict["name"] = self.name
45
+ if self.value:
46
+ type_as_dict["value"] = self.value
47
+
48
+ return Serialization.serialize_dict(type_as_dict)
49
+
50
+ @classmethod
51
+ def _from_json(cls,data):
52
+ qualifier = Qualifier(name=data.get('name',"ERROR: This Qualifier require a 'name' but has none."),value=data.get('value'))
53
+ return qualifier
54
+
@@ -1,18 +1,36 @@
1
1
  from enum import Enum
2
2
  from typing import List, Optional
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: relationship.py
7
+ Author: David J. Cartwright
8
+ Purpose:
3
9
 
4
- from .Attribution import Attribution
5
- from .Conclusion import ConfidenceLevel
6
- from .EvidenceReference import EvidenceReference
7
- from .Fact import Fact
8
- from .Identifier import Identifier
9
- from .Note import Note
10
- from .Person import Person
11
- from .Serialization import Serialization
12
- from .SourceReference import SourceReference
13
- from .Resource import Resource
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-09-31: filename PEP8 standard
13
+
14
+ ======================================================================
15
+ """
16
+
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
22
+ from .attribution import Attribution
23
+ from .conclusion import ConfidenceLevel
24
+ from .evidence_reference import EvidenceReference
25
+ from .fact import Fact
26
+ from .identifier import Identifier
27
+ from .note import Note
28
+ from .person import Person
29
+ from .resource import Resource
30
+ from .source_reference import SourceReference
31
+ from .subject import Subject
32
+ #=====================================================================
14
33
 
15
- from .Subject import Subject
16
34
 
17
35
  class RelationshipType(Enum):
18
36
  Couple = "http://gedcomx.org/Couple"
@@ -77,11 +95,12 @@ class Relationship(Subject):
77
95
 
78
96
  @property
79
97
  def _as_dict_(self):
98
+ from .serialization import Serialization
80
99
  type_as_dict = super()._as_dict_
81
100
  type_as_dict.update({
82
101
  "type": self.type.value if isinstance(self.type, RelationshipType) else self.type,
83
- "person1": self.person1._as_dict_ if self.person1 else None,
84
- "person2": self.person2._as_dict_ if self.person2 else None,
102
+ "person1": Resource(target=self.person1)._as_dict_ if self.person1 else None,
103
+ "person2": Resource(target=self.person1)._as_dict_ if self.person2 else None,
85
104
  "facts": [fact for fact in self.facts] if self.facts else None
86
105
  })
87
106
  return Serialization.serialize_dict(type_as_dict)
@@ -91,5 +110,6 @@ class Relationship(Subject):
91
110
  """
92
111
  Create a Person instance from a JSON-dict (already parsed).
93
112
  """
113
+ from .serialization import Serialization
94
114
  return Serialization.deserialize(data, Relationship)
95
115
 
gedcomx/resource.py ADDED
@@ -0,0 +1,85 @@
1
+ from typing import Optional
2
+
3
+ """
4
+ ======================================================================
5
+ Project: Gedcom-X
6
+ File: Resource.py
7
+ Author: David J. Cartwright
8
+ Purpose: References TopLevel Types for Serialization
9
+
10
+ Created: 2025-08-25
11
+ Updated:
12
+ - 2025-08-31: working on target=Resource and deserialization issues
13
+
14
+ ======================================================================
15
+ """
16
+
17
+ """
18
+ ======================================================================
19
+ GEDCOM Module Types
20
+ ======================================================================
21
+ """
22
+
23
+ from .uri import URI
24
+
25
+ class Resource:
26
+ """
27
+ Class used to track and resolve URIs and references between datastores.
28
+
29
+ Parameters
30
+ ----------
31
+
32
+ Raises
33
+ ------
34
+ ValueError
35
+ If `id` is not a valid UUID.
36
+ """
37
+ # TODO, Deal with a resouce being passed, as it may be unresolved.
38
+ def __init__(self,uri: Optional[URI|str] = None, id:Optional[str] = None,top_lvl_object: Optional[object] = None,target= None) -> None:
39
+
40
+ self.resource = URI.from_url(uri.value) if isinstance(uri,URI) else URI.from_url(uri) if isinstance(uri,str) else None
41
+ self.Id = id
42
+
43
+ self.type = None
44
+ self.resolved = False
45
+ self.target: object = target
46
+ self.remote: bool | None = None # is the resource pointed to persitent on a remote datastore?
47
+
48
+ if target:
49
+ if isinstance(target,Resource):
50
+ self.resource = target.resource
51
+ self.Id = target.Id
52
+ self.target = target.target
53
+ else:
54
+ self.resource = target.uri
55
+ self.Id = target.id
56
+ self.type = type(target)
57
+
58
+ @property
59
+ def uri(self):
60
+ return self.resource
61
+
62
+ @property
63
+ def _as_dict_(self):
64
+ from .serialization import Serialization
65
+ typ_as_dict = {}
66
+ if self.resource:
67
+ typ_as_dict['resource'] = self.resource.value if self.resource else None
68
+ if self.Id:
69
+ typ_as_dict['resourceId'] = self.Id
70
+ return Serialization.serialize_dict(typ_as_dict)
71
+
72
+ @classmethod
73
+ def _from_json_(cls,data):
74
+ # TODO This is not used but taken care of in Serialization
75
+ r = Resource(uri=data.get('resource'),id=data.get('resourceId',None))
76
+ #return r
77
+
78
+ def __repr__(self) -> str:
79
+ return f"Resource(uri={self.resource}, id={self.Id}, target={self.target})"
80
+
81
+ def __str__(self) -> str:
82
+ return f"{self.resource}{f', id={self.Id}' if self.Id else ''}"
83
+
84
+
85
+