Antworten

L'épineuse question des fusions de gedcom...

Pour les utilisateurs du logiciel Gramps.
valentil
male
Beiträge: 25
Eingabeform: Text
Navigation: Text
Den Stammbaum ansehen
Pour ceux qui sont intéressés par un script qui permet de rajouter/enlever un attribut sosa
=> attention ce script n'est probablement pas exempt de bug... je n'ai pas détecté de problème majeurs mais l'utilisation à vos risques et périls évidemment :roll: ) d'autant que ça fait une quinzaine d'année que je n'ai pas touché une ligne de code donc... c'est largement perfectible

gramps\plugins\tool\AddSosaAttrib.gpr.py

Code: Alles auswählen

#
#
"""Add/Remove a SOSA Attribute to people"""

register(TOOL,
         id = 'AddSosaAttrib',
         name = _("Add/Remove Sosa Attribute Tool"),
         description = _("Add or remove Sosa as an attribute from people"),
         version = '0.0.0',
         gramps_target_version = "5.1",
         status = STABLE,
         fname = 'AddSosaAttrib.py',
         authors = ["LV"],
         authors_email = ["[email protected]"],
         category = TOOL_DBPROC,
         toolclass = 'AddSosa',
         optionclass = 'AddSosaOptions',
         tool_modes = [TOOL_MODE_GUI],
         )
gramps\plugins\tool\AddSosaAttrib.py:

Code: Alles auswählen

#
#
"""Add sosa number in a tag on people"""

# -------------------------------------------------
#
# GRAMPS modules
#
# -------------------------------------------------
from gramps.gui.plug import MenuToolOptions, PluginWindows
from gramps.gen.plug.menu import FilterOption, TextOption, PersonOption, BooleanOption, NumberOption
from gramps.gen.db import DbTxn
from gramps.gui.dialog import OkDialog, WarningDialog
from gramps.gen.filters import CustomFilters, GenericFilterFactory, rules
from gramps.gen.lib import ChildRefType
from gramps.gen.lib import Tag, Attribute

#------------------------------------------------------------------------
# Internationalisation
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
try:
    _trans = glocale.get_addon_translator(__file__)
except ValueError:
    _trans = glocale.translation
_ = _trans.gettext


#------------------------------------------------------------------------
#
# AddSosaOptions
#
#------------------------------------------------------------------------
class AddSosaOptions(MenuToolOptions):

    """
    Defines options and provides handling interface.
    """

    def __init__(self, name, person_id=None, dbstate=None):
        self.db = dbstate.get_database()
        MenuToolOptions.__init__(self, name, person_id, dbstate)
 
#    def get_subject(self):
#        """ Return a string that describes the subject of the report. """
#        gid = self.__pid.get_value()
#        person = self.__db.get_person_from_gramps_id(gid)
#        return _nd.display(person)

    def add_menu_options(self, menu):
        """
        Add options to the menu for the ancestor report.
        """
        category_name = _("Tool Options")


        # Add or remove tags menu option
        self.add_remove = FilterOption(_("Add/Remove"), 0)
        self.add_remove.set_help(_("Add or remove Sosa attributes from objects."))
        self.add_remove.set_items([(0, _("Add Sosa Attributes")), (1, _("Remove Sosa Attributes"))])
        menu.add_option(category_name, "add_remove", self.add_remove)

        self.__pid = PersonOption(_("Center Person"))
        self.__pid.set_help(_("The center person for applying sosa (sosa=1 person)"))
        menu.add_option(category_name, "pid", self.__pid)

        maxgen = NumberOption(_("Generations"), 50, 1, 100)
        maxgen.set_help(
            _("The number of generations to apply sosa"))
        menu.add_option(category_name, "maxgen", maxgen)
        
        self.__filter_options(menu)

    def __filter_options(self, menu):
        """Menu Options for filter option tab."""
        self.filter_dict = {}

        # get all filter rules, used for generic filters
        all_persons = rules.person.Everyone([])

        # create a list used for menu filter option creation later
        lst = [(_("Person Filter"), 'Person', _("Persons"), all_persons)]

        for entry in lst:
            # create a filter option for each category e.g. person, events
            # filter option is a combination of custom filters and
            # a generic filter for all objects of one category
            filter_name = FilterOption(entry[0], 0)
            menu.add_option(_("Filter options"),
                            entry[1].lower(),
                            filter_name)
            self.filter_dict[entry[1]] = filter_name

            # custom filter:
            filter_list = CustomFilters.get_filters(entry[1])

            # generic filter:
            GenericFilter = GenericFilterFactory(entry[1])
            all_filter = GenericFilter()
            all_filter.set_name(_("All %s") % (entry[2]))
            all_filter.add_rule(entry[3])

            # only add the generic filter if it isn't already in the menu
            all_filter_in_list = False
            for fltr in filter_list:
                if fltr.get_name() == all_filter.get_name():
                    all_filter_in_list = True
            if not all_filter_in_list:
                filter_list.insert(0, all_filter)

            # add the list of custom and generic filters
            # to the filter option
            self.filter_dict[entry[1]].set_filters(filter_list)
   
