diff --git a/GeneanetForGramps/GeneanetForGramps.gpr.py b/GeneanetForGramps/GeneanetForGramps.gpr.py new file mode 100644 index 000000000..fc5f3daf8 --- /dev/null +++ b/GeneanetForGramps/GeneanetForGramps.gpr.py @@ -0,0 +1,44 @@ +# +# Copyright (C) 2020 Bruno Cornec +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# $Id: $ + +#------------------------------------------------------------------------ +#------------------------------------------------------------------------ + +#print('Before registering Geneanet Plugin') +register(TOOL, + id = 'Import Geneanet data for Gramps', + name = _("Import Geneanet data for Gramps"), + #name_accell = _("Geneanet for Gramps"), + description = _("Extension to import data from Geneanet into Gramps."), + version = '1.0.0', + gramps_target_version = '5.1', + status = STABLE, + fname = 'GeneanetForGramps.py', + authors = ['Bruno Cornec'], + authors_email = ['bruno@flossita.org'], + category = TOOL_DBPROC, + toolclass = 'GeneanetForGramps', + optionclass = 'GeneanetForGrampsOptions', + tool_modes = [TOOL_MODE_GUI], + #height = 100, + #expand=True, + #gramplet = 'GeneanetForGramps', + #gramplet_title=_("Import Geneanet data for Gramps"), + #detached_width = 500, + #detached_height = 400, + #help_url = "5.1_Addons#Addon_List", + #navtypes=["Person"], +) +#print('After registering Geneanet Plugin') diff --git a/GeneanetForGramps/GeneanetForGramps.py b/GeneanetForGramps/GeneanetForGramps.py new file mode 100755 index 000000000..819ca7d3e --- /dev/null +++ b/GeneanetForGramps/GeneanetForGramps.py @@ -0,0 +1,1859 @@ +#!/usr/bin/python3 +# +# GeneanetForGramps +# +# Copyright (C) 2020 Bruno Cornec +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the Affero GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +# $Id: $ + +""" +Geneanet Import Tool +Import into Gramps persons from Geneanet +""" +#------------------------------------------------------------------------- +# +# Used Python Modules +# +#------------------------------------------------------------------------- +import os +import time +import io +import sys +import re +import random +from lxml import html +import requests +import argparse +from datetime import datetime + +#------------------------------------------------------------------------ +# +# GTK modules +# +#------------------------------------------------------------------------ +from gi.repository import Gtk +from gi.repository import GObject + +from gramps.gen.const import GRAMPS_LOCALE as glocale +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.gettext + +#------------------------------------------------------------------------ +# +# Gramps modules +# +#------------------------------------------------------------------------ +import logging +from gramps.gen.config import config +from gramps.gen.db import DbTxn +from gramps.gen.dbstate import DbState +from gramps.cli.grampscli import CLIManager +from gramps.gen.lib import Person, Name, Surname, NameType, Event, EventType, Date, Place, EventRoleType, EventRef, PlaceName, Family, ChildRef, FamilyRelType, Url, UrlType + +# Gramps GUI +from gramps.gen.const import URL_MANUAL_PAGE +from gramps.gen.display.name import displayer as name_displayer +from gramps.gui.managedwindow import ManagedWindow +from gramps.gui.display import display_help +from gramps.gui.plug import MenuToolOptions, PluginWindows +from gramps.gen.plug.menu import StringOption, PersonOption, BooleanOption, NumberOption, FilterOption, MediaOption +from gramps.gui.utils import ProgressMeter + +LOG = logging.getLogger("GeneanetForGramps") + +TIMEOUT = 5 + +# TODO: Is it useful ? +LANGUAGES = { + 'cs' : 'Czech', 'da' : 'Danish','nl' : 'Dutch', + 'en' : 'English','eo' : 'Esperanto', 'fi' : 'Finnish', + 'fr' : 'French', 'de' : 'German', 'hu' : 'Hungarian', + 'it' : 'Italian', 'lt' : 'Latvian', 'lv' : 'Lithuanian', + 'no' : 'Norwegian', 'po' : 'Polish', 'pt' : 'Portuguese', + 'ro' : 'Romanian', 'sk' : 'Slovak', 'es' : 'Spanish', + 'sv' : 'Swedish', 'ru' : 'Russian', + } +WIKI_HELP_PAGE = '%s_-_Tools' % URL_MANUAL_PAGE +WIKI_HELP_SEC = _('manual|GeneanetForGramps') + +# Global variables +db = None +gname = None +verbosity = 0 +force = False +ascendants = False +descendants = False +spouses = False +LEVEL = 2 +ROOTURL = 'https://gw.geneanet.org/' +GUIMODE = False +progress = None + +CONFIG_NAME = "geneanetforgramps" +CONFIG = config.register_manager(CONFIG_NAME) +CONFIG.register("pref.ascendants", ascendants) +CONFIG.register("pref.descendants", descendants) +CONFIG.register("pref.spouses", spouses) +CONFIG.register("pref.level", LEVEL) +CONFIG.register("pref.force", force) +CONFIG.register("pref.verbosity", verbosity) +CONFIG.load() + +def save_config(): + CONFIG.set("pref.ascendants", ascendants) + CONFIG.set("pref.descendants", descendants) + CONFIG.set("pref.spouses", spouses) + CONFIG.set("pref.level", LEVEL) + CONFIG.set("pref.force", force) + CONFIG.set("pref.verbosity", verbosity) + CONFIG.save() + +save_config() + +# Generic functions + +def format_ca(date): + """ + Change the 'ca' chain into the 'vers' chain for now in Geneanet analysis + """ + # If ca for an about date, replace with vers (for now) + if date[0:2] == "ca": + date = _("about")+date[2:] + return(date) + +def format_year(date): + """ + Remove potential empty month/day coming from Gramps (00) + """ + if not date: + return(date) + if (date[-6:] == "-00-00"): + return(date[0:-6]) + else: + return(date) + +def format_iso(date_tuple): + """ + Format an iso date. + """ + year, month, day = date_tuple + # Format with a leading 0 if needed + month = str(month).zfill(2) + day = str(day).zfill(2) + if year == None or year == 0: + iso_date = '' + elif month == None or month == 0: + iso_date = str(year) + elif day == None or day == 0: + iso_date = '%s-%s' % (year, month) + else: + iso_date = '%s-%s-%s' % (year, month, day) + return iso_date + +def format_noniso(date_tuple): + """ + Format an non-iso tuple into an iso date + """ + day, month, year = date_tuple + return(format_iso(year, month, day)) + +def convert_date(datetab): + ''' Convert the Geneanet date format for birth/death/married lines + into an ISO date format + ''' + + if verbosity >= 3: + print(_("datetab received:"),datetab) + + if len(datetab) == 0: + return(None) + idx = 0 + if datetab[0] == 'en': + # avoid a potential month + if datetab[1].isalpha(): + return(datetab[2][0:4]) + # avoid a potential , after the year + elif datetab[1].isnumeric(): + return(datetab[1][0:4]) + if (datetab[0][0:2] == _("about")[0:2] or datetab[0][0:2] == _("after")[0:2] or datetab[0][0:2] == _("before")[0:2]) and (len(datetab) == 2): + return(datetab[0]+" "+datetab[1][0:4]) + # In case of french language remove the 'le' prefix + if datetab[0] == 'le': + idx = 1 + # In case of french language remove the 'er' prefix + if datetab[idx] == "1er": + datetab[idx] = "1" + bd1 = datetab[idx]+" "+datetab[idx+1]+" "+datetab[idx+2][0:4] + bd2 = datetime.strptime(bd1, "%d %B %Y") + return(bd2.strftime("%Y-%m-%d")) + +# GUI Part +class GeneanetForGrampsOptions(MenuToolOptions): + """ + Defines options and provides handling interface. + """ + def __init__(self, name, person_id=None, dbstate=None): + """ Initialize the options class """ + if verbosity >= 3: + print(_("Init Plugin Options")) + MenuToolOptions.__init__(self, name, person_id, dbstate) + + def add_menu_options(self, menu): + """ + Add all menu options to the tool window. + """ + if verbosity >= 3: + print(_("Add Plugin Menu Options")) + #category_name = _("Options") + category_name = _("Geneanet Import Options") + + self.__pid = PersonOption(_("Center Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + + if verbosity >= 3: + print(_("Before URL")) + self.__gui_url = StringOption(_("Geneanet URL for the selected person"), ROOTURL) + self.__gui_url.set_help(_("URL on Geneanet of the person you have selected which will be used as an import base such as https://gw.geneanet.org/agnesy?lang=fr&n=queffelec&oc=17&p=marie+anne")) + menu.add_option(category_name, "gui_url", self.__gui_url) + + if verbosity >= 3: + print(_("Before ASC")) + gui_asc = CONFIG.get('pref.ascendants') + if verbosity >= 3: + if gui_asc: + print(_("ASC True")) + else: + print(_("ASC False")) + self.__gui_asc = BooleanOption(_("Import ascendants"), gui_asc) + self.__gui_asc.set_help(_("Import ascendants of the selected person up to level number")) + menu.add_option(category_name, "gui_asc", self.__gui_asc) + + if verbosity >= 3: + print(_("Before DSC")) + gui_dsc = CONFIG.get('pref.descendants') + if verbosity >= 3: + if gui_dsc: + print(_("DSC True")) + else: + print(_("DSC False")) + self.__gui_dsc = BooleanOption(_("Import descendants"), gui_dsc) + self.__gui_dsc.set_help(_("Import descendants of the selected person up to level number")) + menu.add_option(category_name, "gui_dsc", self.__gui_dsc) + + if verbosity >= 3: + print(_("Before SPO")) + gui_spo = CONFIG.get('pref.spouses') + if verbosity >= 3: + if gui_spo: + print(_("SPO True")) + else: + print(_("SPO False")) + self.__gui_spo = BooleanOption(_("Import spouses"), gui_spo) + self.__gui_spo.set_help(_("Import all spouses of the selected person")) + menu.add_option(category_name, "gui_spo", self.__gui_spo) + + if verbosity >= 3: + print(_("Before LVL")) + gui_lvl = CONFIG.get('pref.level') + if verbosity >= 3: + print(_("LVL:"), gui_lvl) + self.__gui_level = NumberOption(_("Level of Import"), gui_lvl, 1, 100) + self.__gui_level.set_help(_("Maximum of upper or lower search done in the family tree - keep it small")) + menu.add_option(category_name, "gui_level", self.__gui_level) + + if verbosity >= 3: + print(_("Before FORCE")) + gui_force = CONFIG.get('pref.force') + if verbosity >= 3: + if gui_force: + print(_("FORCE True")) + else: + print(_("FORCE False")) + self.__gui_force = BooleanOption(_("Force Import"), gui_force) + self.__gui_force.set_help(_("Force import of existing persons")) + menu.add_option(category_name, "gui_force", self.__gui_force) + + if verbosity >= 3: + print(_("Before VRB")) + gui_verb = CONFIG.get('pref.verbosity') + if verbosity >= 3: + print(_("VRB:"), gui_verb) + self.__gui_verb = NumberOption(_("Verbosity"), gui_verb, 0, 3) + self.__gui_verb.set_help(_("Verbosity level from 0 (minimal) to 3 (very verbose)")) + menu.add_option(category_name, "gui_verb", self.__gui_verb) + + if verbosity >= 3: + print(_("Menu Added")) + +class GeneanetForGramps(PluginWindows.ToolManagedWindowBatch): + """ + Plugin that gives simplified interface to the import from Geneanet + """ + def __init__(self, dbstate, user, options_class, name, callback): + if verbosity >= 3: + print(_("Init Plugin itself")) + PluginWindows.ToolManagedWindowBatch.__init__(self, dbstate, user, options_class, name, callback) + + def get_title(self): + if verbosity >= 3: + print(_("Plugin get_title")) + return _("Geneanet Import Tool") # tool window title + + def initial_frame(self): + if verbosity >= 3: + print(_("Plugin initial_frame")) + return _("Geneanet Import Options") # tab title + + def run(self): + """ + Main function running the Geneanet Import Tool + """ + global db + global GUIMODE + global progress + + if verbosity >= 3: + print(_("Plugin run")) + db = self.dbstate.db + self.__get_menu_options() + hdr = _('Importing from %s for user %s') % (self.purl,self.gid) + msg = _('Geneanet Import into Gramps') + progress = ProgressMeter(msg, hdr) + #progress = ProgressMeter(msg, hdr, False, None, True, None) + progress.set_pass(hdr,100,mode=ProgressMeter.MODE_ACTIVITY) + if verbosity >= 2: + print(msg) + GUIMODE = True + g2gaction(self.gid,self.purl) + + def __get_menu_options(self): + """ + General menu option processing. + """ + global force + global ascendants + global descendants + global spouses + global LEVEL + global verbosity + if verbosity >= 3: + print(_("Plugin __get_menu_options")) + + menu = self.options.menu + + self.gid = self.options.menu.get_option_by_name('pid').get_value() + if verbosity >= 3: + print(_("GID:"),self.gid) + self.purl = self.options.menu.get_option_by_name('gui_url').get_value() + if verbosity >= 3: + print(_("URL:"),self.purl) + force = self.options.menu.get_option_by_name('gui_force').get_value() + ascendants = self.options.menu.get_option_by_name('gui_asc').get_value() + if verbosity >= 3: + if ascendants: + print(_("ASC True")) + else: + print(_("ASC False")) + descendants = self.options.menu.get_option_by_name('gui_dsc').get_value() + spouses = self.options.menu.get_option_by_name('gui_spo').get_value() + LEVEL = self.options.menu.get_option_by_name('gui_level').get_value() + verbosity = self.options.menu.get_option_by_name('gui_verb').get_value() + if verbosity >= 3: + print(_("LVL:"),LEVEL) + save_config() + +class GBase: + + def __init__(self): + pass + + def _smartcopy(self,attr): + ''' + Smart Copying an attribute from geneanet (g_ attrs) into attr + Works for GPerson and GFamily + ''' + if verbosity >= 3: + print(_("Smart Copying Attributes"),attr) + + # By default do not copy as Gramps is the master reference + scopy = False + + # Find the case where copy is to be done + # Nothing yet + if not self.__dict__[attr]: + scopy = True + + # Empty field + if self.__dict__[attr] and self.__dict__[attr] == "" and self.__dict__['g_'+attr] and self.__dict__['g_'+attr] != "": + scopy = True + + # Force the copy + if self.__dict__[attr] != self.__dict__['g_'+attr] and force: + scopy = True + + # Managing sex, Gramps is always right except when unknown + # Warn on conflict + if attr == 'sex' and self.__dict__[attr] == 'U' and self.__dict__['g_'+attr] != 'U': + scopy = True + if (self.__dict__[attr] == 'F' and self.__dict__['g_'+attr] == 'M') \ + or (self.__dict__[attr] == 'M' and self.__dict__['g_'+attr] == 'F'): + if verbosity >= 1: + print(_("WARNING: Gender conflict between Geneanet (%s) and Gramps (%s), keeping Gramps value")%(self.__dict__['g_'+attr],self.__dict__[attr])) + scopy = False + + if attr == 'lastname' and self.__dict__[attr] != self.__dict__['g_'+attr]: + if verbosity >= 1 and self.__dict__[attr] != "": + print(_("WARNING: Lastname conflict between Geneanet (%s) and Gramps (%s), keeping Gramps value")%(self.__dict__['g_'+attr],self.__dict__[attr])) + if attr == 'lastname' and self.__dict__[attr] == "": + scopy = True + + if attr == 'firstname' and self.__dict__[attr] != self.__dict__['g_'+attr]: + if verbosity >= 1 and self.__dict__[attr] != "": + print(_("WARNING: Firstname conflict between Geneanet (%s) and Gramps (%s), keeping Gramps value")%(self.__dict__['g_'+attr],self.__dict__[attr])) + if attr == 'firstname' and self.__dict__[attr] == "": + scopy = True + + # Copy only if code is more precise + match = re.search(r'code$', attr) + if match: + if not self.__dict__[attr]: + scopy = True + else: + if not self.__dict__['g_'+attr]: + scopy = False + else: + if int(self.__dict__[attr]) < int(self.__dict__['g_'+attr]): + scopy = True + + # Copy only if date is more precise + match = re.search(r'date$', attr) + if match: + if not self.__dict__[attr]: + scopy = True + else: + if not self.__dict__['g_'+attr]: + scopy = False + else: + if self.__dict__[attr] == "" and self.__dict__['g_'+attr] != "": + scopy = True + elif self.__dict__[attr] < self.__dict__['g_'+attr]: + scopy = True + + if scopy: + if verbosity >= 2: + print(_("Copying Person attribute %s (former value %s newer value %s)")%(attr, self.__dict__[attr],self.__dict__['g_'+attr])) + + self.__dict__[attr] = self.__dict__['g_'+attr] + else: + if verbosity >= 3: + print(_("Not Copying Person attribute (%s, value %s) onto %s")%(attr, self.__dict__[attr],self.__dict__['g_'+attr])) + + + def get_or_create_place(self,event,placename): + ''' + Create Place for Events or get an existing one based on the name + ''' + try: + pl = event.get_place_handle() + except: + place = Place() + return(place) + + if pl: + try: + place = db.get_place_from_handle(pl) + if verbosity >= 2: + print(_("Reuse Place from Event:"), place.get_name().value) + except: + place = Place() + else: + if placename == None: + place = Place() + return(place) + keep = None + # Check whether our place already exists + for handle in db.get_place_handles(): + pl = db.get_place_from_handle(handle) + explace = pl.get_name().value + if verbosity >= 4: + print(_("DEBUG: search for ")+str(placename)+_(" in ")+str(explace)) + if str(explace) == str(placename): + keep = pl + break + if keep == None: + if verbosity >= 2: + print(_("Create Place:"), placename) + place = Place() + else: + if verbosity >= 2: + print(_("Reuse existing Place:"), placename) + place = keep + return(place) + + def get_or_create_event(self,gobj,attr,tran): + ''' + Create Birth and Death Events for a person + and Marriage Events for a family or get an existing one + self is GPerson or GFamily + gobj is a gramps object Person or Family + ''' + + event = None + # Manages name indirection for person + if gobj.__class__.__name__ == 'Person': + role = EventRoleType.PRIMARY + func = getattr(gobj,'get_'+attr+'_ref') + reffunc = func() + if reffunc: + event = db.get_event_from_handle(reffunc.ref) + if verbosity >= 2: + print(_("Existing ")+attr+_(" Event")) + elif gobj.__class__.__name__ == 'Family': + role = EventRoleType.FAMILY + if attr == 'marriage': + marev = None + for event_ref in gobj.get_event_ref_list(): + event = db.get_event_from_handle(event_ref.ref) + if (event.get_type() == EventType.MARRIAGE and + (event_ref.get_role() == EventRoleType.FAMILY or + event_ref.get_role() == EventRoleType.PRIMARY)): + marev = event + if marev: + event = marev + if verbosity >= 2: + print(_("Existing ")+attr+_(" Event")) + else: + print(_("ERROR: Unable to handle class %s in get_or_create_all_event")%(gobj.__class__.__name__)) + + if event is None: + event = Event() + uptype = getattr(EventType,attr.upper()) + event.set_type(EventType(uptype)) + event.set_description('Imported from Geaneanet') + db.add_event(event,tran) + + eventref = EventRef() + eventref.set_role(role) + eventref.set_reference_handle(event.get_handle()) + if gobj.__class__.__name__ == 'Person': + func = getattr(gobj,'set_'+attr+'_ref') + reffunc = func(eventref) + db.commit_event(event,tran) + db.commit_person(gobj,tran) + elif gobj.__class__.__name__ == 'Family': + eventref.set_role(EventRoleType.FAMILY) + gobj.add_event_ref(eventref) + if attr == 'marriage': + gobj.set_relationship(FamilyRelType(FamilyRelType.MARRIED)) + db.commit_event(event,tran) + db.commit_family(gobj,tran) + if verbosity >= 2: + print(_("Creating ")+attr+" ("+str(uptype)+") "+_("Event")) + + if self.__dict__[attr+'date'] \ + or self.__dict__[attr+'place'] \ + or self.__dict__[attr+'placecode'] : + # Get or create the event date + date = event.get_date_object() + if self.__dict__[attr+'date']: + idx = 0 + mod = Date.MOD_NONE + if self.__dict__[attr+'date'][0:2] == _("about")[0:2]: + idx = 1 + mod = Date.MOD_ABOUT + elif self.__dict__[attr+'date'][0:2] == _("before")[0:2]: + idx = 1 + mod = Date.MOD_BEFORE + elif self.__dict__[attr+'date'][0:2] == _("after")[0:2]: + idx = 1 + mod = Date.MOD_AFTER + # Only in case of french language analysis + elif self.__dict__[attr+'date'][0:2] == _("in")[0:2]: + idx = 1 + else: + pass + if idx == 1: + # we need to removed the first word + string = self.__dict__[attr+'date'].split(' ',1)[1] + else: + string = self.__dict__[attr+'date'] + # ISO string, put in a tuple, reversed + tab = string.split('-') + if len(tab) == 3: + date.set_yr_mon_day(int(tab[0]),int(tab[1]),int(tab[2])) + elif len(tab) == 2: + date.set_yr_mon_day(int(tab[0]),int(tab[1]),0) + elif len(tab) == 1: + date.set_year(int(tab[0])) + elif len(tab) == 0: + print(_("WARNING: Trying to affect an empty date")) + pass + else: + print(_("WARNING: Trying to affect an extra numbered date")) + pass + if mod: + date.set_modifier(mod) + if verbosity >= 2 and self.__dict__[attr+'date']: + print(_("Update ")+attr+_(" Date to ")+self.__dict__[attr+'date']) + event.set_date_object(date) + db.commit_event(event,tran) + + if self.__dict__[attr+'place'] \ + or self.__dict__[attr+'placecode'] : + if self.__dict__[attr+'place']: + placename = self.__dict__[attr+'place'] + else: + placename = "" + place = self.get_or_create_place(event,placename) + # TODO: Here we overwrite any existing value. + # Check whether that can be a problem + place.set_name(PlaceName(value=placename)) + if self.__dict__[attr+'placecode']: + place.set_code(self.__dict__[attr+'placecode']) + db.add_place(place,tran) + event.set_place_handle(place.get_handle()) + db.commit_event(event,tran) + + db.commit_event(event,tran) + return + + def get_gramps_date(self,evttype): + ''' + Give back the date of the event related to the GPerson or GFamily + as a string ISO formated + ''' + + if verbosity >= 4: + print(_("EventType: %d")%(evttype)) + + if not self: + return(None) + + if evttype == EventType.BIRTH: + ref = self.grampsp.get_birth_ref() + elif evttype == EventType.DEATH: + ref = self.grampsp.get_death_ref() + elif evttype == EventType.MARRIAGE: + eventref = None + for eventref in self.family.get_event_ref_list(): + event = db.get_event_from_handle(eventref.ref) + if (event.get_type() == EventType.MARRIAGE + and (eventref.get_role() == EventRoleType.FAMILY + or eventref.get_role() == EventRoleType.PRIMARY)): + break + ref = eventref + else: + print(_("Didn't find a known EventType: "),evttype) + return(None) + + if ref: + if verbosity >= 4: + print(_("Ref:"),ref) + try: + event = db.get_event_from_handle(ref.ref) + except: + print(_("Didn't find a known ref for this ref date: "),ref) + return(None) + if event: + if verbosity >= 4: + print(_("Event")+":",event) + date = event.get_date_object() + moddate = date.get_modifier() + tab = date.get_dmy() + if verbosity >= 4: + print(_("Found date: "),tab) + if len(tab) == 3: + tab = date.get_ymd() + if verbosity >= 4: + print(_("Found date2: "),tab) + ret = format_iso(tab) + else: + ret = format_noniso(tab) + if moddate == Date.MOD_BEFORE: + pref = _("before")+" " + elif moddate == Date.MOD_AFTER: + pref = _("after")+" " + elif moddate == Date.MOD_ABOUT: + pref = _("about")+" " + else: + pref = "" + if verbosity >= 3: + print(_("Returned date: ")+pref+ret) + return(pref+ret) + else: + return(None) + else: + return(None) + + +class GFamily(GBase): + ''' + Family as seen by Gramps and Geneanet + ''' + def __init__(self,father,mother): + # The 2 GPersons parents in this family should exist + # and properties filled before we create the family + # Gramps properties + self.marriagedate = None + self.marriageplace = None + self.marriageplacecode = None + self.gid = None + # Pointer to the Gramps Family instance + self.family = None + # Geneanet properties + self.g_marriagedate = None + self.g_marriageplace = None + self.g_marriageplacecode = None + self.g_childref = [] + + if verbosity >= 1: + print(_("Creating GFamily: ")+father.firstname+" "+father.lastname+" - "+mother.firstname+" "+mother.lastname) + self.url = father.url + if self.url == "": + self.url = mother.url + # TODO: what if father or mother is None + self.father = father + self.mother = mother + + def create_grampsf(self): + ''' + Create a Family in Gramps and return it + ''' + with DbTxn("Geneanet import", db) as tran: + grampsf = Family() + db.add_family(grampsf,tran) + self.gid = grampsf.gramps_id + self.family = grampsf + if verbosity >= 2: + print(_("Create new Gramps Family: ")+self.gid) + + def find_grampsf(self): + ''' + Find a Family in Gramps and return it + ''' + if verbosity >= 2: + print(_("Look for a Gramps Family")) + f = None + ids = db.get_family_gramps_ids() + for i in ids: + f = db.get_family_from_gramps_id(i) + if verbosity >= 3: + print(_("Analysing Gramps Family ")+f.gramps_id) + # Do these people already form a family + father = None + fh = f.get_father_handle() + if fh: + father = db.get_person_from_handle(fh) + mother = None + mh = f.get_mother_handle() + if mh: + mother = db.get_person_from_handle(mh) + if verbosity >= 3: + if not father: + fgid = None + else: + fgid = father.gramps_id + if not fgid: + fgid = "None" + sfgid = self.father.gid + if not sfgid: + sfgid = "None" + print(_("Check father ids: ")+fgid+_(" vs ")+sfgid) + if not mother: + mgid = None + else: + mgid = mother.gramps_id + if not mgid: + mgid = "None" + smgid = self.mother.gid + if not smgid: + smgid = "None" + print(_("Check mother ids: ")+mgid+_(" vs ")+smgid) + if self.father and father and father.gramps_id == self.father.gid \ + and self.mother and mother and mother.gramps_id == self.mother.gid: + return(f) + #TODO: What about preexisting families not created in this run ? + return(None) + + def from_geneanet(self): + ''' + Initiate the GFamily from Geneanet data + ''' + # Once we get the right spouses, then we can have the marriage info + idx = 0 + for sr in self.father.spouseref: + if verbosity >= 3: + print(_("Comparing sr %s to %s (idx: %d)")%(sr,self.mother.url,idx)) + if sr == self.mother.url: + if verbosity >= 2: + print(_("Spouse %s found (idx: %d)")%(sr,idx)) + break + idx = idx + 1 + + if idx < len(self.father.spouseref): + # We found one + self.g_marriagedate = self.father.marriagedate[idx] + self.g_marriageplace = self.father.marriageplace[idx] + self.g_marriageplacecode = self.father.marriageplacecode[idx] + for c in self.father.childref[idx]: + self.g_childref.append(c) + + if self.g_marriagedate and self.g_marriageplace and self.g_marriageplacecode: + if verbosity >= 2: + print(_("Geneanet Marriage found the %s at %s (%s)")%(self.g_marriagedate,self.g_marriageplace,self.g_marriageplacecode)) + + + def from_gramps(self,gid): + ''' + Initiate the GFamily from Gramps data + ''' + if verbosity >= 2: + print(_("Calling from_gramps with gid: %s")%(gid)) + + # If our gid was already setup and we didn't pass one + if not gid and self.gid: + gid = self.gid + + if verbosity >= 2: + print(_("Now gid is: %s")%(gid)) + + found = None + try: + found = db.get_family_from_gramps_id(gid) + self.gid = gid + self.family = found + if verbosity >= 2: + print(_("Existing gid of a Gramps Family: %s")%(self.gid)) + except: + if verbosity >= 1: + print(_("WARNING: Unable to retrieve id %s from the gramps db %s")%(gid,gname)) + + if not found: + # If we don't know which family this is, try to find it in Gramps + # This supposes that Geneanet data are already present in GFamily + self.family = self.find_grampsf() + if self.family: + if verbosity >= 2: + print(_("Found an existing Gramps family ")+self.family.gramps_id) + self.gid = self.family.gramps_id + # And if we haven't found it, create it in gramps + if self.family == None: + self.create_grampsf() + + if self.family: + self.marriagedate = self.get_gramps_date(EventType.MARRIAGE) + if self.marriagedate == "": + self.marriagedate = None + for eventref in self.family.get_event_ref_list(): + event = db.get_event_from_handle(eventref.ref) + if (event.get_type() == EventType.MARRIAGE + and (eventref.get_role() == EventRoleType.FAMILY + or eventref.get_role() == EventRoleType.PRIMARY)): + place = self.get_or_create_place(event,None) + self.marriageplace = place.get_name().value + self.marriageplacecode = place.get_code() + break + + if verbosity >= 2: + if self.marriagedate and self.marriageplace and self.marriageplacecode: + print(_("Gramps Marriage found the %s at %s (%s)")%(self.marriagedate,self.marriageplace,self.marriageplacecode)) + + def to_gramps(self): + ''' + ''' + # Smart copy from Geneanet to Gramps inside GFamily + self.smartcopy() + with DbTxn("Geneanet import", db) as tran: + # When it's not the case create the family + if self.family == None: + self.family = Family() + db.add_family(self.family,tran) + + try: + grampsp0 = db.get_person_from_gramps_id(self.father.gid) + except: + if verbosity >= 2: + print(_("No father for this family")) + grampsp0 = None + + if grampsp0: + try: + self.family.set_father_handle(grampsp0.get_handle()) + except: + if verbosity >= 2: + print(_("Can't affect father to the family")) + + db.commit_family(self.family,tran) + grampsp0.add_family_handle(self.family.get_handle()) + db.commit_person(grampsp0,tran) + + try: + grampsp1 = db.get_person_from_gramps_id(self.mother.gid) + except: + if verbosity >= 2: + print(_("No mother for this family")) + grampsp1 = None + + if grampsp1: + try: + self.family.set_mother_handle(grampsp1.get_handle()) + except: + if verbosity >= 2: + print(_("Can't affect mother to the family")) + + db.commit_family(self.family,tran) + grampsp1.add_family_handle(self.family.get_handle()) + db.commit_person(grampsp1,tran) + + # Now celebrate the marriage ! (if needed) + self.get_or_create_event(self.family,'marriage',tran) + + def smartcopy(self): + ''' + Smart Copying GFamily + ''' + if verbosity >= 2: + print(_("Smart Copying Family")) + self._smartcopy("marriagedate") + self._smartcopy("marriageplace") + self._smartcopy("marriageplacecode") + + def add_child(self,child): + ''' + Adds a child GPerson child to the GFamily + ''' + found = None + i = 0 + # Avoid handling already processed children in Gramps + for cr in self.family.get_child_ref_list(): + c = db.get_person_from_handle(cr.ref) + if c.gramps_id == child.gid: + found = child + if verbosity >= 1: + print(_("Child already existing : ")+child.firstname+" "+child.lastname) + break + # Ensure that the child is part of the family + + if not found: + if child: + if verbosity >= 2: + print(_("Adding child: ")+child.firstname+" "+child.lastname) + childref = ChildRef() + if child.grampsp: + try: + childref.set_reference_handle(child.grampsp.get_handle()) + except: + if verbosity >= 2: + print(_("No handle for this child")) + self.family.add_child_ref(childref) + with DbTxn("Geneanet import", db) as tran: + db.commit_family(self.family,tran) + child.grampsp.add_parent_family_handle(self.family.get_handle()) + db.commit_person(child.grampsp,tran) + + def recurse_children(self,level): + ''' + analyze recursively the children of the GFamily passed in parameter + ''' + try: + cpt = len(self.g_childref) + except: + if verbosity >= 1: + print(_("Stopping exploration as there are no more children for family ")+self.fater.firstname+" "+self.father.lastname+" - "+self.mother.firstname+" "+self.mother.lastname) + return + loop = False + # Recurse while we have children urls and level not reached + if level <= LEVEL and (cpt > 0): + loop = True + level = level + 1 + + if not self.family: + print(_("WARNING: No family found whereas there should be one :-(")) + return + + # Create a GPerson from all children mentioned in Geneanet + for c in self.g_childref: + child = geneanet_to_gramps(None,level-1,None,c) + if verbosity >= 2: + print(_("=> Recursion on the child of ")+self.father.lastname+' - '+self.mother.lastname+': '+child.firstname+' '+child.lastname) + self.add_child(child) + + fam = [] + if spouses: + fam = child.add_spouses(level) + if ascendants: + for f in fam: + if child.sex == 'M': + f.mother.recurse_parents(level-1) + if child.sex == 'F': + f.father.recurse_parents(level-1) + if descendants: + for f in fam: + f.recurse_children(level) + + if verbosity >= 2: + print(_("=> End of recursion on the child of ")+self.father.lastname+' - '+self.mother.lastname+': '+child.firstname+' '+child.lastname) + + if not loop: + if cpt == 0: + if verbosity >= 1: + print(_("Stopping exploration for family ")+self.father.firstname+" "+self.father.lastname+' - '+self.mother.firstname+" "+self.mother.lastname+_(" as there are no more children")) + return + + if level > LEVEL: + if verbosity >= 1: + print(_("Stopping exploration for family ")+self.father.firstname+" "+self.father.lastname+' - '+self.mother.firstname+" "+self.mother.lastname+_(" as we reached level ")+str(level)) + return + +class GPerson(GBase): + ''' + Generic Person common between Gramps and Geneanet + ''' + def __init__(self,level): + if verbosity >= 3: + print(_("Initialize Person at level %d")%(level)) + # Counter + self.level = level + # Gramps + self.firstname = "" + self.lastname = "" + self.sex = 'U' + self.birthdate = None + self.birthplace = None + self.birthplacecode = None + self.deathdate = None + self.deathplace = None + self.deathplacecode = None + self.gid = None + self.grampsp = None + # Father and Mother and Spouses GPersons + self.father = None + self.mother = None + self.spouse = [] + # GFamilies + self.family = [] + # Geneanet + self.g_firstname = "" + self.g_lastname = "" + self.g_sex = 'U' + self.g_birthdate = None + self.g_birthplace = None + self.g_birthplacecode = None + self.g_deathdate = None + self.g_deathplace = None + self.g_deathplacecode = None + self.url = "" + self.spouseref = [] + self.fref = "" + self.mref = "" + self.marriagedate = [] + self.marriageplace = [] + self.marriageplacecode = [] + self.childref = [] + + def smartcopy(self): + ''' + Smart Copying GPerson + ''' + if verbosity >= 2: + print(_("Smart Copying Person"),self.gid) + self._smartcopy("firstname") + self._smartcopy("lastname") + self._smartcopy("sex") + self._smartcopy("birthdate") + self._smartcopy("birthplace") + self._smartcopy("birthplacecode") + self._smartcopy("deathdate") + self._smartcopy("deathplace") + self._smartcopy("deathplacecode") + + def from_geneanet(self,purl): + ''' Use XPath to retrieve the details of a person + Used example from https://gist.github.com/IanHopkinson/ad45831a2fb73f537a79 + and doc from https://www.w3schools.com/xml/xpath_axes.asp + and https://docs.python-guide.org/scenarios/scrape/ + + lxml can return _ElementUnicodeResult instead of str so cast + ''' + + # Needed as Geneanet returned relative links + headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'} + + + if verbosity >= 3: + print(_("Purl:"),purl) + if not purl: + return() + try: + if verbosity >= 1: + print("-----------------------------------------------------------") + print(_("Page considered:"),purl) + page = requests.get(purl) + if verbosity >= 3: + print(_("Return code:"),page.status_code) + except: + print(_("We failed to reach the server at"),purl) + else: + if page.ok: + try: + tree = html.fromstring(page.content) + except: + print(_("Unable to perform HTML analysis")) + + self.url = purl + # Wait after a Genanet request to be fair with the site + # between 2 and 7 seconds + time.sleep(random.randint(2,7)) + try: + # Should return M or F + sex = tree.xpath('//div[@id="person-title"]//img/attribute::alt') + self.g_sex = sex[0] + # Seems we have a french codification on the site + if sex[0] == 'H': + self.g_sex = 'M' + except: + self.g_sex = 'U' + try: + name = tree.xpath('//div[@id="person-title"]//a/text()') + self.g_firstname = str(name[0]) + self.g_lastname = str(name[1]) + except: + self.g_firstname = "" + self.g_lastname = "" + if verbosity >= 1: + print(_("==> GENEANET Name (L%d): %s %s")%(self.level,self.g_firstname,self.g_lastname)) + if verbosity >= 2: + print(_("Sex:"), self.g_sex) + try: + sstring = '//li[contains(., "'+_("Born")+'")]/text()' + if verbosity >= 3: + print("sstring: "+sstring) + birth = tree.xpath(sstring) + except: + birth = [""] + if verbosity >= 3: + print(_("birth")+": %s"%(birth)) + try: + sstring = '//li[contains(., "'+_("Deceased")+'")]/text()' + if verbosity >= 3: + print("sstring: "+sstring) + death = tree.xpath(sstring) + except: + death = [""] + if verbosity >= 3: + print(_("death")+": %s"%(death)) + try: + # sometime parents are using circle, somtimes disc ! + parents = tree.xpath('//ul[not(descendant-or-self::*[@class="fiche_union"])]//li[@style="vertical-align:middle;list-style-type:disc" or @style="vertical-align:middle;list-style-type:circle"]') + except: + parents = [] + try: + spouses = tree.xpath('//ul[@class="fiche_union"]/li') + except: + spouses = [] + try: + ld = convert_date(birth[0].split('-')[0].split()[1:]) + if verbosity >= 2: + print(_("Birth:"), ld) + self.g_birthdate = format_ca(ld) + except: + self.g_birthdate = None + try: + self.g_birthplace = str(' '.join(birth[0].split('-')[1:]).split(',')[0].strip()) + if verbosity >= 2: + print(_("Birth place:"), self.g_birthplace) + except: + self.g_birthplace = None + try: + self.g_birthplacecode = str(' '.join(birth[0].split('-')[1:]).split(',')[1]).strip() + match = re.search(r'\d\d\d\d\d', self.g_birthplacecode) + if not match: + self.g_birthplacecode = None + else: + if verbosity >= 2: + print(_("Birth place code:"), self.g_birthplacecode) + except: + self.g_birthplacecode = None + try: + ld = convert_date(death[0].split('-')[0].split()[1:]) + if verbosity >= 2: + print(_("Death:"), ld) + self.g_deathdate = format_ca(ld) + except: + self.g_deathdate = None + try: + self.g_deathplace = str(' '.join(death[0].split('-')[1:]).split(',')[0]).strip() + if verbosity >= 2: + print(_("Death place:"), self.g_deathplace) + except: + self.g_deathplace = None + try: + self.g_deathplacecode = str(' '.join(death[0].split('-')[1:]).split(',')[1]).strip() + match = re.search(r'\d\d\d\d\d', self.g_deathplacecode) + if not match: + self.g_deathplacecode = None + else: + if verbosity >= 2: + print(_("Death place code:"), self.g_deathplacecode) + except: + self.g_deathplacecode = None + + s = 0 + sname = [] + sref = [] + marriage = [] + for spouse in spouses: + try: + sname.append(str(spouse.xpath('a/text()')[0])) + if verbosity >= 2: + print(_("Spouse name:"), sname[s]) + except: + sname.append("") + + try: + sref.append(str(spouse.xpath('a/attribute::href')[0])) + if verbosity >= 2: + print(_("Spouse ref:"), ROOTURL+sref[s]) + except: + sref.append("") + self.spouseref.append(ROOTURL+sref[s]) + + try: + marriage.append(str(spouse.xpath('em/text()')[0])) + except: + marriage.append(None) + try: + ld = convert_date(marriage[s].split(',')[0].split()[1:]) + if verbosity >= 2: + print(_("Married:"), ld) + self.marriagedate.append(format_ca(ld)) + except: + self.marriagedate.append(None) + try: + self.marriageplace.append(str(marriage[s].split(',')[1][1:])) + if verbosity >= 2: + print(_("Married place:"), self.marriageplace[s]) + except: + self.marriageplace.append(None) + try: + marriageplacecode = str(marriage[s].split(',')[2][1:]) + match = re.search(r'\d\d\d\d\d', marriageplacecode) + if not match: + self.marriageplacecode.append(None) + else: + if verbosity >= 2: + print(_("Married place code:"), self.marriageplacecode[s]) + self.marriageplacecode.append(marriageplacecode) + except: + self.marriageplacecode.append(None) + + cnum = 0 + clist = [] + for c in spouse.xpath('ul/li'): + try: + cname = c.xpath('a/text()')[0] + if verbosity >= 2: + print(_("Child %d name: %s")%(cnum,cname)) + except: + cname = None + try: + cref = ROOTURL+str(c.xpath('a/attribute::href')[0]) + if verbosity >= 2: + print(_("Child %d ref: %s")%(cnum,cref)) + except: + cref = None + clist.append(cref) + cnum = cnum + 1 + self.childref.append(clist) + s = s + 1 + # End spouse loop + + self.fref = "" + self.mref = "" + prefl = [] + for p in parents: + if verbosity >= 3: + print(p.xpath('text()')) + if p.xpath('text()')[0] == '\n': + try: + pname = p.xpath('a/text()')[0] + except: + pname = "" + # if pname is ? ? then go to next one + try: + pref = p.xpath('a/attribute::href')[0] + except: + pref = "" + if verbosity >= 1: + print(_("Parent name: %s (%s)")%(pname,ROOTURL+pref)) + prefl.append(ROOTURL+str(pref)) + try: + self.fref = prefl[0] + except: + self.fref = "" + try: + self.mref = prefl[1] + except: + self.mref = "" + if verbosity >= 2: + print("-----------------------------------------------------------") + + else: + print(_("We failed to be ok with the server")) + + + def create_grampsp(self): + ''' + Create a Person in Gramps and return it + ''' + with DbTxn("Geneanet import", db) as tran: + grampsp = Person() + db.add_person(grampsp,tran) + self.gid = grampsp.gramps_id + self.grampsp = grampsp + if verbosity >= 1: + print(_("Create new Gramps Person: ")+self.gid+' ('+self.g_firstname+' '+self.g_lastname+')') + + + def find_grampsp(self): + ''' + Find a Person in Gramps and return it + The parameter precises the relationship with our person + ''' + p = None + ids = db.get_person_gramps_ids() + for i in ids: + if verbosity >= 3: + print(_("DEBUG: Looking after ")+i) + p = db.get_person_from_gramps_id(i) + try: + name = p.primary_name.get_name().split(', ') + except: + continue + if len(name) == 0: + continue + elif len(name) == 1: + name.append(None) + if name[0]: + lastname = name[0] + else: + lastname = "" + if name[1]: + firstname = name[1] + else: + firstname = "" + # Assumption it's the right one + self.grampsp = p + bd = self.get_gramps_date(EventType.BIRTH) + # Remove empty month/day if needed to compare below with just a year potentially + bd = format_year(bd) + dd = self.get_gramps_date(EventType.DEATH) + dd = format_year(dd) + if verbosity >= 3: + print(_("DEBUG: firstname: ")+firstname+_(" vs g_firstname: ")+self.g_firstname) + print(_("DEBUG: lastname: ")+lastname+_(" vs g_lastname: ")+self.g_lastname) + if not bd: + pbd = "None" + else: + pbd = bd + if not dd: + pdd = "None" + else: + pdd = dd + if not self.g_birthdate: + g_pbd = "None" + else: + g_pbd = self.g_birthdate + if not self.g_deathdate: + g_pdd = "None" + else: + g_pdd = self.g_deathdate + print(_("DEBUG: bd: ")+pbd+_(" vs g_bd: ")+g_pbd) + print(_("DEBUG: dd: ")+pdd+_(" vs g_dd: ")+g_pdd) + if firstname != self.g_firstname or lastname != self.g_lastname: + # it's not the right person finally + self.grampsp = None + continue + if not bd and not dd and not self.g_birthdate and not self.g_deathdate: + # we skip a person for which we have no date at all + # this may create duplicates, but is the best apparoach + self.grampsp = None + continue + if bd == self.g_birthdate or dd == self.g_deathdate: + self.gid = p.gramps_id + if verbosity >= 2: + print(_("Found a Gramps Person: ")+self.g_firstname+' '+self.g_lastname+ " ("+self.gid+")") + # Found it we can exit + break + else: + # still not found + self.grampsp = None + + def to_gramps(self): + ''' + Push into Gramps the GPerson + ''' + + # Smart copy from Geneanet to Gramps inside GPerson + self.smartcopy() + + with DbTxn("Geneanet import", db) as tran: + db.disable_signals() + grampsp = self.grampsp + if not grampsp: + if verbosity >= 2: + print(_("ERROR: Unable sync unknown Gramps Person")) + return + + if self.sex == 'M': + grampsp.set_gender(Person.MALE) + elif self.sex == 'F': + grampsp.set_gender(Person.FEMALE) + else: + grampsp.set_gender(Person.UNKNOWN) + + n = Name() + n.set_type(NameType(NameType.BIRTH)) + n.set_first_name(self.firstname) + s = n.get_primary_surname() + s.set_surname(self.lastname) + grampsp.set_primary_name(n) + + # We need to create events for Birth and Death + for ev in ['birth', 'death']: + self.get_or_create_event(grampsp,ev,tran) + + # Store the importation place as an Internet note + if self.url != "": + found = False + for u in grampsp.get_url_list(): + if u.get_type() == UrlType.WEB_HOME \ + and u.get_path() == self.url: + found = True + if not found: + url = Url() + url.set_description("Imported from Geneanet") + url.set_type(UrlType.WEB_HOME) + url.set_path(self.url) + grampsp.add_url(url) + + db.commit_person(grampsp,tran) + db.enable_signals() + db.request_rebuild() + + def from_gramps(self,gid): + ''' + Fill a GPerson with its Gramps data + ''' + + GENDER = ['F', 'M', 'U'] + + if verbosity >= 2: + print(_("Calling from_gramps with gid: %s")%(gid)) + + # If our gid was already setup and we didn't pass one + if not gid and self.gid: + gid = self.gid + + if verbosity >= 3: + print(_("Now gid is: %s")%(gid)) + + found = None + try: + found = db.get_person_from_gramps_id(gid) + self.gid = gid + self.grampsp = found + if verbosity >= 2 and self.gid: + print(_("Existing Gramps Person: %s")%(self.gid)) + except: + if verbosity >= 1: + print(_("WARNING: Unable to retrieve id %s from the gramps db %s")%(gid,gname)) + + if not found: + # If we don't know who this is, try to find it in Gramps + # This supposes that Geneanet data are already present in GPerson + self.find_grampsp() + # And if we haven't found it, create it in gramps + if self.grampsp == None: + self.create_grampsp() + + if self.grampsp.gender: + self.sex = GENDER[self.grampsp.gender] + if verbosity >= 2: + print(_("Gender:"),self.sex) + + try: + name = self.grampsp.primary_name.get_name().split(', ') + except: + name = [None, None] + + if name[0]: + self.firstname = name[1] + if name[1]: + self.lastname = name[0] + if verbosity >= 2: + print(_("===> Gramps Name of %s: %s %s")%(self.gid,self.firstname,self.lastname)) + + try: + bd = self.get_gramps_date(EventType.BIRTH) + if bd: + if verbosity >= 2: + print(_("Birth:"),bd) + self.birthdate = bd + else: + if verbosity >= 2: + print(_("No Birth date")) + except: + if verbosity >= 1: + print(_("WARNING: Unable to retrieve birth date for id %s")%(self.gid)) + + try: + dd = self.get_gramps_date(EventType.DEATH) + if dd: + if verbosity >= 2: + print(_("Death:"),dd) + self.deathdate = dd + else: + if verbosity >= 2: + print(_("No Death date")) + except: + if verbosity >= 1: + print(_("WARNING: Unable to retrieve death date for id %s")%(self.gid)) + + # Deal with the parents now, as they necessarily exist + self.father = GPerson(self.level+1) + self.mother = GPerson(self.level+1) + try: + fh = self.grampsp.get_main_parents_family_handle() + if fh: + if verbosity >= 3: + print(_("Family:"),fh) + fam = db.get_family_from_handle(fh) + if fam: + if verbosity >= 3: + print(_("Family:"),fam) + + # find father from the family + fh = fam.get_father_handle() + if fh: + if verbosity >= 3: + print(_("Father H:"),fh) + father = db.get_person_from_handle(fh) + if father: + if verbosity >= 1: + print(_("Father name:"),father.primary_name.get_name()) + self.father.gid = father.gramps_id + + # find mother from the family + mh = fam.get_mother_handle() + if mh: + if verbosity >= 3: + print(_("Mother H:"),mh) + mother = db.get_person_from_handle(mh) + if mother: + if verbosity >= 1: + print(_("Mother name:"),mother.primary_name.get_name()) + self.mother.gid = mother.gramps_id + + except: + if verbosity >= 1: + print(_("NOTE: Unable to retrieve family for id %s")%(self.gid)) + + def add_spouses(self,level): + ''' + Add all spouses for this person, with corresponding families + returns all the families created in a list + ''' + i = 0 + ret = [] + while i < len(self.spouseref): + spouse = None + # Avoid handling already processed spouses + for s in self.spouse: + if s.url == self.spouseref[i]: + spouse = s + break + if not spouse: + spouse = geneanet_to_gramps(None,level,None,self.spouseref[i]) + if spouse: + self.spouse.append(spouse) + spouse.spouse.append(self) + # Create a GFamily with them and do a Geaneanet to Gramps for it + if verbosity >= 2: + print(_("=> Initialize Family of ")+self.firstname+" "+self.lastname+" & "+spouse.firstname+" "+spouse.lastname) + if self.sex == 'M': + f = GFamily(self,spouse) + elif self.sex == 'F': + f = GFamily(spouse,self) + else: + if verbosity >= 1: + print(_("Unable to Initialize Family of ")+self.firstname+" "+self.lastname+_(" sex unknown")) + break + + f.from_geneanet() + f.from_gramps(f.gid) + f.to_gramps() + self.family.append(f) + if spouse: + spouse.family.append(f) + ret.append(f) + i = i + 1 + return(ret) + + def recurse_parents(self,level): + ''' + analyze the parents of the person passed in parameter recursively + ''' + loop = False + # Recurse while we have parents urls and level not reached + if level <= LEVEL and (self.fref != "" or self.mref != ""): + loop = True + level = level + 1 + + if self.father: + geneanet_to_gramps(self.father,level,self.father.gid,self.fref) + if self.mother: + self.mother.spouse.append(self.father) + + if verbosity >= 2: + print(_("=> Recursing on the parents of ")+self.father.firstname+" "+self.father.lastname) + self.father.recurse_parents(level) + + if verbosity >= 2: + print(_("=> End of recursion on the parents of ")+self.father.firstname+" "+self.father.lastname) + + if self.mother: + geneanet_to_gramps(self.mother,level,self.mother.gid,self.mref) + if self.father: + self.father.spouse.append(self.mother) + if verbosity >= 2: + print(_("=> Recursing on the mother of ")+self.mother.firstname+" "+self.mother.lastname) + self.mother.recurse_parents(level) + + if verbosity >= 2: + print(_("=> End of recursing on the mother of ")+self.mother.firstname+" "+self.mother.lastname) + + # Create a GFamily with them and do a Geaneanet to Gramps for it + if verbosity >= 2: + print(_("=> Initialize Parents Family of ")+self.firstname+" "+self.lastname) + f = GFamily(self.father,self.mother) + f.from_geneanet() + f.from_gramps(f.gid) + f.to_gramps() + if self.father: + self.father.family.append(f) + if self.mother: + self.mother.family.append(f) + + # Deal with other spouses + if spouses: + fam = self.father.add_spouses(level) + if ascendants: + for ff in fam: + if ff.gid != f.gid: + ff.mother.recurse_parents(level) + if descendants: + for ff in fam: + if ff.gid != f.gid: + ff.recurse_children(level) + fam = self.mother.add_spouses(level) + if ascendants: + for mf in fam: + if mf.gid != f.gid: + mf.father.recurse_parents(level) + if descendants: + for mf in fam: + if mf.gid != f.gid: + mf.recurse_children(level) + + + # Now do what is needed depending on options + if descendants: + f.recurse_children(level) + else: + f.add_child(self) + + if not loop: + if level > LEVEL: + if verbosity >= 2: + print(_("Stopping exploration as we reached level ")+str(level)) + else: + if verbosity >= 1: + print(_("Stopping exploration as there are no more parents")) + return + + +def geneanet_to_gramps(p, level, gid, url): + ''' + Function to create a person from Geneanet into gramps + ''' + global progress + + # Create the Person coming from Geneanet + if not p: + p = GPerson(level) + p.from_geneanet(url) + + # Create the Person coming from Gramps + # Done after so we can try to find it in Gramps with the Geneanet data + p.from_gramps(gid) + + # Check we point to the same person + if gid != None: + if (p.firstname != p.g_firstname or p.lastname != p.g_lastname) and (not force): + print(_("Gramps person: %s %s")%(p.firstname,p.lastname)) + print(_("Geneanet person: %s %s")%(p.g_firstname,p.g_lastname)) + if not GUIMODE: + db.close() + sys.exit(_("Do not continue without force")) + else: + return(None) + + # Fix potential empty dates + if p.g_birthdate == "": + p.g_birthdate = None + if p.birthdate == "": + p.birthdate = None + if p.g_deathdate == "": + p.g_deathdate = None + if p.deathdate == "": + p.deathdate = None + + if p.birthdate == p.g_birthdate or p.deathdate == p.g_deathdate or force: + pass + else: + print(_("Gramps person birth/death: %s / %s")%(p.birthdate,p.deathdate)) + print(_("Geneanet person birth/death: %s / %s")%(p.g_birthdate,p.g_deathdate)) + if not GUIMODE: + db.close() + sys.exit(_("Do not continue without force")) + else: + print(_("Please fix the person in gramps")) + return(None) + + # Copy from Geneanet into Gramps and commit + p.to_gramps() + if GUIMODE: + progress.set_header(_("Adding Gramps Person %s %s (%s | %s)")%(p.firstname,p.lastname,p.birthdate,p.deathdate)) + progress.step() + return(p) + +def g2gaction(gid,purl): + global progress + + # Create the first Person + gp = geneanet_to_gramps(None,0,gid,purl) + + if gp != None: + if ascendants: + gp.recurse_parents(0) + + fam = [] + if spouses: + fam = gp.add_spouses(0) + else: + # TODO: If we don't ask for spouses, we won't get children at all + pass + + if descendants: + for f in fam: + f.recurse_children(0) + if GUIMODE: + progress.close() + +def main(): + + # global allow local modification of these global variables + global db + global gname + global verbosity + global force + global ascendants + global descendants + global spouses + global LEVEL + + + parser = argparse.ArgumentParser(description=_("Import Geneanet subtrees into Gramps")) + parser.add_argument("-v", "--verbosity", action="count", default=0, help=_("Increase verbosity")) + parser.add_argument("-a", "--ascendants", default=False, action='store_true', help=_("Includes ascendants (off by default)")) + parser.add_argument("-d", "--descendants", default=False, action='store_true', help=_("Includes descendants (off by default)")) + parser.add_argument("-s", "--spouses", default=False, action='store_true', help=_("Includes all spouses (off by default)")) + parser.add_argument("-l", "--level", default=2, type=int, help=_("Number of level to explore (2 by default)")) + parser.add_argument("-g", "--grampsfile", type=str, help=_("Full path of the Gramps database (under $HOME/.gramps/grampsdb)")) + parser.add_argument("-i", "--id", type=str, help=_("ID of the person to start from in Gramps")) + parser.add_argument("-f", "--force", default=False, action='store_true', help=_("Force processing")) + parser.add_argument("searchedperson", type=str, nargs='?', help=_("Url of the person to search in Geneanet")) + args = parser.parse_args() + + if args.searchedperson == None: + #purl = 'https://gw.geneanet.org/agnesy?lang=fr&pz=hugo+mathis&nz=renard&p=marie+sebastienne&n=helgouach' + #purl = 'https://gw.geneanet.org/agnesy?lang=fr&n=queffelec&oc=17&p=marie+anne' + print(_("Please provide a person to search for")) + sys.exit(-1) + else: + purl = args.searchedperson + + gname = args.grampsfile + verbosity = args.verbosity + force = args.force + ascendants = args.ascendants + descendants = args.descendants + spouses = args.spouses + LEVEL = args.level + + # TODO: do a backup before opening and remove fixed path + if gname == None: + #gname = "Test import" + # To be searched in ~/.gramps/recent-files-gramps.xml + #gname = "/users/bruno/.gramps/grampsdb/5ec17554" + print(_("Please provide a grampsfile to search into")) + sys.exit(-1) + try: + dbstate = DbState() + climanager = CLIManager(dbstate, True, None) + climanager.open_activate(gname) + db = dbstate.db + except: + ErrorDialog(_("Opening the '%s' database") % gname, + _("An attempt to convert the database failed. " + "Perhaps it needs updating."), parent=self.top) + sys.exit(-1) + + gid = args.id + if gid == None: + gid = "0000" + gid = "I"+gid + + ids = db.get_person_gramps_ids() + for i in ids: + if verbosity >= 3: + print(_("DEBUG: existing gramps id:")+i) + + if verbosity >= 1 and force: + print(_("WARNING: Force mode activated")) + time.sleep(TIMEOUT) + + g2gaction(gid,purl) + + db.close() + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/GeneanetForGramps/LICENSE b/GeneanetForGramps/LICENSE new file mode 100644 index 000000000..b617af905 --- /dev/null +++ b/GeneanetForGramps/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/GeneanetForGramps/MANIFEST b/GeneanetForGramps/MANIFEST new file mode 100644 index 000000000..32b0fd127 --- /dev/null +++ b/GeneanetForGramps/MANIFEST @@ -0,0 +1,4 @@ +LICENSE +po/* +README.md +GeneanetForGramps.png diff --git a/GeneanetForGramps/README.md b/GeneanetForGramps/README.md new file mode 100644 index 000000000..c730bc0e8 --- /dev/null +++ b/GeneanetForGramps/README.md @@ -0,0 +1,4 @@ +# Plugin and script GeneanetForGramps + +Plugin to import into Gramps a person from Geneanet +Works also as a standalone script diff --git a/GeneanetForGramps/po/fr-local.po b/GeneanetForGramps/po/fr-local.po new file mode 100644 index 000000000..eead8cf80 --- /dev/null +++ b/GeneanetForGramps/po/fr-local.po @@ -0,0 +1,933 @@ +# French translation for Gramps +# This file is distributed under the same license as the Gramps package. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# Quelques variables pour la cohérence: +# ancestor : ascendant +# descendant : descendant +# person, individual, people : individu(s) +# remove (context) : enlever une référence ou supprimer de la base +# home : souche +# active person : individu actif +# media : media (pas de 's' ou accent) : un medium +# les médias : journaux, tv +# call name : prénom usuel +# unknown (context) : inconnu ou non-connu (individu) +# log : journal +# addon : greffon +# regular expressions : expressions rationnelles +# Gramps ID = Identifiant Gramps +# Please... = Veuillez... +# bug : bogue +# afficher / masquer +# sélectionner +# +# Traits d'union: +# non-substantif +# non adjectif +# +# +# Laurent Protois , 2001-2004. +# Matthieu Pupat , 2004, 2005. +# Guillaume Pratte , 2005. +# Frederic Chateaux, 2007-2008. +# Jérôme Rapinat , 2005-2017. +# Mathieu MD , 2012. +# Christophe , 2018. +# Gil da Costa , 2019. +# Patrick Gerlier , 2020. +# geryonesime , 2020. +msgid "" +msgstr "" +"Project-Id-Version: 5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-12-05 19:04+0100\n" +"PO-Revision-Date: 2020-04-30 07:10+0100\n" +"Last-Translator: \n" +"Language-Team: français <>\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.5.4\n" + +#: GeneanetForGramps/GeneanetForGramps.gpr.py:22 +msgid "Import Geneanet data for Gramps" +msgstr "Import des données Geneanet dans Gramps" + +#: GeneanetForGramps/GeneanetForGramps.gpr.py:24 +msgid "Extension to import data from Geneanet into Gramps." +msgstr "Extension pour importer les données de Geneanet dans Gramps" + +#: GeneanetForGramps/GeneanetForGramps.py:91 +msgid "manual|GeneanetForGramps" +msgstr "manuel|GeneanetForGramps" + +#: GeneanetForGramps/GeneanetForGramps.py:135 +#: GeneanetForGramps/GeneanetForGramps.py:192 +#: GeneanetForGramps/GeneanetForGramps.py:576 +#: GeneanetForGramps/GeneanetForGramps.py:692 +msgid "about" +msgstr "vers" + +#: GeneanetForGramps/GeneanetForGramps.py:180 +msgid "datetab received:" +msgstr "datetab reçue:" + +#: GeneanetForGramps/GeneanetForGramps.py:192 +#: GeneanetForGramps/GeneanetForGramps.py:582 +#: GeneanetForGramps/GeneanetForGramps.py:690 +msgid "after" +msgstr "après" + +#: GeneanetForGramps/GeneanetForGramps.py:192 +#: GeneanetForGramps/GeneanetForGramps.py:579 +#: GeneanetForGramps/GeneanetForGramps.py:688 +msgid "before" +msgstr "avant" + +#: GeneanetForGramps/GeneanetForGramps.py:212 +msgid "Init Plugin Options" +msgstr "Initialisation du greffon - Options" + +#: GeneanetForGramps/GeneanetForGramps.py:220 +msgid "Add Plugin Menu Options" +msgstr "Ajout des options du menu du greffon" + +#: GeneanetForGramps/GeneanetForGramps.py:222 +#: GeneanetForGramps/GeneanetForGramps.py:320 +msgid "Geneanet Import Options" +msgstr "Options de l'import Geneanet" + +#: GeneanetForGramps/GeneanetForGramps.py:224 +msgid "Center Person" +msgstr "Centrer sur l'individu" + +#: GeneanetForGramps/GeneanetForGramps.py:225 +msgid "The center person for the filter" +msgstr "Individu central pour ce filtre" + +#: GeneanetForGramps/GeneanetForGramps.py:229 +msgid "Before URL" +msgstr "Avant URL" + +#: GeneanetForGramps/GeneanetForGramps.py:230 +msgid "Geneanet URL for the selected person" +msgstr "URL Geneanet pour l'individu sélectionné" + +#: GeneanetForGramps/GeneanetForGramps.py:231 +msgid "" +"URL on Geneanet of the person you have selected which will be used as an " +"import base such as https://gw.geneanet.org/agnesy?" +"lang=fr&n=queffelec&oc=17&p=marie+anne" +msgstr "" +"URL sur Geneanet de l'individu que vous avez sélectionnée et qui sera " +"utilisée comme base d'importation telle que https://gw.geneanet.org/agnesy?" +"lang=fr&n=queffelec&oc=17&p=marie+anne" + +#: GeneanetForGramps/GeneanetForGramps.py:235 +msgid "Before ASC" +msgstr "Avant ASC" + +#: GeneanetForGramps/GeneanetForGramps.py:239 +#: GeneanetForGramps/GeneanetForGramps.py:369 +msgid "ASC True" +msgstr "ASC vrai" + +#: GeneanetForGramps/GeneanetForGramps.py:241 +#: GeneanetForGramps/GeneanetForGramps.py:371 +msgid "ASC False" +msgstr "ASC faux" + +#: GeneanetForGramps/GeneanetForGramps.py:242 +msgid "Import ascendants" +msgstr "Importation des ascendants" + +#: GeneanetForGramps/GeneanetForGramps.py:243 +msgid "Import ascendants of the selected person up to level number" +msgstr "" +"Importation des ascendants de l'individu sélectioné jusqu'au niveau indiqué" + +#: GeneanetForGramps/GeneanetForGramps.py:247 +msgid "Before DSC" +msgstr "Avant DSC" + +#: GeneanetForGramps/GeneanetForGramps.py:251 +msgid "DSC True" +msgstr "DSC vrai" + +#: GeneanetForGramps/GeneanetForGramps.py:253 +msgid "DSC False" +msgstr "DSC faux" + +#: GeneanetForGramps/GeneanetForGramps.py:254 +msgid "Import descendants" +msgstr "Importation des descendants" + +#: GeneanetForGramps/GeneanetForGramps.py:255 +msgid "Import descendants of the selected person up to level number" +msgstr "" +"Importation des descendants de l'individu sélectioné jusqu'au niveau indiqué" + +#: GeneanetForGramps/GeneanetForGramps.py:259 +msgid "Before SPO" +msgstr "Avant SPO" + +#: GeneanetForGramps/GeneanetForGramps.py:263 +msgid "SPO True" +msgstr "SPO vrai" + +#: GeneanetForGramps/GeneanetForGramps.py:265 +msgid "SPO False" +msgstr "SPO faux" + +#: GeneanetForGramps/GeneanetForGramps.py:266 +msgid "Import spouses" +msgstr "Importation des conjoints" + +#: GeneanetForGramps/GeneanetForGramps.py:267 +msgid "Import all spouses of the selected person" +msgstr "Importation de tous les conjoints de l'individu sélectionné" + +#: GeneanetForGramps/GeneanetForGramps.py:271 +msgid "Before LVL" +msgstr "Avant LVL" + +#: GeneanetForGramps/GeneanetForGramps.py:274 +#: GeneanetForGramps/GeneanetForGramps.py:377 +msgid "LVL:" +msgstr "LVL:" + +#: GeneanetForGramps/GeneanetForGramps.py:275 +msgid "Level of Import" +msgstr "Niveau de l'import" + +#: GeneanetForGramps/GeneanetForGramps.py:276 +msgid "" +"Maximum of upper or lower search done in the family tree - keep it small" +msgstr "" +"Niveau maximum de recherche effectuée dans l'arbre familial sur les " +"ascendants et descendants - gardez le petit" + +#: GeneanetForGramps/GeneanetForGramps.py:280 +msgid "Before FORCE" +msgstr "Avant FORCE" + +#: GeneanetForGramps/GeneanetForGramps.py:284 +msgid "FORCE True" +msgstr "FORCE vrai" + +#: GeneanetForGramps/GeneanetForGramps.py:286 +msgid "FORCE False" +msgstr "FORCE faux" + +#: GeneanetForGramps/GeneanetForGramps.py:287 +msgid "Force Import" +msgstr "Force l'importation" + +#: GeneanetForGramps/GeneanetForGramps.py:288 +msgid "Force import of existing persons" +msgstr "Force l'importation d'individus existants" + +#: GeneanetForGramps/GeneanetForGramps.py:292 +msgid "Before VRB" +msgstr "Avant VRB" + +#: GeneanetForGramps/GeneanetForGramps.py:295 +msgid "VRB:" +msgstr "VRB:" + +#: GeneanetForGramps/GeneanetForGramps.py:296 +msgid "Verbosity" +msgstr "Verbosité" + +#: GeneanetForGramps/GeneanetForGramps.py:297 +msgid "Verbosity level from 0 (minimal) to 3 (very verbose)" +msgstr "Niveau de verbosité de 0 (minimum) à 3 (maximum)" + +#: GeneanetForGramps/GeneanetForGramps.py:301 +msgid "Menu Added" +msgstr "Menu ajouté" + +#: GeneanetForGramps/GeneanetForGramps.py:309 +msgid "Init Plugin itself" +msgstr "Initialisation du greffon lui-même" + +#: GeneanetForGramps/GeneanetForGramps.py:314 +msgid "Plugin get_title" +msgstr "Greffon get_title" + +#: GeneanetForGramps/GeneanetForGramps.py:315 +msgid "Geneanet Import Tool" +msgstr "Outil d'import de Geneanet" + +#: GeneanetForGramps/GeneanetForGramps.py:319 +msgid "Plugin initial_frame" +msgstr "Greffon initial_frame" + +#: GeneanetForGramps/GeneanetForGramps.py:331 +msgid "Plugin run" +msgstr "Exécution du greffon" + +#: GeneanetForGramps/GeneanetForGramps.py:334 +#, python-format +msgid "Importing from %s for user %s" +msgstr "Import de %s pour l'utilisateur %s" + +#: GeneanetForGramps/GeneanetForGramps.py:335 +msgid "Geneanet Import into Gramps" +msgstr "Import de Geneanet dans Gramps" + +#: GeneanetForGramps/GeneanetForGramps.py:355 +msgid "Plugin __get_menu_options" +msgstr "Greffon __get_menu_options" + +#: GeneanetForGramps/GeneanetForGramps.py:361 +msgid "GID:" +msgstr "GID:" + +#: GeneanetForGramps/GeneanetForGramps.py:364 +msgid "URL:" +msgstr "URL :" + +#: GeneanetForGramps/GeneanetForGramps.py:391 +msgid "Smart Copying Attributes" +msgstr "Copy futée d'attributs" + +#: GeneanetForGramps/GeneanetForGramps.py:416 +#, python-format +msgid "" +"WARNING: Gender conflict between Geneanet (%s) and Gramps (%s), keeping " +"Gramps value" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:421 +#, python-format +msgid "" +"WARNING: Lastname conflict between Geneanet (%s) and Gramps (%s), keeping " +"Gramps value" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:427 +#, python-format +msgid "" +"WARNING: Firstname conflict between Geneanet (%s) and Gramps (%s), keeping " +"Gramps value" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:459 +#, python-format +msgid "Copying Person attribute %s (former value %s newer value %s)" +msgstr "" +"Copie de l'attribut %s de l'individu (valeur précédente : %s, nouvelle " +"valeur : %s)" + +#: GeneanetForGramps/GeneanetForGramps.py:464 +#, python-format +msgid "Not Copying Person attribute (%s, value %s) onto %s" +msgstr "Pas de copie de l'attribut (%s, valeur %s) sur %s" + +#: GeneanetForGramps/GeneanetForGramps.py:481 +msgid "Reuse Place from Event:" +msgstr "Réutilise le lieu de l'événement:" + +#: GeneanetForGramps/GeneanetForGramps.py:494 +msgid "DEBUG: search for " +msgstr "DEBUG: Recherche de " + +#: GeneanetForGramps/GeneanetForGramps.py:494 +msgid " in " +msgstr " dans " + +#: GeneanetForGramps/GeneanetForGramps.py:500 +msgid "Create Place:" +msgstr "Creation d'un lieu:" + +#: GeneanetForGramps/GeneanetForGramps.py:504 +msgid "Reuse existing Place:" +msgstr "Réutilisation d'un lieu existant:" + +#: GeneanetForGramps/GeneanetForGramps.py:525 +#: GeneanetForGramps/GeneanetForGramps.py:539 +msgid "Existing " +msgstr "Existant " + +#: GeneanetForGramps/GeneanetForGramps.py:525 +#: GeneanetForGramps/GeneanetForGramps.py:539 +msgid " Event" +msgstr " Événement" + +#: GeneanetForGramps/GeneanetForGramps.py:541 +#, python-format +msgid "ERROR: Unable to handle class %s in get_or_create_all_event" +msgstr "ERREUR: Impossible de gérer la classe %s dans get_or_create_all_event" + +#: GeneanetForGramps/GeneanetForGramps.py:566 +msgid "Creating " +msgstr "Création de " + +# L'espace finale est pour précéder le « : » codé en dur dans certains contextes. +# /!\ vérifier double espace avec search bar "%(titre colonne)s contient" +#: GeneanetForGramps/GeneanetForGramps.py:566 +#: GeneanetForGramps/GeneanetForGramps.py:674 +msgid "Event" +msgstr "Événement" + +#: GeneanetForGramps/GeneanetForGramps.py:586 +msgid "in" +msgstr "dans" + +#: GeneanetForGramps/GeneanetForGramps.py:604 +msgid "WARNING: Trying to affect an empty date" +msgstr "ATTENTION: Tentative d'affectation d'une date vide" + +#: GeneanetForGramps/GeneanetForGramps.py:607 +msgid "WARNING: Trying to affect an extra numbered date" +msgstr "ATTENTION: Tentative d'affectation d'une date supplémentaire" + +#: GeneanetForGramps/GeneanetForGramps.py:612 +msgid "Update " +msgstr "Mise à jour " + +#: GeneanetForGramps/GeneanetForGramps.py:612 +msgid " Date to " +msgstr "Date pour " + +#: GeneanetForGramps/GeneanetForGramps.py:642 +#, python-format +msgid "EventType: %d" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:661 +msgid "Didn't find a known EventType: " +msgstr "Impossible de trouver un EventType connu: " + +#: GeneanetForGramps/GeneanetForGramps.py:666 +msgid "Ref:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:670 +msgid "Didn't find a known ref for this ref date: " +msgstr "" +"Impossible de trouver une référence connue pour cette référence de date: " + +#: GeneanetForGramps/GeneanetForGramps.py:679 +msgid "Found date: " +msgstr "Date trouvée: " + +#: GeneanetForGramps/GeneanetForGramps.py:683 +msgid "Found date2: " +msgstr "Date trouvée (2):" + +#: GeneanetForGramps/GeneanetForGramps.py:696 +msgid "Returned date: " +msgstr "Date retournée" + +#: GeneanetForGramps/GeneanetForGramps.py:725 +msgid "Creating GFamily: " +msgstr "Creation d'une GFamily: " + +#: GeneanetForGramps/GeneanetForGramps.py:743 +msgid "Create new Gramps Family: " +msgstr "Création d'une nouvelle Famille Gramps: " + +#: GeneanetForGramps/GeneanetForGramps.py:750 +msgid "Look for a Gramps Family" +msgstr "Recherche d'une Famille Gramps" + +#: GeneanetForGramps/GeneanetForGramps.py:756 +msgid "Analysing Gramps Family " +msgstr "Analyze d'une famille Gramps " + +#: GeneanetForGramps/GeneanetForGramps.py:776 +msgid "Check father ids: " +msgstr "Vérification de l'id du père : " + +#: GeneanetForGramps/GeneanetForGramps.py:776 +#: GeneanetForGramps/GeneanetForGramps.py:786 +msgid " vs " +msgstr " en comparaison de " + +#: GeneanetForGramps/GeneanetForGramps.py:786 +msgid "Check mother ids: " +msgstr "Vérification de l'id de la mère : " + +#: GeneanetForGramps/GeneanetForGramps.py:801 +#, python-format +msgid "Comparing sr %s to %s (idx: %d)" +msgstr "Comparaison de la ref conjoint %s vis à vis de %s (idx: %d)" + +#: GeneanetForGramps/GeneanetForGramps.py:804 +#, python-format +msgid "Spouse %s found (idx: %d)" +msgstr "Épou(x|se) %s trouvé(e) (idx: %d)" + +#: GeneanetForGramps/GeneanetForGramps.py:818 +#, python-format +msgid "Geneanet Marriage found the %s at %s (%s)" +msgstr "Mariage Geneanet trouvé à la date du %s à %s (%s)" + +#: GeneanetForGramps/GeneanetForGramps.py:826 +#: GeneanetForGramps/GeneanetForGramps.py:1467 +#, python-format +msgid "Calling from_gramps with gid: %s" +msgstr "Appel de from_gramps avec le gid: %s" + +#: GeneanetForGramps/GeneanetForGramps.py:833 +#: GeneanetForGramps/GeneanetForGramps.py:1474 +#, python-format +msgid "Now gid is: %s" +msgstr "Le gid est maintenant: %s" + +#: GeneanetForGramps/GeneanetForGramps.py:841 +#, python-format +msgid "Existing gid of a Gramps Family: %s" +msgstr "gid existant d'une Famille Gramps: %s" + +#: GeneanetForGramps/GeneanetForGramps.py:844 +#: GeneanetForGramps/GeneanetForGramps.py:1485 +#, python-format +msgid "WARNING: Unable to retrieve id %s from the gramps db %s" +msgstr "ATTENTION: Impossible de trouver l'id %s dans la base gramps %s" + +#: GeneanetForGramps/GeneanetForGramps.py:852 +msgid "Found an existing Gramps family " +msgstr "Famille Gramps existante trouvée " + +#: GeneanetForGramps/GeneanetForGramps.py:874 +#, python-format +msgid "Gramps Marriage found the %s at %s (%s)" +msgstr "Mariage Gramps trouvé le %s à %s (%s)" + +#: GeneanetForGramps/GeneanetForGramps.py:891 +msgid "No father for this family" +msgstr "Pas de père pour cette famille" + +#: GeneanetForGramps/GeneanetForGramps.py:899 +msgid "Can't affect father to the family" +msgstr "Impossible de positionner un père pour cette famille" + +#: GeneanetForGramps/GeneanetForGramps.py:909 +msgid "No mother for this family" +msgstr "Pas de mère pour cette famille" + +#: GeneanetForGramps/GeneanetForGramps.py:917 +msgid "Can't affect mother to the family" +msgstr "Impossible de positionner une mère pour cette famille" + +#: GeneanetForGramps/GeneanetForGramps.py:931 +msgid "Smart Copying Family" +msgstr "Copie futée de Famille" + +#: GeneanetForGramps/GeneanetForGramps.py:948 +msgid "Child already existing : " +msgstr "Enfant existant déjà : " + +#: GeneanetForGramps/GeneanetForGramps.py:955 +msgid "Adding child: " +msgstr "Ajout de l'enfant : " + +#: GeneanetForGramps/GeneanetForGramps.py:962 +msgid "No handle for this child" +msgstr "Pas d'entrée pour cet enfant" + +#: GeneanetForGramps/GeneanetForGramps.py:977 +msgid "Stopping exploration as there are no more children for family " +msgstr "Arrêt de l'exploration car il n'y a plus d'enfants pour cette famille" + +#: GeneanetForGramps/GeneanetForGramps.py:986 +msgid "WARNING: No family found whereas there should be one :-(" +msgstr "" +"ATTENTION: Aucune famille trouvée alors qu'il aurait dû y en avoir une :-(" + +#: GeneanetForGramps/GeneanetForGramps.py:993 +msgid "=> Recursion on the child of " +msgstr "=> Récursivité sur l'enfant de " + +#: GeneanetForGramps/GeneanetForGramps.py:1010 +msgid "=> End of recursion on the child of " +msgstr "=> Fin de récursivité sur l'enfant de " + +#: GeneanetForGramps/GeneanetForGramps.py:1015 +#: GeneanetForGramps/GeneanetForGramps.py:1020 +msgid "Stopping exploration for family " +msgstr "Arrêt de l'exploration pour la famille " + +#: GeneanetForGramps/GeneanetForGramps.py:1015 +msgid " as there are no more children" +msgstr " car il n'y a plus d'enfants" + +#: GeneanetForGramps/GeneanetForGramps.py:1020 +msgid " as we reached level " +msgstr " après atteinte du niveau " + +#: GeneanetForGramps/GeneanetForGramps.py:1029 +#, python-format +msgid "Initialize Person at level %d" +msgstr "Créé l'individu au niveau %d" + +#: GeneanetForGramps/GeneanetForGramps.py:1074 +msgid "Smart Copying Person" +msgstr "Copie futée d'un individu" + +#: GeneanetForGramps/GeneanetForGramps.py:1099 +msgid "Purl:" +msgstr "Purl :" + +#: GeneanetForGramps/GeneanetForGramps.py:1105 +msgid "Page considered:" +msgstr "Page considérée :" + +#: GeneanetForGramps/GeneanetForGramps.py:1108 +msgid "Return code:" +msgstr "Code de retour :" + +#: GeneanetForGramps/GeneanetForGramps.py:1110 +msgid "We failed to reach the server at" +msgstr "Impossible d'atteindre le serveur à" + +#: GeneanetForGramps/GeneanetForGramps.py:1116 +msgid "Unable to perform HTML analysis" +msgstr "Impossible d'effectuer l'analyze HTML" + +#: GeneanetForGramps/GeneanetForGramps.py:1139 +#, python-format +msgid "==> GENEANET Name (L%d): %s %s" +msgstr "==> Nom GENEANET (L%d): %s %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1141 +msgid "Sex:" +msgstr "Sexe :" + +#: GeneanetForGramps/GeneanetForGramps.py:1143 +msgid "Born" +msgstr "Né" + +#: GeneanetForGramps/GeneanetForGramps.py:1150 +msgid "birth" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1152 +msgid "Deceased" +msgstr "Décédé" + +#: GeneanetForGramps/GeneanetForGramps.py:1159 +msgid "death" +msgstr "décès" + +#: GeneanetForGramps/GeneanetForGramps.py:1172 +#: GeneanetForGramps/GeneanetForGramps.py:1516 +msgid "Birth:" +msgstr "Naissance :" + +#: GeneanetForGramps/GeneanetForGramps.py:1179 +msgid "Birth place:" +msgstr "Lieu de naissance :" + +#: GeneanetForGramps/GeneanetForGramps.py:1189 +msgid "Birth place code:" +msgstr "Code du lieu de naissance" + +#: GeneanetForGramps/GeneanetForGramps.py:1195 +#: GeneanetForGramps/GeneanetForGramps.py:1529 +msgid "Death:" +msgstr "Décès :" + +#: GeneanetForGramps/GeneanetForGramps.py:1202 +msgid "Death place:" +msgstr "Lieu de décès :" + +#: GeneanetForGramps/GeneanetForGramps.py:1212 +msgid "Death place code:" +msgstr "Code du lieu de décès :" + +#: GeneanetForGramps/GeneanetForGramps.py:1224 +msgid "Spouse name:" +msgstr "Nom du conjoint" + +#: GeneanetForGramps/GeneanetForGramps.py:1231 +msgid "Spouse ref:" +msgstr "Référence du conjoint :" + +#: GeneanetForGramps/GeneanetForGramps.py:1243 +msgid "Married:" +msgstr "Marié" + +#: GeneanetForGramps/GeneanetForGramps.py:1250 +msgid "Married place:" +msgstr "Lieu de mariage :" + +#: GeneanetForGramps/GeneanetForGramps.py:1260 +msgid "Married place code:" +msgstr "Code du lieu de mariage :" + +#: GeneanetForGramps/GeneanetForGramps.py:1271 +#, python-format +msgid "Child %d name: %s" +msgstr "Enfant %d nom : %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1277 +#, python-format +msgid "Child %d ref: %s" +msgstr "Enfant %d référence : %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1303 +#, python-format +msgid "Parent name: %s (%s)" +msgstr "Nom du parent : %s (%s)" + +#: GeneanetForGramps/GeneanetForGramps.py:1317 +msgid "We failed to be ok with the server" +msgstr "Echec de l'entente avec le serveur" + +#: GeneanetForGramps/GeneanetForGramps.py:1330 +msgid "Create new Gramps Person: " +msgstr "Création dun nouvel individu Gramps : " + +#: GeneanetForGramps/GeneanetForGramps.py:1342 +msgid "DEBUG: Looking after " +msgstr "DEBUG: Recherche " + +#: GeneanetForGramps/GeneanetForGramps.py:1368 +msgid "DEBUG: firstname: " +msgstr "DEBUG : prénom : " + +#: GeneanetForGramps/GeneanetForGramps.py:1368 +msgid " vs g_firstname: " +msgstr " comparé au prénom Gramps : " + +#: GeneanetForGramps/GeneanetForGramps.py:1369 +msgid "DEBUG: lastname: " +msgstr "DEBUG : nom : " + +#: GeneanetForGramps/GeneanetForGramps.py:1369 +msgid " vs g_lastname: " +msgstr " comparé au nom Gramps : " + +#: GeneanetForGramps/GeneanetForGramps.py:1386 +msgid "DEBUG: bd: " +msgstr "DEBUG : bd : " + +#: GeneanetForGramps/GeneanetForGramps.py:1386 +msgid " vs g_bd: " +msgstr " comparé à g_bd : " + +#: GeneanetForGramps/GeneanetForGramps.py:1387 +msgid "DEBUG: dd: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1387 +msgid " vs g_dd: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1400 +msgid "Found a Gramps Person: " +msgstr "Individu Gramps trouvé : " + +#: GeneanetForGramps/GeneanetForGramps.py:1420 +msgid "ERROR: Unable sync unknown Gramps Person" +msgstr "ERREUR : Synchronisation impossible, individu Gramps inconnu" + +#: GeneanetForGramps/GeneanetForGramps.py:1482 +#, python-format +msgid "Existing Gramps Person: %s" +msgstr "Individu Gramps existant : %s" + +# trunk +#: GeneanetForGramps/GeneanetForGramps.py:1498 +msgid "Gender:" +msgstr "Sexe :" + +#: GeneanetForGramps/GeneanetForGramps.py:1510 +#, python-format +msgid "===> Gramps Name of %s: %s %s" +msgstr "===> Nom Gramps de %s : %s %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1520 +msgid "No Birth date" +msgstr "Pas de date de naissance" + +#: GeneanetForGramps/GeneanetForGramps.py:1523 +#, python-format +msgid "WARNING: Unable to retrieve birth date for id %s" +msgstr "ATTENTION : impossible de récupérer la date de naissance pour l'id %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1533 +msgid "No Death date" +msgstr "Pas de date de décès" + +#: GeneanetForGramps/GeneanetForGramps.py:1536 +#, python-format +msgid "WARNING: Unable to retrieve death date for id %s" +msgstr "ATTENTION : impossible de récupérer la date de décès pour l'id %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1545 +#: GeneanetForGramps/GeneanetForGramps.py:1549 +msgid "Family:" +msgstr "Famille :" + +#: GeneanetForGramps/GeneanetForGramps.py:1555 +msgid "Father H:" +msgstr "Lien du père :" + +#: GeneanetForGramps/GeneanetForGramps.py:1559 +msgid "Father name:" +msgstr "Nom du père :" + +#: GeneanetForGramps/GeneanetForGramps.py:1566 +msgid "Mother H:" +msgstr "Lien de la mère :" + +#: GeneanetForGramps/GeneanetForGramps.py:1570 +msgid "Mother name:" +msgstr "Nom de la mère :" + +#: GeneanetForGramps/GeneanetForGramps.py:1575 +#, python-format +msgid "NOTE: Unable to retrieve family for id %s" +msgstr "NOTE: Impossible de trouver une famille d'id %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1598 +msgid "=> Initialize Family of " +msgstr "=> Création d'une famille de " + +#: GeneanetForGramps/GeneanetForGramps.py:1605 +msgid "Unable to Initialize Family of " +msgstr "Impossible de créer une famille de " + +#: GeneanetForGramps/GeneanetForGramps.py:1605 +msgid " sex unknown" +msgstr " sexe inconnu" + +#: GeneanetForGramps/GeneanetForGramps.py:1634 +msgid "=> Recursing on the parents of " +msgstr "=> Récursivité sur les parents de " + +#: GeneanetForGramps/GeneanetForGramps.py:1638 +msgid "=> End of recursion on the parents of " +msgstr "=> Fin de récursivité sur les parents de " + +#: GeneanetForGramps/GeneanetForGramps.py:1645 +msgid "=> Recursing on the mother of " +msgstr "=> Récursivité sur la mère de " + +#: GeneanetForGramps/GeneanetForGramps.py:1649 +msgid "=> End of recursing on the mother of " +msgstr "=> Fin de récursivité sur la mère de " + +#: GeneanetForGramps/GeneanetForGramps.py:1653 +msgid "=> Initialize Parents Family of " +msgstr "Création de la famille des parents de " + +#: GeneanetForGramps/GeneanetForGramps.py:1694 +msgid "Stopping exploration as we reached level " +msgstr "Arrêt de l'exploration, en raison de l'atteinte du niveau " + +#: GeneanetForGramps/GeneanetForGramps.py:1697 +msgid "Stopping exploration as there are no more parents" +msgstr "Arrêt de l'exploration, car il n'y a plus de parents" + +#: GeneanetForGramps/GeneanetForGramps.py:1719 +#, python-format +msgid "Gramps person: %s %s" +msgstr "Individu Gramps : %s %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1720 +#, python-format +msgid "Geneanet person: %s %s" +msgstr "Individu Geneanet : %s %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1723 +#: GeneanetForGramps/GeneanetForGramps.py:1744 +msgid "Do not continue without force" +msgstr "Impossible de continuer sans forcer l'importation" + +#: GeneanetForGramps/GeneanetForGramps.py:1740 +#, python-format +msgid "Gramps person birth/death: %s / %s" +msgstr "Individu Gramps naissance/décès : %s / %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1741 +#, python-format +msgid "Geneanet person birth/death: %s / %s" +msgstr "Individu Geneanets naissance/décès : %s / %s" + +#: GeneanetForGramps/GeneanetForGramps.py:1746 +msgid "Please fix the person in gramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1752 +#, python-format +msgid "Adding Gramps Person %s %s (%s | %s)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1792 +msgid "Import Geneanet subtrees into Gramps" +msgstr "Importation des sous-arbres Genanet dans Gramps" + +#: GeneanetForGramps/GeneanetForGramps.py:1793 +msgid "Increase verbosity" +msgstr "Augmenter la verbosité" + +#: GeneanetForGramps/GeneanetForGramps.py:1794 +msgid "Includes ascendants (off by default)" +msgstr "Inclure les ascendants (désactivé par défaut)" + +#: GeneanetForGramps/GeneanetForGramps.py:1795 +msgid "Includes descendants (off by default)" +msgstr "Inclure les descendants (désactivé par défaut)" + +#: GeneanetForGramps/GeneanetForGramps.py:1796 +msgid "Includes all spouses (off by default)" +msgstr "Inclure les conjoints (désactivé par défaut)" + +#: GeneanetForGramps/GeneanetForGramps.py:1797 +msgid "Number of level to explore (2 by default)" +msgstr "Nombre de niveaux à explorer (2 par défaut)" + +#: GeneanetForGramps/GeneanetForGramps.py:1798 +msgid "Full path of the Gramps database (under $HOME/.gramps/grampsdb)" +msgstr "" +"Chemin complet de la base de données Gramps (sous $HOME/.gramps/grampsdb)" + +#: GeneanetForGramps/GeneanetForGramps.py:1799 +msgid "ID of the person to start from in Gramps" +msgstr "Id de l'individu de départ dans Gramps" + +#: GeneanetForGramps/GeneanetForGramps.py:1800 +msgid "Force processing" +msgstr "Forcer le traitement" + +#: GeneanetForGramps/GeneanetForGramps.py:1801 +msgid "Url of the person to search in Geneanet" +msgstr "URL de l'individu à rechercher sur Geneanet" + +#: GeneanetForGramps/GeneanetForGramps.py:1807 +msgid "Please provide a person to search for" +msgstr "Merci de fournir un individu à rechercher" + +#: GeneanetForGramps/GeneanetForGramps.py:1825 +msgid "Please provide a grampsfile to search into" +msgstr "Merci de fournir une base Gramps pour la recherche" + +# master +#: GeneanetForGramps/GeneanetForGramps.py:1833 +#, python-format +msgid "Opening the '%s' database" +msgstr "Ouverture de la base de données « %s »" + +# master +#: GeneanetForGramps/GeneanetForGramps.py:1834 +msgid "An attempt to convert the database failed. Perhaps it needs updating." +msgstr "" +"La tentative de conversion de la base de donnée a échoué. Une mise à jour " +"est peut-être nécessaire." + +#: GeneanetForGramps/GeneanetForGramps.py:1846 +msgid "DEBUG: existing gramps id:" +msgstr "DEBUG : Id Gramps existant :" + +#: GeneanetForGramps/GeneanetForGramps.py:1849 +msgid "WARNING: Force mode activated" +msgstr "ATTENTION : Mode forcé activé" diff --git a/GeneanetForGramps/po/template.pot b/GeneanetForGramps/po/template.pot new file mode 100644 index 000000000..bbc8c1df5 --- /dev/null +++ b/GeneanetForGramps/po/template.pot @@ -0,0 +1,879 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-12-06 01:28+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: GeneanetForGramps/GeneanetForGramps.gpr.py:22 +msgid "Import Geneanet data for Gramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.gpr.py:24 +msgid "Extension to import data from Geneanet into Gramps." +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:91 +msgid "manual|GeneanetForGramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:135 +#: GeneanetForGramps/GeneanetForGramps.py:192 +#: GeneanetForGramps/GeneanetForGramps.py:576 +#: GeneanetForGramps/GeneanetForGramps.py:692 +msgid "about" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:180 +msgid "datetab received:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:192 +#: GeneanetForGramps/GeneanetForGramps.py:582 +#: GeneanetForGramps/GeneanetForGramps.py:690 +msgid "after" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:192 +#: GeneanetForGramps/GeneanetForGramps.py:579 +#: GeneanetForGramps/GeneanetForGramps.py:688 +msgid "before" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:212 +msgid "Init Plugin Options" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:220 +msgid "Add Plugin Menu Options" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:222 +#: GeneanetForGramps/GeneanetForGramps.py:320 +msgid "Geneanet Import Options" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:224 +msgid "Center Person" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:225 +msgid "The center person for the filter" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:229 +msgid "Before URL" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:230 +msgid "Geneanet URL for the selected person" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:231 +msgid "" +"URL on Geneanet of the person you have selected which will be used as an " +"import base such as https://gw.geneanet.org/agnesy?" +"lang=fr&n=queffelec&oc=17&p=marie+anne" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:235 +msgid "Before ASC" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:239 +#: GeneanetForGramps/GeneanetForGramps.py:369 +msgid "ASC True" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:241 +#: GeneanetForGramps/GeneanetForGramps.py:371 +msgid "ASC False" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:242 +msgid "Import ascendants" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:243 +msgid "Import ascendants of the selected person up to level number" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:247 +msgid "Before DSC" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:251 +msgid "DSC True" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:253 +msgid "DSC False" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:254 +msgid "Import descendants" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:255 +msgid "Import descendants of the selected person up to level number" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:259 +msgid "Before SPO" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:263 +msgid "SPO True" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:265 +msgid "SPO False" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:266 +msgid "Import spouses" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:267 +msgid "Import all spouses of the selected person" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:271 +msgid "Before LVL" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:274 +#: GeneanetForGramps/GeneanetForGramps.py:377 +msgid "LVL:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:275 +msgid "Level of Import" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:276 +msgid "" +"Maximum of upper or lower search done in the family tree - keep it small" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:280 +msgid "Before FORCE" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:284 +msgid "FORCE True" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:286 +msgid "FORCE False" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:287 +msgid "Force Import" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:288 +msgid "Force import of existing persons" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:292 +msgid "Before VRB" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:295 +msgid "VRB:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:296 +msgid "Verbosity" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:297 +msgid "Verbosity level from 0 (minimal) to 3 (very verbose)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:301 +msgid "Menu Added" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:309 +msgid "Init Plugin itself" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:314 +msgid "Plugin get_title" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:315 +msgid "Geneanet Import Tool" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:319 +msgid "Plugin initial_frame" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:331 +msgid "Plugin run" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:334 +#, python-format +msgid "Importing from %s for user %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:335 +msgid "Geneanet Import into Gramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:355 +msgid "Plugin __get_menu_options" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:361 +msgid "GID:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:364 +msgid "URL:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:391 +msgid "Smart Copying Attributes" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:416 +#, python-format +msgid "" +"WARNING: Gender conflict between Geneanet (%s) and Gramps (%s), keeping " +"Gramps value" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:421 +#, python-format +msgid "" +"WARNING: Lastname conflict between Geneanet (%s) and Gramps (%s), keeping " +"Gramps value" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:427 +#, python-format +msgid "" +"WARNING: Firstname conflict between Geneanet (%s) and Gramps (%s), keeping " +"Gramps value" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:459 +#, python-format +msgid "Copying Person attribute %s (former value %s newer value %s)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:464 +#, python-format +msgid "Not Copying Person attribute (%s, value %s) onto %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:481 +msgid "Reuse Place from Event:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:494 +msgid "DEBUG: search for " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:494 +msgid " in " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:500 +msgid "Create Place:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:504 +msgid "Reuse existing Place:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:525 +#: GeneanetForGramps/GeneanetForGramps.py:539 +msgid "Existing " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:525 +#: GeneanetForGramps/GeneanetForGramps.py:539 +msgid " Event" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:541 +#, python-format +msgid "ERROR: Unable to handle class %s in get_or_create_all_event" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:566 +msgid "Creating " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:566 +#: GeneanetForGramps/GeneanetForGramps.py:674 +msgid "Event" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:586 +msgid "in" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:604 +msgid "WARNING: Trying to affect an empty date" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:607 +msgid "WARNING: Trying to affect an extra numbered date" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:612 +msgid "Update " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:612 +msgid " Date to " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:642 +#, python-format +msgid "EventType: %d" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:661 +msgid "Didn't find a known EventType: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:666 +msgid "Ref:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:670 +msgid "Didn't find a known ref for this ref date: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:679 +msgid "Found date: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:683 +msgid "Found date2: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:696 +msgid "Returned date: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:725 +msgid "Creating GFamily: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:743 +msgid "Create new Gramps Family: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:750 +msgid "Look for a Gramps Family" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:756 +msgid "Analysing Gramps Family " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:776 +msgid "Check father ids: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:776 +#: GeneanetForGramps/GeneanetForGramps.py:786 +msgid " vs " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:786 +msgid "Check mother ids: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:801 +#, python-format +msgid "Comparing sr %s to %s (idx: %d)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:804 +#, python-format +msgid "Spouse %s found (idx: %d)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:818 +#, python-format +msgid "Geneanet Marriage found the %s at %s (%s)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:826 +#: GeneanetForGramps/GeneanetForGramps.py:1467 +#, python-format +msgid "Calling from_gramps with gid: %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:833 +#: GeneanetForGramps/GeneanetForGramps.py:1474 +#, python-format +msgid "Now gid is: %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:841 +#, python-format +msgid "Existing gid of a Gramps Family: %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:844 +#: GeneanetForGramps/GeneanetForGramps.py:1485 +#, python-format +msgid "WARNING: Unable to retrieve id %s from the gramps db %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:852 +msgid "Found an existing Gramps family " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:874 +#, python-format +msgid "Gramps Marriage found the %s at %s (%s)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:891 +msgid "No father for this family" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:899 +msgid "Can't affect father to the family" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:909 +msgid "No mother for this family" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:917 +msgid "Can't affect mother to the family" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:931 +msgid "Smart Copying Family" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:948 +msgid "Child already existing : " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:955 +msgid "Adding child: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:962 +msgid "No handle for this child" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:977 +msgid "Stopping exploration as there are no more children for family " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:986 +msgid "WARNING: No family found whereas there should be one :-(" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:993 +msgid "=> Recursion on the child of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1010 +msgid "=> End of recursion on the child of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1015 +#: GeneanetForGramps/GeneanetForGramps.py:1020 +msgid "Stopping exploration for family " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1015 +msgid " as there are no more children" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1020 +msgid " as we reached level " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1029 +#, python-format +msgid "Initialize Person at level %d" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1074 +msgid "Smart Copying Person" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1099 +msgid "Purl:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1105 +msgid "Page considered:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1108 +msgid "Return code:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1110 +msgid "We failed to reach the server at" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1116 +msgid "Unable to perform HTML analysis" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1139 +#, python-format +msgid "==> GENEANET Name (L%d): %s %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1141 +msgid "Sex:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1143 +msgid "Born" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1150 +msgid "birth" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1152 +msgid "Deceased" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1159 +msgid "death" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1172 +#: GeneanetForGramps/GeneanetForGramps.py:1516 +msgid "Birth:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1179 +msgid "Birth place:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1189 +msgid "Birth place code:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1195 +#: GeneanetForGramps/GeneanetForGramps.py:1529 +msgid "Death:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1202 +msgid "Death place:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1212 +msgid "Death place code:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1224 +msgid "Spouse name:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1231 +msgid "Spouse ref:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1243 +msgid "Married:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1250 +msgid "Married place:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1260 +msgid "Married place code:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1271 +#, python-format +msgid "Child %d name: %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1277 +#, python-format +msgid "Child %d ref: %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1303 +#, python-format +msgid "Parent name: %s (%s)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1317 +msgid "We failed to be ok with the server" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1330 +msgid "Create new Gramps Person: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1342 +msgid "DEBUG: Looking after " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1368 +msgid "DEBUG: firstname: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1368 +msgid " vs g_firstname: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1369 +msgid "DEBUG: lastname: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1369 +msgid " vs g_lastname: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1386 +msgid "DEBUG: bd: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1386 +msgid " vs g_bd: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1387 +msgid "DEBUG: dd: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1387 +msgid " vs g_dd: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1400 +msgid "Found a Gramps Person: " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1420 +msgid "ERROR: Unable sync unknown Gramps Person" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1482 +#, python-format +msgid "Existing Gramps Person: %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1498 +msgid "Gender:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1510 +#, python-format +msgid "===> Gramps Name of %s: %s %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1520 +msgid "No Birth date" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1523 +#, python-format +msgid "WARNING: Unable to retrieve birth date for id %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1533 +msgid "No Death date" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1536 +#, python-format +msgid "WARNING: Unable to retrieve death date for id %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1545 +#: GeneanetForGramps/GeneanetForGramps.py:1549 +msgid "Family:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1555 +msgid "Father H:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1559 +msgid "Father name:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1566 +msgid "Mother H:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1570 +msgid "Mother name:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1575 +#, python-format +msgid "NOTE: Unable to retrieve family for id %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1598 +msgid "=> Initialize Family of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1605 +msgid "Unable to Initialize Family of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1605 +msgid " sex unknown" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1634 +msgid "=> Recursing on the parents of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1638 +msgid "=> End of recursion on the parents of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1645 +msgid "=> Recursing on the mother of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1649 +msgid "=> End of recursing on the mother of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1653 +msgid "=> Initialize Parents Family of " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1694 +msgid "Stopping exploration as we reached level " +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1697 +msgid "Stopping exploration as there are no more parents" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1719 +#, python-format +msgid "Gramps person: %s %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1720 +#, python-format +msgid "Geneanet person: %s %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1723 +#: GeneanetForGramps/GeneanetForGramps.py:1744 +msgid "Do not continue without force" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1740 +#, python-format +msgid "Gramps person birth/death: %s / %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1741 +#, python-format +msgid "Geneanet person birth/death: %s / %s" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1746 +msgid "Please fix the person in gramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1752 +#, python-format +msgid "Adding Gramps Person %s %s (%s | %s)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1792 +msgid "Import Geneanet subtrees into Gramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1793 +msgid "Increase verbosity" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1794 +msgid "Includes ascendants (off by default)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1795 +msgid "Includes descendants (off by default)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1796 +msgid "Includes all spouses (off by default)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1797 +msgid "Number of level to explore (2 by default)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1798 +msgid "Full path of the Gramps database (under $HOME/.gramps/grampsdb)" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1799 +msgid "ID of the person to start from in Gramps" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1800 +msgid "Force processing" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1801 +msgid "Url of the person to search in Geneanet" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1807 +msgid "Please provide a person to search for" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1825 +msgid "Please provide a grampsfile to search into" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1833 +#, python-format +msgid "Opening the '%s' database" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1834 +msgid "An attempt to convert the database failed. Perhaps it needs updating." +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1846 +msgid "DEBUG: existing gramps id:" +msgstr "" + +#: GeneanetForGramps/GeneanetForGramps.py:1849 +msgid "WARNING: Force mode activated" +msgstr ""