Re: Minimal python program to create a Person

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Re: Minimal python program to create a Person

Bruno Cornec
Hello,

I'm new to this ML and Gramps programing, so apologies if this question
is naive, but I've not found an obvious answer in the dev docs, nor in
this forum, nor throught existing import plugin code.

I'd like to add persons in a Gramps database using python. I'm trying to
write a plugin, but for now, I'm having issues and tried to reduce the
size of the example to the minimum so it could be reproduceable by
others.

That program works without giving an error. However, afterwards when I
open the modified DB with gramps, it errors when I click on the new
person created. So I'm sure I'm missing somthing in the way I need to
link events to persons, but I've not found it despite having passed now
many hours since 2 weeks on it. So I decided to bother you for that.

The python program:
-----------------------------------------------------------------
#!/usr/bin/python3

import os
import time
import io
import sys
from gramps.gen.datehandler import get_date
from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback
import zipfile
import logging
from gramps.version import VERSION
from gramps.gen.config import config
from gramps.gen.display.place import displayer as _pd
from gramps.gen.utils.location import get_main_location
from gramps.gen.utils.place import conv_lat_lon
from gramps.gen.db import DbTxn
from gramps.gen.db.utils import open_database
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
from datetime import datetime

name = "/users/bruno/.gramps/grampsdb/5ec17554"

dbstate = DbState()
climanager = CLIManager(dbstate, True, None)
climanager.open_activate(name)
db = dbstate.db
gid = "I0001"
with DbTxn("Import", db) as tran:
    db.disable_signals()
    grampsp = Person()
    db.add_person(grampsp,tran)
    db.commit_person(grampsp,tran)
    #gid = grampsp.gramps_id
    grampsp.set_gender(Person.MALE)
    n = Name()
    n.set_type(NameType(NameType.BIRTH))
    n.set_first_name("Jean")
    s = n.get_primary_surname()
    s.set_surname("Bon")
    grampsp.set_primary_name(n)
    event = Event()
    event.personal = True
    event.set_description('Imported')
    event.set_type(EventType(EventType.BIRTH))
    eventref = EventRef()
    eventref.set_role(EventRoleType.PRIMARY)
    eventref.set_reference_handle(event.get_handle())
    grampsp.set_birth_ref(eventref)
    db.add_event(event,tran)
    db.commit_event(event,tran)
    event = Event()
    event.personal = True
    event.set_description('Imported')
    event.set_type(EventType(EventType.DEATH))
    eventref = EventRef()
    eventref.set_role(EventRoleType.PRIMARY)
    eventref.set_reference_handle(event.get_handle())
    grampsp.set_death_ref(eventref)
    db.add_event(event,tran)
    db.commit_event(event,tran)
    db.commit_person(grampsp,tran)
    db.enable_signals()
    db.transaction_commit(tran)
db.request_rebuild()
db.close()
sys.exit()
-----------------------------------------------------------------

Errors obtained when clicking on the person created:
-----------------------------------------------------------------
Warning: History: Exception occurred in callback function.
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/gramps/gen/utils/callback.py", line 405, in emit
    fn(*args)
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 163, in _active_changed
    self.active_changed(handle)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/persondetails.py", line 99, in active_changed
    self.update()
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 302, in update
    self.update_has_data()
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/persondetails.py", line 118, in update_has_data
    event = self.get_event(active_person, event_type)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/persondetails.py", line 253, in get_event
    event = self.dbstate.db.get_event_from_handle(event_ref.ref)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1273, in get_event_from_handle
    return self._get_from_handle(EVENT_KEY, Event, handle)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1263, in _get_from_handle
    raise HandleError('Handle is None')
gramps.gen.errors.HandleError: Handle is None
Warning: History: Exception occurred in callback function.
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/gramps/gen/utils/callback.py", line 405, in emit
    fn(*args)
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 163, in _active_changed
    self.active_changed(handle)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 307, in active_changed
    self.update()
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 302, in update
    self.update_has_data()
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 313, in update_has_data
    self.set_has_data(self.get_has_data(active))
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 359, in get_has_data
    if self.check_eventref_citations(person):
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 215, in check_eventref_citations
    event = self.dbstate.db.get_event_from_handle(event_ref.ref)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1273, in get_event_from_handle
    return self._get_from_handle(EVENT_KEY, Event, handle)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1263, in _get_from_handle
    raise HandleError('Handle is None')
