gramps-gedcom7 0.0.3__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 David Straub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: gramps_gedcom7
3
+ Version: 0.0.3
4
+ Summary: A GEDCOM 7 support library for Gramps genealogy software
5
+ Author-email: David Straub <straub@protonmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/DavidMStraub/gramps-gedcom7
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: gramps>=6.0.0
12
+ Requires-Dist: gedcom7>=0.3.0
13
+ Dynamic: license-file
14
+
15
+ # gramps-gedcom7
16
+
17
+ A GEDCOM 7 import library for Gramps.
@@ -0,0 +1,3 @@
1
+ # gramps-gedcom7
2
+
3
+ A GEDCOM 7 import library for Gramps.
@@ -0,0 +1 @@
1
+ from .importer import import_gedcom
@@ -0,0 +1,110 @@
1
+ """Process GEDCOM 7 event data."""
2
+
3
+ from gedcom7 import const as g7const
4
+ from gedcom7 import grammar as g7grammar
5
+ from gedcom7 import types as g7types
6
+ from gedcom7 import util as g7util
7
+ from gramps.gen.lib import Event, EventType, Place, PlaceName
8
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
9
+
10
+ from . import util
11
+
12
+
13
+ def handle_event(
14
+ structure: g7types.GedcomStructure,
15
+ xref_handle_map: dict[str, str],
16
+ event_type_map: dict[str, int],
17
+ ) -> tuple[Event, list[BasicPrimaryObject]]:
18
+ """Convert a GEDCOM event structure to a Gramps Event object.
19
+
20
+ Args:
21
+ structure: The GEDCOM structure containing the event data.
22
+ xref_handle_map: A map of XREFs to Gramps handles.
23
+ event_type_map: A mapping of GEDCOM event tags to Gramps EventType values.
24
+
25
+ Returns:
26
+ A tuple containing the Gramps Event object and a list of additional objects created.
27
+ """
28
+ event = Event()
29
+ event.set_type(event_type_map.get(structure.tag, EventType.CUSTOM))
30
+ event.handle = util.make_handle()
31
+ objects = []
32
+ for child in structure.children:
33
+ if child.tag == g7const.SNOTE and child.pointer != g7grammar.voidptr:
34
+ try:
35
+ note_handle = xref_handle_map[child.pointer]
36
+ except KeyError:
37
+ raise ValueError(f"Shared note {child.pointer} not found")
38
+ event.add_note(note_handle)
39
+ elif child.tag == g7const.NOTE:
40
+ event, note = util.add_note_to_object(child, event)
41
+ objects.append(note)
42
+ # TODO handle media
43
+ # TODO handle source citation
44
+ elif child.tag == g7const.PLAC:
45
+ place, other_objects = handle_place(child, xref_handle_map)
46
+ event.set_place_handle(place.handle)
47
+ objects.append(place)
48
+ objects.extend(other_objects)
49
+ elif child.tag == g7const.DATE:
50
+ assert isinstance(
51
+ child.value,
52
+ (
53
+ g7types.Date,
54
+ g7types.DatePeriod,
55
+ g7types.DateApprox,
56
+ g7types.DateRange,
57
+ ),
58
+ ), "Expected value to be a date-related object"
59
+ date = util.gedcom_date_value_to_gramps_date(child.value)
60
+ event.set_date_object(date)
61
+ return event, objects
62
+
63
+
64
+ def handle_place(
65
+ structure: g7types.GedcomStructure,
66
+ xref_handle_map: dict[str, str],
67
+ ) -> tuple[Place, list[BasicPrimaryObject]]:
68
+ """Convert a GEDCOM place structure to a Gramps Place object.
69
+
70
+ Args:
71
+ structure: The GEDCOM structure containing the place data.
72
+ xref_handle_map: A map of XREFs to Gramps handles.
73
+
74
+ Returns:
75
+ A Gramps Place object created from the GEDCOM structure.
76
+ """
77
+ place = Place()
78
+ objects = []
79
+ place.handle = util.make_handle()
80
+ if structure.value:
81
+ name = PlaceName()
82
+ name.set_value(structure.value)
83
+ place.set_name(name)
84
+ for child in structure.children:
85
+ if child.tag == g7const.MAP:
86
+ lat = g7util.get_first_child_with_tag(child, g7const.LATI)
87
+ lon = g7util.get_first_child_with_tag(child, g7const.LONG)
88
+ if lat is not None and lon is not None:
89
+ if not isinstance(lat.value, str) or not isinstance(lon.value, str):
90
+ raise ValueError("Latitude and longitude must be strings")
91
+ place.set_latitude(lat.value)
92
+ place.set_longitude(lon.value)
93
+ elif child.tag == g7const.LANG and child.value:
94
+ place.name.set_language(child.value)
95
+ elif child.tag == g7const.TRAN:
96
+ alt_name = PlaceName()
97
+ alt_name.set_value(child.value)
98
+ if lang := g7util.get_first_child_with_tag(child, g7const.LANG):
99
+ alt_name.set_language(lang.value)
100
+ elif child.tag == g7const.SNOTE and child.pointer != g7grammar.voidptr:
101
+ try:
102
+ note_handle = xref_handle_map[child.pointer]
103
+ except KeyError:
104
+ raise ValueError(f"Shared note {child.pointer} not found")
105
+ place.add_note(note_handle)
106
+ elif child.tag == g7const.NOTE:
107
+ place, note = util.add_note_to_object(child, place)
108
+ # Add the note to the list of objects to be returned
109
+ objects.append(note)
110
+ return place, objects
@@ -0,0 +1,100 @@
1
+ """Handle GEDCOM family records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+
5
+ from gedcom7 import const as g7const
6
+ from gedcom7 import grammar as g7grammar
7
+ from gedcom7 import types as g7types
8
+ from gramps.gen.lib import ChildRef, ChildRefType, Family, MediaRef, EventRef, EventType
9
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
10
+
11
+ from . import util
12
+ from .event import handle_event
13
+
14
+ EVENT_TYPE_MAP = {
15
+ g7const.ANUL: EventType.ANNULMENT,
16
+ g7const.CENS: EventType.CENSUS,
17
+ g7const.DIV: EventType.DIVORCE,
18
+ g7const.DIVF: EventType.DIV_FILING,
19
+ g7const.ENGA: EventType.ENGAGEMENT,
20
+ g7const.MARB: EventType.MARR_BANNS,
21
+ g7const.MARC: EventType.MARR_CONTR,
22
+ g7const.MARL: EventType.MARR_LIC,
23
+ g7const.MARS: EventType.MARR_SETTL,
24
+ g7const.EVEN: EventType.CUSTOM,
25
+ }
26
+
27
+
28
+ def handle_family(
29
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
30
+ ) -> List[BasicPrimaryObject]:
31
+ """Handle an family record and convert it to Gramps objects.
32
+
33
+ Args:
34
+ structure: The GEDCOM note structure to handle.
35
+ xref_handle_map: A map of XREFs to Gramps handles.
36
+
37
+ Returns:
38
+ A list of Gramps objects created from the GEDCOM structure.
39
+ """
40
+ family = Family()
41
+ objects = []
42
+ for child in structure.children:
43
+ # TODO handle RESN
44
+ # TODO handle family attributes
45
+ if child.tag == g7const.HUSB and child.pointer != g7grammar.voidptr:
46
+ person_handle = xref_handle_map.get(child.pointer)
47
+ if not person_handle:
48
+ raise ValueError(f"Person {child.pointer} not found")
49
+ family.set_father_handle(person_handle)
50
+ # TODO handle HUSB PHRASE
51
+ elif child.tag == g7const.WIFE and child.pointer != g7grammar.voidptr:
52
+ person_handle = xref_handle_map.get(child.pointer)
53
+ if not person_handle:
54
+ raise ValueError(f"Person {child.pointer} not found")
55
+ family.set_mother_handle(person_handle)
56
+ # TODO handle WIFE PHRASE
57
+ elif child.tag == g7const.CHIL and child.pointer != g7grammar.voidptr:
58
+ person_handle = xref_handle_map.get(child.pointer)
59
+ if not person_handle:
60
+ raise ValueError(f"Child {child.pointer} not found")
61
+ child_ref = ChildRef()
62
+ child_ref.ref = person_handle
63
+ family.add_child_ref(child_ref)
64
+ # TODO handle CHIL PHRASE
65
+ # TODO handle associations
66
+ elif child.tag == g7const.SNOTE and child.pointer != g7grammar.voidptr:
67
+ try:
68
+ note_handle = xref_handle_map[child.pointer]
69
+ except KeyError:
70
+ raise ValueError(f"Shared note {child.pointer} not found")
71
+ family.add_note(note_handle)
72
+ elif child.tag == g7const.NOTE:
73
+ family, note = util.add_note_to_object(child, family)
74
+ objects.append(note)
75
+ # TODO handle source citations
76
+ # TODO handle indentifier
77
+ elif child.tag == g7const.OBJE:
78
+ media_ref = MediaRef()
79
+ if child.pointer != g7grammar.voidptr:
80
+ media_handle = xref_handle_map.get(child.pointer)
81
+ if not media_handle:
82
+ raise ValueError(f"Multimedia object {child.pointer} not found")
83
+ media_ref.ref = media_handle
84
+ # TODO crop is not implement yet
85
+ # one complication is that GEDCOM uses pixels, Gramps uses fractions.
86
+ # Consequently, image dimensions need to be known to convert.
87
+ family.add_media_reference(media_ref)
88
+ elif child.tag in EVENT_TYPE_MAP:
89
+ event, other_objects = handle_event(
90
+ child, xref_handle_map=xref_handle_map, event_type_map=EVENT_TYPE_MAP
91
+ )
92
+ objects.extend(other_objects)
93
+ event_ref = EventRef()
94
+ event_ref.ref = event.handle
95
+ family.add_event_ref(event_ref)
96
+ objects.append(event)
97
+ family = util.add_ids(family, structure=structure, xref_handle_map=xref_handle_map)
98
+ util.set_change_date(structure=structure, obj=family)
99
+ objects.append(family)
100
+ return objects
@@ -0,0 +1,15 @@
1
+ """Handle GEDCOM header records and import them into the Gramps database."""
2
+
3
+ from gramps.gen.db import DbWriteBase
4
+ from gedcom7 import types as g7types
5
+
6
+
7
+ def handle_header(structure: g7types.GedcomStructure, db: DbWriteBase):
8
+ """Handle a header record and import it into the Gramps database.
9
+
10
+ Args:
11
+ structure: The GEDCOM header structure to handle.
12
+ db: The Gramps database to import into.
13
+ """
14
+ # TODO: Implement header import
15
+ pass
@@ -0,0 +1,35 @@
1
+ """Import a GEDCOM file into a Gramps database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from gramps.gen.db import DbWriteBase
6
+ import gedcom7
7
+ from pathlib import Path
8
+ from typing import TextIO, BinaryIO
9
+
10
+ from . import process
11
+
12
+
13
+ def import_gedcom(file_input: str | Path | TextIO | BinaryIO, db: DbWriteBase) -> None:
14
+ """Import a GEDCOM file into a Gramps database.
15
+
16
+ Args:
17
+
18
+ - file_input: The GEDCOM file to import. This can be a string, Path object, or file-like object.
19
+ - db: The Gramps database to import the GEDCOM file into.
20
+ """
21
+ # Check if file_input is a string or Path object
22
+ if isinstance(file_input, (str, Path)):
23
+ with open(file_input, "r", encoding="utf-8") as f:
24
+ gedcom_data: str = f.read()
25
+ elif isinstance(file_input, TextIO):
26
+ gedcom_data = file_input.read()
27
+ elif isinstance(file_input, BinaryIO):
28
+ gedcom_data = file_input.read().decode("utf-8")
29
+ else:
30
+ raise TypeError(
31
+ "file_input must be a string, Path object, or file-like object."
32
+ )
33
+
34
+ gedcom_structures = gedcom7.loads(gedcom_data)
35
+ process.process_gedcom_structures(gedcom_structures, db)
@@ -0,0 +1,214 @@
1
+ """Handle GEDCOM individual records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+
5
+ from gedcom7 import const as g7const
6
+ from gedcom7 import grammar as g7grammar
7
+ from gedcom7 import types as g7types
8
+ from gramps.gen.lib import (
9
+ EventRef,
10
+ EventType,
11
+ MediaRef,
12
+ Name,
13
+ NameType,
14
+ Person,
15
+ Surname,
16
+ )
17
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
18
+
19
+ from . import util
20
+ from .event import handle_event
21
+
22
+ GENDER_MAP = {
23
+ "M": Person.MALE,
24
+ "F": Person.FEMALE,
25
+ "U": Person.UNKNOWN,
26
+ "X": Person.OTHER,
27
+ }
28
+
29
+ NAME_TYPE_MAP = {
30
+ "AKA": NameType(NameType.AKA),
31
+ "BIRTH": NameType(NameType.BIRTH),
32
+ "MARR": NameType(NameType.MARRIED),
33
+ "OTHER": NameType(NameType.CUSTOM),
34
+ }
35
+
36
+ EVENT_TYPE_MAP = {
37
+ g7const.ADOP: EventType.ADOPT,
38
+ g7const.BAPM: EventType.BAPTISM,
39
+ g7const.BARM: EventType.BAR_MITZVAH,
40
+ g7const.BASM: EventType.BAS_MITZVAH,
41
+ g7const.BIRT: EventType.BIRTH,
42
+ g7const.BLES: EventType.BLESS,
43
+ g7const.BURI: EventType.BURIAL,
44
+ g7const.CENS: EventType.CENSUS,
45
+ g7const.CHR: EventType.CHRISTEN,
46
+ g7const.CHRA: EventType.ADULT_CHRISTEN,
47
+ g7const.CONF: EventType.CONFIRMATION,
48
+ g7const.CREM: EventType.CREMATION,
49
+ g7const.DEAT: EventType.DEATH,
50
+ g7const.EMIG: EventType.EMIGRATION,
51
+ g7const.FCOM: EventType.FIRST_COMMUN,
52
+ g7const.GRAD: EventType.GRADUATION,
53
+ g7const.IMMI: EventType.IMMIGRATION,
54
+ g7const.NATU: EventType.NATURALIZATION,
55
+ g7const.ORDN: EventType.ORDINATION,
56
+ g7const.PROB: EventType.PROBATE,
57
+ g7const.RETI: EventType.RETIREMENT,
58
+ g7const.WILL: EventType.WILL,
59
+ g7const.EVEN: EventType.CUSTOM,
60
+ }
61
+
62
+
63
+ def handle_individual(
64
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
65
+ ) -> List[BasicPrimaryObject]:
66
+ """Handle an individual record and convert it to Gramps objects.
67
+
68
+ Args:
69
+ structure: The GEDCOM note structure to handle.
70
+ xref_handle_map: A map of XREFs to Gramps handles.
71
+
72
+ Returns:
73
+ A list of Gramps objects created from the GEDCOM structure.
74
+ """
75
+ person = Person()
76
+ objects = []
77
+ for child in structure.children:
78
+ # TODO handle RESN
79
+ if child.tag == g7const.SEX:
80
+ assert isinstance(child.value, str), "Expected SEX to be a string"
81
+ try:
82
+ gender = GENDER_MAP[child.value]
83
+ person.set_gender(gender)
84
+ except KeyError:
85
+ raise ValueError(f"SEX value '{child.value}' is not valid")
86
+ elif child.tag == g7const.NAME:
87
+ name, other_objects = handle_name(child, xref_handle_map=xref_handle_map)
88
+ objects.extend(other_objects)
89
+ if person.primary_name.is_empty():
90
+ person.set_primary_name(name)
91
+ else:
92
+ person.add_alternate_name(name)
93
+ # TODO handle attributes
94
+ # TODO handle SUBM
95
+ # TODO handle associations
96
+ # TODO handle ALIA
97
+ # TODO handle ANCI
98
+ # TODO handle DESI
99
+ # TODO handle source citations
100
+ # TODO handle indentifier
101
+ elif child.tag == g7const.FAMC and child.pointer != g7grammar.voidptr:
102
+ family_handle = xref_handle_map.get(child.pointer)
103
+ if not family_handle:
104
+ raise ValueError(f"Family {child.pointer} not found")
105
+ person.add_parent_family_handle(family_handle)
106
+ # TODO child ref type should be handled in the family!
107
+ # TODO handle FAMC PHRASE
108
+ elif child.tag == g7const.FAMS and child.pointer != g7grammar.voidptr:
109
+ family_handle = xref_handle_map.get(child.pointer)
110
+ if not family_handle:
111
+ raise ValueError(f"Family {child.pointer} not found")
112
+ person.add_family_handle(family_handle)
113
+ # TODO handle FAMS PHRASE
114
+ elif child.tag == g7const.SNOTE and child.pointer != g7grammar.voidptr:
115
+ try:
116
+ note_handle = xref_handle_map[child.pointer]
117
+ except KeyError:
118
+ raise ValueError(f"Shared note {child.pointer} not found")
119
+ person.add_note(note_handle)
120
+ elif child.tag == g7const.NOTE:
121
+ person, note = util.add_note_to_object(child, person)
122
+ objects.append(note)
123
+ elif child.tag == g7const.OBJE:
124
+ media_ref = MediaRef()
125
+ if child.pointer != g7grammar.voidptr:
126
+ media_handle = xref_handle_map.get(child.pointer)
127
+ if not media_handle:
128
+ raise ValueError(f"Multimedia object {child.pointer} not found")
129
+ media_ref.ref = media_handle
130
+ # TODO crop is not implement yet
131
+ # one complication is that GEDCOM uses pixels, Gramps uses fractions.
132
+ # Consequently, image dimensions need to be known to convert.
133
+ person.add_media_reference(media_ref)
134
+ elif child.tag in EVENT_TYPE_MAP:
135
+ event, other_objects = handle_event(
136
+ child, xref_handle_map=xref_handle_map, event_type_map=EVENT_TYPE_MAP
137
+ )
138
+ objects.extend(other_objects)
139
+ event_ref = EventRef()
140
+ event_ref.ref = event.handle
141
+ person.add_event_ref(event_ref)
142
+ objects.append(event)
143
+ person = util.add_ids(person, structure=structure, xref_handle_map=xref_handle_map)
144
+ util.set_change_date(structure=structure, obj=person)
145
+ objects.append(person)
146
+ return objects
147
+
148
+
149
+ def handle_name(
150
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
151
+ ) -> tuple[Name, list[BasicPrimaryObject]]:
152
+ """Convert a GEDCOM structure to a Gramps Name object.
153
+
154
+ Args:
155
+ structure: The GEDCOM structure containing the name data.
156
+
157
+ Returns:
158
+ A tuple containing the Gramps Name object and a list of additional objects created.
159
+ """
160
+ name = Name()
161
+ surname = Surname()
162
+ objects = []
163
+ personal_name = structure.value
164
+ assert isinstance(
165
+ personal_name, g7types.PersonalName
166
+ ), "Expected structure value to be a PersonalName"
167
+ for child in structure.children:
168
+ if child.tag == g7const.TYPE:
169
+ assert isinstance(child.value, str), "Expected TYPE value to be a string"
170
+ gramps_name_type = NAME_TYPE_MAP.get(child.value, NameType.CUSTOM)
171
+ if gramps_name_type == NameType.CUSTOM:
172
+ pass # TODO handle custom name types
173
+ name.set_type(NameType(gramps_name_type))
174
+ elif child.tag == g7const.NPFX:
175
+ assert isinstance(child.value, str), "Expected NPFX value to be a string"
176
+ name.set_title(child.value)
177
+ elif child.tag == g7const.GIVN:
178
+ assert isinstance(child.value, str), "Expected GIVN value to be a string"
179
+ name.set_first_name(child.value)
180
+ elif child.tag == g7const.NICK:
181
+ assert isinstance(child.value, str), "Expected NICK value to be a string"
182
+ name.set_nick_name(child.value)
183
+ elif child.tag == g7const.SPFX:
184
+ assert isinstance(child.value, str), "Expected SPFX value to be a string"
185
+ surname.set_prefix(child.value)
186
+ elif child.tag == g7const.SURN:
187
+ assert isinstance(child.value, str), "Expected SURN value to be a string"
188
+ surname.set_surname(child.value)
189
+ elif child.tag == g7const.NSFX:
190
+ assert isinstance(child.value, str), "Expected NSFX value to be a string"
191
+ name.set_suffix(child.value)
192
+ elif child.tag == g7const.SNOTE and child.pointer != g7grammar.voidptr:
193
+ try:
194
+ note_handle = xref_handle_map[child.pointer]
195
+ except KeyError:
196
+ raise ValueError(f"Shared note {child.pointer} not found")
197
+ name.add_note(note_handle)
198
+ elif child.tag == g7const.NOTE:
199
+ name, note = util.add_note_to_object(child, name)
200
+ objects.append(note)
201
+ # TODO handle source citation
202
+ # if name parts are not given, use the personal name parts
203
+ if not name.first_name and personal_name.given:
204
+ name.set_first_name(personal_name.given)
205
+ if not surname.surname and personal_name.surname:
206
+ surname.set_surname(personal_name.surname)
207
+ if not name.suffix and personal_name.suffix:
208
+ name.set_suffix(personal_name.suffix)
209
+ if not surname.is_empty():
210
+ name.add_surname(surname)
211
+ # last resort - only single string, no surname markers - take as first name only
212
+ if name.is_empty():
213
+ name.set_first_name(personal_name.fullname)
214
+ return name, objects
@@ -0,0 +1,53 @@
1
+ """Handle GEDCOM multimedia records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+
5
+ from gedcom7 import const as g7const
6
+ from gedcom7 import types as g7types
7
+ from gedcom7 import util as g7util
8
+ from gramps.gen.lib import Media
9
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
10
+
11
+ from . import util
12
+
13
+
14
+ def handle_multimedia(
15
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
16
+ ) -> List[BasicPrimaryObject]:
17
+ """Handle a multimedia record and convert it to Gramps objects.
18
+
19
+ Args:
20
+ structure: The GEDCOM note structure to handle.
21
+ xref_handle_map: A map of XREFs to Gramps handles.
22
+
23
+ Returns:
24
+ A list of Gramps objects created from the GEDCOM structure.
25
+ """
26
+ media = Media()
27
+ objects = []
28
+ for child in structure.children:
29
+ if child.tag == g7const.SNOTE:
30
+ try:
31
+ note_handle = xref_handle_map[child.pointer]
32
+ except KeyError:
33
+ raise ValueError(f"Shared note {child.pointer} not found")
34
+ media.add_note(note_handle)
35
+ elif child.tag == g7const.NOTE:
36
+ media, note = util.add_note_to_object(child, media)
37
+ objects.append(note)
38
+ # TODO handle RESN
39
+ # TODO handle multiple files
40
+ file_structure = g7util.get_first_child_with_tag(structure, g7const.FILE)
41
+ assert file_structure is not None, "Multimedia structure must have a FILE tag"
42
+ assert isinstance(file_structure.value, str), "Expected FILE value to be a string"
43
+ media.set_path(file_structure.value.removeprefix("file://"))
44
+ form_structure = g7util.get_first_child_with_tag(file_structure, g7const.FORM)
45
+ assert form_structure is not None, "Multimedia file must have a FORM tag"
46
+ assert isinstance(
47
+ form_structure.value, g7types.MediaType
48
+ ), "Expected FORM value to be a MediaType"
49
+ media.set_mime_type(form_structure.value.media_type)
50
+ media = util.add_ids(media, structure=structure, xref_handle_map=xref_handle_map)
51
+ util.set_change_date(structure=structure, obj=media)
52
+ objects.append(media)
53
+ return objects
@@ -0,0 +1,26 @@
1
+ """Handle GEDCOM shared note records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
5
+ from gedcom7 import types as g7types
6
+ from . import util
7
+
8
+
9
+ def handle_shared_note(
10
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
11
+ ) -> List[BasicPrimaryObject]:
12
+ """Handle a shared note record and convert it to Gramps objects.
13
+
14
+ Args:
15
+ structure: The GEDCOM note structure to handle.
16
+ xref_handle_map: A map of XREFs to Gramps handles.
17
+
18
+ Returns:
19
+ A list of Gramps objects created from the GEDCOM structure.
20
+ """
21
+ note = util.structure_to_note(structure)
22
+ # set note handle and Gramps ID
23
+ note = util.add_ids(note, structure=structure, xref_handle_map=xref_handle_map)
24
+ # set last change timestamp
25
+ util.set_change_date(structure=structure, obj=note)
26
+ return [note]
@@ -0,0 +1,106 @@
1
+ """Process GEDCOM structures and import them into the Gramps database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from gedcom7 import const as g7const
6
+ from gedcom7 import types as g7types
7
+ from gramps.gen.db import DbTxn, DbWriteBase
8
+
9
+ from .family import handle_family
10
+ from .header import handle_header
11
+ from .individual import handle_individual
12
+ from .multimedia import handle_multimedia
13
+ from .note import handle_shared_note
14
+ from .repository import handle_repository
15
+ from .source import handle_source
16
+ from .submitter import handle_submitter
17
+ from .util import make_handle
18
+
19
+
20
+ def process_gedcom_structures(
21
+ gedcom_structures: list[g7types.GedcomStructure], db: DbWriteBase
22
+ ):
23
+ """Process GEDCOM structures and import them into the Gramps database.
24
+
25
+ Args:
26
+ gedcom_structures: The GEDCOM structures to process.
27
+ db: The Gramps database to import the GEDCOM structures into.
28
+ """
29
+ if len(gedcom_structures) < 2:
30
+ raise ValueError("No GEDCOM structures to process.")
31
+ first_structure = gedcom_structures[0]
32
+ if first_structure.tag != g7const.HEAD:
33
+ raise ValueError(
34
+ f"First structure must be a HEAD structure, but got {first_structure.tag}"
35
+ )
36
+ last_structure = gedcom_structures[-1]
37
+ if last_structure.tag != g7const.TRLR:
38
+ raise ValueError(
39
+ f"Last structure must be a TRLR structure, but got {last_structure.tag}"
40
+ )
41
+
42
+ handle_header(first_structure, db)
43
+
44
+ # Create a map of handles to XREFs
45
+ xref_handle_map = {}
46
+ for structure in gedcom_structures:
47
+ if structure.xref and structure.xref not in xref_handle_map:
48
+ xref_handle_map[structure.xref] = make_handle()
49
+
50
+ # Handle the remaining structures (excluding header and trailer)
51
+ objects = []
52
+ for structure in gedcom_structures[1:-1]:
53
+ objects += handle_structure(structure, xref_handle_map=xref_handle_map) or []
54
+
55
+ add_objects_to_database(objects, db)
56
+
57
+
58
+ def handle_structure(
59
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
60
+ ) -> list | None:
61
+ """Handle a single GEDCOM structure and import it into the Gramps database.
62
+
63
+ Args:
64
+ structure: The GEDCOM structure to handle.
65
+ db: The Gramps database to import the GEDCOM structure into.
66
+ """
67
+ if structure.tag == g7const.FAM:
68
+ return handle_family(structure, xref_handle_map=xref_handle_map)
69
+ elif structure.tag == g7const.INDI:
70
+ return handle_individual(structure, xref_handle_map=xref_handle_map)
71
+ elif structure.tag == g7const.OBJE:
72
+ return handle_multimedia(structure, xref_handle_map=xref_handle_map)
73
+ elif structure.tag == g7const.REPO:
74
+ return handle_repository(structure, xref_handle_map=xref_handle_map)
75
+ elif structure.tag == g7const.SNOTE:
76
+ return handle_shared_note(structure, xref_handle_map=xref_handle_map)
77
+ elif structure.tag == g7const.SOUR:
78
+ return handle_source(structure, xref_handle_map=xref_handle_map)
79
+ elif structure.tag == g7const.SUBM:
80
+ return handle_submitter(structure)
81
+ return None
82
+
83
+
84
+ def add_objects_to_database(objects, db):
85
+ with DbTxn("Add child to family", db) as transaction:
86
+ for obj in objects:
87
+ if obj.__class__.__name__ == "Person":
88
+ db.add_person(obj, transaction)
89
+ elif obj.__class__.__name__ == "Family":
90
+ db.add_family(obj, transaction)
91
+ elif obj.__class__.__name__ == "Event":
92
+ db.add_event(obj, transaction)
93
+ elif obj.__class__.__name__ == "Citation":
94
+ db.add_citation(obj, transaction)
95
+ elif obj.__class__.__name__ == "Source":
96
+ db.add_source(obj, transaction)
97
+ elif obj.__class__.__name__ == "Note":
98
+ db.add_note(obj, transaction)
99
+ elif obj.__class__.__name__ == "Media":
100
+ db.add_media(obj, transaction)
101
+ elif obj.__class__.__name__ == "Place":
102
+ db.add_place(obj, transaction)
103
+ elif obj.__class__.__name__ == "Repository":
104
+ db.add_repository(obj, transaction)
105
+ elif obj.__class__.__name__ == "Tag":
106
+ db.add_tag(obj, transaction)
@@ -0,0 +1,56 @@
1
+ """Handle GEDCOM repository records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+ from gramps.gen.lib import Repository, NoteType, Url, UrlType
5
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
6
+ from gedcom7 import types as g7types, const as g7const, util as g7util
7
+ from . import util
8
+
9
+
10
+ def handle_repository(
11
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
12
+ ) -> List[BasicPrimaryObject]:
13
+ """Handle a repository record and convert it to Gramps objects.
14
+
15
+ Args:
16
+ structure: The GEDCOM note structure to handle.
17
+ xref_handle_map: A map of XREFs to Gramps handles.
18
+
19
+ Returns:
20
+ A list of Gramps objects created from the GEDCOM structure.
21
+ """
22
+ repository = Repository()
23
+ objects = []
24
+ for child in structure.children:
25
+ if child.tag == g7const.NAME:
26
+ # set repository name
27
+ if child.value is not None:
28
+ assert isinstance(child.value, str), "Expected value to be a string"
29
+ repository.set_name(child.value)
30
+ elif child.tag == g7const.SNOTE:
31
+ try:
32
+ note_handle = xref_handle_map[child.pointer]
33
+ except KeyError:
34
+ raise ValueError(f"Shared note {child.pointer} not found")
35
+ repository.add_note(note_handle)
36
+ elif child.tag == g7const.WWW:
37
+ assert isinstance(child.value, str), "Expected value to be a string"
38
+ url = Url()
39
+ url.set_path(child.value)
40
+ url.set_type(UrlType(UrlType.WEB_HOME))
41
+ repository.add_url(url)
42
+ elif child.tag == g7const.EMAIL:
43
+ assert isinstance(child.value, str), "Expected value to be a string"
44
+ url = Url()
45
+ url.set_path(child.value)
46
+ url.set_type(UrlType(UrlType.EMAIL))
47
+ repository.add_url(url)
48
+ elif child.tag == g7const.NOTE:
49
+ repository, note = util.add_note_to_object(child, repository)
50
+ objects.append(note)
51
+ repository = util.add_ids(
52
+ repository, structure=structure, xref_handle_map=xref_handle_map
53
+ )
54
+ util.set_change_date(structure=structure, obj=repository)
55
+ objects.append(repository)
56
+ return objects
@@ -0,0 +1,117 @@
1
+ """Handle GEDCOM source records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+
5
+ from gedcom7 import const as g7const
6
+ from gedcom7 import types as g7types
7
+ from gedcom7 import util as g7util
8
+ from gramps.gen.lib import MediaRef, Note, NoteType, RepoRef, Source, SourceMediaType
9
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
10
+
11
+ from . import util
12
+
13
+ MEDIA_TYPE_MAP = {
14
+ "AUDIO": SourceMediaType.AUDIO,
15
+ "BOOK": SourceMediaType.BOOK,
16
+ "CARD": SourceMediaType.CARD,
17
+ "ELECTRONIC": SourceMediaType.ELECTRONIC,
18
+ "FICHE": SourceMediaType.FICHE,
19
+ "FILM": SourceMediaType.FILM,
20
+ "MAGAZINE": SourceMediaType.MAGAZINE,
21
+ "MANUSCRIPT": SourceMediaType.MANUSCRIPT,
22
+ "MAP": SourceMediaType.MAP,
23
+ "NEWSPAPER": SourceMediaType.NEWSPAPER,
24
+ "PHOTO": SourceMediaType.PHOTO,
25
+ "TOMBSTONE": SourceMediaType.TOMBSTONE,
26
+ "VIDEO": SourceMediaType.VIDEO,
27
+ "OTHER": SourceMediaType.CUSTOM,
28
+ }
29
+
30
+
31
+ def handle_source(
32
+ structure: g7types.GedcomStructure, xref_handle_map: dict[str, str]
33
+ ) -> List[BasicPrimaryObject]:
34
+ """Handle a source record and convert it to Gramps objects.
35
+
36
+ Args:
37
+ structure: The GEDCOM note structure to handle.
38
+ xref_handle_map: A map of XREFs to Gramps handles.
39
+
40
+ Returns:
41
+ A list of Gramps objects created from the GEDCOM structure.
42
+ """
43
+ source = Source()
44
+ objects = []
45
+ for child in structure.children:
46
+ if child.tag == g7const.TITL:
47
+ # set source title
48
+ if child.value is not None:
49
+ assert isinstance(child.value, str), "Expected value to be a string"
50
+ source.set_title(child.value)
51
+ elif child.tag == g7const.AUTH:
52
+ # set source author
53
+ if child.value is not None:
54
+ assert isinstance(child.value, str), "Expected value to be a string"
55
+ source.set_author(child.value)
56
+ elif child.tag == g7const.PUBL:
57
+ # set source publisher
58
+ if child.value is not None:
59
+ assert isinstance(child.value, str), "Expected value to be a string"
60
+ source.set_publication_info(child.value)
61
+ elif child.tag == g7const.ABBR:
62
+ # set source abbreviation
63
+ if child.value is not None:
64
+ assert isinstance(child.value, str), "Expected value to be a string"
65
+ source.set_abbreviation(child.value)
66
+ # add the note text as a source note
67
+ elif child.tag == g7const.TEXT:
68
+ note = Note()
69
+ note.type = NoteType(NoteType.SOURCE_TEXT)
70
+ if child.value is not None:
71
+ assert isinstance(child.value, str), "Expected value to be a string"
72
+ note.set(child.value)
73
+ note.handle = util.make_handle()
74
+ source.add_note(note.handle)
75
+ elif child.tag == g7const.REPO:
76
+ repo_ref = RepoRef()
77
+ try:
78
+ repo_handle = xref_handle_map[child.pointer]
79
+ except KeyError:
80
+ raise ValueError(f"Repository {child.pointer} not found")
81
+ repo_ref.ref = repo_handle
82
+ call_number = g7util.get_first_child_with_tag(child, g7const.CALN)
83
+ if call_number:
84
+ repo_ref.set_call_number(call_number.value)
85
+ media_type = g7util.get_first_child_with_tag(call_number, g7const.MEDI)
86
+ if media_type:
87
+ assert isinstance(
88
+ media_type.value, str
89
+ ), "Expected value to be a string"
90
+ gramps_source_media_type = MEDIA_TYPE_MAP.get(
91
+ media_type.value, SourceMediaType.CUSTOM
92
+ )
93
+ repo_ref.set_media_type(SourceMediaType(gramps_source_media_type))
94
+ source.add_repo_reference(repo_ref)
95
+ elif child.tag == g7const.SNOTE:
96
+ try:
97
+ note_handle = xref_handle_map[child.pointer]
98
+ except KeyError:
99
+ raise ValueError(f"Shared note {child.pointer} not found")
100
+ source.add_note(note_handle)
101
+ elif child.tag == g7const.NOTE:
102
+ source, note = util.add_note_to_object(child, source)
103
+ objects.append(note)
104
+ elif child.tag == g7const.OBJE:
105
+ media_ref = MediaRef()
106
+ media_handle = xref_handle_map.get(child.pointer)
107
+ if not media_handle:
108
+ raise ValueError(f"Multimedia object {child.pointer} not found")
109
+ media_ref.ref = media_handle
110
+ # TODO crop is not implement yet
111
+ # one complication is that GEDCOM uses pixels, Gramps uses fractions.
112
+ # Consequently, image dimensions need to be known to convert.
113
+ source.add_media_reference(media_ref)
114
+ source = util.add_ids(source, structure=structure, xref_handle_map=xref_handle_map)
115
+ util.set_change_date(structure=structure, obj=source)
116
+ objects.append(source)
117
+ return objects
@@ -0,0 +1,18 @@
1
+ """Handle GEDCOM submitter records and import them into the Gramps database."""
2
+
3
+ from typing import List
4
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
5
+ from gedcom7 import types as g7types
6
+
7
+
8
+ def handle_submitter(structure: g7types.GedcomStructure) -> List[BasicPrimaryObject]:
9
+ """Handle a submitter record and convert it to Gramps objects.
10
+
11
+ Args:
12
+ structure: The GEDCOM submitter structure to handle.
13
+
14
+ Returns:
15
+ A list of Gramps objects created from the GEDCOM structure.
16
+ """
17
+ # TODO: Implement submitter import
18
+ return []
@@ -0,0 +1,7 @@
1
+ from typing import TypeVar
2
+
3
+ from gramps.gen.lib.primaryobj import BasicPrimaryObject
4
+ from gramps.gen.lib.notebase import NoteBase
5
+
6
+ BasicPrimaryObjectT = TypeVar("BasicPrimaryObjectT", bound=BasicPrimaryObject)
7
+ NoteBaseT = TypeVar("NoteBaseT", bound=NoteBase)
@@ -0,0 +1,168 @@
1
+ """Utility functions for handling GEDCOM 7 data in Gramps."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+
7
+ from gedcom7 import const as g7const
8
+ from gedcom7 import types as g7types
9
+ from gedcom7 import util as g7util
10
+ from gramps.gen.lib import Date, Note, NoteType
11
+
12
+ from .types import BasicPrimaryObjectT, NoteBaseT
13
+
14
+
15
+ def make_handle() -> str:
16
+ """Generate a unique handle for a new object."""
17
+ return uuid.uuid4().hex
18
+
19
+
20
+ def add_ids(
21
+ obj: BasicPrimaryObjectT,
22
+ structure: g7types.GedcomStructure,
23
+ xref_handle_map: dict[str, str],
24
+ ) -> BasicPrimaryObjectT:
25
+ """Add a handle and Gramps ID to a new Gramps object."""
26
+ if not structure.xref or len(structure.xref) < 3:
27
+ raise ValueError(f"Invalid xref ID: {structure.xref}")
28
+ if structure.xref not in xref_handle_map:
29
+ raise ValueError(f"Xref ID {structure.xref} not found in xref_handle_map")
30
+ obj.handle = xref_handle_map[structure.xref]
31
+ obj.gramps_id = structure.xref[1:-1]
32
+ return obj
33
+
34
+
35
+ GEDCOM_MONTHS = {
36
+ "JAN": 1,
37
+ "FEB": 2,
38
+ "MAR": 3,
39
+ "APR": 4,
40
+ "MAY": 5,
41
+ "JUN": 6,
42
+ "JUL": 7,
43
+ "AUG": 8,
44
+ "SEP": 9,
45
+ "OCT": 10,
46
+ "NOV": 11,
47
+ "DEC": 12,
48
+ }
49
+
50
+
51
+ def set_change_date(
52
+ structure: g7types.GedcomStructure,
53
+ obj: BasicPrimaryObjectT,
54
+ ) -> BasicPrimaryObjectT:
55
+ """Set the change date for a Gramps object."""
56
+ change_structure = g7util.get_first_child_with_tag(structure, g7const.CHAN)
57
+ if not change_structure:
58
+ # take creation date as fallback
59
+ change_structure = g7util.get_first_child_with_tag(structure, g7const.CREA)
60
+ if not change_structure:
61
+ # no date found
62
+ return obj
63
+ date_structure = g7util.get_first_child_with_tag(change_structure, g7const.DATE)
64
+ if not date_structure:
65
+ # no date found
66
+ return obj
67
+ assert isinstance(
68
+ date_structure.value, g7types.DateExact
69
+ ), "Expected date to be a DateExact object"
70
+ time_structure = g7util.get_first_child_with_tag(change_structure, g7const.TIME)
71
+ if time_structure:
72
+ assert isinstance(
73
+ time_structure.value, g7types.Time
74
+ ), "Expected time to be a Time object"
75
+ time = time_structure.value
76
+ else:
77
+ time = None
78
+ datetime_value = g7util.date_exact_and_time_to_python_datetime(
79
+ date=date_structure.value,
80
+ time=time,
81
+ )
82
+ obj.change = int(datetime_value.timestamp())
83
+ return obj
84
+
85
+
86
+ def structure_to_note(structure: g7types.GedcomStructure) -> Note:
87
+ """Create a note from a GEDCOM structure of type NOTE or SNOTE.
88
+
89
+ Args:
90
+ structure: The GEDCOM note structure to handle.
91
+
92
+ Returns:
93
+ A note object.
94
+ """
95
+ note = Note()
96
+ if structure.value is not None:
97
+ assert isinstance(structure.value, str), "Expected value to be a string"
98
+ note.set(structure.value)
99
+ for child in structure.children:
100
+ # set note type to HTML if MIME type is HTML
101
+ if child.tag == g7const.MIME:
102
+ if child.value == g7const.MIME_HTML:
103
+ note.type = NoteType(NoteType.HTML_CODE)
104
+ elif child.tag == g7const.TRAN:
105
+ # iterate over translations - we just append them
106
+ if child.value is None:
107
+ continue
108
+ assert isinstance(child.value, str), "Expected value to be a string"
109
+ note.append("\n\n" + child.value)
110
+ return note
111
+
112
+
113
+ def add_note_to_object(
114
+ structure: g7types.GedcomStructure,
115
+ obj: NoteBaseT,
116
+ ) -> tuple[NoteBaseT, Note]:
117
+ """Add a note to a Gramps object."""
118
+ note = structure_to_note(structure)
119
+ note.type = NoteType(NoteType.SOURCE)
120
+ note.handle = make_handle()
121
+ # set note change date to parent change date
122
+ set_change_date(structure=structure, obj=note)
123
+ obj.add_note(note.handle)
124
+ return obj, note
125
+
126
+
127
+ def get_next_gramps_id(
128
+ xref_handle_map: dict[str, str],
129
+ prefix: str,
130
+ ) -> str:
131
+ """Get the next available Gramps ID for a given prefix."""
132
+ existing_ids = {handle[1:-1] for handle in xref_handle_map.values()}
133
+ next_id = 1
134
+ while f"{prefix}{next_id:04d}" in existing_ids:
135
+ next_id += 1
136
+ return f"{prefix}{next_id:04d}"
137
+
138
+
139
+ CALENDAR_MAP = {
140
+ "GREGORIAN": Date.CAL_GREGORIAN,
141
+ "JULIAN": Date.CAL_JULIAN,
142
+ "HEBREW": Date.CAL_HEBREW,
143
+ "FRENCH_R": Date.CAL_FRENCH,
144
+ }
145
+
146
+
147
+ def gedcom_date_value_to_gramps_date(
148
+ date_value: g7types.DateValue,
149
+ ) -> Date:
150
+ """Convert a GEDCOM date value to a Gramps date."""
151
+ date = Date()
152
+ if isinstance(date_value, g7types.Date):
153
+ year = date_value.year or 0
154
+ month = GEDCOM_MONTHS.get(date_value.month or "", 0)
155
+ day = date_value.day or 0
156
+ date.set_yr_mon_day(year=year, month=month, day=day)
157
+ if date_value.calendar is not None and date_value.calendar in CALENDAR_MAP:
158
+ date.set_calendar(CALENDAR_MAP[date_value.calendar])
159
+ elif isinstance(date_value, g7types.DatePeriod):
160
+ # TODO
161
+ pass
162
+ elif isinstance(date_value, g7types.DateApprox):
163
+ # TODO
164
+ pass
165
+ elif isinstance(date_value, g7types.DateRange):
166
+ # TODO
167
+ pass
168
+ return date
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: gramps_gedcom7
3
+ Version: 0.0.3
4
+ Summary: A GEDCOM 7 support library for Gramps genealogy software
5
+ Author-email: David Straub <straub@protonmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/DavidMStraub/gramps-gedcom7
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: gramps>=6.0.0
12
+ Requires-Dist: gedcom7>=0.3.0
13
+ Dynamic: license-file
14
+
15
+ # gramps-gedcom7
16
+
17
+ A GEDCOM 7 import library for Gramps.
@@ -0,0 +1,23 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ gramps_gedcom7/__init__.py
5
+ gramps_gedcom7/event.py
6
+ gramps_gedcom7/family.py
7
+ gramps_gedcom7/header.py
8
+ gramps_gedcom7/importer.py
9
+ gramps_gedcom7/individual.py
10
+ gramps_gedcom7/multimedia.py
11
+ gramps_gedcom7/note.py
12
+ gramps_gedcom7/process.py
13
+ gramps_gedcom7/repository.py
14
+ gramps_gedcom7/source.py
15
+ gramps_gedcom7/submitter.py
16
+ gramps_gedcom7/types.py
17
+ gramps_gedcom7/util.py
18
+ gramps_gedcom7.egg-info/PKG-INFO
19
+ gramps_gedcom7.egg-info/SOURCES.txt
20
+ gramps_gedcom7.egg-info/dependency_links.txt
21
+ gramps_gedcom7.egg-info/requires.txt
22
+ gramps_gedcom7.egg-info/top_level.txt
23
+ test/test_importer.py
@@ -0,0 +1,2 @@
1
+ gramps>=6.0.0
2
+ gedcom7>=0.3.0
@@ -0,0 +1 @@
1
+ gramps_gedcom7
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gramps_gedcom7"
7
+ version = "0.0.3"
8
+ description = "A GEDCOM 7 support library for Gramps genealogy software"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "David Straub", email = "straub@protonmail.com"},
14
+ ]
15
+ dependencies = [
16
+ "gramps>=6.0.0",
17
+ "gedcom7>=0.3.0",
18
+ ]
19
+
20
+ [project.urls]
21
+ "Homepage" = "https://github.com/DavidMStraub/gramps-gedcom7"
22
+
23
+ [tool.setuptools]
24
+ packages = ["gramps_gedcom7"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ from gramps_gedcom7.importer import import_gedcom
2
+
3
+ from gramps.gen.db.utils import make_database
4
+
5
+ def test_importer_maximal70():
6
+ # Test the import_gedcom function with a maximal GEDCOM 7.0 file
7
+ gedcom_file = "test/data/maximal70.ged"
8
+ db = make_database("sqlite")
9
+ db.load(":memory:")
10
+ import_gedcom(gedcom_file, db)