gedcom-x 0.5.7__py3-none-any.whl → 0.5.9__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 (69) hide show
  1. {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/METADATA +1 -1
  2. gedcom_x-0.5.9.dist-info/RECORD +56 -0
  3. gedcomx/Extensions/rs10/rsLink.py +110 -60
  4. gedcomx/TopLevelTypeCollection.py +1 -1
  5. gedcomx/__init__.py +43 -42
  6. gedcomx/address.py +217 -0
  7. gedcomx/{Agent.py → agent.py} +107 -34
  8. gedcomx/attribution.py +115 -0
  9. gedcomx/{Conclusion.py → conclusion.py} +120 -51
  10. gedcomx/{Converter.py → converter.py} +261 -116
  11. gedcomx/coverage.py +64 -0
  12. gedcomx/{Date.py → date.py} +43 -9
  13. gedcomx/{Document.py → document.py} +60 -12
  14. gedcomx/{Event.py → event.py} +88 -31
  15. gedcomx/evidence_reference.py +20 -0
  16. gedcomx/{Fact.py → fact.py} +81 -74
  17. gedcomx/{Gedcom.py → gedcom.py} +10 -0
  18. gedcomx/{Gedcom5x.py → gedcom5x.py} +31 -21
  19. gedcomx/gedcom7/Exceptions.py +9 -0
  20. gedcomx/gedcom7/GedcomStructure.py +94 -0
  21. gedcomx/gedcom7/Specification.py +347 -0
  22. gedcomx/gedcom7/__init__.py +26 -0
  23. gedcomx/gedcom7/g7interop.py +205 -0
  24. gedcomx/gedcom7/gedcom7.py +160 -0
  25. gedcomx/gedcom7/logger.py +19 -0
  26. gedcomx/{GedcomX.py → gedcomx.py} +109 -106
  27. gedcomx/gender.py +91 -0
  28. gedcomx/group.py +72 -0
  29. gedcomx/{Identifier.py → identifier.py} +48 -21
  30. gedcomx/{LoggingHub.py → logging_hub.py} +19 -0
  31. gedcomx/{Mutations.py → mutations.py} +59 -30
  32. gedcomx/{Name.py → name.py} +88 -47
  33. gedcomx/note.py +105 -0
  34. gedcomx/online_account.py +19 -0
  35. gedcomx/{Person.py → person.py} +61 -41
  36. gedcomx/{PlaceDescription.py → place_description.py} +71 -23
  37. gedcomx/{PlaceReference.py → place_reference.py} +32 -10
  38. gedcomx/{Qualifier.py → qualifier.py} +20 -4
  39. gedcomx/relationship.py +156 -0
  40. gedcomx/resource.py +112 -0
  41. gedcomx/serialization.py +794 -0
  42. gedcomx/source_citation.py +37 -0
  43. gedcomx/source_description.py +401 -0
  44. gedcomx/{SourceReference.py → source_reference.py} +56 -21
  45. gedcomx/subject.py +122 -0
  46. gedcomx/textvalue.py +89 -0
  47. gedcomx/{Translation.py → translation.py} +4 -4
  48. gedcomx/uri.py +273 -0
  49. gedcom_x-0.5.7.dist-info/RECORD +0 -49
  50. gedcomx/Address.py +0 -131
  51. gedcomx/Attribution.py +0 -91
  52. gedcomx/Coverage.py +0 -37
  53. gedcomx/EvidenceReference.py +0 -11
  54. gedcomx/Gender.py +0 -65
  55. gedcomx/Group.py +0 -37
  56. gedcomx/Note.py +0 -73
  57. gedcomx/OnlineAccount.py +0 -10
  58. gedcomx/Relationship.py +0 -97
  59. gedcomx/Resource.py +0 -85
  60. gedcomx/Serialization.py +0 -816
  61. gedcomx/SourceCitation.py +0 -25
  62. gedcomx/SourceDescription.py +0 -314
  63. gedcomx/Subject.py +0 -59
  64. gedcomx/TextValue.py +0 -35
  65. gedcomx/URI.py +0 -105
  66. {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/WHEEL +0 -0
  67. {gedcom_x-0.5.7.dist-info → gedcom_x-0.5.9.dist-info}/top_level.txt +0 -0
  68. /gedcomx/{Exceptions.py → exceptions.py} +0 -0
  69. /gedcomx/{ExtensibleEnum.py → extensible_enum.py} +0 -0
@@ -0,0 +1,205 @@
1
+ from .. import *
2
+
3
+ g7toX = {
4
+ "ABBR": "https://gedcom.io/terms/v7/ABBR",
5
+ "ADDR": "https://gedcom.io/terms/v7/ADDR",
6
+ "ADOP": "https://gedcom.io/terms/v7/ADOP",
7
+ "ADOP-FAMC": "https://gedcom.io/terms/v7/ADOP-FAMC",
8
+ "ADR1": "https://gedcom.io/terms/v7/ADR1",
9
+ "ADR2": "https://gedcom.io/terms/v7/ADR2",
10
+ "ADR3": "https://gedcom.io/terms/v7/ADR3",
11
+ "AGE": "https://gedcom.io/terms/v7/AGE",
12
+ "AGNC": "https://gedcom.io/terms/v7/AGNC",
13
+ "ALIA": "https://gedcom.io/terms/v7/ALIA",
14
+ "ANCI": "https://gedcom.io/terms/v7/ANCI",
15
+ "ANUL": "https://gedcom.io/terms/v7/ANUL",
16
+ "ASSO": "https://gedcom.io/terms/v7/ASSO",
17
+ "AUTH": "https://gedcom.io/terms/v7/AUTH",
18
+ "BAPL": "https://gedcom.io/terms/v7/BAPL",
19
+ "BAPM": "https://gedcom.io/terms/v7/BAPM",
20
+ "BARM": "https://gedcom.io/terms/v7/BARM",
21
+ "BASM": "https://gedcom.io/terms/v7/BASM",
22
+ "BIRT": "https://gedcom.io/terms/v7/BIRT",
23
+ "BLES": "https://gedcom.io/terms/v7/BLES",
24
+ "BURI": "https://gedcom.io/terms/v7/BURI",
25
+ "CALN": "https://gedcom.io/terms/v7/CALN",
26
+ "CAST": "https://gedcom.io/terms/v7/CAST",
27
+ "CAUS": "https://gedcom.io/terms/v7/CAUS",
28
+ "CENS": "https://gedcom.io/terms/v7/CENS",
29
+ "CHAN": "https://gedcom.io/terms/v7/CHAN",
30
+ "CHIL": "https://gedcom.io/terms/v7/CHIL",
31
+ "CHR": "https://gedcom.io/terms/v7/CHR",
32
+ "CHRA": "https://gedcom.io/terms/v7/CHRA",
33
+ "CITY": "https://gedcom.io/terms/v7/CITY",
34
+ "CONF": "https://gedcom.io/terms/v7/CONF",
35
+ "CONL": "https://gedcom.io/terms/v7/CONL",
36
+ "CONT": "https://gedcom.io/terms/v7/CONT",
37
+ "COPR": "https://gedcom.io/terms/v7/COPR",
38
+ "CORP": "https://gedcom.io/terms/v7/CORP",
39
+ "CREA": "https://gedcom.io/terms/v7/CREA",
40
+ "CREM": "https://gedcom.io/terms/v7/CREM",
41
+ "CROP": "https://gedcom.io/terms/v7/CROP",
42
+ "CTRY": "https://gedcom.io/terms/v7/CTRY",
43
+ "DATA": "https://gedcom.io/terms/v7/DATA",
44
+ "DATA-EVEN": "https://gedcom.io/terms/v7/DATA-EVEN",
45
+ "DATA-EVEN-DATE": "https://gedcom.io/terms/v7/DATA-EVEN-DATE",
46
+ "DATE": "https://gedcom.io/terms/v7/DATE",
47
+ "DATE-exact": "https://gedcom.io/terms/v7/DATE-exact",
48
+ "DEAT": "https://gedcom.io/terms/v7/DEAT",
49
+ "DESI": "https://gedcom.io/terms/v7/DESI",
50
+ "DEST": "https://gedcom.io/terms/v7/DEST",
51
+ "DIV": "https://gedcom.io/terms/v7/DIV",
52
+ "DIVF": "https://gedcom.io/terms/v7/DIVF",
53
+ "DSCR": "https://gedcom.io/terms/v7/DSCR",
54
+ "EDUC": "https://gedcom.io/terms/v7/EDUC",
55
+ "EMAIL": "https://gedcom.io/terms/v7/EMAIL",
56
+ "EMIG": "https://gedcom.io/terms/v7/EMIG",
57
+ "ENDL": "https://gedcom.io/terms/v7/ENDL",
58
+ "ENGA": "https://gedcom.io/terms/v7/ENGA",
59
+ "EVEN": "https://gedcom.io/terms/v7/EVEN",
60
+ "EXID": "https://gedcom.io/terms/v7/EXID",
61
+ "EXID-TYPE": "https://gedcom.io/terms/v7/EXID-TYPE",
62
+ "FACT": "https://gedcom.io/terms/v7/FACT",
63
+ "FAM": "https://gedcom.io/terms/v7/FAM",
64
+ "FAM-CENS": "https://gedcom.io/terms/v7/FAM-CENS",
65
+ "FAM-EVEN": "https://gedcom.io/terms/v7/FAM-EVEN",
66
+ "FAM-FACT": "https://gedcom.io/terms/v7/FAM-FACT",
67
+ "FAM-HUSB": "https://gedcom.io/terms/v7/FAM-HUSB",
68
+ "FAM-NCHI": "https://gedcom.io/terms/v7/FAM-NCHI",
69
+ "FAM-RESI": "https://gedcom.io/terms/v7/FAM-RESI",
70
+ "FAM-WIFE": "https://gedcom.io/terms/v7/FAM-WIFE",
71
+ "FAMC": "https://gedcom.io/terms/v7/FAMC",
72
+ "FAMC-ADOP": "https://gedcom.io/terms/v7/FAMC-ADOP",
73
+ "FAMC-STAT": "https://gedcom.io/terms/v7/FAMC-STAT",
74
+ "FAMS": "https://gedcom.io/terms/v7/FAMS",
75
+ "FAX": "https://gedcom.io/terms/v7/FAX",
76
+ "FCOM": "https://gedcom.io/terms/v7/FCOM",
77
+ "FILE": "https://gedcom.io/terms/v7/FILE",
78
+ "FILE-TRAN": "https://gedcom.io/terms/v7/FILE-TRAN",
79
+ "FORM": "https://gedcom.io/terms/v7/FORM",
80
+ "GEDC": "https://gedcom.io/terms/v7/GEDC",
81
+ "GEDC-VERS": "https://gedcom.io/terms/v7/GEDC-VERS",
82
+ "GIVN": "https://gedcom.io/terms/v7/GIVN",
83
+ "GRAD": "https://gedcom.io/terms/v7/GRAD",
84
+ "HEAD": "https://gedcom.io/terms/v7/HEAD",
85
+ "HEAD-DATE": "https://gedcom.io/terms/v7/HEAD-DATE",
86
+ "HEAD-LANG": "https://gedcom.io/terms/v7/HEAD-LANG",
87
+ "HEAD-PLAC": "https://gedcom.io/terms/v7/HEAD-PLAC",
88
+ "HEAD-PLAC-FORM": "https://gedcom.io/terms/v7/HEAD-PLAC-FORM",
89
+ "HEAD-SOUR": "https://gedcom.io/terms/v7/HEAD-SOUR",
90
+ "HEAD-SOUR-DATA": "https://gedcom.io/terms/v7/HEAD-SOUR-DATA",
91
+ "HEIGHT": "https://gedcom.io/terms/v7/HEIGHT",
92
+ "HUSB": "https://gedcom.io/terms/v7/HUSB",
93
+ "IDNO": "https://gedcom.io/terms/v7/IDNO",
94
+ "IMMI": "https://gedcom.io/terms/v7/IMMI",
95
+ "INDI": "https://gedcom.io/terms/v7/INDI",
96
+ "INDI-CENS": "https://gedcom.io/terms/v7/INDI-CENS",
97
+ "INDI-EVEN": "https://gedcom.io/terms/v7/INDI-EVEN",
98
+ "INDI-FACT": "https://gedcom.io/terms/v7/INDI-FACT",
99
+ "INDI-FAMC": "https://gedcom.io/terms/v7/INDI-FAMC",
100
+ "INDI-NAME": "https://gedcom.io/terms/v7/INDI-NAME",
101
+ "INDI-NCHI": "https://gedcom.io/terms/v7/INDI-NCHI",
102
+ "INDI-RELI": "https://gedcom.io/terms/v7/INDI-RELI",
103
+ "INDI-RESI": "https://gedcom.io/terms/v7/INDI-RESI",
104
+ "INDI-TITL": "https://gedcom.io/terms/v7/INDI-TITL",
105
+ "INIL": "https://gedcom.io/terms/v7/INIL",
106
+ "LANG": "https://gedcom.io/terms/v7/LANG",
107
+ "LATI": "https://gedcom.io/terms/v7/LATI",
108
+ "LEFT": "https://gedcom.io/terms/v7/LEFT",
109
+ "LONG": "https://gedcom.io/terms/v7/LONG",
110
+ "MAP": "https://gedcom.io/terms/v7/MAP",
111
+ "MARB": "https://gedcom.io/terms/v7/MARB",
112
+ "MARC": "https://gedcom.io/terms/v7/MARC",
113
+ "MARL": "https://gedcom.io/terms/v7/MARL",
114
+ "MARR": "https://gedcom.io/terms/v7/MARR",
115
+ "MARS": "https://gedcom.io/terms/v7/MARS",
116
+ "MEDI": "https://gedcom.io/terms/v7/MEDI",
117
+ "MIME": "https://gedcom.io/terms/v7/MIME",
118
+ "NAME": "https://gedcom.io/terms/v7/NAME",
119
+ "NAME-TRAN": "https://gedcom.io/terms/v7/NAME-TRAN",
120
+ "NAME-TYPE": "https://gedcom.io/terms/v7/NAME-TYPE",
121
+ "NATI": "https://gedcom.io/terms/v7/NATI",
122
+ "NATU": "https://gedcom.io/terms/v7/NATU",
123
+ "NCHI": "https://gedcom.io/terms/v7/NCHI",
124
+ "NICK": "https://gedcom.io/terms/v7/NICK",
125
+ "NMR": "https://gedcom.io/terms/v7/NMR",
126
+ "NO": "https://gedcom.io/terms/v7/NO",
127
+ "NO-DATE": "https://gedcom.io/terms/v7/NO-DATE",
128
+ "NOTE": "https://gedcom.io/terms/v7/NOTE",
129
+ "NOTE-TRAN": "https://gedcom.io/terms/v7/NOTE-TRAN",
130
+ "NPFX": "https://gedcom.io/terms/v7/NPFX",
131
+ "NSFX": "https://gedcom.io/terms/v7/NSFX",
132
+ "OBJE": "https://gedcom.io/terms/v7/OBJE",
133
+ "OCCU": "https://gedcom.io/terms/v7/OCCU",
134
+ "ORDN": "https://gedcom.io/terms/v7/ORDN",
135
+ "PAGE": "https://gedcom.io/terms/v7/PAGE",
136
+ "PEDI": "https://gedcom.io/terms/v7/PEDI",
137
+ "PHON": "https://gedcom.io/terms/v7/PHON",
138
+ "PHRASE": "https://gedcom.io/terms/v7/PHRASE",
139
+ "PLAC": "https://gedcom.io/terms/v7/PLAC",
140
+ "PLAC-FORM": "https://gedcom.io/terms/v7/PLAC-FORM",
141
+ "PLAC-TRAN": "https://gedcom.io/terms/v7/PLAC-TRAN",
142
+ "POST": "https://gedcom.io/terms/v7/POST",
143
+ "PROB": "https://gedcom.io/terms/v7/PROB",
144
+ "PROP": "https://gedcom.io/terms/v7/PROP",
145
+ "PUBL": "https://gedcom.io/terms/v7/PUBL",
146
+ "QUAY": "https://gedcom.io/terms/v7/QUAY",
147
+ "REFN": "https://gedcom.io/terms/v7/REFN",
148
+ "RELI": "https://gedcom.io/terms/v7/RELI",
149
+ "REPO": "https://gedcom.io/terms/v7/REPO",
150
+ "RESI": "https://gedcom.io/terms/v7/RESI",
151
+ "RESN": "https://gedcom.io/terms/v7/RESN",
152
+ "RETI": "https://gedcom.io/terms/v7/RETI",
153
+ "ROLE": "https://gedcom.io/terms/v7/ROLE",
154
+ "SCHMA": "https://gedcom.io/terms/v7/SCHMA",
155
+ "SDATE": "https://gedcom.io/terms/v7/SDATE",
156
+ "SEX": "https://gedcom.io/terms/v7/SEX",
157
+ "SLGC": "https://gedcom.io/terms/v7/SLGC",
158
+ "SLGS": "https://gedcom.io/terms/v7/SLGS",
159
+ "SNOTE": "https://gedcom.io/terms/v7/SNOTE",
160
+ "SOUR": "https://gedcom.io/terms/v7/SOUR",
161
+ "SOUR-DATA": "https://gedcom.io/terms/v7/SOUR-DATA",
162
+ "SOUR-EVEN": "https://gedcom.io/terms/v7/SOUR-EVEN",
163
+ "SPFX": "https://gedcom.io/terms/v7/SPFX",
164
+ "SSN": "https://gedcom.io/terms/v7/SSN",
165
+ "STAE": "https://gedcom.io/terms/v7/STAE",
166
+ "STAT": "https://gedcom.io/terms/v7/STAT",
167
+ "SUBM": "https://gedcom.io/terms/v7/SUBM",
168
+ "SUBM-LANG": "https://gedcom.io/terms/v7/SUBM-LANG",
169
+ "SURN": "https://gedcom.io/terms/v7/SURN",
170
+ "TAG": "https://gedcom.io/terms/v7/TAG",
171
+ "TEMP": "https://gedcom.io/terms/v7/TEMP",
172
+ "TEXT": "https://gedcom.io/terms/v7/TEXT",
173
+ "TIME": "https://gedcom.io/terms/v7/TIME",
174
+ "TITL": "https://gedcom.io/terms/v7/TITL",
175
+ "TOP": "https://gedcom.io/terms/v7/TOP",
176
+ "TRAN": "https://gedcom.io/terms/v7/TRAN",
177
+ "TRLR": "https://gedcom.io/terms/v7/TRLR",
178
+ "TYPE": "https://gedcom.io/terms/v7/TYPE",
179
+ "UID": "https://gedcom.io/terms/v7/UID",
180
+ "VERS": "https://gedcom.io/terms/v7/VERS",
181
+ "WIDTH": "https://gedcom.io/terms/v7/WIDTH",
182
+ "WIFE": "https://gedcom.io/terms/v7/WIFE",
183
+ "WILL": "https://gedcom.io/terms/v7/WILL",
184
+ "WWW": "https://gedcom.io/terms/v7/WWW",
185
+ "enumset-ADOP": "https://gedcom.io/terms/v7/enumset-ADOP",
186
+ "enumset-EVEN": "https://gedcom.io/terms/v7/enumset-EVEN",
187
+ "enumset-EVENATTR": "https://gedcom.io/terms/v7/enumset-EVENATTR",
188
+ "enumset-FAMC-STAT": "https://gedcom.io/terms/v7/enumset-FAMC-STAT",
189
+ "enumset-MEDI": "https://gedcom.io/terms/v7/enumset-MEDI",
190
+ "enumset-NAME-TYPE": "https://gedcom.io/terms/v7/enumset-NAME-TYPE",
191
+ "enumset-PEDI": "https://gedcom.io/terms/v7/enumset-PEDI",
192
+ "enumset-QUAY": "https://gedcom.io/terms/v7/enumset-QUAY",
193
+ "enumset-RESN": "https://gedcom.io/terms/v7/enumset-RESN",
194
+ "enumset-ROLE": "https://gedcom.io/terms/v7/enumset-ROLE",
195
+ "enumset-SEX": "https://gedcom.io/terms/v7/enumset-SEX",
196
+ "ord-STAT": "https://gedcom.io/terms/v7/ord-STAT",
197
+ "record-FAM": "https://gedcom.io/terms/v7/record-FAM",
198
+ "record-INDI": "https://gedcom.io/terms/v7/record-INDI",
199
+ "record-OBJE": "https://gedcom.io/terms/v7/record-OBJE",
200
+ "record-REPO": "https://gedcom.io/terms/v7/record-REPO",
201
+ "record-SNOTE": "https://gedcom.io/terms/v7/record-SNOTE",
202
+ "record-SOUR": "https://gedcom.io/terms/v7/record-SOUR",
203
+ "record-SUBM": "https://gedcom.io/terms/v7/record-SUBM"
204
+ }
205
+
@@ -0,0 +1,160 @@
1
+
2
+ from __future__ import annotations
3
+ from typing import Any, Dict, List, Optional, Union, Iterable
4
+ from collections import defaultdict
5
+
6
+
7
+ from .GedcomStructure import GedcomStructure
8
+ from . import Specification as g7specs
9
+ from .logger import get_logger
10
+
11
+
12
+ from typing import Dict, List, Optional
13
+
14
+
15
+
16
+ class Gedcom7:
17
+ def __init__(self, filepath: Optional[str] = None):
18
+ self.persons: List[Any] = []
19
+ self.families: List[Any] = []
20
+ self.sources: List[Any] = []
21
+ self.records: List['GedcomStructure'] = []
22
+ self._tag_index: Dict[str, List[int]] = defaultdict(list) # tag -> list of record indices
23
+
24
+ # ---- indexing helpers -------------------------------------------------
25
+ @staticmethod
26
+ def _norm_tag(tag: str) -> str:
27
+ return tag.upper()
28
+
29
+ def _rebuild_index(self) -> None:
30
+ self._tag_index.clear()
31
+ for i, rec in enumerate(self.records):
32
+ if getattr(rec, "tag", None):
33
+ self._tag_index[self._norm_tag(rec.tag)].append(i)
34
+
35
+ # Optional: keep index in sync if you append records elsewhere
36
+ def _append_record(self, rec: 'GedcomStructure') -> None:
37
+ self.records.append(rec)
38
+ if getattr(rec, "tag", None):
39
+ self._tag_index[self._norm_tag(rec.tag)].append(len(self.records) - 1)
40
+
41
+ # ---- Python container protocol ----------------------------------------
42
+ def __len__(self) -> int:
43
+ return len(self.records)
44
+
45
+ def __iter__(self) -> Iterable['GedcomStructure']:
46
+ return iter(self.records)
47
+
48
+ def __contains__(self, key: Union[str, 'GedcomStructure']) -> bool:
49
+ if isinstance(key, str):
50
+ return self._norm_tag(key) in self._tag_index
51
+ return key in self.records
52
+
53
+ def __getitem__(self, key: Union[int, slice, str, tuple]) -> Union['GedcomStructure', List['GedcomStructure']]:
54
+ # by position
55
+ if isinstance(key, (int, slice)):
56
+ return self.records[key]
57
+
58
+ # by tag
59
+ if isinstance(key, str):
60
+ idxs = self._tag_index.get(self._norm_tag(key), [])
61
+ return [self.records[i] for i in idxs]
62
+
63
+ # combo: ('INDI', 0) or ('INDI', 0:5)
64
+ if isinstance(key, tuple) and len(key) == 2 and isinstance(key[0], str):
65
+ tag, sub = key
66
+ items = self[tag] # list for that tag
67
+ if isinstance(sub, int) or isinstance(sub, slice):
68
+ return items[sub]
69
+ raise TypeError(f"Unsupported sub-key type: {type(sub)!r}")
70
+
71
+ raise TypeError(f"Unsupported key type: {type(key)!r}")
72
+
73
+ # ---- your existing methods (trimmed) ----------------------------------
74
+ @staticmethod
75
+ def parse_gedcom_line(line: str) -> Optional[Dict[str, Any]]:
76
+
77
+ line = line.lstrip('\ufeff').rstrip('\r\n')
78
+ if not line:
79
+ return None
80
+
81
+ parts = line.split(maxsplit=3)
82
+ if len(parts) < 2:
83
+ return None # not even "0 HEAD"
84
+
85
+ # 1) Level
86
+ try:
87
+ level = int(parts[0])
88
+ except ValueError:
89
+ return None
90
+
91
+ # 2) Is parts[1] an XREF?
92
+ xref = None
93
+ if parts[1].startswith('@') and parts[1].endswith('@'):
94
+ xref = parts[1]
95
+
96
+ # 3) Where is the tag?
97
+ if xref:
98
+ # must have at least ["0", "@X@", "TAG"]
99
+ if len(parts) < 3:
100
+ return None
101
+ tag = parts[2]
102
+ # everything after index 2 is the value
103
+ value_parts = parts[3:] # could be empty or one-element
104
+ else:
105
+ tag = parts[1]
106
+ # everything after index 1 is the value
107
+ value_parts = parts[2:] # could be empty, one- or two-element
108
+
109
+
110
+ # 4) re-assemble the full value
111
+ value = " ".join(value_parts) # empty string if value_parts == []
112
+ if value.startswith('@') and value.endswith('@'):
113
+ xref = parts[1]
114
+
115
+ if tag == 'TAG':
116
+ xtag, uri = value.split()
117
+ g7specs.structure_specs[xtag] = uri
118
+ g7specs.structure_specs[uri] = {'label': 'Extension_' + xtag}
119
+
120
+ return {
121
+ "level": level,
122
+ "xref": xref,
123
+ "tag": tag,
124
+ "value": value
125
+ }
126
+
127
+
128
+ def loadfile(self, filepath: str) -> None:
129
+ log = get_logger('importlog')
130
+ context: Dict[int, GedcomStructure] = {}
131
+ records: List[GedcomStructure] = []
132
+
133
+ with open(filepath, 'r', encoding='utf8') as file:
134
+ for lineno, raw in enumerate(file, start=1):
135
+ record = Gedcom7.parse_gedcom_line(raw)
136
+ if record is None:
137
+ log.error(f'empty line at {lineno}: {raw}')
138
+ continue
139
+
140
+ level = int(record["level"])
141
+ if record["tag"] == g7specs.CONT:
142
+ context[level - 1].value += "\n" + record["value"]
143
+ continue
144
+
145
+ structure = GedcomStructure(
146
+ level=level,
147
+ tag=record["tag"],
148
+ xref=record["xref"],
149
+ text=record["value"],
150
+ parent=context[level - 1] if level > 0 else None,
151
+ line_num=lineno
152
+ )
153
+
154
+ if level == 0:
155
+ records.append(structure)
156
+
157
+ context[level] = structure
158
+
159
+ self.records = records
160
+ self._rebuild_index() # <-- build fast tag index once
@@ -0,0 +1,19 @@
1
+ import logging
2
+
3
+ def get_logger(name=None):
4
+ logger = logging.getLogger(name)
5
+ if not logger.handlers:
6
+ logger.setLevel(logging.DEBUG)
7
+
8
+ formatter = logging.Formatter('[%(asctime)s] %(levelname)s - %(name)s - %(message)s')
9
+
10
+ console_handler = logging.StreamHandler()
11
+ console_handler.setFormatter(formatter)
12
+ logger.addHandler(console_handler)
13
+
14
+ # Optional: file logging
15
+ file_handler = logging.FileHandler(f"{name}.log", encoding="utf-8")
16
+ file_handler.setFormatter(formatter)
17
+ logger.addHandler(file_handler)
18
+
19
+ return logger