gramps.gen.errors.HandleError: Handle is None
2020-05-17 19:35:58.017: ERROR: grampsapp.py: line 157: Unhandled exception
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/gramps/gui/views/listview.py", line 876, in _button_press
    self.edit(obj)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/lib/libpersonview.py", line 437, in edit
    EditPerson(self.dbstate, self.uistate, [], person)
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editperson.py", line 139, in __init__
    dbstate.db.get_person_from_gramps_id, callback)
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editprimary.py", line 92, in __init__
    self._create_tabbed_pages()
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editperson.py", line 463, in _create_tabbed_pages
    start_date=self.get_start_date())
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editperson.py", line 448, in get_start_date
    event = get_birth_or_fallback(self.dbstate.db, self.obj)
  File "/usr/lib/python3.7/site-packages/gramps/gen/utils/db.py", line 56, in get_birth_or_fallback
    event = db.get_event_from_handle(birth_ref.ref)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1273, in get_event_from_handle
    return self._get_from_handle(EVENT_KEY, Event, handle)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1263, in _get_from_handle
    raise HandleError('Handle is None')
gramps.gen.errors.HandleError: Handle is None
-----------------------------------------------------------------

Thanks in advance for any hint.

BTW, once this is solved, I think such a short program could be given
either on the Wiki, or as an attached code example to help newcomers
like me understand the direct sequence (no error handling, just
succession of calls)

Bruno.
--
Des infos sur la musique ancienne  -- http://www.musique-ancienne.org
Des infos sur les logiciels libres -- http://www.HyPer-Linux.org
Home, sweet musical Home -- Lover of Andromède, Béatrice, Early Music,
Josquin, Linux, Mélisande, Recorder, and Ségolène (not in that order)


_______________________________________________
Gramps-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gramps-devel
Reply | Threaded
Open this post in threaded view
|

Re: Minimal python program to create a Person

