diff --git a/ChangeLog b/ChangeLog index 18cb5463a..987ac444a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2006-05-01 Alex Roitman + * various: merge changes from gramps20. + 2006-04-30 Alex Roitman * src/plugins/Verify.py (Verify.run_tool): Typo. diff --git a/po/ChangeLog b/po/ChangeLog index 26501575a..7916f1927 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,6 @@ +2006-05-01 Alex Roitman + * POTFILES.in: Add new file. + 2006-04-27 Alex Roitman * pt_BR.po: Convert to utf8 encoding. * zh_CN.po: Convert to utf8 encoding. diff --git a/po/POTFILES.in b/po/POTFILES.in index 5cb3c4867..1c4a9c1dd 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -81,6 +81,7 @@ src/DateHandler/_Date_lt.py src/DateHandler/_Date_nl.py src/DateHandler/_DateParser.py src/DateHandler/_Date_ru.py +src/DateHandler/_Date_sk.py src/DateHandler/_Date_sv.py src/DateHandler/__init__.py src/docgen/AbiWord2Doc.py @@ -218,6 +219,7 @@ src/plugins/rel_hu.py src/plugins/rel_it.py src/plugins/rel_no.py src/plugins/rel_ru.py +src/plugins/rel_sk.py src/plugins/rel_sv.py src/plugins/ReorderIds.py src/plugins/SimpleBookTitle.py diff --git a/po/check_po b/po/check_po new file mode 100755 index 000000000..b59edbc62 --- /dev/null +++ b/po/check_po @@ -0,0 +1,216 @@ +#! /usr/bin/env python +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2006 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: check_po,v 1.1.2.6 2006/04/22 18:30:33 rshura Exp $ + +import sys +import re + +f = open('template.po') +template_total = 0 +for line in f.xreadlines(): + try: + if (line.split()[0] == 'msgid'): + template_total += 1 + except: + pass +f.close() + +NONE = 0 +MSGID = 1 +MSGSTR = 2 + +all_total = {} +all_fuzzy = {} +all_untranslated = {} +all_percent_s = {} +all_named_s = {} +all_bnamed_s = {} +all_context = {} +all_coverage = {} +all_template_coverage = {} + + +def strip_quotes(st): + if len(st.strip()) > 2: + return st.strip()[1:-1] + else: + return "" + +args = sys.argv +while len(args) > 1: + args = args[1:] + + f = open(args[0],"r") + + mode = NONE + fuzzy = False + fuzzy_count = 0 + string_map = {} + current_msgid = "" + current_msgstr = "" + + for line in f.xreadlines(): + data = line.split(None,1) + if mode == NONE: + if len(data) > 0 and data[0] == "msgid": + mode = MSGID + if len(data) > 1: + current_msgid = strip_quotes(data[1]) + elif (len(data) > 0) and (data[0] == "#,") \ + and (data[1] == 'fuzzy\n'): + fuzzy = True + elif mode == MSGID: + if data[0][0] == '"': + current_msgid += strip_quotes(line) + elif data[0] == "msgstr": + mode = MSGSTR + if len(data) > 1: + current_msgstr = strip_quotes(data[1]) + elif mode == MSGSTR: + if line == "" or line[0] == "#": + mode = NONE + if fuzzy: + fuzzy = False + fuzzy_count += 1 + else: + string_map[current_msgid] = current_msgstr + elif len(data) > 0 and data[0][0] == '"': + current_msgstr += strip_quotes(line) + + f.close() + + named = re.compile('%\((\w+)\)\d*s') + bnamed = re.compile('%\((\w+)\)\d*[^sd]') + + total = len(string_map) + fuzzy_count + untranslated = 0 + percent_s = 0 + percent_s_list = [] + named_s = 0 + named_s_list = [] + bnamed_s = 0 + bnamed_s_list = [] + context = 0 + context_list = [] + + for (msgid,msgstr) in string_map.items(): + if msgstr == "": + untranslated += 1 + continue + + cnt1 = msgid.count('%s') + cnt2 = msgstr.count('%s') + if cnt1 != cnt2: + percent_s += 1 + percent_s_list.append(msgid) + + list1 = named.findall(msgid) + list2 = named.findall(msgstr) + if len(list1) != len(list2): + percent_s += 1 + percent_s_list.append(msgid) + + list1.sort() + list2.sort() + if list1 != list2: + named_s += 1 + named_s_list.append(msgid) + + match = bnamed.match(msgstr) + if match: + bnamed_s +=1 + bnamed_s_list.append(msgstr) + + has_context1 = (msgid.count('|') > 0) + has_context2 = (msgstr.count('|') > 0) + if has_context1 and has_context2 and (msgid != msgstr): + context += 1 + context_list.append(msgid) + + + coverage = (1.0 - (float(untranslated)/float(total))) * 100 + template_coverage = coverage * float(total) / float(template_total) + + print "File: %s" % args[0] + print "Template total: %d" % template_total + print "PO total: %d" % total + all_total[args[0]] = total + print "Fuzzy: %d" % fuzzy_count + all_fuzzy[args[0]] = fuzzy_count + print "Untranslated: %d" % untranslated + all_untranslated[args[0]] = untranslated + print "%%s mismatches: %d" % percent_s + all_percent_s[args[0]] = percent_s + print "%%()s mismatches: %d" % named_s + all_named_s[args[0]] = named_s + print "%%() missing s/d: %d" % bnamed_s + all_bnamed_s[args[0]] = bnamed_s + print "Runaway context: %d" % context + all_context[args[0]] = context + print "PO Coverage: %5.2f%%" % coverage + all_coverage[args[0]] = coverage + print "Template Coverage: %5.2f%%" % template_coverage + all_template_coverage[args[0]] = coverage + + if percent_s: + print "\n-------- %s mismatches --------------" + for i in percent_s_list: + print "'%s' : '%s'" % (i, string_map[i]) + + if named_s: + print "\n-------- %()s mismatches ------------" + for i in named_s_list: + print "'%s' : '%s'" % (i, string_map[i]) + + if bnamed_s: + print "\n-------- %() missing s or d ---------" + for i in bnamed_s_list: + print "'%s' : '%s'" % (i, string_map[i]) + + if context: + print "\n-------- Runaway context in translation ---------" + for i in context_list: + print "'%s' : '%s'" % (i, string_map[i]) + print "" + + +if len(sys.argv) > 2: + print "\n\nFile \tTotal \tFuzzy \tUntranslated \t%s mismatch \t%()s mismatch \tmissing s/d \tcontext \tCoverage" + for pofile in sys.argv[1:]: + print "%s \t%5d \t%7d \t%7d \t%7d \t%7d \t%7d \t%7d \t%3.2f%% \t%3.2f%%" %\ + (pofile, + all_total[pofile], + all_fuzzy[pofile], + all_untranslated[pofile], + all_percent_s[pofile], + all_named_s[pofile], + all_bnamed_s[pofile], + all_context[pofile], + all_coverage[pofile], + all_template_coverage[pofile] + ) + +f = open("used_strings.txt","w") +keys = string_map.keys() +keys.sort() +for i in keys: + f.write(i + "\n") +f.close() diff --git a/src/ArgHandler.py b/src/ArgHandler.py index 5a835b70b..08c0522b2 100644 --- a/src/ArgHandler.py +++ b/src/ArgHandler.py @@ -135,8 +135,11 @@ class ArgHandler: o,v = options[opt_ix] if o in ( '-O', '--open'): fname = v - ftype = Mime.get_type( - os.path.abspath(os.path.expanduser(fname))) + fullpath = os.path.abspath(os.path.expanduser(fname)) + if not os.path.exists(fullpath): + print "Input file does not exist: %s" % fullpath + continue + ftype = Mime.get_type(fullpath) if opt_ix.+)\s+to\s+(?P.+)", re.IGNORECASE) self._range = re.compile("(bet|bet.|between)\s+(?P.+)\s+and\s+(?P.+)", @@ -279,7 +282,7 @@ class DateParser: self._itext2 = re.compile('(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$' % self._imon_str, re.IGNORECASE) self._numeric = re.compile("((\d+)[/\.])?((\d+)[/\.])?(\d+)\s*$") - self._iso = re.compile("(\d+)-(\d+)-(\d+)\s*$") + self._iso = re.compile("(\d+)(/(\d+))?-(\d+)-(\d+)\s*$") self._rfc = re.compile("(%s,)?\s+(\d|\d\d)\s+%s\s+(\d+)\s+\d\d:\d\d(:\d\d)?\s+(\+|-)\d\d\d\d" % (self._rfc_day_str,self._rfc_mon_str)) @@ -325,7 +328,7 @@ class DateParser: if groups[2] == None: y = self._get_int(groups[1]) d = 0 - s = None + s = False else: d = self._get_int(groups[1]) y = int(groups[3]) @@ -346,8 +349,8 @@ class DateParser: d = self._get_int(groups[0]) if groups[2] == None: - y = 0 - s = None + y = None + s = False else: y = int(groups[3]) s = groups[4] != None @@ -378,12 +381,14 @@ class DateParser: if match: groups = match.groups() y = self._get_int(groups[0]) - m = self._get_int(groups[1]) - d = self._get_int(groups[2]) - if gregorian_valid((d,m,y)): - return (d,m,y,False) - else: + m = self._get_int(groups[3]) + d = self._get_int(groups[4]) + if check and not check((d,m,y)): return Date.EMPTY + if groups[2]: + return (d,m,y,True) + else: + return (d,m,y,False) match = self._rfc.match(text) if match: @@ -391,21 +396,32 @@ class DateParser: d = self._get_int(groups[2]) m = self._rfc_mons_to_int[groups[3]] y = self._get_int(groups[4]) - if gregorian_valid((d,m,y)): - return (d,m,y,False) - else: - return Date.EMPTY + value = (d,m,y,False) + if check and not check((d,m,y)): + value = Date.EMPTY + return value match = self._numeric.match(text) if match: groups = match.groups() - if self.dmy: - m = self._get_int(groups[3]) - d = self._get_int(groups[1]) + if self.ymd: + # '1789' and ymd: incomplete date + if groups[1] == None: + y = self._get_int(groups[4]) + m = 0 + d = 0 + else: + y = self._get_int(groups[1]) + m = self._get_int(groups[3]) + d = self._get_int(groups[4]) else: - m = self._get_int(groups[1]) - d = self._get_int(groups[3]) - y = self._get_int(groups[4]) + y = self._get_int(groups[4]) + if self.dmy: + m = self._get_int(groups[3]) + d = self._get_int(groups[1]) + else: + m = self._get_int(groups[1]) + d = self._get_int(groups[3]) value = (d,m,y,False) if check and not check((d,m,y)): value = Date.EMPTY @@ -417,26 +433,24 @@ class DateParser: """ Try parsing calendar. - Return calendar index and the remainder of text. + Return calendar index and the text with calendar removed. """ match = self._cal.match(text) if match: - grps = match.groups() - cal = self.calendar_to_int[grps[1].lower()] - text = grps[0] + cal = self.calendar_to_int[match.group(2).lower()] + text = match.group(1) + match.group(3) return (text,cal) def match_quality(self,text,qual): """ Try matching quality. - Return quality index and the remainder of text. + Return quality index and the text with quality removed. """ match = self._qual.match(text) if match: - grps = match.groups() - qual = self.quality_to_int[grps[0].lower()] - text = grps[1] + qual = self.quality_to_int[match.group(2).lower()] + text = match.group(1) + match.group(3) return (text,qual) def match_span(self,text,cal,qual,date): @@ -448,8 +462,16 @@ class DateParser: match = self._span.match(text) if match: text_parser = self.parser[cal] - start = self._parse_subdate(match.group('start'),text_parser) - stop = self._parse_subdate(match.group('stop'),text_parser) + (text1,bc1) = self.match_bce(match.group('start')) + start = self._parse_subdate(text1,text_parser) + if bc1: + start = self.invert_year(start) + + (text2,bc2) = self.match_bce(match.group('stop')) + stop = self._parse_subdate(text2,text_parser) + if bc2: + stop = self.invert_year(stop) + date.set(qual,Date.MOD_SPAN,cal,start + stop) return 1 return 0 @@ -463,8 +485,16 @@ class DateParser: match = self._range.match(text) if match: text_parser = self.parser[cal] - start = self._parse_subdate(match.group('start'),text_parser) - stop = self._parse_subdate(match.group('stop'),text_parser) + (text1,bc1) = self.match_bce(match.group('start')) + start = self._parse_subdate(text1,text_parser) + if bc1: + start = self.invert_year(start) + + (text2,bc2) = self.match_bce(match.group('stop')) + stop = self._parse_subdate(text2,text_parser) + if bc2: + stop = self.invert_year(stop) + date.set(qual,Date.MOD_RANGE,cal,start + stop) return 1 return 0 @@ -473,12 +503,16 @@ class DateParser: """ Try matching BCE qualifier. - Return BCE (True/False) and the remainder of text. + Return BCE (True/False) and the text with matched part removed. """ match = self._bce_re.match(text) bc = False if match: - text = match.groups()[0] + # bce is in the match.group(2) + try: + text = match.group(1) + match.group(3) + except: + print "MATCH:", match.groups() bc = True return (text,bc) @@ -492,8 +526,8 @@ class DateParser: match = self._modifier.match(text) if match: grps = match.groups() - start = self._parse_subdate(grps[1]) - mod = self.modifier_to_int.get(grps[0].lower(),Date.MOD_NONE) + start = self._parse_subdate(grps[1], self.parser[cal]) + mod = self.modifier_to_int.get(grps[0].lower(), Date.MOD_NONE) if bc: date.set(qual,mod,cal,self.invert_year(start)) else: @@ -504,7 +538,7 @@ class DateParser: match = self._modifier_after.match(text) if match: grps = match.groups() - start = self._parse_subdate(grps[0]) + start = self._parse_subdate(grps[0], self.parser[cal]) mod = self.modifier_after_to_int.get(grps[1].lower(), Date.MOD_NONE) if bc: @@ -529,7 +563,6 @@ class DateParser: Parses the text and sets the date according to the parsing. """ - date.set_text_value(text) qual = Date.QUAL_NONE cal = Date.CAL_GREGORIAN diff --git a/src/DateHandler/_Date_de.py b/src/DateHandler/_Date_de.py index 55dc253a8..f2993aad1 100644 --- a/src/DateHandler/_Date_de.py +++ b/src/DateHandler/_Date_de.py @@ -88,17 +88,17 @@ class DateParserDE(DateParser): } calendar_to_int = { - u'Gregorianisch' : Date.CAL_GREGORIAN, - u'Greg.' : Date.CAL_GREGORIAN, - u'Julianisch' : Date.CAL_JULIAN, - u'Jul.' : Date.CAL_JULIAN, - u'Hebräisch' : Date.CAL_HEBREW, - u'Hebr.' : Date.CAL_HEBREW, - u'Islamisch' : Date.CAL_ISLAMIC, - u'Isl.' : Date.CAL_ISLAMIC, - u'Französisch Republikanisch': Date.CAL_FRENCH, - u'Franz.' : Date.CAL_FRENCH, - u'Persisch' : Date.CAL_PERSIAN, + u'gregorianisch' : Date.CAL_GREGORIAN, + u'greg.' : Date.CAL_GREGORIAN, + u'julianisch' : Date.CAL_JULIAN, + u'jul.' : Date.CAL_JULIAN, + u'hebräisch' : Date.CAL_HEBREW, + u'hebr.' : Date.CAL_HEBREW, + u'islamisch' : Date.CAL_ISLAMIC, + u'isl.' : Date.CAL_ISLAMIC, + u'französisch republikanisch': Date.CAL_FRENCH, + u'franz.' : Date.CAL_FRENCH, + u'persisch' : Date.CAL_PERSIAN, } quality_to_int = { @@ -109,7 +109,10 @@ class DateParserDE(DateParser): u'ber.' : Date.QUAL_CALCULATED, } - bce = DateParser.bce + ["vor (unserer|der) Zeit(rechnung)?", "v\. (u|d)\. Z\.", "vor Christus", "vor Christi Geburt", "v\. Chr\."] + bce = ["vor unserer Zeitrechnung", "vor unserer Zeit", + "vor der Zeitrechnung", "vor der Zeit", + "v. u. Z.", "v. d. Z.", "v.u.Z.", "v.d.Z.", + "vor Christi Geburt", "vor Christus", "v. Chr."] + DateParser.bce def init_strings(self): DateParser.init_strings(self) @@ -128,8 +131,8 @@ class DateParserDE(DateParser): class DateDisplayDE(DateDisplay): calendar = ( - "", u" (Julianisch)", u" (Hebräisch)", - u" (Französisch Republikanisch)", u" (Persisch)", u" (Islamisch)" + "", u" (julianisch)", u" (hebräisch)", + u" (französisch republikanisch)", u" (persisch)", u" (islamisch)" ) _mod_str = ("",u"vor ",u"nach ",u"etwa ","","","") @@ -146,14 +149,17 @@ class DateDisplayDE(DateDisplay): def _display_gregorian(self,date_val): year = self._slash_year(date_val[2],date_val[3]) if self.format == 0: - value = self.display_iso(date_val) + return self.display_iso(date_val) elif self.format == 1: - if date_val[0] == 0 and date_val[1] == 0: - value = str(date_val[2]) + if date_val[3]: + return self.display_iso(date_val) else: - value = self._tformat.replace('%m',str(date_val[1])) - value = value.replace('%d',str(date_val[0])) - value = value.replace('%Y',str(date_val[2])) + if date_val[0] == 0 and date_val[1] == 0: + value = str(date_val[2]) + else: + value = self._tformat.replace('%m',str(date_val[1])) + value = value.replace('%d',str(date_val[0])) + value = value.replace('%Y',str(date_val[2])) elif self.format == 2: # Month Day, Year if date_val[0] == 0: @@ -190,7 +196,10 @@ class DateDisplayDE(DateDisplay): value = "%s %s" % (self._MONS[date_val[1]],year) else: value = "%d. %s %s" % (date_val[0],self._MONS[date_val[1]],year) - return value + if date_val[2] < 0: + return self._bce_str % value + else: + return value def display(self,date): """ diff --git a/src/DateHandler/_Date_es.py b/src/DateHandler/_Date_es.py index fe6f6c501..b339216dd 100644 --- a/src/DateHandler/_Date_es.py +++ b/src/DateHandler/_Date_es.py @@ -97,7 +97,7 @@ class DateParserES(DateParser): DateParser.init_strings(self) _span_1 = [u'de'] _span_2 = [u'a'] - _range_1 = [u'ent.',u'ent',u'entre'] + _range_1 = [u'entre',u'ent\.',u'ent'] _range_2 = [u'y'] self._span = re.compile("(%s)\s+(?P.+)\s+(%s)\s+(?P.+)" % ('|'.join(_span_1),'|'.join(_span_2)), diff --git a/src/DateHandler/_Date_fi.py b/src/DateHandler/_Date_fi.py index db2dd20f7..4d9c46769 100644 --- a/src/DateHandler/_Date_fi.py +++ b/src/DateHandler/_Date_fi.py @@ -72,7 +72,7 @@ class DateParserFI(DateParser): u'j.' : Date.MOD_AFTER, } - bce = ["ekr", "ekr\."] + bce = [u"ekr.", u"ekr"] calendar_to_int = { u'gregoriaaninen' : Date.CAL_GREGORIAN, @@ -99,9 +99,9 @@ class DateParserFI(DateParser): def init_strings(self): DateParser.init_strings(self) # date, whitespace - self._span = re.compile("(?P.+)\s+(-)\s+(?P.+)", + self._span = re.compile(u"(?P.+)\s+(-)\s+(?P.+)", re.IGNORECASE) - self._range = re.compile("(vuosien\s*)?(?P.+)\s+ja\s+(?P.+)\s+välillä", + self._range = re.compile(u"(vuosien\s*)?(?P.+)\s+ja\s+(?P.+)\s+välillä", re.IGNORECASE) #------------------------------------------------------------------------- @@ -112,15 +112,15 @@ class DateParserFI(DateParser): class DateDisplayFI(DateDisplay): calendar = ("", - u"(Juliaaninen)", - u"(Heprealainen)", - u"(Ranskan v.)", - u"(Persialainen)", - u"(Islamilainen)") + u"(juliaaninen)", + u"(heprealainen)", + u"(ranskan v.)", + u"(persialainen)", + u"(islamilainen)") - _qual_str = ("", "arviolta", "laskettuna") + _qual_str = (u"", u"arviolta", u"laskettuna") - _bce_str = "%s ekr." + _bce_str = u"%s ekr." formats = ( "VVVV-KK-PP (ISO)", @@ -139,7 +139,7 @@ class DateDisplayFI(DateDisplay): if mod == Date.MOD_TEXTONLY: return date.get_text() if start == Date.EMPTY: - return "" + return u"" # select numerical date format self.format = 1 @@ -147,32 +147,32 @@ class DateDisplayFI(DateDisplay): if mod == Date.MOD_SPAN: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](date.get_stop_date()) - text = "%s - %s" % (d1, d2) + text = u"%s - %s" % (d1, d2) elif mod == Date.MOD_RANGE: stop = date.get_stop_date() if start[0] == 0 and start[1] == 0 and stop[0] == 0 and stop[1] == 0: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](stop) - text = "vuosien %s ja %s välillä" % (d1, d2) + text = u"vuosien %s ja %s välillä" % (d1, d2) else: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](stop) - text = "%s ja %s välillä" % (d1, d2) + text = u"%s ja %s välillä" % (d1, d2) else: text = self.display_cal[date.get_calendar()](start) if mod == Date.MOD_AFTER: - text = text + " jälkeen" + text = text + u" jälkeen" elif mod == Date.MOD_ABOUT: - text = "noin " + text + text = u"noin " + text elif mod == Date.MOD_BEFORE: - text = "ennen " + text + text = u"ennen " + text if qual: # prepend quality - text = "%s %s" % (self._qual_str[qual], text) + text = u"%s %s" % (self._qual_str[qual], text) if cal: # append calendar type - text = "%s %s" % (text, self.calendar[cal]) + text = u"%s %s" % (text, self.calendar[cal]) return text diff --git a/src/DateHandler/_Date_fr.py b/src/DateHandler/_Date_fr.py index 2b5cab0de..263aa5f6f 100644 --- a/src/DateHandler/_Date_fr.py +++ b/src/DateHandler/_Date_fr.py @@ -90,7 +90,6 @@ class DateParserFR(DateParser): modifier_to_int = { u'avant' : Date.MOD_BEFORE, u'av.' : Date.MOD_BEFORE, - u'av' : Date.MOD_BEFORE, u'après' : Date.MOD_AFTER, u'ap.' : Date.MOD_AFTER, u'ap' : Date.MOD_AFTER, @@ -134,8 +133,11 @@ class DateParserFR(DateParser): def init_strings(self): DateParser.init_strings(self) - self._span = re.compile("(de)\s+(?P.+)\s+(à)\s+(?P.+)",re.IGNORECASE) - self._range = re.compile("(entre|ent|ent.)\s+(?P.+)\s+(et)\s+(?P.+)",re.IGNORECASE) + # This self._numeric is different from the base + # by allowing space after the slash/dot + self._numeric = re.compile("((\d+)[/\. ])?\s*((\d+)[/\.])?\s*(\d+)\s*$") + self._span = re.compile(u"(de)\s+(?P.+)\s+(à)\s+(?P.+)",re.IGNORECASE) + self._range = re.compile(u"(entre|ent\.|ent)\s+(?P.+)\s+(et)\s+(?P.+)",re.IGNORECASE) self._text2 =re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._mon_str, re.IGNORECASE) self._jtext2 =re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._mon_str, @@ -155,24 +157,27 @@ class DateDisplayFR(DateDisplay): _mod_str = ("",u"avant ",u"après ",u"vers ","","","") - _qual_str = ("","estimée ","calculée ","") + _qual_str = ("",u"estimée ",u"calculée ","") formats = ( - "AAAA-MM-DD (ISO)", "Numérique", "Mois Jour, Année", - "MOI Jour, Année", "Jour Mois, Année", "Jour MOIS Année" + "AAAA-MM-JJ (ISO)", "Numérique", "Mois Jour, Année", + "MOI Jour, Année", "Jour Mois, Année", "Jour MOI Année" ) def _display_gregorian(self,date_val): year = self._slash_year(date_val[2],date_val[3]) if self.format == 0: - value = self.display_iso(date_val) + return self.display_iso(date_val) elif self.format == 1: - if date_val[0] == 0 and date_val[1] == 0: - value = str(date_val[2]) + if date_val[3]: + return self.display_iso(date_val) else: - value = self._tformat.replace('%m',str(date_val[1])) - value = value.replace('%d',str(date_val[0])) - value = value.replace('%Y',str(date_val[2])) + if date_val[0] == 0 and date_val[1] == 0: + value = str(date_val[2]) + else: + value = self._tformat.replace('%m',str(date_val[1])) + value = value.replace('%d',str(date_val[0])) + value = value.replace('%Y',str(date_val[2])) elif self.format == 2: # Month Day, Year if date_val[0] == 0: @@ -209,7 +214,11 @@ class DateDisplayFR(DateDisplay): value = "%s %s" % (self._MONS[date_val[1]],year) else: value = "%d. %s %s" % (date_val[0],self._MONS[date_val[1]],year) - return value + if date_val[2] < 0: + return self._bce_str % value + else: + return value + def display(self,date): """ @@ -243,5 +252,6 @@ class DateDisplayFR(DateDisplay): # Register classes # #------------------------------------------------------------------------- -register_datehandler(('fr_FR','fr','french','fr_CA','fr_BE','fr_CH'), - DateParserFR,DateDisplayFR) +register_datehandler( + ('fr_FR','fr','french','fr_CA','fr_BE','fr_CH','fr_LU'), + DateParserFR,DateDisplayFR) diff --git a/src/DateHandler/_Date_lt.py b/src/DateHandler/_Date_lt.py index f23c27554..24c2516f3 100644 --- a/src/DateHandler/_Date_lt.py +++ b/src/DateHandler/_Date_lt.py @@ -56,17 +56,17 @@ class DateParserLT(DateParser): } calendar_to_int = { - u'Grigaliaus' : Date.CAL_GREGORIAN, + u'grigaliaus' : Date.CAL_GREGORIAN, u'g' : Date.CAL_GREGORIAN, - u'Julijaus' : Date.CAL_JULIAN, + u'julijaus' : Date.CAL_JULIAN, u'j' : Date.CAL_JULIAN, - u'Hebrajų' : Date.CAL_HEBREW, + u'hebrajų' : Date.CAL_HEBREW, u'h' : Date.CAL_HEBREW, - u'Islamo' : Date.CAL_ISLAMIC, + u'islamo' : Date.CAL_ISLAMIC, u'i' : Date.CAL_ISLAMIC, - u'Prancuzų Respublikos': Date.CAL_FRENCH, + u'prancuzų respublikos': Date.CAL_FRENCH, u'r' : Date.CAL_FRENCH, - u'Persų' : Date.CAL_PERSIAN, + u'persų' : Date.CAL_PERSIAN, u'p' : Date.CAL_PERSIAN, } @@ -96,18 +96,20 @@ class DateParserLT(DateParser): class DateDisplayLT(DateDisplay): calendar = ( - "", u" (Julijaus)", - u" (Hebrajų)", - u" (Prancuzų Respublikos)", - u" (Persų)", - u" (Islamo)" + u"", u" (julijaus)", + u" (hebrajų)", + u" (prancuzų respublikos)", + u" (persų)", + u" (islamo)" ) - _mod_str = ("",u"iki ", + _mod_str = (u"", + u"prieš ", u"po ", - u"apie ","","","") + u"apie ", + u"",u"",u"") - _qual_str = ("","apytikriai ","apskaičiuota ") + _qual_str = (u"",u"apytikriai ",u"apskaičiuota ") formats = ( "YYYY-MM-DD (ISO)", "Skaitmeninis", "Mėnuo Diena, Metai", @@ -132,7 +134,7 @@ class DateDisplayLT(DateDisplay): elif mod == Date.MOD_SPAN: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](date.get_stop_date()) - return "%sс %s %s %s%s" % (qual_str,d1,u'iki',d2,self.calendar[cal]) + return "%s%s %s %s %s%s" % (qual_str,u'nuo',d1,u'iki',d2,self.calendar[cal]) elif mod == Date.MOD_RANGE: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](date.get_stop_date()) diff --git a/src/DateHandler/_Date_nl.py b/src/DateHandler/_Date_nl.py index d1946d306..94fae618d 100644 --- a/src/DateHandler/_Date_nl.py +++ b/src/DateHandler/_Date_nl.py @@ -92,17 +92,17 @@ class DateParserNL(DateParser): } calendar_to_int = { - u'Gregoriaans' : Date.CAL_GREGORIAN, - u'Greg.' : Date.CAL_GREGORIAN, - u'Juliaans' : Date.CAL_JULIAN, - u'Jul.' : Date.CAL_JULIAN, - u'Hebreeuws' : Date.CAL_HEBREW, - u'Hebr.' : Date.CAL_HEBREW, - u'Islamitisch' : Date.CAL_ISLAMIC, - u'Isl.' : Date.CAL_ISLAMIC, - u'Franse republiek': Date.CAL_FRENCH, - u'Fran.' : Date.CAL_FRENCH, - u'Persisch' : Date.CAL_PERSIAN, + u'gregoriaans' : Date.CAL_GREGORIAN, + u'greg.' : Date.CAL_GREGORIAN, + u'juliaans' : Date.CAL_JULIAN, + u'jul.' : Date.CAL_JULIAN, + u'hebreeuws' : Date.CAL_HEBREW, + u'hebr.' : Date.CAL_HEBREW, + u'islamitisch' : Date.CAL_ISLAMIC, + u'isl.' : Date.CAL_ISLAMIC, + u'franse republiek': Date.CAL_FRENCH, + u'fran.' : Date.CAL_FRENCH, + u'persisch' : Date.CAL_PERSIAN, } quality_to_int = { @@ -112,9 +112,7 @@ class DateParserNL(DateParser): u'ber.' : Date.QUAL_CALCULATED, } - bce = DateParser.bce + ["voor onze tijdrekening", - "voor Christus", - "v\. Chr\."] + bce = ["voor onze tijdrekening","voor Christus","v. Chr."] + DateParser.bce def init_strings(self): DateParser.init_strings(self) @@ -137,8 +135,8 @@ class DateParserNL(DateParser): class DateDisplayNL(DateDisplay): calendar = ( - "", u" (Juliaans)", u" (Hebreeuws)", - u" (Franse Republiek)", u" (Persisch)", u" (Islamitisch)" + "", u" (juliaans)", u" (hebreeuws)", + u" (franse republiek)", u" (persisch)", u" (islamitisch)" ) _mod_str = ("",u"voor ",u"na ",u"rond ","","","") @@ -157,14 +155,17 @@ class DateDisplayNL(DateDisplay): if self.format == 0: return self.display_iso(date_val) elif self.format == 1: - # Numeric - if date_val[0] == 0 and date_val[1] == 0: - value = str(date_val[2]) + if date_val[3]: + return self.display_iso(date_val) else: - value = self._tformat.replace('%m',str(date_val[1])) - value = value.replace('%d',str(date_val[0])) - value = value.replace('%Y',str(abs(date_val[2]))) - value = value.replace('-','/') + # Numeric + if date_val[0] == 0 and date_val[1] == 0: + value = str(date_val[2]) + else: + value = self._tformat.replace('%m',str(date_val[1])) + value = value.replace('%d',str(date_val[0])) + value = value.replace('%Y',str(abs(date_val[2]))) + value = value.replace('-','/') elif self.format == 2: # Month Day, Year if date_val[0] == 0: diff --git a/src/DateHandler/_Date_ru.py b/src/DateHandler/_Date_ru.py index 82fe3a25e..728e07bca 100644 --- a/src/DateHandler/_Date_ru.py +++ b/src/DateHandler/_Date_ru.py @@ -50,7 +50,7 @@ from _DateHandler import register_datehandler class DateParserRU(DateParser): modifier_to_int = { - u'до' : Date.MOD_BEFORE, + u'перед' : Date.MOD_BEFORE, u'по' : Date.MOD_BEFORE, u'после' : Date.MOD_AFTER, u'п.' : Date.MOD_AFTER, @@ -97,11 +97,77 @@ class DateParserRU(DateParser): u'выч' : Date.QUAL_CALCULATED, } + hebrew_to_int = { + u"тишрей":1, + u"хешван":2, + u"кислев":3, + u"тевет":4, + u"шеват":5, + u"адар":6, + u"адар бет":7, + u"нисан":8, + u"ияр":9, + u"сиван":10, + u"таммуз":11, + u"ав":12, + u"элул":13, + } + + islamic_to_int = { + u"мухаррам":1, + u"сафар":2, + u"раби-аль-авваль":3, + u"раби-ассани":4, + u"джумада-аль-уля":5, + u"джумада-аль-ахира":6, + u"раджаб":7, + u"шаабан":8, + u"рамадан":9, + u"шавваль":10, + u"зуль-каада":11, + u"зуль-хиджжа":12, + } + + persian_to_int = { + u"фарвардин":1, + u"урдбихишт":2, + u"хурдад":3, + u"тир":4, + u"мурдад":5, + u"шахривар":6, + u"михр":7, + u"абан":8, + u"азар":9, + u"дай":10, + u"бахман":11, + u"исфаидармуз":12, + } + + french_to_int = { + u"вандемьер":1, + u"брюмер":2, + u"фример":3, + u"нивоз":4, + u"плювиоз":5, + u"вантоз":6, + u"жерминаль":7, + u"флореаль":8, + u"прериаль":9, + u"мессидор":10, + u"термидор":11, + u"фрюктидор":12, + u"дополнит.":13, + } + + bce = [ + u'до нашей эры', u'до н. э.', u'до н.э.', + u'до н э', u'до нэ'] + DateParser.bce + def init_strings(self): DateParser.init_strings(self) _span_1 = [u'с',u'от'] - _span_2 = [u'по',u'до'] - _range_1 = [u'между',u'меж',u'меж.'] + _span_2 = [u'по'] + _range_1 = [u'между',u'меж\.',u'меж'] _range_2 = [u'и'] self._span = re.compile("(%s)\s+(?P.+)\s+(%s)\s+(?P.+)" % ('|'.join(_span_1),'|'.join(_span_2)), @@ -118,24 +184,92 @@ class DateParserRU(DateParser): class DateDisplayRU(DateDisplay): calendar = ( - "", u" (юлианский)", + u"", + u" (юлианский)", u" (еврейский)", u" (республиканский)", u" (персидский)", u" (исламский)" ) - _mod_str = ("",u"до ", + _mod_str = ( + u"", + u"перед ", u"после ", - u"около ","","","") + u"около ", + u"",u"",u"") - _qual_str = ("","оцен ","вычисл ") + _qual_str = (u"",u"оцен ",u"вычисл ") + + _bce_str = u"%s до н.э." formats = ( "ГГГГ-ММ-ДД (ISO)", "Численный", "Месяц День, Год", "МЕС ДД, ГГГГГ", "День Месяц, Год", "ДД МЕС, ГГГГГ" ) + _hebrew = ( u"", + u"Тишрей", + u"Хешван", + u"Кислев", + u"Тевет", + u"Шеват", + u"Адар", + u"Адар бет", + u"Нисан", + u"Ияр", + u"Сиван", + u"Таммуз", + u"Ав", + u"Элул", + ) + + _islamic = ( u"", + u"Мухаррам", + u"Сафар", + u"Раби-аль-авваль", + u"Раби-ассани", + u"Джумада-аль-уля", + u"Джумада-аль-ахира", + u"Раджаб", + u"Шаабан", + u"Рамадан", + u"Шавваль", + u"Зуль-каада", + u"Зуль-хиджжа", + ) + + _persian = ( u"", + u"Фарвардин", + u"Урдбихишт", + u"Хурдад", + u"Тир", + u"Мурдад", + u"Шахривар", + u"Михр", + u"Абан", + u"Азар", + u"Дай", + u"Бахман", + u"Исфаидармуз", + ) + + _french = ( u"", + u"Вандемьер", + u"Брюмер", + u"Фример", + u"Нивоз", + u"Плювиоз", + u"Вантоз", + u"Жерминаль", + u"Флореаль", + u"Прериаль", + u"Мессидор", + u"Термидор", + u"Фрюктидор", + u"Дополнит." + ) + def display(self,date): """ Returns a text string representing the date. diff --git a/src/DateHandler/_Date_sk.py b/src/DateHandler/_Date_sk.py new file mode 100644 index 000000000..cec31ac20 --- /dev/null +++ b/src/DateHandler/_Date_sk.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2004-2006 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: Date_sk.py,v 1.1.2.4 2006/04/16 03:20:06 rshura Exp $ + +""" +Slovak-specific classes for parsing and displaying dates. +""" + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import re + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import Date +from DateParser import DateParser +from DateDisplay import DateDisplay + +#------------------------------------------------------------------------- +# +# Slovak parser +# +#------------------------------------------------------------------------- +class DateParserSK(DateParser): + + modifier_to_int = { + u'pred' : Date.MOD_BEFORE, + u'do' : Date.MOD_BEFORE, + u'po' : Date.MOD_AFTER, + u'asi' : Date.MOD_ABOUT, + u'okolo' : Date.MOD_ABOUT, + u'pribl.' : Date.MOD_ABOUT, + } + + calendar_to_int = { + u'gregoriánsky' : Date.CAL_GREGORIAN, + u'g' : Date.CAL_GREGORIAN, + u'juliánský' : Date.CAL_JULIAN, + u'j' : Date.CAL_JULIAN, + u'hebrejský' : Date.CAL_HEBREW, + u'h' : Date.CAL_HEBREW, + u'islamský' : Date.CAL_ISLAMIC, + u'i' : Date.CAL_ISLAMIC, + u'republikánsky': Date.CAL_FRENCH, + u'r' : Date.CAL_FRENCH, + u'perzský' : Date.CAL_PERSIAN, + u'p' : Date.CAL_PERSIAN, + } + + quality_to_int = { + u'odhadovaný' : Date.QUAL_ESTIMATED, + u'odh.' : Date.QUAL_ESTIMATED, + u'vypočítaný' : Date.QUAL_CALCULATED, + u'vyp.' : Date.QUAL_CALCULATED, + } + + def init_strings(self): + DateParser.init_strings(self) + _span_1 = [u'od'] + _span_2 = [u'do'] + _range_1 = [u'medzi'] + _range_2 = [u'a'] + self._span = re.compile("(%s)\s+(?P.+)\s+(%s)\s+(?P.+)" % + ('|'.join(_span_1),'|'.join(_span_2)), + re.IGNORECASE) + self._range = re.compile("(%s)\s+(?P.+)\s+(%s)\s+(?P.+)" % + ('|'.join(_range_1),'|'.join(_range_2)), + re.IGNORECASE) + +#------------------------------------------------------------------------- +# +# Slovak display +# +#------------------------------------------------------------------------- +class DateDisplaySK(DateDisplay): + + calendar = ( + "", u" (juliánský)", u" (hebrejský)", + u" (republikánsky)", u" (perzský)", u" (islamský)" + ) + + _mod_str = ("",u"pred ",u"po ",u"okolo ","","","") + + _qual_str = ("","odh. ","vyp. ") + + formats = ( + "RRRR-MM-DD (ISO)", "numerický", "Mesiac Deň, Rok", + "MES Deň, Rok", "Deň, Mesiac, Rok", "Deň MES Rok" + ) + + def display(self,date): + """ + Returns a text string representing the date. + """ + mod = date.get_modifier() + cal = date.get_calendar() + qual = date.get_quality() + start = date.get_start_date() + + qual_str = self._qual_str[qual] + + if mod == Date.MOD_TEXTONLY: + return date.get_text() + elif start == Date.EMPTY: + return "" + elif mod == Date.MOD_SPAN: + d1 = self.display_cal[cal](start) + d2 = self.display_cal[cal](date.get_stop_date()) + return "%s%s %s %s %s%s" % (qual_str,u'od',d1,u'do',d2,self.calendar[cal]) + elif mod == Date.MOD_RANGE: + d1 = self.display_cal[cal](start) + d2 = self.display_cal[cal](date.get_stop_date()) + return "%s%s %s %s %s%s" % (qual_str,u'medzi',d1,u'a',d2,self.calendar[cal]) + else: + text = self.display_cal[date.get_calendar()](start) + return "%s%s%s%s" % (qual_str,self._mod_str[mod],text,self.calendar[cal]) + +#------------------------------------------------------------------------- +# +# Register classes +# +#------------------------------------------------------------------------- +from DateHandler import register_datehandler +register_datehandler(('sk_SK','sk','SK'),DateParserSK, DateDisplaySK) diff --git a/src/DateHandler/_Date_sv.py b/src/DateHandler/_Date_sv.py index ff8df7bce..9f2b28cee 100644 --- a/src/DateHandler/_Date_sv.py +++ b/src/DateHandler/_Date_sv.py @@ -145,7 +145,7 @@ class DateDisplaySv(DateDisplay): if mod == Date.MOD_TEXTONLY: return date.get_text() elif start == Date.EMPTY: - return "" + return u"" elif mod == Date.MOD_SPAN: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](date.get_stop_date()) @@ -157,7 +157,7 @@ class DateDisplaySv(DateDisplay): self.calendar[cal]) else: text = self.display_cal[date.get_calendar()](start) - return "%s%s%s%s" % (qual_str,self._mod_str[mod], + return u"%s%s%s%s" % (qual_str,self._mod_str[mod], text,self.calendar[cal]) #------------------------------------------------------------------------- diff --git a/src/GenericFilter.py b/src/GenericFilter.py index fc6bcfdb0..156fe94d3 100644 --- a/src/GenericFilter.py +++ b/src/GenericFilter.py @@ -2126,14 +2126,13 @@ class IsLessThanNthGenerationAncestorOfDefaultPerson(Rule): def prepare(self,db): self.db = db p = db.get_default_person() - if p == 0: - self.apply = lambda db,p: False - else: + if p: self.def_handle = p.get_handle() self.apply = self.apply_real self.map = {} self.init_ancestor_list(self.def_handle, 1) - + else: + self.apply = lambda db,p: False def init_ancestor_list(self,handle,gen): # if self.map.has_key(p.get_handle()) == 1: diff --git a/src/GrampsDb/_ReadGedcom.py b/src/GrampsDb/_ReadGedcom.py index 2af85d305..273b9c864 100644 --- a/src/GrampsDb/_ReadGedcom.py +++ b/src/GrampsDb/_ReadGedcom.py @@ -164,6 +164,10 @@ lds_status = { _event_family_str = _("%(event_name)s of %(family)s") _event_person_str = _("%(event_name)s of %(person)s") +_transtable = string.maketrans('','') +_delc = _transtable[0:31] +_transtable2 = _transtable[0:128] + ('?' * 128) + #------------------------------------------------------------------------- # # GEDCOM events to GRAMPS events conversion @@ -233,7 +237,7 @@ def importData(database, filename, callback=None, use_trans=False): def import2(database, filename, callback, codeset, use_trans): # add some checking here try: - np = NoteParser(filename, False) + np = NoteParser(filename, False, codeset) g = GedcomParser(database,filename, callback, codeset, np.get_map(), np.get_lines(),np.get_persons()) except IOError,msg: @@ -334,43 +338,73 @@ class CurrentState: # #------------------------------------------------------------------------- class NoteParser: - def __init__(self, filename,broken): - self.name_map = {} + def __init__(self, filename,broken,override): + if override: + if override == 1: + self.cnv = ansel_to_utf8 + elif override == 2: + self.cnv = latin_to_utf8 + else: + self.cnv = nocnv + else: + f = open(filename,"rU") + for index in range(50): + line = f.readline().split() + if len(line) > 2 and line[1] == 'CHAR': + if line[2] == "ANSEL": + self.cnv = ansel_to_utf8 + elif line[2] in ["UNICODE","UTF-8","UTF8"]: + self.cnv = nocnv + else: + self.cnv = latin_to_utf8 + f.close() - self.count = 0 - self.person_count = 0 - f = open(filename,"rU") - innote = False + self.name_map = {} + + self.count = 0 + self.person_count = 0 + f = open(filename,"rU") + innote = False - for line in f: + for line in f: + try: + text = string.translate(line,_transtable,_delc) + except: + text = line - self.count += 1 - if innote: - match = contRE.match(line) - if match: - noteobj.append("\n" + match.groups()[0]) - continue + try: + text = self.cnv(text) + except: + text = string.translate(text,_transtable2) - match = concRE.match(line) - if match: - if broken: - noteobj.append(" " + match.groups()[0]) - else: - noteobj.append(match.groups()[0]) - continue - innote = False - else: - match = noteRE.match(line) - if match: - data = match.groups()[0] - noteobj = RelLib.Note() - self.name_map["@%s@" % data] = noteobj - noteobj.append(match.groups()[1]) - innote = True - elif personRE.match(line): - self.person_count += 1 - - f.close() + self.count += 1 + if innote: + match = contRE.match(text) + if match: + noteobj.append("\n" + match.groups()[0]) + continue + + match = concRE.match(text) + if match: + if broken: + noteobj.append(" " + match.groups()[0]) + else: + noteobj.append(match.groups()[0]) + continue + # Here we have finished parsing CONT/CONC tags for the NOTE + # and ignored the rest of the tags (SOUR,CHAN,REFN,RIN). + innote = False + match = noteRE.match(text) + if match: + data = match.groups()[0] + noteobj = RelLib.Note() + self.name_map["@%s@" % data] = noteobj + noteobj.append(match.groups()[1]) + innote = True + elif personRE.match(line): + self.person_count += 1 + + f.close() def get_map(self): return self.name_map @@ -392,9 +426,6 @@ class Reader: self.f = open(name,'rU') self.current_list = [] self.eof = False - self.transtable = string.maketrans('','') - self.delc = self.transtable[0:31] - self.transtable2 = self.transtable[0:128] + ('?' * 128) self.cnv = lambda s: unicode(s) self.broken_conc = False self.cnt = 0 @@ -426,11 +457,11 @@ class Reader: break line = line.split(None,2) + [''] - val = line[2].translate(self.transtable,self.delc) + val = line[2].translate(_transtable,_delc) try: val = self.cnv(val) except: - val = self.cnv(val.translate(self.transtable2)) + val = self.cnv(val.translate(_transtable2)) try: level = int(line[0]) @@ -1168,23 +1199,21 @@ class GedcomParser: del event def parse_note_base(self,matches,obj,level,old_note,task): - note = old_note - if matches[2] and matches[2][0] == "@": # reference to a named note defined elsewhere + # reference to a named note defined elsewhere + if matches[2] and matches[2][0] == "@": note_obj = self.note_map.get(matches[2]) if note_obj: - return note_obj.get() + new_note = note_obj.get() else: - return u"" + new_note = u"" else: - if old_note: - note = u"%s\n%s" % (old_note,matches[2]) - else: - note = matches[2] - if type(note) != unicode: - print type(note),type(matches[2]) - - task(note) + new_note = matches[2] + self.parse_continue_data(level) self.ignore_sub_junk(level+1) + if old_note: + note = u"%s\n%s" % (old_note,matches[2]) + else: + note = new_note + task(note) return note def parse_note(self,matches,obj,level,old_note): @@ -1489,7 +1518,7 @@ class GedcomParser: event.set_cause(info) self.parse_cause(event,level+1) elif matches[1] in (TOKEN_NOTE,TOKEN_OFFI): - info = matches[2] + info = self.parse_note(matches,event,level+1,note) if note == "": note = info else: @@ -1548,7 +1577,7 @@ class GedcomParser: event.set_cause(info) self.parse_cause(event,level+1) elif matches[1] == TOKEN_NOTE: - info = matches[2] + info = self.parse_note(matches,event,level+1,note) if note == "": note = info else: @@ -1604,7 +1633,7 @@ class GedcomParser: elif matches[1] == TOKEN_DATE: note = "%s\n\n" % ("Date : %s" % matches[2]) elif matches[1] == TOKEN_NOTE: - info = matches[2] + info = self.parse_note(matches,attr,level+1,note) if note == "": note = info else: @@ -1741,6 +1770,7 @@ class GedcomParser: def parse_header_source(self): genby = "" + note = "" while True: matches = self.get_next() if int(matches[0]) < 1: @@ -1788,7 +1818,7 @@ class GedcomParser: date.date = matches[2] self.def_src.set_data_item('Creation date',matches[2]) elif matches[1] == TOKEN_NOTE: - note = matches[2] + note = self.parse_note(matches,self.def_src,2,note) elif matches[1] == TOKEN_UNKNOWN: self.ignore_sub_junk(2) else: @@ -2126,7 +2156,7 @@ class GedcomParser: state.add_to_note(self.parse_optional_note(2)) def func_person_famc(self,matches,state): - ftype,note = self.parse_famc_type(2,state.person) + ftype,famc_note = self.parse_famc_type(2,state.person) handle = self.find_family_handle(matches[2][1:-1]) for f in self.person.get_parent_family_handle_list(): diff --git a/src/GrampsDb/_WriteGedcom.py b/src/GrampsDb/_WriteGedcom.py index 9cad08616..0f5952788 100644 --- a/src/GrampsDb/_WriteGedcom.py +++ b/src/GrampsDb/_WriteGedcom.py @@ -325,16 +325,6 @@ def make_date(subdate,calendar,mode): # # #------------------------------------------------------------------------- -def fmtline(text,limit,level,endl): - new_text = [] - while len(text) > limit: - new_text.append(text[0:limit-1]) - text = text[limit:] - if len(text) > 0: - new_text.append(text) - app = "%s%d CONC " % (endl,level+1) - return app.join(new_text) - #------------------------------------------------------------------------- # # @@ -782,6 +772,36 @@ class GedcomWriter: self.dump_event_stats(event) + for attr in family.get_attribute_list(): + if self.private and attr.get_privacy(): + continue + name = attr.get_type() + + if name in ["AFN", "RFN", "_UID"]: + self.writeln("1 %s %s" % ( name, attr.get_value())) + continue + + if Utils.personal_attributes.has_key(name): + val = Utils.personal_attributes[name] + else: + val = "" + value = self.cnvtxt(attr.get_value()).replace('\r',' ') + if val: + if value: + self.writeln("1 %s %s" % (val, value)) + else: + self.writeln("1 %s" % val) + else: + self.writeln("1 EVEN") + if value: + self.writeln("2 TYPE %s %s" % (self.cnvtxt(name), value)) + else: + self.writeln("2 TYPE %s" % self.cnvtxt(name)) + if attr.get_note(): + self.write_long_text("NOTE",2,self.cnvtxt(attr.get_note())) + for srcref in attr.get_source_references(): + self.write_source_ref(2,srcref) + for person_handle in family.get_child_handle_list(): if not self.plist.has_key(person_handle): continue @@ -815,6 +835,9 @@ class GedcomWriter: continue self.write_photo(photo,1) + if family.get_note(): + self.write_long_text("NOTE",1,self.cnvtxt(family.get_note())) + self.write_change(1,family.get_change_time()) self.update() @@ -831,11 +854,17 @@ class GedcomWriter: for (source_id, source) in sorted: self.writeln("0 @%s@ SOUR" % source_id) if source.get_title(): - self.writeln("1 TITL %s" % fmtline(self.cnvtxt(source.get_title()),248,1,self.nl)) + self.write_long_text('TITL',1, + "%s" % self.cnvtxt(source.get_title())) + if source.get_author(): - self.writeln("1 AUTH %s" % self.cnvtxt(source.get_author())) + self.write_long_text("AUTH", 1, + "%s" % self.cnvtxt(source.get_author())) + if source.get_publication_info(): - self.writeln("1 PUBL %s" % self.cnvtxt(source.get_publication_info())) + self.write_long_text("PUBL", 1,"%s" % self.cnvtxt( + source.get_publication_info())) + if source.get_abbreviation(): self.writeln("1 ABBR %s" % self.cnvtxt(source.get_abbreviation())) if self.images: @@ -1113,6 +1142,10 @@ class GedcomWriter: else: for line in textlines: ll = len(line) + if ll == 0: + self.writeln("%s " % prefix) + prefix = "%d CONT" % (level+1) + continue while ll > 0: brkpt = 70 if ll > brkpt: @@ -1142,6 +1175,10 @@ class GedcomWriter: else: for line in textlines: ll = len(line) + if ll == 0: + self.writeln("%s " % prefix) + prefix = "%d CONT" % (level+1) + continue while ll > 0: brkpt = 70 if ll > brkpt: diff --git a/src/QuestionDialog.py b/src/QuestionDialog.py index 44e753d3e..655450ee8 100644 --- a/src/QuestionDialog.py +++ b/src/QuestionDialog.py @@ -47,7 +47,7 @@ import Config try: ICON = pixbuf_new_from_file(const.icon) except: - pass + ICON = None class SaveDialog: def __init__(self,msg1,msg2,task1,task2,parent=None): diff --git a/src/RelLib/_Person.py b/src/RelLib/_Person.py index b8150a588..625e748a8 100644 --- a/src/RelLib/_Person.py +++ b/src/RelLib/_Person.py @@ -231,7 +231,8 @@ class Person(PrimaryObject,SourceBase,NoteBase,MediaBase, elif classname == 'Family': return handle in (self.family_list + self.parent_family_list) elif classname == 'Place': - return handle in self.lds_ord_list + return handle in [ordinance.place for ordinance + in self.lds_ord_list] return False def _remove_handle_references(self, classname, handle_list): @@ -255,9 +256,9 @@ class Person(PrimaryObject,SourceBase,NoteBase,MediaBase, if handle not in handle_list ] self.parent_family_list = new_list elif classname == 'Place': - for ordinance in self.lds_ord_list: - if ordinance.place in handle_list: - ordinance.place = None + new_list = [ordinance for ordinance in self.lds_ord_list + if ordinance.place not in handle_list] + self.lds_ord_list = new_list def _replace_handle_reference(self, classname, old_handle, new_handle): if classname == 'Event': @@ -285,9 +286,11 @@ class Person(PrimaryObject,SourceBase,NoteBase,MediaBase, ix = self.parent_family_list.index(old_handle) self.parent_family_list[ix] = new_handle elif classname == 'Place': - for ordinance in self.lds_ord_list: - if ordinance.place == old_handle: - ordinance.place = new_handle + handle_list = [ordinance.place for ordinance in self.lds_ord_list] + while old_handle in handle_list: + ix = handle_list.index(old_handle) + self.lds_ord_list[ix].place = new_handle + handle_list[ix] = '' def get_text_data_list(self): """ diff --git a/src/Spell.py b/src/Spell.py index c21da6573..acdd23d76 100644 --- a/src/Spell.py +++ b/src/Spell.py @@ -1,7 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2005 Donald N. Allingham +# Copyright (C) 2005-2006 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 @@ -46,11 +46,16 @@ try: import locale lang = locale.getlocale()[0] - gtkspell.Spell(gtk.TextView()).set_language(lang) - success = True - + if lang == None: + print _("Spelling checker cannot be used without language set.") + print _("Set your locale appropriately to use spelling checker.") + else: + gtkspell.Spell(gtk.TextView()).set_language(lang) + success = True except ImportError, msg: print _("Spelling checker is not installed") +except TypeError,msg: + print "Spell.py: ", msg except RuntimeError,msg: print "Spell.py: ", msg except SystemError,msg: diff --git a/src/Utils.py b/src/Utils.py index d115274a0..aa5b584b3 100644 --- a/src/Utils.py +++ b/src/Utils.py @@ -630,7 +630,7 @@ def probably_alive(person,db,current_year=None,limit=0): death = db.get_event_from_handle(person.death_ref.ref) if death.get_date_object().get_start_date() != RelLib.Date.EMPTY: death_year = death.get_date_object().get_year() - if death_year - limit < current_year: + if death_year + limit < current_year: return False # Look for Cause Of Death, Burial or Cremation events. @@ -643,7 +643,7 @@ def probably_alive(person,db,current_year=None,limit=0): if not death_year: death_year = ev.get_date_object().get_year() if ev.get_date_object().get_start_date() != RelLib.Date.EMPTY: - if ev.get_date_object().get_year() - limit < current_year: + if ev.get_date_object().get_year() + limit < current_year: return False # For any other event of this person, check whether it happened # too long ago. If so then the person is likely dead now. diff --git a/src/date_test.py b/src/date_test.py new file mode 100644 index 000000000..75717f30e --- /dev/null +++ b/src/date_test.py @@ -0,0 +1,265 @@ +# -*- coding: utf-8 -*- +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2006 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: date_test.py,v 1.1.2.5 2006/04/20 04:04:31 rshura Exp $ + +"""Testscript for date displayer/parser""" + + +import os +import sys +import traceback +import locale +import gettext + +import gtk + +if os.environ.has_key("GRAMPSI18N"): + loc = os.environ["GRAMPSI18N"] +else: + loc = "/usr/share/locale" + +try: + locale.setlocale(locale.LC_ALL,'C') + locale.setlocale(locale.LC_ALL,'') +except locale.Error: + pass +except ValueError: + pass + +gettext.textdomain("gramps") +gettext.install("gramps",loc,unicode=1) + +import DateHandler +from DateHandler import parser as _dp +from DateHandler import displayer as _dd +import Date + +print locale.getlocale(locale.LC_TIME) +print _dd +print _dp +print + +date_tests = {} + +# first the "basics". +testset = "basic test" +dates = [] +calendar = Date.CAL_GREGORIAN +for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): + for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): + for month in range(1,13): + d = Date.Date() + d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment") + dates.append( d) + for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): + for month1 in range(1,13): + for month2 in range(1,13): + d = Date.Date() + d.set(quality,modifier,calendar,(4,month1,1789,False,5,month2,1876,False),"Text comment") + dates.append( d) + modifier = Date.MOD_TEXTONLY + d = Date.Date() + d.set(quality,modifier,calendar,Date.EMPTY,"This is a textual date") + dates.append( d) +date_tests[testset] = dates + +# incomplete dates (day or month missing) +testset = "partial date" +dates = [] +calendar = Date.CAL_GREGORIAN +for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): + for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): + d = Date.Date() + d.set(quality,modifier,calendar,(0,11,1789,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,0,1789,False),"Text comment") + dates.append( d) + for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): + d = Date.Date() + d.set(quality,modifier,calendar,(4,10,1789,False,0,11,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(4,10,1789,False,0,0,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,10,1789,False,5,11,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,10,1789,False,0,11,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,10,1789,False,0,0,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,0,1789,False,5,11,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,0,1789,False,0,11,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(0,0,1789,False,0,0,1876,False),"Text comment") + dates.append( d) +date_tests[testset] = dates + +# slash-dates +testset = "slash-dates" +dates = [] +calendar = Date.CAL_GREGORIAN +for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): + for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): + # normal date + d = Date.Date() + d.set(quality,modifier,calendar,(4,11,1789,True),"Text comment") + dates.append( d) + for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): + d = Date.Date() + d.set(quality,modifier,calendar,(4,11,1789,True,5,10,1876,False),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(4,11,1789,False,5,10,1876,True),"Text comment") + dates.append( d) + d = Date.Date() + d.set(quality,modifier,calendar,(4,11,1789,True,5,10,1876,True),"Text comment") + dates.append( d) +date_tests[testset] = dates + +# BCE +testset = "B. C. E." +dates = [] +calendar = Date.CAL_GREGORIAN +for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): + for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): + # normal date + d = Date.Date() + d.set(quality,modifier,calendar,(4,11,-90,False),"Text comment") + dates.append( d) + for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): + d = Date.Date() + d.set(quality,modifier,calendar,(5,10,-90,False,4,11,-90,False),"Text comment") + dates.append( d) + d = Date.Date() +date_tests[testset] = dates + +# test for all other different calendars +testset = "Non-gregorian" +dates = [] +for calendar in (Date.CAL_JULIAN, Date.CAL_HEBREW, Date.CAL_ISLAMIC, Date.CAL_FRENCH, Date.CAL_PERSIAN): + for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): + for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): + d = Date.Date() + d.set(quality,modifier,calendar,(4,11,1789,False),"Text comment") + dates.append( d) + for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): + d = Date.Date() + d.set(quality,modifier,calendar,(4,10,1789,False,5,11,1876,False),"Text comment") + dates.append( d) +quality = Date.QUAL_NONE +modifier = Date.MOD_NONE +for calendar in (Date.CAL_JULIAN, Date.CAL_ISLAMIC, Date.CAL_PERSIAN): + for month in range(1,13): + d = Date.Date() + d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment") + dates.append( d) +for calendar in (Date.CAL_HEBREW, Date.CAL_FRENCH): + for month in range(1,14): + d = Date.Date() + d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment") + dates.append( d) +date_tests[testset] = dates + +# now run the tests using all available date formats +cal_str = [ "CAL_GREGORIAN", "CAL_JULIAN", "CAL_HEBREW", "CAL_FRENCH", "CAL_PERSIAN", "CAL_ISLAMIC"] +mod_str = ["MOD_NONE", "MOD_BEFORE", "MOD_AFTER", "MOD_ABOUT", "MOD_RANGE", "MOD_SPAN", "MOD_TEXTONLY"] +qua_str = ["QUAL_NONE", "QUAL_ESTIMATED", "QUAL_CALCULATED"] +stats = {} +formats = DateHandler.get_date_formats() +for testset in date_tests.keys(): + print "\n##### %s:\n" % testset + stats[testset] = [0,0,testset] + for format in range( len( DateHandler.get_date_formats())): + DateHandler.set_format(format) + print "\n## %s:\n" % DateHandler.get_date_formats()[format] + for dateval in date_tests[testset]: + failed = True + ex = None + errmsg = "" + datestr = None + ndate = None + ntxt = None + if dateval.modifier != Date.MOD_TEXTONLY: + dateval.text = "Comment. Format: %s" % DateHandler.get_date_formats()[format] + try: + datestr = _dd.display( dateval) + try: + ndate = _dp.parse( datestr) + ntxt = _dd.display( ndate) + if ndate: + if dateval.is_equal( ndate): + failed = False + else: + if dateval.modifier != Date.MOD_TEXTONLY and ndate.modifier == Date.MOD_TEXTONLY: + errmsg = "FAILED! (was parsed as text)" + else: + errmsg = "FAILED!" + else: + errmsg = "FAILED: DateParser returned no Date" + except: + ex = "Parser" + errmsg = "FAILED: DateParser Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),) + except: + ex = "Display" + errmsg = "FAILED: DateDisplay Exception: %s" % ("".join(traceback.format_exception(*sys.exc_info())),) + if not failed: + stats[testset][1] = stats[testset][1] + 1 + print datestr + print ntxt + print "ok" + else: + stats[testset][0] = stats[testset][0] + 1 + print "input was:" + print " calendar: %s" % cal_str[dateval.calendar] + print " modifier: %s" % mod_str[dateval.modifier] + print " quality: %s" % qua_str[dateval.quality] + print " dateval: %s" % str(dateval.dateval) + print " text: '%s'" % dateval.text + if ex == "Display": + print "This date is not displayable because of an Exception:" + print errmsg + else: + print "DateDisplay gives: '%s'" % datestr + if ex == "Parser": + print "This date is not parsable because of an Exception:" + print errmsg + else: + print "parsed date was:" + print " calendar: %s" % cal_str[ndate.calendar] + print " modifier: %s" % mod_str[ndate.modifier] + print " quality: %s" % qua_str[ndate.quality] + print " dateval: %s" % str(ndate.dateval) + print " text: '%s'" % ndate.text + print "this gives:'%s'" % ntxt + print + +print "RESULT:" +for result in stats: + print "% 13s: % 5d dates ok, % 4d failed." % (stats[result][2],stats[result][1],stats[result][0]) diff --git a/src/docgen/ODFDoc.py b/src/docgen/ODFDoc.py index 45539fc71..89e54eff9 100644 --- a/src/docgen/ODFDoc.py +++ b/src/docgen/ODFDoc.py @@ -101,7 +101,7 @@ class ODFDoc(BaseDoc.BaseDoc): def init(self): - assert(self.init_called==False) + assert (not self.init_called) self.init_called = True current_locale = locale.getlocale() @@ -164,8 +164,6 @@ class ODFDoc(BaseDoc.BaseDoc): self.cntnt.write(' >\n') self.cntnt.write('') self.cntnt.write('') + self.cntnt.write('draw:z-index="1" >') self.cntnt.write('') self.cntnt.write(' 0 or self.missingInfo: + + + self.doc.start_table(("MarriageInfo"),'FGR-ParentTable') + self.doc.start_row() + self.doc.start_cell('FGR-ParentHead',3) + self.doc.start_paragraph('FGR-ParentName') + self.doc.write_text(_("Marriage:")) + self.doc.end_paragraph() + self.doc.end_cell() + self.doc.end_row() + + self.dump_parent_event(_("Marriage"),m) + + for event_handle in family_list: + if event_handle: + event = self.database.get_event_from_handle(event_handle) + if event.get_name() != "Marriage": + self.dump_parent_event(event.get_name(),event) + + self.doc.end_table() + def dump_child_event(self,text,name,event): date = "" place = "" @@ -425,10 +455,11 @@ class FamilyGroup(Report.Report): else: self.doc.start_cell('FGR-TextChild2') self.doc.start_paragraph('FGR-ChildText') + index_str = ("%d" % index) if person.get_gender() == RelLib.Person.MALE: - self.doc.write_text(_("%dM") % index) + self.doc.write_text(index_str + _("acronym for male|M")) elif person.get_gender() == RelLib.Person.FEMALE: - self.doc.write_text(_("%dF") % index) + self.doc.write_text(index_str + _("acronym for female|F")) else: self.doc.write_text(_("%dU") % index) self.doc.end_paragraph() diff --git a/src/plugins/FilterEditor.py b/src/plugins/FilterEditor.py index 54c5de6f4..1c102d87c 100644 --- a/src/plugins/FilterEditor.py +++ b/src/plugins/FilterEditor.py @@ -505,7 +505,7 @@ class EditFilter: def on_help_clicked(self,obj): """Display the relevant portion of GRAMPS manual""" - help_display('gramps-manual','tools-util-cfe') + GrampsDisplay.help('tools-util-cfe') def on_delete_event(self,obj,b): pass @@ -748,7 +748,7 @@ class EditRule: def on_help_clicked(self,obj): """Display the relevant portion of GRAMPS manual""" - help_display('gramps-manual','append-filtref') + GrampsDisplay.help('append-filtref') def on_delete_event(self,obj,b): self.remove_itself_from_menu() diff --git a/src/plugins/GraphViz.py b/src/plugins/GraphViz.py index 3f4985549..bfd480b02 100644 --- a/src/plugins/GraphViz.py +++ b/src/plugins/GraphViz.py @@ -78,18 +78,38 @@ class _options: ) fonts = ( # Last items tells whether strings need to be converted to Latin1 - ("", "Default", _("Default"), 1), - ("Helvetica", "Postscript / Helvetica", _("Postscript / Helvetica"), 1), - ("FreeSans", "Truetype / FreeSans", _("Truetype / FreeSans"), 0), + ("", "Default", _("Default")), + ("Helvetica", "Postscript / Helvetica", _("Postscript / Helvetica")), + ("FreeSans", "Truetype / FreeSans", _("Truetype / FreeSans")), ) colors = ( ("outline", "B&W Outline", _("B&W outline")), ("colored", "Colored outline", _("Colored outline")), ("filled", "Color fill", _("Color fill")), ) + ratio = ( + ("compress", "Minimal size", _("Minimal size")), + ("fill", "Fill the given area", _("Fill the given area")), + ("expand", "Automatically use optimal number of pages", + _("Automatically use optimal number of pages")) + ) rankdir = ( + ("TB", "Vertical", _("Vertical")), ("LR", "Horizontal", _("Horizontal")), - ("RL", "Vertical", _("Vertical")), + ) + pagedir = ( + ("BL", "Bottom, left", _("Bottom, left")), + ("BR", "Bottom, right", _("Bottom, right")), + ("TL", "Top, left", _("Top, left")), + ("TR", "Top, right", _("Top, Right")), + ("RB", "Right, bottom", _("Right, bottom")), + ("RT", "Right, top", _("Right, top")), + ("LB", "Left, bottom", _("Left, bottom")), + ("LT", "Left, top", _("Left, top")), + ) + noteloc = ( + ("t", "Top", _("Top")), + ("b", "Bottom", _("Bottom")), ) arrowstyles = ( ('d', "Descendants <- Ancestors", _("Descendants <- Ancestors")), @@ -98,7 +118,11 @@ class _options: ('', "Descendants - Ancestors", _("Descendants - Ancestors")), ) -dot_found = os.system("dot -V 2>/dev/null") == 0 +_dot_found = os.system("dot -V 2>/dev/null") == 0 + +if os.system("which epstopdf >/dev/null 2>&1") == 0: + _options.formats += (("pdf", "PDF", _("PDF"), "application/pdf"),) + _pdf_pipe = 'epstopdf -f -o=%s' #------------------------------------------------------------------------ # @@ -124,7 +148,7 @@ class GraphViz: The option class carries its number, and the function returning the list of filters. font - Font to use. - latin - Set if font supports only Latin1 + latin - Set if text needs to be converted to latin-1 arrow - Arrow styles for heads and tails. showfamily - Whether to show family nodes. incid - Whether to include IDs. @@ -132,12 +156,17 @@ class GraphViz: justyears - Use years only. placecause - Whether to replace missing dates with place or cause url - Whether to include URLs. - rankdir - Graph direction + rankdir - Graph direction, LR or RL + ratio - Output aspect ration, fill/compress/auto color - Whether to use outline, colored outline or filled color in graph dashedl - Whether to use dashed lines for non-birth relationships. margin - Margins, in cm. pagesh - Number of pages in horizontal direction. pagesv - Number of pages in vertical direction. + pagedir - Paging direction + note - Note to add to the graph + notesize - Note font size (in points) + noteloc - Note location t/b """ colored = { 'male': 'dodgerblue4', @@ -160,11 +189,14 @@ class GraphViz: self.height = self.paper.get_height_inches() options = options_class.handler.options_dict + self.pagedir = options['pagedir'] self.hpages = options['pagesh'] self.vpages = options['pagesv'] margin_cm = options['margin'] self.margin = round(margin_cm/2.54,2) if margin_cm > 0.1: + # GraphViz has rounding errors so have to make the real + # margins slightly smaller than (page - content size) self.margin_small = round((margin_cm-0.1)/2.54,2) else: self.margin_small = 0 @@ -176,8 +208,8 @@ class GraphViz: self.just_years = options['justyears'] self.placecause = options['placecause'] self.rankdir = options['rankdir'] + self.ratio = options['ratio'] self.fontname = options['font'] - self.latin = options['latin'] self.colorize = options['color'] if self.colorize == 'colored': self.colors = colored @@ -193,189 +225,295 @@ class GraphViz: else: self.arrowtailstyle = 'none' + self.latin = options['latin'] + self.noteloc = options['noteloc'] + self.notesize = options['notesize'] + self.note = options['note'] + filter_num = options_class.get_filter_number() filters = options_class.get_report_filters(person) filters.extend(GenericFilter.CustomFilters.get_filters()) self.filter = filters[filter_num] - self.f = open(options_class.get_output(),'w') - self.write_report() + the_buffer = self.get_report() + if self.latin: + self.f.write(the_buffer.encode('iso-8859-1')) + else: + self.f.write(the_buffer) self.f.close() - def write_report(self): - - self.ind_list = self.filter.apply(self.database, + def get_report(self): + "return string of the .dot file contents" + self.person_handles = self.filter.apply(self.database, self.database.get_person_handles(sort_handles=False)) - self.write_header() - self.f.write("digraph GRAMPS_relationship_graph {\n") - self.f.write("bgcolor=white;\n") - self.f.write("rankdir=%s;\n" % self.rankdir) - self.f.write("center=1;\n") - self.f.write("margin=%3.2f;\n" % self.margin_small) - self.f.write("ratio=fill;\n") + # graph size if self.orient == PAPER_LANDSCAPE: - self.f.write("size=\"%3.2f,%3.2f\";\n" % ( - (self.height-self.margin*2)*self.hpages, - (self.width-self.margin*2)*self.vpages - )) + rotate = 90 + sizew = (self.height - self.margin*2) * self.hpages + sizeh = (self.width - self.margin*2) * self.vpages else: - self.f.write("size=\"%3.2f,%3.2f\";\n" % ( - (self.width-self.margin*2)*self.hpages, - (self.height-self.margin*2)*self.vpages - )) - self.f.write("page=\"%3.2f,%3.2f\";\n" % (self.width,self.height)) + rotate = 0 + sizew = (self.width - self.margin*2) * self.hpages + sizeh = (self.height - self.margin*2) * self.vpages + + + buffer = self.get_comment_header() + buffer += """ +digraph GRAMPS_relationship_graph { +/* whole graph attributes */ +bgcolor=white; +center=1; +ratio=%s; +rankdir=%s; +mclimit=2.0; +margin="%3.2f"; +pagedir="%s"; +page="%3.2f,%3.2f"; +size="%3.2f,%3.2f"; +rotate=%d; +/* default node and edge attributes */ +nodesep=0.25; +edge [syle=solid, arrowhead=%s arrowtail=%s]; +""" % ( + self.ratio, + self.rankdir, + self.margin_small, + self.pagedir, + self.width, self.height, + sizew, sizeh, + rotate, + self.arrowheadstyle, + self.arrowtailstyle + ) - if self.orient == PAPER_LANDSCAPE: - self.f.write("rotate=90;\n") + if self.fontname: + font = 'fontname="%s"' % self.fontname + else: + font = '' + if self.colorize == 'filled': + buffer += 'node [style=filled %s];\n' % font + else: + buffer += 'node [%s];\n' % font + if self.latin: + # GraphViz default is UTF-8 + buffer += 'charset="iso-8859-1";\n' + + if len(self.person_handles) > 1: + buffer += "/* persons and their families */\n" + buffer += self.get_persons_and_families() + buffer += "/* link children to families */\n" + buffer += self.get_child_links_to_families() - if len(self.ind_list) > 1: - self.dump_index() - self.dump_person() + if self.note: + buffer += 'labelloc="%s";\n' % self.noteloc + buffer += 'label="%s";\n' % self.note.replace('\n', '\\n').replace('"', '\\\"') + buffer += 'fontsize="%d";\n' % self.notesize # in points - self.f.write("}\n") + return buffer + "}\n" - def dump_person(self): - # Hash people in a dictionary for faster inclusion checking. + + def get_comment_header(self): + "return comment of Gramps options which are not Graphviz options" + return """/* +GRAMPS - Relationship graph + +Generated on %s. + +Report content options: + include URLs : %s + IDs : %s + dates : %s + just year : %s + place or cause : %s + colorize : %s + dotted adoptions : %s + show family nodes : %s + pages horizontal : %s + vertical : %s + +For other options, see graph settings below. + +If you need to switch between iso-8859-1 and utf-8 text encodings, +e.g. because you're using different font or -T output format, +just use iconv: + iconv -f iso-8859-1 -t utf-8 old.dot > new.dot + iconv -t utf-8 -f iso-8859-1 old.dot > new.dot +*/ +""" % ( + asctime(), + bool(self.includeurl), + bool(self.includeid), + bool(self.includedates), + bool(self.just_years), + bool(self.placecause), + bool(self.colorize), + bool(self.adoptionsdashed), + bool(self.show_families), + self.hpages, self.vpages + ) + + + def get_child_links_to_families(self): + "returns string of GraphViz edges linking parents to families or children" person_dict = {} - for p_id in self.ind_list: - person_dict[p_id] = 1 - - for person_handle in self.ind_list: + # Hash people in a dictionary for faster inclusion checking + for person_handle in self.person_handles: + person_dict[person_handle] = 1 + the_buffer = "" + for person_handle in self.person_handles: person = self.database.get_person_from_handle(person_handle) - pid = person.get_gramps_id().replace('-','_') - for family_handle in person.get_parent_family_handle_list(): - family = self.database.get_family_from_handle(family_handle) - madopted = False - fadopted = False - for child_ref in family.get_child_ref_list(): - if child_ref.ref == person_handle: - fadopted = child_ref.frel != RelLib.ChildRefType.BIRTH - madopted = child_ref.mrel != RelLib.ChildRefType.BIRTH + p_id = person.get_gramps_id() + for fam_handle in person.get_parent_family_handle_list(): + family = self.database.get_family_from_handle(fam_handle) father_handle = family.get_father_handle() mother_handle = family.get_mother_handle() - famid = family.get_gramps_id().replace('-','_') + for child_ref in family.get_child_ref_list(): + if child_ref.ref == person_handle: + frel = child_ref.frel + mrel = child_ref.mrel + break if (self.show_families and - (father_handle and person_dict.has_key(father_handle) or - mother_handle and person_dict.has_key(mother_handle))): - # Link to the family node. - self.f.write('p%s -> f%s [' % (pid, famid)) - self.f.write('arrowhead=%s, arrowtail=%s, ' % - (self.arrowheadstyle, self.arrowtailstyle)) - if self.adoptionsdashed and (fadopted or madopted): - self.f.write('style=dotted') - else: - self.f.write('style=solid') - self.f.write('];\n') + ((father_handle and father_handle in person_dict) or + (mother_handle and mother_handle in person_dict))): + # Link to the family node if either parent is in graph + the_buffer += self.get_family_link(p_id,family,frel,mrel) else: - # Link to the parents' nodes directly. - if father_handle and person_dict.has_key(father_handle): - father = self.database.get_person_from_handle(father_handle) - fid = father.get_gramps_id().replace('-','_') - self.f.write('p%s -> p%s [' % (pid, fid)) - self.f.write('arrowhead=%s, arrowtail=%s, ' % - (self.arrowheadstyle, self.arrowtailstyle)) - if self.adoptionsdashed and fadopted: - self.f.write('style=dotted') - else: - self.f.write('style=solid') - self.f.write('];\n') - if mother_handle and person_dict.has_key(mother_handle): - mother = self.database.get_person_from_handle(mother_handle) - mid = mother.get_gramps_id().replace('-','_') - self.f.write('p%s -> p%s [' % (pid, mid)) - self.f.write('arrowhead=%s, arrowtail=%s, ' % - (self.arrowheadstyle, self.arrowtailstyle)) - if self.adoptionsdashed and madopted: - self.f.write('style=dotted') - else: - self.f.write('style=solid') - self.f.write('];\n') - - def dump_index(self): - # The list of families for which we have output the node, so we - # don't do it twice. - families_done = [] - for person_handle in self.ind_list: - person = self.database.get_person_from_handle(person_handle) - # Output the person's node. - label = person.get_primary_name().get_name() - the_id = person.get_gramps_id().replace('-','_') - if self.includeid: - label = label + " (%s)" % the_id - if self.includedates: - birth_ref = person.get_birth_ref() - if birth_ref: - birth_event = self.database.get_event_from_handle(birth_ref.ref) - birth = self.dump_event(birth_event) - else: - birth = '' - death_ref = person.get_death_ref() - if death_ref: - death_event = self.database.get_event_from_handle(death_ref.ref) - death = self.dump_event(death_event) - else: - death = '' - label = label + '\\n(%s - %s)' % (birth, death) - self.f.write('p%s [shape=box, ' % the_id) - if self.includeurl: - h = person.get_handle() - self.f.write('URL="ppl/%s/%s/%s.html", ' % (h[0],h[1],h)) - if self.colorize != 'outline': - if self.colorize == 'filled': - style = 'style=filled, fillcolor' - else: - style = 'color' - gender = person.get_gender() - if gender == person.MALE: - self.f.write('%s=%s, ' % (style, self.colors['male'])) - elif gender == person.FEMALE: - self.f.write('%s=%s, ' % (style, self.colors['female'])) - else: - self.f.write('%s=%s, ' % (style, self.colors['unknown'])) - if self.latin: - label = label.encode('iso-8859-1') - self.f.write('fontname="%s", label="%s"];\n' % (self.fontname,label)) + # Link to the parents' nodes directly, if they are in graph + if father_handle and father_handle in person_dict: + the_buffer += self.get_parent_link(p_id,father_handle,frel) + if mother_handle and mother_handle in person_dict: + the_buffer += self.get_parent_link(p_id,mother_handle,mrel) + return the_buffer - # Output families's nodes. + def get_family_link(self, p_id, family, frel, mrel): + "returns string of GraphViz edge linking child to family" + style = '' + adopted = ((int(frel) != RelLib.ChildRefType.BIRTH) or + (int(mrel) != RelLib.ChildRefType.BIRTH)) + if adopted and self.adoptionsdashed: + style = 'style=dotted' + return '"p%s" -> "f%s" [%s];\n' % (p_id, + family.get_gramps_id(), style) + + def get_parent_link(self, p_id, parent_handle, rel): + "returns string of GraphViz edge linking child to parent" + style = '' + if (int(rel) != RelLib.ChildRefType.BIRTH) and self.adoptionsdashed: + style = 'style=dotted' + parent = self.database.get_person_from_handle(parent_handle) + return '"p%s" -> "p%s" [%s];\n' % (p_id, parent.get_gramps_id(), style) + + def get_persons_and_families(self): + "returns string of GraphViz nodes for persons and their families" + # The list of families for which we have output the node, + # so we don't do it twice + buffer = "" + families_done = {} + for person_handle in self.person_handles: + person = self.database.get_person_from_handle(person_handle) + p_id = person.get_gramps_id() + # Output the person's node + label = self.get_person_label(person) + style = self.get_gender_style(person) + url = "" + if self.includeurl: + h = person_handle + url = ', URL="ppl/%s/%s/%s.html", ' % (h[0],h[1],h) + buffer += '"p%s" [label="%s", %s%s];\n' % (p_id, label, style, url) + + # Output families where person is a parent if self.show_families: family_list = person.get_family_handle_list() for fam_handle in family_list: fam = self.database.get_family_from_handle(fam_handle) - fid = fam.get_gramps_id().replace('-','_') + fam_id = fam.get_gramps_id() if fam_handle not in families_done: - families_done.append(fam_handle) - self.f.write('f%s [shape=ellipse, ' % fid) - if self.colorize == 'colored': - self.f.write('color=%s, ' % self.colors['family']) - elif self.colorize == 'filled': - self.f.write('style=filled fillcolor=%s, ' % self.colors['family']) - - marriage = "" + families_done[fam_handle] = 1 + label = "" for event_ref in fam.get_event_ref_list(): - if event_ref: - event = self.database.get_event_from_handle(event_ref.ref) - if int(event.get_type()) == RelLib.EventType.MARRIAGE: - m = event - break - else: - m = None - - if m: - marriage = self.dump_event(m) + event = self.database.get_event_from_handle( + event_ref.ref) + if int(event.get_type()) == RelLib.EventType.MARRIAGE: + label = self.get_event_string(event) + break if self.includeid: - marriage = marriage + " (%s)" % fid - self.f.write('fontname="%s", label="%s"];\n' - % (self.fontname,marriage)) + label = "%s (%s)" % (label, fam_id) + color = "" + if self.colorize == 'colored': + color = ', color="%s"' % self.colors['family'] + elif self.colorize == 'filled': + color = ', fillcolor="%s"' % self.colors['family'] + buffer += '"f%s" [shape=ellipse, label="%s"%s];\n' % (fam_id, label, color) # Link this person to all his/her families. - self.f.write('f%s -> p%s [' % (fid, the_id)) - self.f.write('arrowhead=%s, arrowtail=%s, ' % - (self.arrowheadstyle, self.arrowtailstyle)) - self.f.write('style=solid];\n') + buffer += '"f%s" -> "p%s";\n' % (fam_id, p_id) + return buffer + + def get_gender_style(self, person): + "return gender specific person style" + gender = person.get_gender() + if gender == person.MALE: + shape = 'shape="box"' + elif gender == person.FEMALE: + shape = 'shape="box", style="rounded"' + else: + shape = 'shape="hexagon"' + if self.colorize == 'outline': + return shape + else: + if gender == person.MALE: + color = self.colors['male'] + elif gender == person.FEMALE: + color = self.colors['female'] + else: + color = self.colors['unknown'] + if self.colorize == 'filled': + # In current GraphViz boxes cannot be both rounded and filled + return 'shape="box", fillcolor="%s"' % color + else: + return '%s, color="%s"' % (shape, color) + + def get_person_label(self, person): + "return person label string" + label = person.get_primary_name().get_name() + p_id = person.get_gramps_id() + if self.includeid: + label = label + " (%s)" % p_id + if self.includedates: + birth, death = self.get_date_strings(person) + label = label + '\\n(%s - %s)' % (birth, death) + return label.replace('"', '\\\"') - def dump_event(self,event): + def get_date_strings(self, person): + "returns tuple of birth/christening and death/burying date strings" + birth_ref = person.get_birth_ref() + if birth_ref: + birth_event = self.database.get_event_from_handle(birth_ref.ref) + birth = self.get_event_string(birth_event) + else: + birth = '' + death_ref = person.get_death_ref() + if death_ref: + death_event = self.database.get_event_from_handle(death_ref) + death = self.get_event_string(death_event) + else: + death = '' + if birth and death: + return (birth, death) + # missing info, use (first) christening/burial instead + for event_ref in person.get_event_ref_list(): + event = self.database.get_event_from_handle(event_ref.ref) + if int(event.get_type()) == RelLib.EventType.CHRISTEN: + if not birth: + birth = self.get_event_string(event) + elif int(event.get_type()) == RelLib.EventType.BURIAL: + if not death: + death = self.get_event_string(event) + return (birth, death) + + def get_event_string(self, event): """ - Compile an event label. + return string for for an event label. Based on the data availability and preferences, we select one of the following for a given event: @@ -399,33 +537,6 @@ class GraphViz: return event.get_cause() return '' - def write_header(self): - """ - Write header listing the options used. - """ - self.f.write("/* GRAMPS - Relationship graph\n") - self.f.write(" *\n") - self.f.write(" * Report options:\n") - self.f.write(" * font style : %s\n" % self.fontname) - self.f.write(" * style arrow head : %s\n" % self.arrowheadstyle) - self.f.write(" * tail : %s\n" % self.arrowtailstyle) - self.f.write(" * graph direction : %s\n" % self.rankdir) - self.f.write(" * include URLs : %s\n" % bool(self.includeurl)) - self.f.write(" * IDs : %s\n" % bool(self.includeid)) - self.f.write(" * dates : %s\n" % bool(self.includedates)) - self.f.write(" * just year : %s\n" % bool(self.just_years)) - self.f.write(" * place or cause : %s\n" % bool(self.placecause)) - self.f.write(" * colorize : %s\n" % bool(self.colorize)) - self.f.write(" * dotted adoptions : %s\n" % bool(self.adoptionsdashed)) - self.f.write(" * show family nodes : %s\n" % bool(self.show_families)) - self.f.write(" * margin : %3.2fin\n" % self.margin_small) - self.f.write(" * pages horizontal : %s\n" % self.hpages) - self.f.write(" * vertical : %s\n" % self.vpages) - self.f.write(" * page width : %3.2fin\n" % self.width) - self.f.write(" * height : %3.2fin\n" % self.height) - self.f.write(" *\n") - self.f.write(" * Generated on %s by GRAMPS\n" % asctime()) - self.f.write(" */\n\n") #------------------------------------------------------------------------ # @@ -453,12 +564,17 @@ class GraphVizOptions(ReportOptions.ReportOptions): 'justyears' : 0, 'placecause' : 1, 'url' : 1, + 'ratio' : "compress", 'rankdir' : "LR", 'color' : "filled", 'dashedl' : 1, 'margin' : 1.0, + 'pagedir' : 'BL', 'pagesh' : 1, 'pagesv' : 1, + 'note' : '', + 'noteloc' : 'b', + 'notesize' : 32, 'gvof' : 'ps', } @@ -490,7 +606,10 @@ class GraphVizOptions(ReportOptions.ReportOptions): 'url' : ("=0/1","Whether to include URLs.", ["Do not include URLs","Include URLs"], True), - 'rankdir' : ("=str","Graph direction.", + 'ratio' : ("=str","Graph aspect ratio.", + [ "%s\t%s" % (item[0],item[1]) for item in _options.ratio ], + False), + 'rankdir' : ("=str","Graph direction.", [ "%s\t%s" % (item[0],item[1]) for item in _options.rankdir ], False), 'color' : ("=str","Whether and how to colorize graph.", @@ -501,10 +620,20 @@ class GraphVizOptions(ReportOptions.ReportOptions): True), 'margin' : ("=num","Margin size.", "Floating point value, in cm"), + 'pagedir' : ("=str","Paging direction.", + [ "%s\t%s" % (item[0],item[1]) for item in _options.pagedir ], + False), 'pagesh' : ("=num","Number of pages in horizontal direction.", "Integer values"), 'pagesv' : ("=num","Number of pages in vertical direction.", "Integer values"), + 'note' : ("=str","Note to add to the graph.", + "Text"), + 'notesize' : ("=num","Note size (in points).", + "Integer values"), + 'noteloc' : ("=str","Note location.", + [ "%s\t%s" % (item[0],item[1]) for item in _options.noteloc ], + False), 'gvof' : ("=str","Output format to convert dot file into.", [ "%s\t%s" % (item[0],item[1]) for item in _options.formats ], False), @@ -546,6 +675,22 @@ class GraphVizOptions(ReportOptions.ReportOptions): def make_doc_menu(self,dialog,active=None): pass + def add_list(self, options, default): + "returns compobox of given options and default value" + box = gtk.ComboBox() + store = gtk.ListStore(str) + box.set_model(store) + cell = gtk.CellRendererText() + box.pack_start(cell,True) + box.add_attribute(cell,'text',0) + index = 0 + for item in options: + store.append(row=[item[2]]) + if item[0] == default: + box.set_active(index) + index = index + 1 + return box + def add_user_options(self,dialog): if self.handler.module_name == "rel_graph2": dialog.make_doc_menu = self.make_doc_menu @@ -599,36 +744,8 @@ class GraphVizOptions(ReportOptions.ReportOptions): _("Include individual and family IDs.")) # GraphViz output options tab - self.rank_box = gtk.ComboBox() - store = gtk.ListStore(str) - self.rank_box.set_model(store) - cell = gtk.CellRendererText() - self.rank_box.pack_start(cell,True) - self.rank_box.add_attribute(cell,'text',0) - index = 0 - for item in _options.rankdir: - store.append(row=[item[2]]) - if item[0] == self.options_dict['rankdir']: - self.rank_box.set_active(index) - index = index + 1 - dialog.add_frame_option(_("GraphViz Options"), - _("Graph direction"), - self.rank_box, - _("Whether generations go from top to bottom " - "or left to right.")) - - self.color_box = gtk.ComboBox() - store = gtk.ListStore(str) - self.color_box.set_model(store) - cell = gtk.CellRendererText() - self.color_box.pack_start(cell,True) - self.color_box.add_attribute(cell,'text',0) - index = 0 - for item in _options.colors: - store.append(row=[item[2]]) - if item[0] == self.options_dict['color']: - self.color_box.set_active(index) - index = index + 1 + self.color_box = self.add_list(_options.colors, + self.options_dict['color']) dialog.add_frame_option(_("GraphViz Options"), _("Graph coloring"), self.color_box, @@ -636,39 +753,15 @@ class GraphVizOptions(ReportOptions.ReportOptions): "with red. If the sex of an individual " "is unknown it will be shown with gray.")) - self.arrowstyle_box = gtk.ComboBox() - store = gtk.ListStore(str) - self.arrowstyle_box.set_model(store) - cell = gtk.CellRendererText() - self.arrowstyle_box.pack_start(cell,True) - self.arrowstyle_box.add_attribute(cell,'text',0) - index = 0 - for item in _options.arrowstyles: - store.append(row=[item[2]]) - if item[0] == self.options_dict['arrow']: - self.arrowstyle_box.set_active(index) - index = index + 1 + self.arrowstyle_box = self.add_list(_options.arrowstyles, + self.options_dict['arrow']) dialog.add_frame_option(_("GraphViz Options"), _("Arrowhead direction"), self.arrowstyle_box, _("Choose the direction that the arrows point.")) - self.font_box = gtk.ComboBox() - store = gtk.ListStore(str) - self.font_box.set_model(store) - cell = gtk.CellRendererText() - self.font_box.pack_start(cell,True) - self.font_box.add_attribute(cell,'text',0) - index = 0 - for item in _options.fonts: - if item[3]: - name = "%s (iso-latin1 font)" % item[2] - else: - name = item[2] - store.append(row=[name]) - if item[0] == self.options_dict['font']: - self.font_box.set_active(index) - index = index + 1 + self.font_box = self.add_list(_options.fonts, + self.options_dict['font']) dialog.add_frame_option(_("GraphViz Options"), _("Font family"), self.font_box, @@ -677,6 +770,13 @@ class GraphVizOptions(ReportOptions.ReportOptions): "FreeSans is available from: " "http://www.nongnu.org/freefont/")) + self.latin_cb = gtk.CheckButton(_("Output format/font requires text as latin-1")) + self.latin_cb.set_active(self.options_dict['latin']) + dialog.add_frame_option(_("GraphViz Options"), '', + self.latin_cb, + _("If text doesn't show correctly in report, use this. " + "Required e.g. for default font with PS output.")) + self.adoptionsdashed_cb = gtk.CheckButton(_("Indicate non-birth relationships with dotted lines")) self.adoptionsdashed_cb.set_active(self.options_dict['dashedl']) dialog.add_frame_option(_("GraphViz Options"), '', @@ -691,13 +791,30 @@ class GraphVizOptions(ReportOptions.ReportOptions): _("Families will show up as ellipses, linked " "to parents and children.")) - # Page options tab + # Page/layout options tab + self.rank_box = self.add_list(_options.rankdir, + self.options_dict['rankdir']) + dialog.add_frame_option(_("Layout Options"), + _("Graph direction"), + self.rank_box, + _("Whether generations go from top to bottom " + "or left to right.")) + + self.ratio_box = self.add_list(_options.ratio, + self.options_dict['ratio']) + dialog.add_frame_option(_("Layout Options"), + _("Aspect ratio"), + self.ratio_box, + _("Affects greatly how the graph is layed out " + "on the page. Multiple pages overrides the " + "pages settings below.")) + margin_adj = gtk.Adjustment(value=self.options_dict['margin'], lower=0.0, upper=10.0, step_incr=1.0) self.margin_sb = gtk.SpinButton(adjustment=margin_adj, digits=1) - dialog.add_frame_option(_("Page Options"), + dialog.add_frame_option(_("Layout Options"), _("Margin size"), self.margin_sb) @@ -709,14 +826,14 @@ class GraphVizOptions(ReportOptions.ReportOptions): self.hpages_sb = gtk.SpinButton(adjustment=hpages_adj, digits=0) self.vpages_sb = gtk.SpinButton(adjustment=vpages_adj, digits=0) - dialog.add_frame_option(_("Page Options"), + dialog.add_frame_option(_("Layout Options"), _("Number of Horizontal Pages"), self.hpages_sb, _("GraphViz can create very large graphs by " "spreading the graph across a rectangular " "array of pages. This controls the number " "pages in the array horizontally.")) - dialog.add_frame_option(_("Page Options"), + dialog.add_frame_option(_("Layout Options"), _("Number of Vertical Pages"), self.vpages_sb, _("GraphViz can create very large graphs " @@ -724,6 +841,41 @@ class GraphVizOptions(ReportOptions.ReportOptions): "rectangular array of pages. This " "controls the number pages in the array " "vertically.")) + self.pagedir_box = self.add_list(_options.pagedir, + self.options_dict['pagedir']) + dialog.add_frame_option(_("Layout Options"), + _("Paging direction"), + self.pagedir_box, + _("The order in which the graph pages are output.")) + + # Notes tab + self.textbox = gtk.TextView() + self.textbox.get_buffer().set_text(self.options_dict['note']) + self.textbox.set_editable(1) + swin = gtk.ScrolledWindow() + swin.set_shadow_type(gtk.SHADOW_IN) + swin.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) + swin.add(self.textbox) + dialog.add_frame_option(_("Notes"), + _("Note to add to the graph"), + swin, + _("This text will be added to the graph.")) + self.noteloc_box = self.add_list(_options.noteloc, + self.options_dict['noteloc']) + dialog.add_frame_option(_("Notes"), + _("Note location"), + self.noteloc_box, + _("Whether note will appear on top " + "or bottom of the page.")) + + notesize_adj = gtk.Adjustment(value=self.options_dict['notesize'], + lower=8, upper=128, step_incr=1) + self.notesize_sb = gtk.SpinButton(adjustment=notesize_adj, digits=0) + dialog.add_frame_option(_("Notes"), + _("Note size (in points)"), + self.notesize_sb, + _("The size of note text, in points.")) + def toggle_date(self, obj): self.just_years_cb.set_sensitive(self.includedates_cb.get_active()) @@ -740,6 +892,8 @@ class GraphVizOptions(ReportOptions.ReportOptions): self.options_dict['incid'] = int(self.includeid_cb.get_active()) self.options_dict['justyears'] = int(self.just_years_cb.get_active()) self.options_dict['placecause'] = int(self.place_cause_cb.get_active()) + self.options_dict['ratio'] = \ + _options.ratio[self.ratio_box.get_active()][0] self.options_dict['rankdir'] = \ _options.rankdir[self.rank_box.get_active()][0] self.options_dict['color'] = \ @@ -748,8 +902,15 @@ class GraphVizOptions(ReportOptions.ReportOptions): _options.arrowstyles[self.arrowstyle_box.get_active()][0] self.options_dict['font'] = \ _options.fonts[self.font_box.get_active()][0] - self.options_dict['latin'] = \ - _options.fonts[self.font_box.get_active()][3] + self.options_dict['pagedir'] = \ + _options.pagedir[self.pagedir_box.get_active()][0] + self.options_dict['noteloc'] = \ + _options.noteloc[self.noteloc_box.get_active()][0] + self.options_dict['notesize'] = self.notesize_sb.get_value_as_int() + b = self.textbox.get_buffer() + self.options_dict['note'] = \ + b.get_text(b.get_start_iter(), b.get_end_iter(), False) + if self.handler.module_name == "rel_graph2": self.options_dict['gvof'] = dialog.format_menu.get_format_str() @@ -837,7 +998,24 @@ class FormatComboBox(gtk.ComboBox): return '.dot' def get_printable(self): - return None + _apptype = _options.formats[self.get_active()][3] + print_label = None + + try: + import Utils + import GrampsMime + + mprog = GrampsMime.get_application(_apptype) + + if Utils.search_for(mprog[0]): + print_label = _("Open in %(program_name)s") % { 'program_name': + mprog[1]} + else: + print_label = None + except: + print_label = None + + return print_label def get_clname(self): return 'dot' @@ -907,7 +1085,6 @@ class GraphicsFormatComboBox(gtk.ComboBox): class EmptyDoc: def __init__(self,styles,type,template,orientation,source=None): self.print_req = 0 - pass def init(self): pass @@ -955,9 +1132,31 @@ class GraphVizGraphics(Report.Report): GraphViz(self.database,self.start_person,self.options_class) def end_report(self): - os.system('dot -T%s -o%s %s ; rm %s' % - (self.the_format,self.user_output, - self.junk_output,self.junk_output)) + if self.the_format == "pdf": + command = ('dot -Tps %s | ' + _pdf_pipe + ' ; rm %s') % \ + (self.junk_output,self.user_output,self.junk_output) + os.system(command) + else: + os.system('dot -T%s -o%s %s ; rm %s' % + (self.the_format,self.user_output, + self.junk_output,self.junk_output)) + + if self.doc.print_req: + _apptype = None + for format in _options.formats: + if format[0] == self.the_format: + _apptype = format[3] + break + if _apptype: + try: + import Utils + import GrampsMime + + app = GrampsMime.get_application(_apptype) + os.environ["FILE"] = self.user_output + os.system ('%s "$FILE" &' % app[0]) + except: + pass if self.doc.print_req: _apptype = None @@ -1011,7 +1210,7 @@ register_report( author_email="don@gramps-project.org" ) -if dot_found: +if _dot_found: register_report( name = 'rel_graph2', category = Report.CATEGORY_DRAW, diff --git a/src/plugins/NarrativeWeb.py b/src/plugins/NarrativeWeb.py index cfe936b2a..4f8e663d6 100644 --- a/src/plugins/NarrativeWeb.py +++ b/src/plugins/NarrativeWeb.py @@ -153,6 +153,7 @@ class BasePage: self.exclude_private = not options.handler.options_dict['NWEBincpriv'] self.usegraph = options.handler.options_dict['NWEBgraph'] self.use_home = self.options.handler.options_dict['NWEBhomenote'] != "" + self.page_title = "" def store_file(self,archive,html_dir,from_path,to_path): if archive: @@ -901,7 +902,6 @@ class MediaPage(BasePage): return None def copy_thumbnail(self,handle,photo): - ext = os.path.splitext(photo.get_path())[1] to_dir = self.build_path(handle,'thumb') to_path = os.path.join(to_dir,handle+".png") if photo.get_mime_type(): @@ -923,16 +923,6 @@ class MediaPage(BasePage): except IOError: print "Could not copy file" - def copy_preview_image(handle,photo): - base = '/desktop/gnome/thumbnailers/%s' % mtype.replace('/','@') - thumbnailer = Config.client.get_string(base + '/command') - enable = Config.client.get_bool(base + '/enable') - if thumbnailer and enable: - run_thumbnailer(thumbnailer,path,_build_thumb_path(path),320) - return path - else: - return None - #------------------------------------------------------------------------ # # @@ -1284,8 +1274,6 @@ class ContactPage(BasePage): self.store_file(archive,self.html_dir,obj.get_path(), newpath) - dirpath = self.build_path(note_id,'img') - of.write('
\n') of.write('') of.write('
') @@ -2050,6 +2038,7 @@ class WebReport(Report.Report): NWEBhomenote NWEBnoid """ + self.database = database self.start_person = person self.options = options @@ -2196,7 +2185,6 @@ class WebReport(Report.Report): # people rule if self.restrict: self.progress.set_pass(_('Filtering living people'),len(ind_list)) - new_list = [] for key in ind_list: self.progress.step() p = self.database.get_person_from_handle(key) @@ -2331,19 +2319,6 @@ class WebReport(Report.Report): def base_pages(self, photo_list, archive): - if self.use_home: - index_page = "index" - surname_page = "surnames" - intro_page = "introduction" - elif self.use_intro: - index_page = "" - surname_page = "surnames" - intro_page = "index" - else: - index_page = "" - surname_page = "index" - intro_page = "" - if self.use_home: HomePage(self.database, self.title, self.options, archive, photo_list) @@ -2380,7 +2355,7 @@ class WebReportOptions(ReportOptions.ReportOptions): Defines options and provides handling interface. """ - def __init__(self,name,database,person_id=None): + def __init__(self,name,database=None,person_id=None): ReportOptions.ReportOptions.__init__(self,name,person_id) self.db = database @@ -2556,18 +2531,21 @@ class WebReportOptions(ReportOptions.ReportOptions): title = _("Page Generation") - cursor = self.db.get_media_cursor() + media_list = [['','']] html_list = [['','']] - data = cursor.first() - while data: - (handle, value) = data - if value[3]: - media_list.append([value[4],handle]) - else: - html_list.append([value[4],handle]) - data = cursor.next() - cursor.close() + + if self.db: + cursor = self.db.get_media_cursor() + data = cursor.first() + while data: + (handle, value) = data + if value[3]: + media_list.append([value[4],handle]) + else: + html_list.append([value[4],handle]) + data = cursor.next() + cursor.close() media_list.sort() html_list.sort() @@ -2901,7 +2879,7 @@ register_report( category = Report.CATEGORY_WEB, report_class = WebReportDialog, options_class = cl_report, - modes = Report.MODE_GUI, + modes = Report.MODE_GUI | Report.MODE_CLI, translated_name = _("Narrative Web Site"), status = _("Stable"), author_name="Donald N. Allingham", diff --git a/src/plugins/WritePkg.py b/src/plugins/WritePkg.py index 07132f21c..0f61c0d7f 100644 --- a/src/plugins/WritePkg.py +++ b/src/plugins/WritePkg.py @@ -80,10 +80,6 @@ class PackageWriter: def __init__(self,database,filename,callback): self.db = database self.callback = callback - - if os.path.splitext(filename)[1] != ".gpkg": - filename = filename + ".gpkg" - self.filename = filename def export(self): diff --git a/src/plugins/rel_hu.py b/src/plugins/rel_hu.py index ed7cfda3d..90630df65 100644 --- a/src/plugins/rel_hu.py +++ b/src/plugins/rel_hu.py @@ -154,7 +154,7 @@ class RelationshipCalculator(Relationship.RelationshipCalculator): other_birth_event = other_person.get_birth() other_birth_date = other_birth_event.get_date_object() if (orig_birth_date == "")or(other_birth_date == "") :return 0 - else :return RelLib.Date.compare_dates(orig_birth_date,other_birth_date) + else :return orig_birth_date>other_birth_date def get_age_brother (self,level): @@ -203,7 +203,7 @@ class RelationshipCalculator(Relationship.RelationshipCalculator): family = self.db.get_family_from_handle(g) if family: if sp_id in family.get_child_handle_list(): - return [sp] + return [sp_id] return [] #------------------------------------------------------------------------ @@ -246,7 +246,7 @@ class RelationshipCalculator(Relationship.RelationshipCalculator): if family: c = family.get_child_handle_list() if (other.get_handle() in c) and (sp_id in c): - return [sp] + return [sp_id] return [] #------------------------------------------------------------------------- diff --git a/src/plugins/rel_sk.py b/src/plugins/rel_sk.py new file mode 100644 index 000000000..3c3662cd9 --- /dev/null +++ b/src/plugins/rel_sk.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2003-2005 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: rel_sk.py,v 1.1.2.1 2006/03/16 16:22:27 rshura Exp $ +# Slovak terms added by Lubo Vasko + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- + +import RelLib +import Relationship +import types +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +#Slovak-specific definitions of relationships +# +#------------------------------------------------------------------------- + +_level_name = [ "", "prvého", "druhého", "tretieho", "štvrtého", "piateho", "šiesteho", + "siedmeho", "ôsmeho", "deviateho", "desiateho", "jedenásteho", "dvanásteho", + "trinásteho", "štrnásteho", "pätnásteho", "šestnásteho", + "sedemnásteho", "osemnásteho", "devätnásteho", "dvadsiateho", "dvadsiatehoprvého", "dvadsiatehodruhého", + "dvadsiatehotretieho", "dvadsiatehoštvrtého","dvadsiatehopiateho","dvadsiatehošiesteho","dvadsiatehosiedmeho", + "dvadsiatehoôsmeho","dvadsiatehodeviateho","tridsiateho" ] + +_parents_level = [ "", "rodičia", "starí rodičia", "prarodičia", + "vzdialení príbuzní", ] + +_father_level = [ "", "otec", "starý otec", "prastarý otec", "prapredok", ] + +_mother_level = [ "", "matka", "stará matka", "prastará matka", "prapredok", ] + +_son_level = [ "", "syn", "vnuk", "pravnuk", ] + +_daughter_level = [ "", "dcéra", "vnučka", "pravnučka", ] + +_sister_level = [ "", "sestra", "teta", "prateta", "praprateta", ] + +_brother_level = [ "", "brat", "strýko", "prastrýko", "praprastrýko", ] + +_nephew_level = [ "", "synovec", "prasynovec", "praprasynovec", ] + +_niece_level = [ "", "neter", "praneter", "prapraneter", ] + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class RelationshipCalculator(Relationship.RelationshipCalculator): + + def __init__(self,db): + Relationship.RelationshipCalculator.__init__(self,db) + + def get_male_cousin(self,level): + if level>len(_level_name)-1: + return "vzdialený príbuzný" + else: + return "bratranec %s stupňa" % (_level_name[level]) + + def get_female_cousin(self,level): + if level>len(_level_name)-1: + return "vzdialená príbuzná" + else: + return "sesternica %s stupňa" % (_level_name[level]) + + def get_parents(self,level): + if level>len(_parents_level)-1: + return "vzdialení príbuzní" + else: + return _parents_level[level] + + def get_father(self,level): + if level>len(_father_level)-1: + return "vzdialený príbuzný" + else: + return _father_level[level] + + def get_son(self,level): + if level>len(_son_level)-1: + return "vzdialený potomok" + else: + return _son_level[level] + + def get_mother(self,level): + if level>len(_mother_level)-1: + return "vzdialený predok" + else: + return _mother_level[level] + + def get_daughter(self,level): + if level>len(_daughter_level)-1: + return "vzdialený potomok" + else: + return _daughter_level[level] + + def get_aunt(self,level): + if level>len(_sister_level)-1: + return "vzdialený predok" + else: + return _sister_level[level] + + def get_uncle(self,level): + if level>len(_brother_level)-1: + return "vzdialený predok" + else: + return _brother_level[level] + + def get_nephew(self,level): + if level>len(_nephew_level)-1: + return "vzdialený potomok" + else: + return _nephew_level[level] + + def get_niece(self,level): + if level>len(_niece_level)-1: + return "vzdialený potomok" + else: + return _niece_level[level] + + def get_relationship(self,orig_person,other_person): + """ + Returns a string representing the relationshp between the two people, + along with a list of common ancestors (typically father,mother) + + Special cases: relation strings "", "undefined" and "spouse". + """ + + if orig_person == None: + return ("undefined",[]) + + if orig_person.get_handle() == other_person.get_handle(): + return ('', []) + + is_spouse = self.is_spouse(orig_person,other_person) + if is_spouse: + return (is_spouse,[]) + + (firstRel,secondRel,common) = self.get_relationship_distance(orig_person,other_person) + + if type(common) == types.StringType or type(common) == types.UnicodeType: + return (common,[]) + elif common: + person_handle = common[0] + else: + return ("",[]) + + firstRel = len(firstRel) + secondRel = len(secondRel) + + if firstRel == 0: + if secondRel == 0: + return ('',common) + elif other_person.get_gender() == RelLib.Person.MALE: + return (self.get_father(secondRel),common) + else: + return (self.get_mother(secondRel),common) + elif secondRel == 0: + if other_person.get_gender() == RelLib.Person.MALE: + return (self.get_son(firstRel),common) + else: + return (self.get_daughter(firstRel),common) + elif firstRel == 1: + if other_person.get_gender() == RelLib.Person.MALE: + return (self.get_uncle(secondRel),common) + else: + return (self.get_aunt(secondRel),common) + elif secondRel == 1: + if other_person.get_gender() == RelLib.Person.MALE: + return (self.get_nephew(firstRel-1),common) + else: + return (self.get_niece(firstRel-1),common) + elif firstRel == 2 and secondRel == 2: + if other_person.get_gender() == RelLib.Person.MALE: + return ('vlastný bratranec',common) + else: + return ('vlastná sesternica',common) + elif firstRel == 3 and secondRel == 2: + if other_person.get_gender() == RelLib.Person.MALE: + return ('bratranec druhého stupňa',common) + else: + return ('sesternica druhého stupňa',common) + elif firstRel == 2 and secondRel == 3: + if other_person.get_gender() == RelLib.Person.MALE: + return ('bratranec druhého stupňa',common) + else: + return ('sesternica druhého stupňa',common) + else: + if other_person.get_gender() == RelLib.Person.MALE: + if firstRel+secondRel>len(_level_name)-1: + return (self.get_male_cousin(firstRel+secondRel),common) + else: + return ('vzdialený bratranec',common) + else: + if firstRel+secondRel>len(_level_name)-1: + return (self.get_female_cousin(firstRel+secondRel),common) + else: + return ('vzdialená sesternica',common) + +#------------------------------------------------------------------------- +# +# Register this class with the Plugins system +# +#------------------------------------------------------------------------- +from PluginMgr import register_relcalc + +register_relcalc(RelationshipCalculator, + ["sk", "SK", "sk_SK", "slovensky", "slovak", "Slovak", "sk_SK.UTF8", "sk_SK.UTF-8", "sk_SK.utf-8", "sk_SK.utf8"]) diff --git a/test/dates.sh b/test/dates.sh new file mode 100755 index 000000000..efc03ea01 --- /dev/null +++ b/test/dates.sh @@ -0,0 +1,33 @@ +#! /bin/sh +# +# Date Handler test for GRAMPS: +# o Run date_test.py for every available locale. + +# $Id: dates.sh,v 1.1.2.2 2006/04/15 20:21:03 loshawlos Exp $ + +TOP_DIR=`dirname $PWD` +SRC_DIR=$TOP_DIR/src +PRG="python date_test.py" + +export PYTHONPATH=$SRC_DIR + +# Get the list of xx_XX language codes +LANG_LIST=`locale -a | grep _ | cut -f 1 -d . | sort | uniq` +for lang in $LANG_LIST; do + # for each xx_XX language code, try all available locales + LOC_LIST=`locale -a | grep $lang` + false + for loc in $LOC_LIST; do + export LANG=$loc + # Run test + res=`cd $SRC_DIR && $PRG` + # Print results + echo "$res" + if [ $?=0 ]; then + # Finish with this LANG if succeeded. + echo "Done testing $LANG" + read -p" ENTER to continue " + break + fi + done +done diff --git a/test/impex.sh b/test/impex.sh index d44e712e3..4848c20ad 100755 --- a/test/impex.sh +++ b/test/impex.sh @@ -1,8 +1,13 @@ #! /bin/sh # -# Import/export test for GRAMPS: Import example XML data and create GRDB, -# check data for integrity, output in all formats, check resulting XML for -# well-formedness and validate it against DTD and RelaxNG schema. +# Import/export test for GRAMPS: +# o Import example XML data and create GRDB +# o Open produced GRDB, then +# * check data for integrity +# * output in all formats +# o Check resulting XML for well-formedness and validate it +# against DTD and RelaxNG schema. +# o Import ever file produced and run summary on it. # $Id$ @@ -13,6 +18,7 @@ PRG="python gramps.py" EXAMPLE_XML=$TOP_DIR/example/gramps/example.gramps OUT_FMT="gedcom gramps-xml gramps-pkg wft geneweb" +IN_FMT="gedcom gramps-xml gramps-pkg" DATA_DIR=$TEST_DIR/data mkdir -p $DATA_DIR if [ -f $DATA_DIR/example.grdb ]; then @@ -53,3 +59,12 @@ echo "* Post-parsing DTD validation" xmllint --noout --postvalid $DATA_DIR/example.gramps-xml echo "* Validate against RelaxNG schema" xmllint --noout --relaxng $TOP_DIR/doc/grampsxml.rng $DATA_DIR/example.gramps-xml + +echo "" +echo "+--------------------------------------------------------------" +echo "| Import all produced files and print summary" +echo "+--------------------------------------------------------------" +for fmt in $IN_FMT; do + OPTS="-i $DATA_DIR/example.$fmt -f $fmt -a summary" + (cd $SRC_DIR; $PRG $OPTS) +done