# ----------------------------------------------------------------------------
#
# Tool Class
#
# ----------------------------------------------------------------------------
class AddSosa(PluginWindows.ToolManagedWindowBatch):
    """Add Sosa Tag Tool."""

    def get_title(self):
        """Window title."""
        return _("Add/Remove Sosa Attribute Tool")

    def initial_frame(self):
        """Category tab title."""
        return _("Options")

    def run(self):
        """Run function of Remove attribute Tool."""
        self.db = self.dbstate.get_database()
        self.remove = bool(self.__opt_by_name('add_remove').get_value())
        self.__menu_opt_handling('person')


    def __opt_by_name(self, opt_name):
        """Get an option by its name."""
        return self.options.menu.get_option_by_name(opt_name)

    def __menu_opt_handling(self, category):
        """General menu option handling."""
        self.map = {}
        iter_ = list(self.db.method('iter_%s_handles', category)())
        if iter_ == []:
            txt = _("No %s objects were found in database." % category)
            WarningDialog(_("WARNING"), txt, parent=self.window)
            return  # stop the tool
        filter_opt = self.__opt_by_name(category)
        filter_ = filter_opt.get_filter()
        objects = filter_.apply(self.dbstate.db, iter_)
        self.max_generations = self.__opt_by_name('maxgen').get_value()
        self.__addsosa_from(objects, category)

    def SosaLoop(self, person_handle, index, generation=1):
        """
        Recursive function to walk back all parents of the current person.
        When max_generations are hit, we stop the traversal.
        """

        # check for end of the current recursion level. This happens
        # if the person handle is None, or if the max_generations is hit

        if not person_handle or generation > self.max_generations:
            return

        # store the person in the map based off their index number
        # which is passed to the routine.
        self.map[index] = person_handle

        # retrieve the Person instance from the database from the
        # passed person_handle and find the parents from the list.
        # Since this report is for natural parents (birth parents),
        # we have to handle that parents may not

        person = self.db.get_person_from_handle(person_handle)
        if person is None:
            return

        father_handle = None
        mother_handle = None
        for family_handle in person.get_parent_family_handle_list():
            family = self.db.get_family_from_handle(family_handle)

            # filter the child_ref_list to find the reference that matches
            # the passed person. There should be exactly one, but there is
            # nothing that prevents the same child in the list multiple times.

            ref = [ c for c in family.get_child_ref_list()
                    if c.get_reference_handle() == person_handle]
            if ref:

                # If the father_handle is not defined and the relationship is
                # BIRTH, then we have found the birth father. Same applies to
                # the birth mother. If for some reason, the we have multiple
                # people defined as the birth parents, we will select based on
                # priority in the list

                if not father_handle and \
                   ref[0].get_father_relation() == ChildRefType.BIRTH:
                    father_handle = family.get_father_handle()
                if not mother_handle and \
                   ref[0].get_mother_relation() == ChildRefType.BIRTH:
                    mother_handle = family.get_mother_handle()

        # Recursively call the function. It is okay if the handle is None,
        # since routine handles a handle of None

        self.SosaLoop(father_handle, index*2, generation+1)
        self.SosaLoop(mother_handle, (index*2)+1, generation+1)

 
    def __addsosa_from(self, objects, category):
        """Add (or remove) sosa as a tag to persons"""
        counter = [0, 0]
        num = len(objects)
        name = _("Add/Remove Sosa Tag Tool")

        pid = self.__opt_by_name('pid').get_value()
        self.center_person = self.db.get_person_from_gramps_id(pid)
        #build an array with all sosa ancestr, with corresponding sosa number
        self.SosaLoop(self.center_person.get_handle(), 1)
        

        with DbTxn(name, self.db, batch=True) as self.trans:
            self.db.disable_signals()
            self.progress.set_pass(_('Process tags...'), num)

            #loop on collected individuals to assign or remove sosa attributes
            for key in sorted(self.map):
                person = self.db.get_person_from_handle(self.map[key])
                SosaValue = '#%09d' % key
                if self.remove:
                    for attr in person.get_attribute_list():
                        if attr.get_type() == 'SOSA':
                            person.remove_attribute(attr)
                            # Update global attribute list:
                            self.db.individual_attributes.update([str(attr.type)])
                            break
                    self.db.method("commit_%s", category)(person, self.trans)
                elif not self.remove: 
                    if person is None:
                        continue
                    attr = Attribute()
                    attr.set_type('SOSA')
                    attr.set_value(SosaValue)
                    person.add_attribute(attr)
                    # Update global attribute list:
                    if attr.type.is_custom() and str(attr.type):
                        self.db.individual_attributes.update([str(attr.type)])
                    self.db.method("commit_%s", category)(person, self.trans)

            self.db.enable_signals()
            self.db.request_rebuild()