prculley
I think the immediate cause of your error is that you are trying to set an Event handle into the EventRef before the event handle exists.  The db.add_* code creates the handle, and adds in a gramps_id, if you have not provided one, then commits the object to the db.  The 'commit' simply stores the object as it is, only changing the 'change' time attribute.  (As an aside, you don't have to both add and commit, unless you modify the object after the add).  So the following should work:
with DbTxn("Import", db) as tran:
    db.disable_signals()
    grampsp = Person()
    #gid = grampsp.gramps_id
    grampsp.set_gender(Person.MALE)
    n = Name()
    n.set_type(NameType(NameType.BIRTH))
    n.set_first_name("Jean")
    s = n.get_primary_surname()
    s.set_surname("Bon")
    grampsp.set_primary_name(n)
    event = Event()
    event.personal = True  # What is this?  Event has no normal attribute 'personal'!
    event.set_description('Imported')
    event.set_type(EventType(EventType.BIRTH))
    db.add_event(event,tran)
    eventref = EventRef()
    eventref.set_role(EventRoleType.PRIMARY)
    eventref.set_reference_handle(event.get_handle())
    grampsp.set_birth_ref(eventref)
    event = Event()
    event.personal = True  # ???
    event.set_description('Imported')
    event.set_type(EventType(EventType.DEATH))
    db.add_event(event,tran)
    eventref = EventRef()
    eventref.set_role(EventRoleType.PRIMARY)
    eventref.set_reference_handle(event.get_handle())
    grampsp.set_death_ref(eventref)
    db.add_person(grampsp,tran)
#    db.transaction_commit(tran)  # un-needed: the 'with DbTxn' causes this to happen at the end of the 'with'
db.enable_signals()
db.request_rebuild()
db.close()
sys.exit()

Note the reordering of the db.add/commit statements.

The disable/enable_signals and request_rebuild methods are used when performing "batch" operations with a lot of individual commits, and the expectation that the results cannot be undone.  At the moment, you left out the 'batch=True' on the 'with' statement, so these are not actually necessary.  Furthermore, since this is a standalone program, with no GUI, you don't need the enable_signals at all, since there is no GUI to receive the signals.

Paul C.

On Sun, May 17, 2020 at 6:33 PM Bruno Cornec <[hidden email]> wrote:
Hello,

I'm new to this ML and Gramps programing, so apologies if this question
is naive, but I've not found an obvious answer in the dev docs, nor in
this forum, nor throught existing import plugin code.

I'd like to add persons in a Gramps database using python. I'm trying to
write a plugin, but for now, I'm having issues and tried to reduce the
size of the example to the minimum so it could be reproduceable by
others.

That program works without giving an error. However, afterwards when I
open the modified DB with gramps, it errors when I click on the new
person created. So I'm sure I'm missing somthing in the way I need to
link events to persons, but I've not found it despite having passed now
many hours since 2 weeks on it. So I decided to bother you for that.

The python program:
-----------------------------------------------------------------
#!/usr/bin/python3

import os
import time
import io
import sys
from gramps.gen.datehandler import get_date
from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback
import zipfile
import logging
from gramps.version import VERSION
from gramps.gen.config import config
from gramps.gen.display.place import displayer as _pd
from gramps.gen.utils.location import get_main_location
from gramps.gen.utils.place import conv_lat_lon
from gramps.gen.db import DbTxn
from gramps.gen.db.utils import open_database
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
from datetime import datetime

name = "/users/bruno/.gramps/grampsdb/5ec17554"

dbstate = DbState()
climanager = CLIManager(dbstate, True, None)
climanager.open_activate(name)
db = dbstate.db
gid = "I0001"
with DbTxn("Import", db) as tran:
    db.disable_signals()
    grampsp = Person()
    db.add_person(grampsp,tran)
    db.commit_person(grampsp,tran)
    #gid = grampsp.gramps_id
    grampsp.set_gender(Person.MALE)
    n = Name()
    n.set_type(NameType(NameType.BIRTH))
    n.set_first_name("Jean")
    s = n.get_primary_surname()
    s.set_surname("Bon")
    grampsp.set_primary_name(n)
    event = Event()
    event.personal = True
    event.set_description('Imported')
    event.set_type(EventType(EventType.BIRTH))
    eventref = EventRef()
    eventref.set_role(EventRoleType.PRIMARY)
    eventref.set_reference_handle(event.get_handle())
    grampsp.set_birth_ref(eventref)
    db.add_event(event,tran)
    db.commit_event(event,tran)
    event = Event()
    event.personal = True
    event.set_description('Imported')
    event.set_type(EventType(EventType.DEATH))
    eventref = EventRef()
    eventref.set_role(EventRoleType.PRIMARY)
    eventref.set_reference_handle(event.get_handle())
    grampsp.set_death_ref(eventref)
    db.add_event(event,tran)
    db.commit_event(event,tran)
    db.commit_person(grampsp,tran)
    db.enable_signals()
    db.transaction_commit(tran)
db.request_rebuild()
db.close()
sys.exit()
-----------------------------------------------------------------

Errors obtained when clicking on the person created:
-----------------------------------------------------------------
Warning: History: Exception occurred in callback function.
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/gramps/gen/utils/callback.py", line 405, in emit
    fn(*args)
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 163, in _active_changed
    self.active_changed(handle)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/persondetails.py", line 99, in active_changed
    self.update()
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 302, in update
    self.update_has_data()
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/persondetails.py", line 118, in update_has_data
    event = self.get_event(active_person, event_type)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/persondetails.py", line 253, in get_event
    event = self.dbstate.db.get_event_from_handle(event_ref.ref)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1273, in get_event_from_handle
    return self._get_from_handle(EVENT_KEY, Event, handle)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1263, in _get_from_handle
    raise HandleError('Handle is None')
gramps.gen.errors.HandleError: Handle is None
Warning: History: Exception occurred in callback function.
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/gramps/gen/utils/callback.py", line 405, in emit
    fn(*args)
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 163, in _active_changed
    self.active_changed(handle)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 307, in active_changed
    self.update()
  File "/usr/lib/python3.7/site-packages/gramps/gen/plug/_gramplet.py", line 302, in update
    self.update_has_data()
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 313, in update_has_data
    self.set_has_data(self.get_has_data(active))
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 359, in get_has_data
    if self.check_eventref_citations(person):
  File "/usr/lib/python3.7/site-packages/gramps/plugins/gramplet/citations.py", line 215, in check_eventref_citations
    event = self.dbstate.db.get_event_from_handle(event_ref.ref)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1273, in get_event_from_handle
    return self._get_from_handle(EVENT_KEY, Event, handle)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1263, in _get_from_handle
    raise HandleError('Handle is None')
gramps.gen.errors.HandleError: Handle is None
2020-05-17 19:35:58.017: ERROR: grampsapp.py: line 157: Unhandled exception
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/gramps/gui/views/listview.py", line 876, in _button_press
    self.edit(obj)
  File "/usr/lib/python3.7/site-packages/gramps/plugins/lib/libpersonview.py", line 437, in edit
    EditPerson(self.dbstate, self.uistate, [], person)
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editperson.py", line 139, in __init__
    dbstate.db.get_person_from_gramps_id, callback)
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editprimary.py", line 92, in __init__
    self._create_tabbed_pages()
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editperson.py", line 463, in _create_tabbed_pages
    start_date=self.get_start_date())
  File "/usr/lib/python3.7/site-packages/gramps/gui/editors/editperson.py", line 448, in get_start_date
    event = get_birth_or_fallback(self.dbstate.db, self.obj)
  File "/usr/lib/python3.7/site-packages/gramps/gen/utils/db.py", line 56, in get_birth_or_fallback
    event = db.get_event_from_handle(birth_ref.ref)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1273, in get_event_from_handle
    return self._get_from_handle(EVENT_KEY, Event, handle)
  File "/usr/lib/python3.7/site-packages/gramps/gen/db/generic.py", line 1263, in _get_from_handle
    raise HandleError('Handle is None')
