diff --git a/gramps2/ChangeLog b/gramps2/ChangeLog index 114288e9c..5540a75e9 100644 --- a/gramps2/ChangeLog +++ b/gramps2/ChangeLog @@ -1,3 +1,13 @@ +2004-09-24 Alex Roitman + * src/data/gnome-mime-application-x-geneweb.png: Add to CVS. + * src/data/Makefile.am, src/plugins/Makefile.am: Ship new files. + +2004-09-24 Martin Hawlisch + * src/plugins/ImportGeneWeb.py, src/plugins/WriteGeneWeb.py, + src/plugins/genewebexport.glade: Add to CVS. + * src/data/gramps.applications, src/data/gramps.keys, + src/data/gramps.mime, src/data/gramps.xml: Add GeneWeb format. + 2004-09-24 Don Allingham * src/DateDisplay.py: use LC_TIME and D_FMT to determine the locale date format diff --git a/gramps2/src/data/Makefile.am b/gramps2/src/data/Makefile.am index 84d22cc9a..65bed1487 100644 --- a/gramps2/src/data/Makefile.am +++ b/gramps2/src/data/Makefile.am @@ -10,6 +10,7 @@ dist_pkgdata_DATA = \ gnome-mime-application-x-gedcom.png \ gnome-mime-application-x-gramps-package.png \ gnome-mime-application-x-gramps-xml.png \ + gnome-mime-application-x-geneweb.png \ gramps.applications \ gramps.desktop \ gramps.svg \ diff --git a/gramps2/src/data/gnome-mime-application-x-geneweb.png b/gramps2/src/data/gnome-mime-application-x-geneweb.png new file mode 100644 index 000000000..93a4d48d7 Binary files /dev/null and b/gramps2/src/data/gnome-mime-application-x-geneweb.png differ diff --git a/gramps2/src/data/gramps.applications b/gramps2/src/data/gramps.applications index 98192823b..65edea604 100644 --- a/gramps2/src/data/gramps.applications +++ b/gramps2/src/data/gramps.applications @@ -4,4 +4,4 @@ gramps can_open_multiple_files=false expects_uris=false requires_terminal=false - mime_types=application/x-gramps,application/x-gedcom,application/x-gramps-package,application/x-gramps-xml + mime_types=application/x-gramps,application/x-gedcom,application/x-gramps-package,application/x-gramps-xml,application/x-geneweb diff --git a/gramps2/src/data/gramps.keys b/gramps2/src/data/gramps.keys index f70a05bd7..e534e2f11 100644 --- a/gramps2/src/data/gramps.keys +++ b/gramps2/src/data/gramps.keys @@ -41,3 +41,14 @@ application/x-gedcom: category=Documents/Genealogy icon-filename=/usr/share/gramps/gramps.png open=gramps %f + +application/x-geneweb: + description=GeneWeb + default_action_type=application + short_list_application_ids=gramps + short_list_application_ids_for_novice_user_level=gramps + short_list_application_ids_for_intermediate_user_level=gramps + short_list_application_ids_for_advanced_user_level=gramps + category=Documents/Genealogy + icon-filename=/usr/share/gramps/gramps.png + open=gramps %f diff --git a/gramps2/src/data/gramps.mime b/gramps2/src/data/gramps.mime index 073597b54..f11609f16 100644 --- a/gramps2/src/data/gramps.mime +++ b/gramps2/src/data/gramps.mime @@ -9,3 +9,6 @@ application/x-gramps-package application/x-gedcom ext: ged gedcom GED GEDCOM + +application/x-geneweb + ext: gw GW diff --git a/gramps2/src/data/gramps.xml b/gramps2/src/data/gramps.xml index e29914ebc..2340a5bea 100644 --- a/gramps2/src/data/gramps.xml +++ b/gramps2/src/data/gramps.xml @@ -16,4 +16,8 @@ GRAMPS XML database + + GeneWeb source file + + diff --git a/gramps2/src/plugins/ImportGeneWeb.py b/gramps2/src/plugins/ImportGeneWeb.py new file mode 100644 index 000000000..4eb0a5013 --- /dev/null +++ b/gramps2/src/plugins/ImportGeneWeb.py @@ -0,0 +1,689 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2004 Martin Hawlisch, Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +"Import from GeneWeb" + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +import os +import re +import string +import const +import time + +#------------------------------------------------------------------------- +# +# GTK/GNOME Modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import Errors +import RelLib +import Date +import DateHandler +import latin_utf8 +import Utils +from QuestionDialog import ErrorDialog +from gettext import gettext as _ + + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def importData(database, filename, cb=None): + + global callback + + try: + g = GeneWebParser(database,filename) + except IOError,msg: + ErrorDialog(_("%s could not be opened\n") % filename,str(msg)) + return + except: + ErrorDialog(_("%s could not be opened\n") % filename) + import DisplayTrace + DisplayTrace.DisplayTrace() + return + + try: + status = g.parse_geneweb_file() + except IOError,msg: + errmsg = _("%s could not be opened\n") % filename + ErrorDialog(errmsg,str(msg)) + return + except: + import DisplayTrace + DisplayTrace.DisplayTrace() + return + + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class GeneWebParser: + def __init__(self, dbase, file): + self.db = dbase + self.f = open(file,"rU") + self.filename = file + self.dp = DateHandler.create_parser() + + def get_next_line(self): + line = self.f.readline() + if line: + line = line.strip() + else: + line = None + return line + + def parse_geneweb_file(self): + self.trans = self.db.transaction_begin() + t = time.time() + self.index = 0 + self.fam_count = 0 + self.indi_count = 0 + + self.fkeys = [] + self.ikeys = {} + self.pkeys = {} + self.skeys = {} + + self.current_mode = None + self.current_family = None + self.current_husband_handle = None + try: + while 1: + line = self.get_next_line() + if line == None: + break + if line == "": + continue + + fields = line.split(" ") + + print "LINE: %s" %line + if fields[0] == "fam": + self.read_family_line(line,fields) + elif fields[0] == "src": + self.read_source_line(line,fields) + elif fields[0] == "wit": + self.read_witness_line(line,fields) + elif fields[0] == "cbp": + self.read_children_birthplace_line(line,fields) + elif fields[0] == "beg": + self.read_children_lines() + elif fields[0] == "comm": + self.read_family_comment(line,fields) + elif fields[0] == "notes": + self.read_notes_lines(line,fields) + elif fields[0] == "end": + self.current_mode = None + else: + print "Token >%s< unknown. line skipped: %s" % (fields[0],line) + except Errors.GedcomError, err: + self.errmsg(str(err)) + + t = time.time() - t + msg = _('Import Complete: %d seconds') % t + + self.db.transaction_commit(self.trans,_("GeneWeb import")) + + print msg + print "Families: %d" % len(self.fkeys) + print "Individuals: %d" % len(self.ikeys) + return None + + def read_family_line(self,line,fields): + self.current_husband_handle = None + self.current_family = RelLib.Family() + self.db.add_family(self.current_family,self.trans) + self.db.commit_family(self.current_family,self.trans) + self.fkeys.append(self.current_family.get_handle()) + idx = 1; + + print "\nHusband:" + (idx,husband) = self.parse_person(fields,idx,RelLib.Person.male,None) + if husband: + self.current_husband_handle = husband.get_handle() + self.current_family.set_father_handle(husband.get_handle()) + self.db.commit_family(self.current_family,self.trans) + husband.add_family_handle(self.current_family.get_handle()) + self.db.commit_person(husband,self.trans) + print "Marriage:" + idx = self.parse_marriage(fields,idx) + print "Wife:" + (idx,wife) = self.parse_person(fields,idx,RelLib.Person.female,None) + if wife: + self.current_family.set_mother_handle(wife.get_handle()) + self.db.commit_family(self.current_family,self.trans) + wife.add_family_handle(self.current_family.get_handle()) + self.db.commit_person(wife,self.trans) + return None + + def read_source_line(self,line,fields): + if not self.current_family: + print "Unknown family of child!" + return None + source = self.get_or_create_source(self.decode(fields[1])) + self.current_family.add_source_reference(source) + self.db.commit_family(self.current_family,self.trans) + return None + + def read_witness_line(self,line,fields): + print "Witness:" + if fields[1] == "m:": + self.parse_person(fields,2,RelLib.Person.male,None) + elif fields[1] == "f:": + self.parse_person(fields,2,RelLib.Person.female,None) + else: + self.parse_person(fields,1,None,None) + return None + + def read_children_lines(self): + father_surname = "Dummy" + if not self.current_husband_handle: + print "Unknown father for child!" + return None + husb = self.db.get_person_from_handle(self.current_husband_handle) + father_surname = husb.get_primary_name().get_surname() + if not self.current_family: + print "Unknown family of child!" + return None + while 1: + line = self.get_next_line() + if line == None: + break + if line == "": + continue + + fields = line.split(" ") + if fields[0] == "-": + print "Child:" + child = None + if fields[1] == "h": + (idx,child) = self.parse_person(fields,2,RelLib.Person.male,father_surname) + elif fields[1] == "f": + (idx,child) = self.parse_person(fields,2,RelLib.Person.female,father_surname) + else: + (idx,child) = self.parse_person(fields,1,None,father_surname) + + if child: + self.current_family.add_child_handle(child.get_handle()) + self.db.commit_family(self.current_family,self.trans) + child.add_parent_family_handle(self.current_family.get_handle(),"Birth","Birth") + self.db.commit_person(child,self.trans) + else: + break + return None + + + def read_children_birthplace_line(self,line,fields): + return None + + def read_family_comment(self,line,fields): + if not self.current_family: + print "Unknown family of child!" + return None + self.current_family.set_note(self.cnv(line)) + self.db.commit_family(self.current_family,self.trans) + return None + + def read_notes_lines(self,line,fields): + (idx,person) = self.parse_person(fields,1,None,None) + note_txt = "" + while 1: + line = self.get_next_line() + if line == None: + break + + fields = line.split(" ") + if fields[0] == "end" and fields[1] == "notes": + break + elif fields[0] == "beg": + continue + else: + if note_txt: + note_txt = note_txt + "\n" + self.cnv(line) + else: + note_txt = note_txt + self.cnv(line) + if note_txt: + person.set_note(note_txt) + self.db.commit_person(person,self.trans) + return None + + def parse_marriage(self,fields,idx): + mdate = self.parse_date(fields[idx]) + mariageDataRe = re.compile("^[+#-0-9].*$") + + mar_date = None + mar_place = None + mar_source = None + + sep_date = None + div_date = None + + married = 1 + engaged = 0 + + # skip to marriage date in case person contained unmatches tokens + #Alex: this failed when fields[idx] was an empty line. Fixed. + #while idx < len(fields) and not fields[idx][0] == "+": + while idx < len(fields) and not (fields[idx] and fields[idx][0] == "+"): + print "Unknown field: "+fields[idx] + idx = idx + 1 + + while idx < len(fields) and mariageDataRe.match(fields[idx]): + if fields[idx][0] == "+": + mar_date = self.parse_date(self.decode(fields[idx])) + print " Married at: %s" % fields[idx] + idx = idx + 1 + elif fields[idx][0] == "-": + div_date = self.parse_date(self.decode(fields[idx])) + print " Div at: %s" % fields[idx] + idx = idx + 1 + elif fields[idx] == "#mp": + idx = idx + 1 + mar_place = self.get_or_create_place(self.decode(fields[idx])) + print " Marriage place: %s" % fields[idx] + idx = idx + 1 + elif fields[idx] == "#ms": + idx = idx + 1 + mar_source = self.get_or_create_source(self.decode(fields[idx])) + print " Marriage source: %s" % fields[idx] + idx = idx + 1 + elif fields[idx] == "#sep": + idx = idx + 1 + sep_date = self.parse_date(self.decode(fields[idx])) + print " Seperated since: %s" % fields[idx] + idx = idx + 1 + elif fields[idx] == "#np": + print " Are not married." + married = 0 + idx = idx + 1 + elif fields[idx] == "#eng": + print " Are engaged." + engaged = 1 + idx = idx + 1 + else: + print "Unknown field for mariage: %s" % fields[idx] + idx = idx + 1 + + if mar_date or mar_place or mar_source: + mar = self.create_event("Marriage", None, mar_date, mar_place, mar_source) + self.current_family.add_event_handle(mar.get_handle()) + + if div_date: + div = self.create_event("Divorce", None, div_date, None, None) + self.current_family.add_event_handle(div.get_handle()) + + if sep_date or engaged: + sep = self.create_event("Engagement", None, sep_date, None, None) + self.current_family.add_event_handle(sep.get_handle()) + + if not married: + self.current_family.set_relationship(const.FAMILY_UNMARRIED) + + self.db.commit_family(self.current_family,self.trans) + return idx + + def parse_person(self,fields,idx,gender,father_surname): + firstname = "" + surname = "" + occupation = "" + birthplace = "" + alias = "" + + if not father_surname: + if not idx < len(fields): + print "Missing surname of person" + return (idx,None) + surname = self.decode(fields[idx]) + idx = idx + 1 + else: + surname = father_surname + + if not idx < len(fields): + print "Missing firstname of person" + return (idx,None) + firstname = self.decode(self.cnv(fields[idx])) + idx = idx + 1 + if idx < len(fields) and father_surname: + noSurnameRe = re.compile("^[({\[~>~#\[({!].*$") + dateRe = re.compile("^[0-9~<>?]+.*$") + + source = None + birth_date = None + birth_place = None + birth_source = None + + bapt_date = None + bapt_place = None + bapt_source = None + + death_date = None + death_place = None + death_source = None + + crem_date = None + bur_date = None + bur_place = None + bur_source = None + + public_name = None + firstname_aliases = [] + nick_names = [] + name_aliases = [] + surname_aliases = [] + + while idx < len(fields) and personDataRe.match(fields[idx]): + if fields[idx][0] == '(': + print "Public Name: %s" % fields[idx] + public_name = self.decode(fields[idx]) + idx = idx + 1 + elif fields[idx][0] == '{': + print "Firstsname Alias: %s" % fields[idx] + firstname_aliases.append(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx][0] == '[': + print "Titles: %s" % fields[idx] + idx = idx + 1 + elif fields[idx] == '#nick': + idx = idx + 1 + print "Nick Name: %s" % fields[idx] + nick_names.append(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#occu': + idx = idx + 1 + print "Occupation: %s" % fields[idx] + occu = self.create_event("Occupation",self.decode(fields[idx])) + person.add_event_handle(occu.get_handle()) + self.db.commit_person(person,self.trans) + idx = idx + 1 + elif fields[idx] == '#alias': + idx = idx + 1 + print "Name Alias: %s" % fields[idx] + name_aliases.append(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#salias': + idx = idx + 1 + print "Surname Alias: %s" % fields[idx] + surname_aliases.append(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#image': + idx = idx + 1 + print "Image: %s" % fields[idx] + idx = idx + 1 + elif fields[idx] == '#src': + idx = idx + 1 + print "Source: %s" % fields[idx] + source = self.get_or_create_source(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#bs': + idx = idx + 1 + print "Birth Source: %s" % fields[idx] + birth_source = self.get_or_create_source(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx][0] == '!': + print "Baptize at: %s" % fields[idx] + bapt_date = self.parse_date(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#bp': + idx = idx + 1 + print "Birth Place: %s" % fields[idx] + birth_place = self.get_or_create_place(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#pp': + idx = idx + 1 + print "Baptize Place: %s" % fields[idx] + bapt_place = self.get_or_create_place(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#ps': + idx = idx + 1 + print "Baptize Source: %s" % fields[idx] + bapt_source = self.get_or_create_source(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#dp': + idx = idx + 1 + print "Death Place: %s" % fields[idx] + death_place = self.get_or_create_place(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#ds': + idx = idx + 1 + print "Death Source: %s" % fields[idx] + death_source = self.get_or_create_source(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#buri': + idx = idx + 1 + print "Burial Date: %s" % fields[idx] + bur_date = self.parse_date(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#crem': + idx = idx + 1 + print "Cremention Date: %s" % fields[idx] + crem_date = self.parse_date(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#bp': + idx = idx + 1 + print "Burial Place: %s" % fields[idx] + bur_place = self.get_or_create_place(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#rs': + idx = idx + 1 + print "Burial Source: %s" % fields[idx] + bur_source = self.get_or_create_source(self.decode(fields[idx])) + idx = idx + 1 + elif fields[idx] == '#apubl': + print "This is a public record" + idx = idx + 1 + elif fields[idx] == '#apriv': + print "This is a private record" + idx = idx + 1 + elif dateRe.match( fields[idx]): + if not birth_date: + print "Birth Date: %s" % fields[idx] + birth_date = self.parse_date(self.decode(fields[idx])) + else: + print "Death Date: %s" % fields[idx] + death_date = self.parse_date(self.decode(fields[idx])) + idx = idx + 1 + else: + print "Unknown field for person: %s" % fields[idx] + idx = idx + 1 + + if public_name: + name = person.get_primary_name() + name.set_type("Birth Name") + person.add_alternate_name(name) + name = RelLib.Name() + name.set_type("Also Known As") + name.set_first_name(public_name) + name.set_surname(surname) + person.set_primary_name(name) + + i = 0 + for aka in nick_names: + if i == 0: + person.set_nick_name(aka) + else: + name = RelLib.Name() + name.set_type("Also Known As") + name.set_first_name(aka) + name.set_surname(surname) + person.add_alternate_name(name) + i = i + 1 + + for aka in firstname_aliases: + name = RelLib.Name() + name.set_type("Also Known As") + name.set_first_name(aka) + name.set_surname(surname) + person.add_alternate_name(name) + + for aka in name_aliases: + name = RelLib.Name() + name.set_type("Also Known As") + name.set_first_name(aka) + name.set_surname(surname) + person.add_alternate_name(name) + + for aka in surname_aliases: + name = RelLib.Name() + name.set_type("Also Known As") + if public_name: + name.set_first_name(public_name) + else: + name.set_first_name(firstname) + name.set_surname(aka) + person.add_alternate_name(name) + + if source: + person.add_source_reference(source) + + if birth_date or birth_place or birth_source: + birth = self.create_event("Birth", None, birth_date, birth_place, birth_source) + person.set_birth_handle(birth.get_handle()) + + if bapt_date or bapt_place or bapt_source: + babt = self.create_event("Baptism", None, bapt_date, bapt_place, bapt_source) + person.add_event_handle(babt.get_handle()) + + if death_date or death_place or death_source: + babt = self.create_event("Death", None, death_date, death_place, death_source) + person.set_death_handle(babt.get_handle()) + + if bur_date: + babt = self.create_event("Burial", None, bur_date, bur_place, bur_source) + person.add_event_handle(babt.get_handle()) + + if crem_date: + babt = self.create_event("Cremation", None, crem_date, bur_place, bur_source) + person.add_event_handle(babt.get_handle()) + + self.db.commit_person(person,self.trans) + + return (idx,person) + + def parse_date(self,field): + #Alex: this should do the trick + date = self.dp.parse(field) + return date + + def create_event(self,type,desc=None,date=None,place=None,source=None): + event = RelLib.Event() + if type: + event.set_name(type) + if desc: + event.set_description(desc) + if date: + event.set_date_object(date) + if place: + event.set_place_handle(place.get_handle()) + if source: + event.add_source_reference(source) + self.db.add_event(event,self.trans) + self.db.commit_event(event,self.trans) + return event + + def get_or_create_person(self,firstname,lastname): + person = None + mykey = firstname+lastname + if mykey in self.ikeys: + person = self.db.get_person_from_handle(self.ikeys[mykey]) + else: + person = RelLib.Person() + self.db.add_person(person,self.trans) + self.db.commit_person(person,self.trans) + self.ikeys[mykey] = person.get_handle() + return person + + def get_or_create_place(self,place_name): + place = None + if place_name in self.pkeys: + person = self.db.get_place_from_handle(self.pkeys[place_name]) + else: + place = RelLib.Place() + place.set_title(place_name) + self.db.add_place(place,self.trans) + self.db.commit_place(place,self.trans) + self.pkeys[place_name] = place.get_handle() + return place + + def get_or_create_source(self,source_name): + source = None + if source_name in self.skeys: + source = self.db.get_source_from_handle(self.skeys[source_name]) + else: + source = RelLib.Source() + source.set_title(source_name) + self.db.add_source(source,self.trans) + self.db.commit_source(source,self.trans) + self.skeys[source_name] = source.get_handle() + sref = RelLib.SourceRef() + sref.set_base_handle(source.get_handle()) + return sref + + def decode(self,s): + return( latin_utf8.latin_to_utf8( s.replace('_',' '))) + + def cnv(seld,s): + return( latin_utf8.latin_to_utf8(s)) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +_mime_type = 'application/x-geneweb' +_filter = gtk.FileFilter() +_filter.set_name(_('GeneWeb files')) +_filter.add_mime_type(_mime_type) + +from Plugins import register_import +register_import(importData,_filter,_mime_type,1) diff --git a/gramps2/src/plugins/Makefile.am b/gramps2/src/plugins/Makefile.am index 43cdd93b7..7197f30b8 100644 --- a/gramps2/src/plugins/Makefile.am +++ b/gramps2/src/plugins/Makefile.am @@ -55,7 +55,10 @@ pkgdata_PYTHON = \ rel_fi.py\ rel_fr.py\ rel_no.py\ - rel_sv.py + rel_sv.py\ + ImportGeneWeb.py\ + WriteGeneWeb.py + pkgpyexecdir = @pkgpyexecdir@/plugins pkgpythondir = @pkgpythondir@/plugins @@ -74,7 +77,8 @@ GLADEFILES = \ eval.glade\ leak.glade\ book.glade\ - writeftree.glade + writeftree.glade\ + genewebexport.glade dist_pkgdata_DATA = $(GLADEFILES) diff --git a/gramps2/src/plugins/WriteGeneWeb.py b/gramps2/src/plugins/WriteGeneWeb.py new file mode 100644 index 000000000..692e56973 --- /dev/null +++ b/gramps2/src/plugins/WriteGeneWeb.py @@ -0,0 +1,587 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2004 Martin Hawlisch +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# $Id$ + +"Export to GeneWeb" + +#------------------------------------------------------------------------- +# +# Standard Python Modules +# +#------------------------------------------------------------------------- +import os +import string +import time +import re + +#------------------------------------------------------------------------- +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade +import gnome + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import RelLib +import GenericFilter +import const +import Utils +import Date +import Errors +from gettext import gettext as _ +from QuestionDialog import ErrorDialog + +_title_string = _("Export to GeneWeb") + + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class GeneWebWriterOptionBox: + """ + Create a VBox with the option widgets and define methods to retrieve + the options. + """ + def __init__(self,person): + self.person = person + + def get_option_box(self): + self.restrict = 1 + self.private = 1 + + glade_file = "%s/genewebexport.glade" % os.path.dirname(__file__) + if not os.path.isfile(glade_file): + glade_file = "plugins/genewebexport.glade" + + self.topDialog = gtk.glade.XML(glade_file,"genewebExport","gramps") + self.topDialog.signal_autoconnect({ + "on_restrict_toggled": self.on_restrict_toggled + }) + + filter_obj = self.topDialog.get_widget("filter") + self.copy = 0 + + all = GenericFilter.GenericFilter() + all.set_name(_("Entire Database")) + all.add_rule(GenericFilter.Everyone([])) + + des = GenericFilter.GenericFilter() + des.set_name(_("Descendants of %s") % self.person.get_primary_name().get_name()) + des.add_rule(GenericFilter.IsDescendantOf([self.person.get_handle(),1])) + + ans = GenericFilter.GenericFilter() + ans.set_name(_("Ancestors of %s") % self.person.get_primary_name().get_name()) + ans.add_rule(GenericFilter.IsAncestorOf([self.person.get_handle(),1])) + + com = GenericFilter.GenericFilter() + com.set_name(_("People with common ancestor with %s") % + self.person.get_primary_name().get_name()) + com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.get_handle()])) + + self.filter_menu = GenericFilter.build_filter_menu([all,des,ans,com]) + filter_obj.set_menu(self.filter_menu) + + the_box = self.topDialog.get_widget('vbox1') + the_parent = self.topDialog.get_widget('dialog-vbox1') + the_parent.remove(the_box) + self.topDialog.get_widget("genewebExport").destroy() + return the_box + + def on_restrict_toggled(self,restrict): + active = restrict.get_active () + map (lambda x: x.set_sensitive (active), + [self.topDialog.get_widget("living"), + self.topDialog.get_widget("notes"), + self.topDialog.get_widget("sources")]) + + def parse_options(self): + self.restrict = self.topDialog.get_widget("restrict").get_active() + self.living = (self.restrict and + self.topDialog.get_widget("living").get_active()) + self.exclnotes = (self.restrict and + self.topDialog.get_widget("notes").get_active()) + self.exclsrcs = (self.restrict and + self.topDialog.get_widget("sources").get_active()) + self.private = self.topDialog.get_widget("private").get_active() + + self.cfilter = self.filter_menu.get_active().get_data("filter") + + self.images = self.topDialog.get_widget ("images").get_active () + if self.images: + images_path = self.topDialog.get_widget ("images_path") + self.images_path = unicode(images_path.get_text ()) + else: + self.images_path = "" + +class GeneWebWriter: + def __init__(self,database,person,cl=0,filename="",option_box=None): + self.db = database + self.person = person + self.option_box = option_box + self.cl = cl + self.filename = filename + + self.plist = {} + self.flist = {} + + self.persons_details_done = [] + self.persons_notes_done = [] + self.person_ids = {} + + if not option_box: + self.cl_setup() + else: + self.option_box.parse_options() + + self.restrict = self.option_box.restrict + self.living = self.option_box.living + self.exclnotes = self.option_box.exclnotes + self.exclsrcs = self.option_box.exclsrcs + self.private = self.option_box.private + self.copy = self.option_box.copy + self.images = self.option_box.images + self.images_path = self.option_box.images_path + + if self.option_box.cfilter == None: + for p in self.db.get_person_handles(sort_handles=False): + self.plist[p] = 1 + else: + try: + for p in self.option_box.cfilter.apply(self.db, self.db.get_person_handles(sort_handles=False)): + self.plist[p] = 1 + except Errors.FilterError, msg: + (m1,m2) = msg.messages() + ErrorDialog(m1,m2) + return + + self.flist = {} + for key in self.plist: + p = self.db.get_person_from_handle(key) + for family_handle in p.get_family_handle_list(): + self.flist[family_handle] = 1 + + def cl_setup(self): + self.restrict = 0 + self.private = 0 + self.copy = 0 + self.images = 0 + + for p in self.db.get_person_handles(sort_handles=False): + self.plist[p] = 1 + + self.flist = {} + + for key in self.plist: + p = self.db.get_person_from_handle(key) + for family_handle in p.get_family_handle_list(): + self.flist[family_handle] = 1 + + def writeln(self,text): + self.g.write(self.iso8859('%s\n' % (text))) + + def export_data(self,filename): + + self.dirname = os.path.dirname (filename) + try: + self.g = open(filename,"w") + except IOError,msg: + msg2 = _("Could not create %s") % filename + ErrorDialog(msg2,str(msg)) + return 0 + except: + ErrorDialog(_("Could not create %s") % filename) + return 0 + + if len(self.flist) < 1: + ErrorDialog(_("No families matched by selected filter")) + return 0 + + for key in self.flist: + self.write_family(key) + self.writeln("") + + self.g.close() + return 1 + + def write_family(self,family_handle): + family = self.db.get_family_from_handle(family_handle) + if family: + father_handle = family.get_father_handle() + if father_handle: + father = self.db.get_person_from_handle(father_handle) + mother_handle = family.get_mother_handle() + if mother_handle: + mother = self.db.get_person_from_handle(mother_handle) + self.writeln("fam %s %s +%s %s %s" % (self.get_ref_name(father), self.get_full_person_info_fam(father), self.get_wedding_data(family), self.get_ref_name(mother), self.get_full_person_info_fam(mother))) + self.write_witness( family) + self.write_sources( family.get_source_references()) + self.write_children( family, father) + self.write_notes( family, father, mother) + if not (self.restrict and self.exclnotes): + note = family.get_note() + if note and note != "": + note = note.replace('\n\r',' ') + note = note.replace('\r\n',' ') + note = note.replace('\n',' ') + note = note.replace('\r',' ') + self.writeln("comm %s" % note) + + def write_witness(self,family): + if self.restrict: + return + event_list = family.get_event_list() + for event_handle in event_list: + event = self.db.get_event_from_handle(event_handle) + if event.get_name() == "Marriage": + w_list = event.get_witness_list() + if w_list: + for witness in w_list: + if witness and witness.type == RelLib.Event.ID: + person = self.db.get_person_from_handle(witness.get_value()) + if person: + gender = "" + if person.get_gender() == RelLib.Person.male: + gender = "h" + elif person.get_gender() == RelLib.Person.female: + gender = "f" + self.writeln("wit %s %s %s" % (gender, self.get_ref_name(person), self.get_full_person_info_fam(person))) + + + def write_sources(self,reflist): + if self.restrict and self.exclnotes: + return + + if reflist: + for sr in reflist: + sbase = sr.get_base_handle() + if sbase: + source = self.db.get_source_from_handle(sbase) + if source: + self.writeln( "src %s" % (self.rem_spaces(source.get_title()))) + + def write_children(self,family, father): + father_lastname = father.get_primary_name().get_surname() + child_list = family.get_child_handle_list() + if child_list: + self.writeln("beg") + for child_handle in child_list: + child = self.db.get_person_from_handle(child_handle) + if child: + gender = "" + if child.get_gender() == RelLib.Person.male: + gender = "h" + elif child.get_gender() == RelLib.Person.female: + gender = "f" + self.writeln("- %s %s %s" % (gender, self.get_child_ref_name(child, father_lastname), self.get_full_person_info_child(child))) + self.writeln("end") + + def write_notes(self,family, father, mother): + if self.restrict and self.exclnotes: + return + + self.write_note_of_person(father) + self.write_note_of_person(mother) + child_list = family.get_child_handle_list() + if child_list: + for child_handle in child_list: + child = self.db.get_person_from_handle(child_handle) + if child: + self.write_note_of_person(child) + event_list = family.get_event_list() + for event_handle in event_list: + event = self.db.get_event_from_handle(event_handle) + if event.get_name() == "Marriage": + w_list = event.get_witness_list() + if w_list: + for witness in w_list: + if witness and witness.type == RelLib.Event.ID: + person = self.db.get_person_from_handle(witness.get_value()) + if person: + self.write_note_of_person(person) + + def write_note_of_person(self,person): + if self.persons_notes_done.count(person.get_handle()) == 0: + self.persons_notes_done.append(person.get_handle()) + note = person.get_note() + if note and note != "": + self.writeln("") + self.writeln("notes %s" % self.get_ref_name(person)) + self.writeln("beg") + self.writeln(note) + self.writeln("end notes") + + def get_full_person_info(self, person): + if self.restrict: + return "0 " + + retval = "" + + b_date = "0" + b_place = "" + birth_handle = person.get_birth_handle() + if birth_handle: + birth = self.db.get_event_from_handle(birth_handle) + if birth: + b_date = self.format_date( birth.get_date_object()) + place_handle = birth.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + b_place = place.get_title() + + if person.probably_alive(self.db): + d_date = "" + else: + d_date = "0" + d_place = "" + death_handle = person.get_death_handle() + if death_handle: + death = self.db.get_event_from_handle(death_handle) + if death: + d_date = self.format_date( death.get_date_object()) + place_handle = death.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + d_place = place.get_title() + + retval = retval + "%s " % b_date + if b_place != "": + retval = retval + "#bp %s " % self.rem_spaces(b_place) + retval = retval + "%s " % d_date + if d_place != "": + retval = retval + "#dp %s " % self.rem_spaces(d_place) + return retval + + def get_full_person_info_fam(self, person): + """Output full person data of a family member only if the person is not listed as a child""" + retval = "" + if self.persons_details_done.count(person.get_handle()) == 0: + is_child = 0 + pf_list = person.get_parent_family_handle_list() + if pf_list: + for (family_handle,mrel,frel) in pf_list: + if self.flist.has_key(family_handle): + is_child = 1 + if is_child == 0: + self.persons_details_done.append(person.get_handle()) + retval = self.get_full_person_info(person) + return retval + + + def get_full_person_info_child(self, person): + """Output full person data for a child, if not printed somewhere else""" + retval = "" + if self.persons_details_done.count(person.get_handle()) == 0: + self.persons_details_done.append(person.get_handle()) + retval = self.get_full_person_info(person) + return retval + + def rem_spaces(self,string): + string = string.replace(' ','_') + return string + + def get_ref_name(self,person): + surname = self.rem_spaces( person.get_primary_name().get_surname()) + firstname = "Living" + if not (person.probably_alive(self.db) and self.restrict and self.living): + firstname = self.rem_spaces( person.get_primary_name().get_first_name()) + if not self.person_ids.has_key(person.get_handle()): + self.person_ids[person.get_handle()] = len(self.person_ids) + ret = "%s %s.%d" % (surname, firstname, self.person_ids[person.get_handle()]) + return ret + + def get_child_ref_name(self,person,father_lastname): + surname = self.rem_spaces( person.get_primary_name().get_surname()) + firstname = "Living" + if not (person.probably_alive(self.db) and self.restrict and self.living): + firstname = self.rem_spaces( person.get_primary_name().get_first_name()) + if not self.person_ids.has_key(person.get_handle()): + self.person_ids[person.get_handle()] = len(self.person_ids) + if surname != father_lastname: + ret = "%s.%d %s" % (firstname, self.person_ids[person.get_handle()], surname) + else: + ret = "%s.%d" % (firstname, self.person_ids[person.get_handle()]) + return ret + + def get_wedding_data(self,family): + ret = ""; + event_list = family.get_event_list() + m_date = "" + m_place = "" + m_source = "" + married = 0 + eng_date = "" + eng_place = "" + eng_source = "" + engagend = 0 + div_date = "" + divorced = 0 + for event_handle in event_list: + event = self.db.get_event_from_handle(event_handle) + if event.get_name() == "Marriage": + married = 1 + m_date = self.format_date( event.get_date_object()) + place_handle = event.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + m_place = place.get_title() + m_source = self.get_primary_source( event.get_source_references()) + if event.get_name() == "Engagement": + engagend = 1 + eng_date = self.format_date( event.get_date_object()) + place_handle = event.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + eng_place = place.get_title() + eng_source = self.get_primary_source( event.get_source_references()) + if event.get_name() == "Divorce": + divorced = 1 + div_date = self.format_date( event.get_date_object()) + if married == 1: + if m_date != "": + ret = ret + m_date + ret = ret + " " + + if m_place != "": + ret = ret + "#mp %s " % self.rem_spaces( m_place) + + if m_source != "": + ret = ret + "#ms %s " % self.rem_spaces( m_source) + elif engagend == 1: + """Geneweb only supports either Marriage or engagement""" + if eng_date != "": + ret = ret + eng_date + ret = ret + " " + + if eng_place != "": + ret = ret + "#mp %s " % self.rem_spaces( m_place) + + if eng_source != "": + ret = ret + "#ms %s " % self.rem_spaces( m_source) + else: + if family.get_relationship() != const.FAMILY_MARRIED: + """Not married or engaged""" + ret = ret + " #nm " + + if divorced == 1: + if div_date != "": + ret = ret + "-%s " %div_date + else: + ret = ret + "-0 " + + return ret + + def get_primary_source(self,reflist): + ret = "" + if reflist: + for sr in reflist: + sbase = sr.get_base_handle() + if sbase: + source = self.db.get_source_from_handle(sbase) + if source: + if ret != "": + ret = ret + ", " + ret = ret + source.get_title() + return ret + + def format_single_date(self,subdate,cal,mode): + retval = "" + (day,month,year,sl) = subdate + + cal_type = "" + if cal == Date.CAL_HEBREW: + type = "H" + elif cal == Date.CAL_FRENCH: + type = "F" + elif cal == Date.CAL_JULIAN: + type = "J" + + mode_prefix = "" + if mode == Date.MOD_ABOUT: + mode_prefix = "~" + elif mode == Date.MOD_BEFORE: + mode_prefix = "<" + elif mode == Date.MOD_AFTER: + mode_prefix = ">" + + if year > 0: + if month > 0: + if day > 0: + retval = "%s%s/%s/%s%s" % (mode_prefix,day,month,year,cal_type) + else: + retval = "%s%s/%s%s" % (mode_prefix,month,year,cal_type) + else: + retval = "%s%s%s" % (mode_prefix,year,cal_type) + return retval + + + def format_date(self,date): + retval = "" + + strval = date.get_text() + if strval: + retval = "0(%s)" % self.rem_spaces(strval) + elif not date.is_empty(): + mod = date.get_modifier() + cal = cal = date.get_calendar() + if mod == Date.MOD_SPAN or mod == Date.MOD_RANGE: + retval = "%s..%s" % (self.format_single_date(date.get_start_date(), cal,mod), + self.format_single_date(date.get_stop_date(),cal,mod)) + else: + retval = self.format_single_date(date.get_start_date(),cal,mod) + return retval + + def iso8859(self,s): + return s.encode('iso-8859-1') + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def exportData(database,filename,person,option_box): + ret = 0 + try: + gw = GeneWebWriter(database,person,0,filename,option_box) + ret = gw.export_data(filename) + except: + import DisplayTrace + DisplayTrace.DisplayTrace() + return ret + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +_title = _('GeneWeb') +_description = _('GeneWeb is a web based genealogy program.') +_config = (_('GeneWeb export options'),GeneWebWriterOptionBox) +_filename = 'gw' + +from Plugins import register_export +register_export(exportData,_title,_description,_config,_filename) diff --git a/gramps2/src/plugins/genewebexport.glade b/gramps2/src/plugins/genewebexport.glade new file mode 100644 index 000000000..79cb1eaaf --- /dev/null +++ b/gramps2/src/plugins/genewebexport.glade @@ -0,0 +1,387 @@ + + + + + + + + True + + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + 400 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + False + + + + True + False + 8 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-help + True + GTK_RELIEF_NORMAL + True + -11 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + 12 + True + 6 + 3 + False + 6 + 12 + + + + True + <b>Options</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 3 + 0 + 1 + fill + + + + + + + True + _Filter: + True + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + filter + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + -1 + + + + True + + + + + 2 + 3 + 1 + 2 + fill + + + + + + + 3 + True + True + _Do not include records marked private + True + GTK_RELIEF_NORMAL + True + True + False + True + + + 1 + 3 + 2 + 3 + fill + + + + + + + 3 + True + True + _Restrict data on living people + True + GTK_RELIEF_NORMAL + True + True + False + True + + + + 1 + 3 + 3 + 4 + fill + + + + + + + True + 3 + 2 + False + 6 + 12 + + + + True + True + Exclude _notes + True + GTK_RELIEF_NORMAL + True + True + False + True + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + Exclude sour_ces + True + GTK_RELIEF_NORMAL + True + True + False + True + + + 1 + 2 + 2 + 3 + fill + + + + + + + True + True + Use _Living as first name + True + GTK_RELIEF_NORMAL + True + True + False + True + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + + + 0 + 1 + 0 + 1 + shrink|fill + fill + + + + + 1 + 3 + 4 + 5 + fill + + + + + + True + False + 0 + + + + True + True + R_eference images from path: + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + True + True + 0 + media + True + * + False + + + 0 + True + True + + + + + 1 + 3 + 5 + 6 + fill + + + + + 0 + False + False + GTK_PACK_END + + + + + 0 + True + True + + + + + + +