Zuletzt geändert von valentil am 09 Juni 2021, 14:32, insgesamt 1-mal geändert.
valentil
male
Beiträge: 25
Eingabeform: Text
Navigation: Text
Den Stammbaum ansehen
patricelegoux hat geschrieben: 07 Juni 2021, 09:14 Et comparer/réconcilier les deux bases dans openrefine ?
Je ne connais pas openrefine, mais c'est peut être une bonne solution pour traiter les gedcom, merci, je vais regarder ça aussi!
romjerome
male
Beiträge: 1511
Eingabeform: Grafisch
Navigation: Text
Den Stammbaum ansehen
Hello,
valentil hat geschrieben: 09 Juni 2021, 12:56 File "C:\Users\l\AppData\Local\GrampsAIO64-5.1.3\gramps\gen\utils\grampslocale.py", line 926, in sort_key
return hexlify(self.collator.getCollationKey(string).getByteArray()).decode()
icu.InvalidArgsError: (<class 'icu.RuleBasedCollator'>, 'getCollationKey', (0,))[/code]
icu est une "extension" pour les langues et l'unicode.
Est-ce que votre gedcom était "vraiment" encodé en UTF-8 ?
(ou la chaîne Excel <-> Windows ?)
j'ai récupéré l'arbre plus ou moins manuellement, à base d'export, de copier-coller, et de formules excel puis de conversion vers gedcom. à la fin j'obtient un gedcom a peu près correct de 40k individus
Est-ce que vous avez la bibliothèque lxml dans votre "environnement" gramps ?
Il est possible de tester une version de 'GeneanetForGramps' sans la dépendance à 'Requests' (mais il faut toujours un support de 'lxml'). Théoriquement cela devrait fonctionner sous Windows.
valentil
male
Beiträge: 25
Eingabeform: Text
Navigation: Text
Den Stammbaum ansehen
romjerome hat geschrieben: 09 Juni 2021, 20:16 icu est une "extension" pour les langues et l'unicode.
Est-ce que votre gedcom était "vraiment" encodé en UTF-8 ?
(ou la chaîne Excel <-> Windows ?)
j'ai trouvé, c'est ma bidouille pour afficher le sosa qui était en cause, je n'avais pas correctement initialisé la colonne, certaines lignes étant vide, ça fait exploser le tri
romjerome hat geschrieben: 09 Juni 2021, 20:16 Est-ce que vous avez la bibliothèque lxml dans votre "environnement" gramps ?
Il est possible de tester une version de 'GeneanetForGramps' sans la dépendance à 'Requests' (mais il faut toujours un support de 'lxml'). Théoriquement cela devrait fonctionner sous Windows.
oui j'ai installé lxml effectivement, 'GeneanetForGramps' fonctionne également avec roglo?
romjerome
male
Beiträge: 1511
Eingabeform: Grafisch
Navigation: Text
Den Stammbaum ansehen
oui j'ai installé lxml effectivement, 'GeneanetForGramps' fonctionne également avec roglo?
pour ce qui est des liens "récursifs" (et relatifs), il faudra sans doute modifier le ROOTURL (ligne 107) mais sinon, étant basé sur geneweb, j'imagine que cela fonctionne aussi...

Contrairement à geneanet, roglo n'a sûrement pas mis en place un code de retour "201" (qui entraîne la création d'une autre ressource via requests). Honnêtement, il suffit de copier les pages HTML en local si geneanet souhaite vraiment cela !

ps: il y a aussi une limitation au niveau des liens récursifs dans ma version, mais j'ai l'impression qu'il y a un souci avec certains "thèmes/templates" de geneanet...
Zuletzt geändert von romjerome am 10 Juni 2021, 16:36, insgesamt 1-mal geändert.
valentil
male
Beiträge: 25
Eingabeform: Text
Navigation: Text
Den Stammbaum ansehen
ok top! je vais essayer :)
par contre ou puis je récupérer la dernière version svp?
merci merci
romjerome
male
Beiträge: 1511
Eingabeform: Grafisch
Navigation: Text
Den Stammbaum ansehen
valentil hat geschrieben: 10 Juni 2021, 13:23 ok top! je vais essayer :)
par contre ou puis je récupérer la dernière version svp?
merci merci
C'est une adaptation de la version originelle...
Normalement vous avez reçu un lien via l'adresse qui vous avez renseigné un peu plus haut.
/!\ il y a visiblement une limitation pour l'importation dans gramps (sécurité de nos données ?),
car l'analyse de l'outil va loin, mais uniquement une petite partie est importée.
valentil
male
Beiträge: 25
Eingabeform: Text
Navigation: Text
Den Stammbaum ansehen
merci j'ai bien reçu le paquet, j'essaie ça
romjerome
male
Beiträge: 1511
Eingabeform: Grafisch
Navigation: Text
Den Stammbaum ansehen
valentil hat geschrieben: 10 Juni 2021, 23:28 merci j'ai bien reçu le paquet, j'essaie ça
Si vous avez un problème pour importer les ascendants, essayez la version 0.0.3
qui utilise une autre méthode... Il y aura sûrement une version 0.0.4 pour les descendants et une version 0.0.5 pour la gestion des conjoints !

Plus sérieusement je commence doucement à comprendre la logique de l'outil,
théoriquement cette version devrait aussi fonctionner sous Windows, mais je n'assure aucun support...
Antworten

Zurück zu „Gramps“