gramps.gen.errors.HandleError: Handle is None
-----------------------------------------------------------------

Thanks in advance for any hint.

BTW, once this is solved, I think such a short program could be given
either on the Wiki, or as an attached code example to help newcomers
like me understand the direct sequence (no error handling, just
succession of calls)

Bruno.
--
Des infos sur la musique ancienne  -- http://www.musique-ancienne.org
Des infos sur les logiciels libres -- http://www.HyPer-Linux.org
Home, sweet musical Home -- Lover of Andromède, Béatrice, Early Music,
Josquin, Linux, Mélisande, Recorder, and Ségolène (not in that order)


_______________________________________________
Gramps-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gramps-devel


_______________________________________________
Gramps-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gramps-devel
Reply | Threaded
Open this post in threaded view
|

Re: Minimal python program to create a Person

Bruno Cornec
Hello Paul,

Many thanks for your answer !
Some comments below:

On Mon, May 18, 2020 at 08:47:38AM -0500, Paul Culley wrote:
> I think the immediate cause of your error is that you are trying to set an
> Event handle into the EventRef before the event handle exists.  The
> db.add_* code creates the handle, and adds in a gramps_id, if you have not
> provided one, then commits the object to the db.  

Ok I understand now where it comes from. In fact I was trying to mimic
what I found in various import plugins, but without really understanding
all the subtleties. Now I get your point, and indeed the test code is now
working fine !

> So the following should work:

Right !
Is that interesting that it's put on the wiki somewhere or provided as
an example script to help people start ? I'm ready to do that, and also
to add a bit more content such as a real date and place at least for one
event to show how it's done as well (I removed it from the test I sent).

>     event.personal = True  # What is this?  Event has no normal attribute 'personal'!

Well I found it here:
./gramps/plugins/importer/importxml.py:            event.personal = False
./gramps/plugins/importer/importxml.py:            event.personal = True

So thought it was useful. Again sorry was rying to read as much code as
I could t understand how that was working, and for use didn't get
everything right as you can see :-)

> Note the reordering of the db.add/commit statements.

My next step will be to bring these modifications back into my more
complete program.

> The disable/enable_signals and request_rebuild methods are used when
> performing "batch" operations with a lot of individual commits, and the
> expectation that the results cannot be undone.  At the moment, you left out
> the 'batch=True' on the 'with' statement, so these are not actually
> necessary.  Furthermore, since this is a standalone program, with no GUI,
> you don't need the enable_signals at all, since there is no GUI to receive
> the signals.

As I plan to have a real plugin later on, then I think I can keep these
disable/enable_signals to avoid forgetting them. Thanks for explaining
their role.

Wrt request_rebuild, if I import 50 persons in the DB one after each
other, is there a need to call it then, or can I just do all the other
calls in the right order (as you gave) without having issues (except
maybe less performance, which I don't really care at the moment).

Again many thanks for having taken time to answer, you've unblokced me
and hopefully I'll be able to contribute later to a useful addon.

Best regards,
Bruno.
--
Des infos sur la musique ancienne  -- http://www.musique-ancienne.org
Des infos sur les logiciels libres -- http://www.HyPer-Linux.org
Home, sweet musical Home -- Lover of Andromède, Béatrice, Early Music,
Josquin, Linux, Mélisande, Recorder, and Ségolène (not in that order)


_______________________________________________
Gramps-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gramps-devel
Reply | Threaded
Open this post in threaded view
|

Re: Minimal python program to create a Person

prculley
As for the wiki, short and descriptive articles are probably a great way to help people get up to speed.  If you do that, you might want to add enough comments to describe what each line does.

I suspect that event.personal in importxml is a leftover from a much older version of Gramps, or maybe an idea that was tried and not completely removed.

request_rebuild is another GUI call; it asks the GUI to rebuild all its screens because the underlying data has changed (and the disable_signals prevented the GUI from getting an indication for each transaction/commit).  Again, with no GUI in use this is not needed, but will be important later when doing something with Gramps GUI running (via addon).  And again, these things are only used in "batch" operations, the more usual transaction sends signals at each transaction, and updates the undo/redo db to allow that to work.  Undo/redo doesn't work for batch.

Paul C.

On Mon, May 18, 2020 at 6:25 PM Bruno Cornec <[hidden email]> wrote:
Hello Paul,

Many thanks for your answer !
Some comments below:

On Mon, May 18, 2020 at 08:47:38AM -0500, Paul Culley wrote:
> I think the immediate cause of your error is that you are trying to set an
> Event handle into the EventRef before the event handle exists.  The
> db.add_* code creates the handle, and adds in a gramps_id, if you have not
> provided one, then commits the object to the db. 

Ok I understand now where it comes from. In fact I was trying to mimic
what I found in various import plugins, but without really understanding
all the subtleties. Now I get your point, and indeed the test code is now
working fine !

> So the following should work:

Right !
Is that interesting that it's put on the wiki somewhere or provided as
an example script to help people start ? I'm ready to do that, and also
to add a bit more content such as a real date and place at least for one
event to show how it's done as well (I removed it from the test I sent).

>     event.personal = True  # What is this?  Event has no normal attribute 'personal'!

Well I found it here:
./gramps/plugins/importer/importxml.py:            event.personal = False
./gramps/plugins/importer/importxml.py:            event.personal = True

So thought it was useful. Again sorry was rying to read as much code as
I could t understand how that was working, and for use didn't get
everything right as you can see :-)

> Note the reordering of the db.add/commit statements.

My next step will be to bring these modifications back into my more
complete program.

> The disable/enable_signals and request_rebuild methods are used when
> performing "batch" operations with a lot of individual commits, and the
> expectation that the results cannot be undone.  At the moment, you left out
> the 'batch=True' on the 'with' statement, so these are not actually
> necessary.  Furthermore, since this is a standalone program, with no GUI,
> you don't need the enable_signals at all, since there is no GUI to receive
> the signals.

As I plan to have a real plugin later on, then I think I can keep these
disable/enable_signals to avoid forgetting them. Thanks for explaining
their role.

Wrt request_rebuild, if I import 50 persons in the DB one after each
other, is there a need to call it then, or can I just do all the other
calls in the right order (as you gave) without having issues (except
maybe less performance, which I don't really care at the moment).

Again many thanks for having taken time to answer, you've unblokced me
and hopefully I'll be able to contribute later to a useful addon.

Best regards,
Bruno.
--
Des infos sur la musique ancienne  -- http://www.musique-ancienne.org
Des infos sur les logiciels libres -- http://www.HyPer-Linux.org
Home, sweet musical Home -- Lover of Andromède, Béatrice, Early Music,
Josquin, Linux, Mélisande, Recorder, and Ségolène (not in that order)


_______________________________________________
Gramps-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gramps-devel