Merge branch 'gramps50'
This commit is contained in:
commit
1482fedb9c
@ -1,12 +1,12 @@
|
||||
0 HEAD
|
||||
1 SOUR Gramps
|
||||
2 VERS 5.0.0-alpha1
|
||||
2 VERS 5.0.0-alpha2
|
||||
2 NAME Gramps
|
||||
1 DATE 29 OCT 2016
|
||||
2 TIME 15:10:31
|
||||
1 DATE 1 SEP 2017
|
||||
2 TIME 12:10:38
|
||||
1 SUBM @SUBM@
|
||||
1 FILE C:\Users\prc\AppData\Roaming\gramps\temp\exp_sample_ged.ged
|
||||
1 COPR Copyright (c) 2016 Alex Roitman,,,.
|
||||
1 COPR Copyright (c) 2017 Alex Roitman,,,.
|
||||
1 GEDC
|
||||
2 VERS 5.5.1
|
||||
2 FORM LINEAGE-LINKED
|
||||
@ -17,7 +17,7 @@
|
||||
1 ADDR Not Provided
|
||||
2 ADR1 Not Provided
|
||||
1 PHON 666-555-4444
|
||||
1 EMAIL an_email@gmail.com
|
||||
1 EMAIL an_email@@gmail.com
|
||||
0 @I0000@ INDI
|
||||
1 NAME Anna /Hansdotter/
|
||||
2 GIVN Anna
|
||||
@ -789,7 +789,7 @@
|
||||
1 OBJE
|
||||
2 FORM jpeg
|
||||
2 TITL Michael O'Toole 2015-11
|
||||
2 FILE c:\grampsaio64-4.9.9\share\gramps\tests\O0.jpg
|
||||
2 FILE c:\grampsaio64-5.0.0\share\gramps\tests\O0.jpg
|
||||
2 NOTE @N0019@
|
||||
1 NOTE @N0007@
|
||||
1 CHAN
|
||||
@ -820,7 +820,7 @@
|
||||
3 CTRY USA
|
||||
2 PHON 440-871-3400
|
||||
2 PHON 800-871-3400
|
||||
2 EMAIL thetester@gmail.com
|
||||
2 EMAIL thetester@@gmail.com
|
||||
2 FAX 440-123-4567
|
||||
2 WWW http://thetester.com
|
||||
1 EVEN A very bad day
|
||||
@ -862,7 +862,7 @@
|
||||
2 DATE 30 DEC 1954
|
||||
2 PLAC 123 Main St., Winslow, PA, 12345
|
||||
2 PHON 440-871-3401
|
||||
2 EMAIL mrstester@gmail.com
|
||||
2 EMAIL mrstester@@gmail.com
|
||||
2 FAX 440-321-4568
|
||||
2 WWW http://mrstester.com
|
||||
2 NOTE @N0011@
|
||||
@ -879,7 +879,7 @@
|
||||
3 POST 12345
|
||||
1 PHON 440-871-3401
|
||||
1 PHON 800-871-3401
|
||||
1 EMAIL mrstester@gmail.com
|
||||
1 EMAIL mrstester@@gmail.com
|
||||
1 FAX 440-321-4568
|
||||
1 WWW http://mrstester.com
|
||||
1 NOTE @N0010@
|
||||
@ -904,7 +904,7 @@
|
||||
3 STAE Colorado
|
||||
3 CTRY USA
|
||||
2 PHON 440-871-3402
|
||||
2 EMAIL tomtester@gmail.com
|
||||
2 EMAIL tomtester@@gmail.com
|
||||
2 FAX 440-321-4569
|
||||
2 WWW http://tomtester.com
|
||||
1 RESI
|
||||
@ -1316,10 +1316,10 @@
|
||||
2 ADR1 360 W 4800 N, Provo, UT 84604
|
||||
1 PHON (801) 705-7000
|
||||
1 FAX (801) 705-7001
|
||||
1 EMAIL help@ancestry.com
|
||||
1 EMAIL help@@ancestry.com
|
||||
1 WWW http://www.ancestry.com
|
||||
0 @R0001@ REPO
|
||||
1 NAME SUBM (Submitter): (@SUBM@) The Subm /Tester/
|
||||
1 NAME SUBM (Submitter): (@@SUBM@@) The Subm /Tester/
|
||||
1 ADDR 123 Main St.
|
||||
2 CONT Winslow
|
||||
2 CONT PA
|
||||
@ -1329,7 +1329,7 @@
|
||||
2 STAE PA
|
||||
2 POST 12345
|
||||
1 PHON 440-871-3401
|
||||
1 EMAIL mrstester@gmail.com
|
||||
1 EMAIL mrstester@@gmail.com
|
||||
1 FAX 440-321-4568
|
||||
1 WWW http://mrstester.com
|
||||
1 NOTE @N0009@
|
||||
@ -1362,7 +1362,7 @@
|
||||
1 ADDR 123 High St., OSF village, CA, USA
|
||||
2 ADR1 123 High St., OSF village, CA, USA
|
||||
1 PHON 988-765-4321
|
||||
1 EMAIL tester_repo@osf.com
|
||||
1 EMAIL tester_repo@@osf.com
|
||||
1 FAX 987-654-3210
|
||||
1 WWW http://www.tester_repo.com
|
||||
1 NOTE @N0012@
|
||||
@ -1401,8 +1401,8 @@
|
||||
1 CONT
|
||||
1 CONT Only one phone number supported Lin
|
||||
1 CONC e 9: 3 PHON (800) 705-7000
|
||||
0 @N0009@ NOTE Records not imported into SUBM (Submitter): (@SUBM@) The Subm /Test
|
||||
1 CONC er/:
|
||||
0 @N0009@ NOTE Records not imported into SUBM (Submitter): (@@SUBM@@) The Subm /Te
|
||||
1 CONC ster/:
|
||||
1 CONT
|
||||
1 CONT Only one phone number supported Lin
|
||||
1 CONC e 29: 1 PHON 800-871-3401
|
||||
|
@ -44,7 +44,7 @@
|
||||
3 FORM Street, City, County, State, Country, Zip code
|
||||
2 PHON 440-871-3400
|
||||
2 PHON 800-871-3400
|
||||
2 EMAIL thetester@gmail.com
|
||||
2 EMAIL thetester@@gmail.com
|
||||
2 FAX 440-123-4567
|
||||
2 WWW http://thetester.com
|
||||
0 @I1@ INDI
|
||||
|
@ -171,12 +171,19 @@ class CLIDbManager:
|
||||
retval = {_("Unavailable"): "locked"}
|
||||
retval.update({_("Family Tree"): name,
|
||||
_("Path"): dirpath,
|
||||
_("Database"): dbid,
|
||||
_("Database"): self.get_backend_name_from_dbid(dbid),
|
||||
_("Last accessed"): time_val(dirpath)[1],
|
||||
_("Locked?"): self.is_locked(dirpath),
|
||||
})
|
||||
return retval
|
||||
|
||||
def get_backend_name_from_dbid(self, dbid):
|
||||
pmgr = BasePluginManager.get_instance()
|
||||
for plugin in pmgr.get_reg_databases():
|
||||
if plugin.id == dbid:
|
||||
return plugin._name
|
||||
return _("Unknown")
|
||||
|
||||
def print_family_tree_summaries(self, database_names=None):
|
||||
"""
|
||||
Prints a detailed list of the known family trees.
|
||||
@ -184,7 +191,7 @@ class CLIDbManager:
|
||||
print(_('Gramps Family Trees:'))
|
||||
for item in self.current_names:
|
||||
(name, dirpath, path_name, last,
|
||||
tval, enable, stock_id, backend_type, version) = item
|
||||
tval, enable, stock_id, backend_type) = item
|
||||
if (database_names is None or
|
||||
any([(re.match("^" + dbname + "$", name) or
|
||||
dbname == name)
|
||||
@ -206,7 +213,7 @@ class CLIDbManager:
|
||||
summary_list = []
|
||||
for item in self.current_names:
|
||||
(name, dirpath, path_name, last,
|
||||
tval, enable, stock_id, backend_type, version) = item
|
||||
tval, enable, stock_id, backend_type) = item
|
||||
if (database_names is None or
|
||||
any([(re.match("^" + dbname + "$", name) or
|
||||
dbname == name)
|
||||
@ -233,15 +240,6 @@ class CLIDbManager:
|
||||
backend_type = file.read()
|
||||
except:
|
||||
backend_type = "bsddb"
|
||||
try:
|
||||
with open(os.path.join(dirpath, "bdbversion.txt")) as file:
|
||||
version = file.read()
|
||||
except:
|
||||
version = "(0, 0, 0)"
|
||||
try:
|
||||
version = ast.literal_eval(version)
|
||||
except:
|
||||
version = (0, 0, 0)
|
||||
if os.path.isfile(path_name):
|
||||
with open(path_name, 'r', encoding='utf8') as file:
|
||||
name = file.readline().strip()
|
||||
@ -255,7 +253,7 @@ class CLIDbManager:
|
||||
|
||||
self.current_names.append(
|
||||
(name, os.path.join(dbdir, dpath), path_name,
|
||||
last, tval, enable, stock_id, backend_type, version))
|
||||
last, tval, enable, stock_id, backend_type))
|
||||
|
||||
self.current_names.sort()
|
||||
|
||||
|
@ -446,7 +446,7 @@ class CommandLineReport:
|
||||
else:
|
||||
print(_("Unknown option: %s") % option, file=sys.stderr)
|
||||
print(_(" Valid options are:") +
|
||||
", ".join(list(self.options_dict.keys())),
|
||||
_(", ").join(list(self.options_dict.keys())), # Arabic OK
|
||||
file=sys.stderr)
|
||||
print(_(" Use '%(donottranslate)s' to see description "
|
||||
"and acceptable values"
|
||||
@ -527,7 +527,7 @@ class CommandLineReport:
|
||||
else:
|
||||
print(_("Ignoring unknown option: %s") % opt, file=sys.stderr)
|
||||
print(_(" Valid options are:"),
|
||||
", ".join(list(self.options_dict.keys())),
|
||||
_(", ").join(list(self.options_dict.keys())), # Arabic OK
|
||||
file=sys.stderr)
|
||||
print(_(" Use '%(donottranslate)s' to see description "
|
||||
"and acceptable values"
|
||||
|
@ -229,6 +229,7 @@ class DateParserHU(DateParser):
|
||||
|
||||
self._numeric = re.compile(
|
||||
"((\d+)[/\.])?\s*((\d+)[/\.])?\s*(\d+)[/\. ]?$")
|
||||
# this next RE has the (possibly-slashed) year at the string's start
|
||||
self._text2 = re.compile('((\d+)(/\d+)?\.)?\s+?%s\.?\s*(\d+\.)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
_span_1 = ['-tó\(ő\)l', '-tól', '-től']
|
||||
|
@ -130,6 +130,7 @@ class DateParserLT(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
# this next RE has the (possibly-slashed) year at the string's start
|
||||
self._text2 = re.compile('((\d+)(/\d+)?)?\s+?m\.\s+%s\s*(\d+)?\s*d?\.?$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
_span_1 = ['nuo']
|
||||
|
@ -95,6 +95,10 @@ class DateParserSv(DateParser):
|
||||
def init_strings(self):
|
||||
""" Define, in Swedish, span and range regular expressions"""
|
||||
DateParser.init_strings(self)
|
||||
self._numeric = re.compile("((\d+)/)?\s*((\d+)/)?\s*(\d+)[/ ]?$")
|
||||
# this next RE has the (possibly-slashed) year at the string's start
|
||||
self._text2 = re.compile('((\d+)(/\d+)?)?\s+?%s\s*(\d+)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._span = re.compile("(från)?\s*(?P<start>.+)\s*(till|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(mellan)\s+(?P<start>.+)\s+och\s+(?P<stop>.+)",
|
||||
@ -109,72 +113,70 @@ class DateDisplaySv(DateDisplay):
|
||||
"""
|
||||
Swedish language date display class.
|
||||
"""
|
||||
long_months = ( "", "januari", "februari", "mars", "april", "maj",
|
||||
"juni", "juli", "augusti", "september", "oktober",
|
||||
"november", "december" )
|
||||
|
||||
short_months = ( "", "jan", "feb", "mar", "apr", "maj", "jun",
|
||||
"jul", "aug", "sep", "okt", "nov", "dec" )
|
||||
_bce_str = "%s f Kr"
|
||||
|
||||
formats = (
|
||||
"ÅÅÅÅ-MM-DD (ISO)",
|
||||
"År/mån/dag",
|
||||
"Månad dag, år",
|
||||
"MÅN DAG ÅR",
|
||||
"Dag månad år",
|
||||
"DAG MÅN ÅR",
|
||||
"År månad dag",
|
||||
"År mån dag",
|
||||
)
|
||||
# this must agree with DateDisplayEn's "formats" definition
|
||||
# (since no locale-specific _display_gregorian exists, here)
|
||||
# this definition must agree with its "_display_calendar" method
|
||||
|
||||
calendar = (
|
||||
"",
|
||||
"juliansk",
|
||||
"hebreisk",
|
||||
"fransk republikansk",
|
||||
"persisk",
|
||||
"islamisk",
|
||||
"svensk"
|
||||
)
|
||||
def _display_calendar(self, date_val, long_months, short_months = None,
|
||||
inflect=""):
|
||||
# this must agree with its locale-specific "formats" definition
|
||||
|
||||
_mod_str = ("", "före ", "efter ", "c:a ", "", "", "")
|
||||
if short_months is None:
|
||||
# Let the short formats work the same as long formats
|
||||
short_months = long_months
|
||||
|
||||
_qual_str = ("", "uppskattat ", "beräknat ")
|
||||
|
||||
_bce_str = "%s f Kr"
|
||||
|
||||
def display(self, date):
|
||||
"""
|
||||
Return a text string representing the date.
|
||||
"""
|
||||
mod = date.get_modifier()
|
||||
cal = date.get_calendar()
|
||||
qual = date.get_quality()
|
||||
start = date.get_start_date()
|
||||
newyear = date.get_new_year()
|
||||
|
||||
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())
|
||||
scal = self.format_extras(cal, newyear)
|
||||
return "%sfrån %s till %s%s" % (qual_str, d1, d2, scal)
|
||||
elif mod == Date.MOD_RANGE:
|
||||
d1 = self.display_cal[cal](start)
|
||||
d2 = self.display_cal[cal](date.get_stop_date())
|
||||
scal = self.format_extras(cal, newyear)
|
||||
return "%smellan %s och %s%s" % (qual_str, d1, d2,
|
||||
scal)
|
||||
if self.format == 0:
|
||||
return self.display_iso(date_val)
|
||||
elif self.format == 1:
|
||||
# numerical: year/month/day (with slashes)
|
||||
value = self.dd_dformat01(date_val)
|
||||
elif self.format == 2:
|
||||
# year month_name day
|
||||
value = self.dd_dformat02_sv(date_val, long_months)
|
||||
# elif self.format == 3:
|
||||
else:
|
||||
text = self.display_cal[date.get_calendar()](start)
|
||||
scal = self.format_extras(cal, newyear)
|
||||
return "%s%s%s%s" % (qual_str, self._mod_str[mod],
|
||||
text, scal)
|
||||
# year month_abbreviation day
|
||||
value = self.dd_dformat03_sv(date_val, short_months)
|
||||
if date_val[2] < 0:
|
||||
# TODO fix BUG 7064: non-Gregorian calendars wrongly use BCE notation for negative dates
|
||||
return self._bce_str % value
|
||||
else:
|
||||
return value
|
||||
|
||||
def dd_dformat02_sv(self, date_val, long_months):
|
||||
# year month_name day
|
||||
year = self._slash_year(date_val[2], date_val[3])
|
||||
if date_val[0] == 0:
|
||||
if date_val[1] == 0:
|
||||
return year
|
||||
else:
|
||||
return "%s %s" % (year, long_months[date_val[1]])
|
||||
elif date_val[1] == 0: # month is zero but day is not (see 8477)
|
||||
return self.display_iso(date_val)
|
||||
else:
|
||||
return "%s %s %s" % (year, long_months[date_val[1]], date_val[0])
|
||||
|
||||
def dd_dformat03_sv(self, date_val, short_months):
|
||||
# year month_abbreviation day
|
||||
year = self._slash_year(date_val[2], date_val[3])
|
||||
if date_val[0] == 0:
|
||||
if date_val[1] == 0:
|
||||
return year
|
||||
else:
|
||||
return "%s %s" % (year, short_months[date_val[1]])
|
||||
elif date_val[1] == 0: # month is zero but day is not (see 8477)
|
||||
return self.display_iso(date_val)
|
||||
else:
|
||||
return "%s %s %s" % (year, short_months[date_val[1]], date_val[0])
|
||||
|
||||
display = DateDisplay.display_formatted
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
@ -342,6 +342,7 @@ class DateDisplay:
|
||||
for item in [scal, snewyear]:
|
||||
if item:
|
||||
if retval:
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
retval += ", "
|
||||
retval += item
|
||||
if retval:
|
||||
@ -608,6 +609,9 @@ class DateDisplay:
|
||||
value = value.replace('%A', self._get_long_weekday(date_val))
|
||||
if date_val[0] == 0: # ignore the zero day and its delimiter
|
||||
i_day = value.find('%d')
|
||||
if len(value) == i_day + 2: # delimiter is left of the day
|
||||
value = value.replace(value[i_day-1:i_day+2], '')
|
||||
else: # delimiter is to the right of the day
|
||||
value = value.replace(value[i_day:i_day+3], '')
|
||||
value = value.replace('%d', str(date_val[0]))
|
||||
value = value.replace('%Y', str(abs(date_val[2])))
|
||||
|
@ -449,8 +449,9 @@ class DateParser:
|
||||
self._modifier_after = re.compile('(.*)\s+%s' % self._mod_after_str,
|
||||
re.IGNORECASE)
|
||||
self._abt2 = re.compile('<(.*)>', re.IGNORECASE)
|
||||
self._text = re.compile('%s\.?\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._text = re.compile('%s\.?(\s+\d+)?\s*,?\s+((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
# this next RE has the (possibly-slashed) year at the string's end
|
||||
self._text2 = re.compile('(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$' % self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext = re.compile('%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._jmon_str,
|
||||
@ -521,7 +522,7 @@ class DateParser:
|
||||
|
||||
def _parse_calendar(self, text, regex1, regex2, mmap, check=None):
|
||||
match = regex1.match(text.lower())
|
||||
if match:
|
||||
if match: # user typed in 'month-name day year' or 'month-name year'
|
||||
groups = match.groups()
|
||||
if groups[0] is None:
|
||||
m = 0
|
||||
@ -534,19 +535,21 @@ class DateParser:
|
||||
s = False
|
||||
else:
|
||||
d = self._get_int(groups[1])
|
||||
if groups[4] is not None: # slash year "/80"
|
||||
if groups[4] is not None:
|
||||
y = int(groups[3]) + 1 # fullyear + 1
|
||||
s = True
|
||||
else: # regular, non-slash date
|
||||
s = True # slash year
|
||||
else: # regular year
|
||||
y = int(groups[3])
|
||||
s = False
|
||||
value = (d, m, y, s)
|
||||
if check and not check((d, m, y)):
|
||||
if s and julian_valid(value): # slash year
|
||||
pass
|
||||
elif check and not check((d, m, y)):
|
||||
value = Date.EMPTY
|
||||
return value
|
||||
|
||||
match = regex2.match(text.lower())
|
||||
if match:
|
||||
if match: # user typed in day month-name year or year month-name day
|
||||
groups = match.groups()
|
||||
if self.ymd:
|
||||
if groups[3] is None:
|
||||
@ -558,10 +561,10 @@ class DateParser:
|
||||
y = None
|
||||
s = False
|
||||
else:
|
||||
if groups[2] is not None: # slash year digit
|
||||
if groups[2] is not None:
|
||||
y = int(groups[1]) + 1 # fullyear + 1
|
||||
s = True
|
||||
else: # regular, non-slash year
|
||||
s = True # slash year
|
||||
else: # regular year
|
||||
y = int(groups[1])
|
||||
s = False
|
||||
else:
|
||||
@ -574,10 +577,10 @@ class DateParser:
|
||||
y = None
|
||||
s = False
|
||||
else:
|
||||
if groups[4] is not None: # slash year digit
|
||||
if groups[4] is not None:
|
||||
y = int(groups[3]) + 1 # fullyear + 1
|
||||
s = True
|
||||
else: # regular, non-slash year
|
||||
s = True # slash year
|
||||
else: # regular year
|
||||
y = int(groups[3])
|
||||
s = False
|
||||
value = (d, m, y, s)
|
||||
@ -618,8 +621,8 @@ class DateParser:
|
||||
y = self._get_int(groups[0])
|
||||
m = self._get_int(groups[3])
|
||||
d = self._get_int(groups[4])
|
||||
if groups[2] and julian_valid((d, m, y + 1)): # slash year digit
|
||||
return (d, m, y + 1, True)
|
||||
if groups[2] and julian_valid((d, m, y + 1)):
|
||||
return (d, m, y + 1, True) # slash year
|
||||
if check is None or check((d, m, y)):
|
||||
return (d, m, y, False)
|
||||
return Date.EMPTY
|
||||
@ -669,6 +672,15 @@ class DateParser:
|
||||
y = self._get_int(groups[1])
|
||||
m = self._get_int(groups[3])
|
||||
d = self._get_int(groups[4])
|
||||
if m > 12: # maybe a slash year, not a month (1722/3 is March)
|
||||
if y % 100 == 99:
|
||||
modyear = (y + 1) % 1000
|
||||
elif y % 10 == 9:
|
||||
modyear = (y + 1) % 100
|
||||
else:
|
||||
modyear = (y + 1) % 10
|
||||
if m == modyear:
|
||||
return (0, 0, y + 1, True) # slash year
|
||||
else:
|
||||
y = self._get_int(groups[4])
|
||||
if self.dmy:
|
||||
@ -681,6 +693,15 @@ class DateParser:
|
||||
else:
|
||||
m = self._get_int(groups[1])
|
||||
d = self._get_int(groups[3])
|
||||
if m > 12: # maybe a slash year, not a month
|
||||
if m % 100 == 99:
|
||||
modyear = (m + 1) % 1000
|
||||
elif m % 10 == 9:
|
||||
modyear = (m + 1) % 100
|
||||
else:
|
||||
modyear = (m + 1) % 10
|
||||
if y == modyear:
|
||||
return (0, 0, m + 1, True) # slash year
|
||||
value = (d, m, y, False)
|
||||
if check and not check((d, m, y)):
|
||||
value = Date.EMPTY
|
||||
@ -906,8 +927,6 @@ class DateParser:
|
||||
if subdate == Date.EMPTY and text != "":
|
||||
date.set_as_text(text)
|
||||
return
|
||||
#else:
|
||||
# print 'valid subdate', text, subdate
|
||||
except:
|
||||
date.set_as_text(text)
|
||||
return
|
||||
|
@ -601,6 +601,11 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
# run backend-specific code:
|
||||
self._initialize(directory)
|
||||
|
||||
# We use the existence of the person table as a proxy for the database
|
||||
# being new
|
||||
if not self.dbapi.table_exists("person"):
|
||||
self._create_schema()
|
||||
|
||||
# Load metadata
|
||||
self.name_formats = self._get_metadata('name_formats')
|
||||
self.owner = self._get_metadata('researcher', default=Researcher())
|
||||
@ -674,7 +679,7 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
Close the database.
|
||||
if update is False, don't change access times, etc.
|
||||
"""
|
||||
if self._directory:
|
||||
if self._directory != ":memory:":
|
||||
if update:
|
||||
# This is just a dummy file to indicate last modified time of
|
||||
# the database for gramps.cli.clidbman:
|
||||
@ -2461,7 +2466,7 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
_("Number of repositories"): self.get_number_of_repositories(),
|
||||
_("Number of notes"): self.get_number_of_notes(),
|
||||
_("Number of tags"): self.get_number_of_tags(),
|
||||
_("Data version"): ".".join([str(v) for v in self.VERSION]),
|
||||
_("Schema version"): ".".join([str(v) for v in self.VERSION]),
|
||||
}
|
||||
|
||||
def _order_by_person_key(self, person):
|
||||
|
@ -129,8 +129,8 @@ def import_as_dict(filename, user, skp_imp_adds=True):
|
||||
"""
|
||||
Import the filename into a InMemoryDB and return it.
|
||||
"""
|
||||
db = make_database("inmemorydb")
|
||||
db.load(None)
|
||||
db = make_database("sqlite")
|
||||
db.load(":memory:")
|
||||
db.set_feature("skip-import-additions", skp_imp_adds)
|
||||
status = import_from_filename(db, filename, user)
|
||||
return db if status else None
|
||||
|
@ -80,6 +80,7 @@ class PlaceDisplay:
|
||||
if config.get('preferences.place-reverse'):
|
||||
names.reverse()
|
||||
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ", ".join(names)
|
||||
|
||||
def _find_populated_place(places):
|
||||
|
@ -74,6 +74,7 @@ class HasRepo(Rule):
|
||||
if self.list[2]:
|
||||
addr_match = False
|
||||
for addr in repo.address_list:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
addr_text = ', '.join(addr.get_text_data_list())
|
||||
if self.match_substring(2, addr_text):
|
||||
addr_match = True
|
||||
@ -84,6 +85,7 @@ class HasRepo(Rule):
|
||||
if self.list[3]:
|
||||
url_match = False
|
||||
for url in repo.urls:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
url_text = ', '.join(url.get_text_data_list())
|
||||
if self.match_substring(3, url_text):
|
||||
url_match = True
|
||||
|
@ -111,7 +111,7 @@ class Note(BasicPrimaryObject):
|
||||
"_class": {"enum": [cls.__name__]},
|
||||
"handle": {"type": "string",
|
||||
"maxLength": 50,
|
||||
"title": ("Handle")},
|
||||
"title": _("Handle")},
|
||||
"gramps_id": {"type": "string",
|
||||
"title": _("Gramps ID")},
|
||||
"text": StyledText.get_schema(),
|
||||
|
@ -27,6 +27,7 @@
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from copy import copy
|
||||
from .styledtexttag import StyledTextTag
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
@ -77,6 +78,8 @@ class StyledText:
|
||||
There could be a 'merge_tags' functionality in :py:meth:`__init__`,
|
||||
however :py:class:`StyledTextBuffer` will merge them automatically if
|
||||
the text is displayed.
|
||||
3. Warning: Some of these operations modify the source tag ranges in place
|
||||
so if you intend to use a source tag more than once, copy it for use.
|
||||
"""
|
||||
(POS_TEXT, POS_TAGS) = list(range(2))
|
||||
|
||||
@ -198,17 +201,27 @@ class StyledText:
|
||||
new_string = self._string.join([str(string) for string in seq])
|
||||
|
||||
offset = 0
|
||||
not_first = False
|
||||
new_tags = []
|
||||
self_len = len(self._string)
|
||||
|
||||
for text in seq:
|
||||
if not_first: # if not first time through...
|
||||
# put the joined element tag(s) into place
|
||||
for tag in self.tags:
|
||||
ntag = copy(tag)
|
||||
ntag.ranges = [(start + offset, end + offset)
|
||||
for (start, end) in tag.ranges]
|
||||
new_tags += [ntag]
|
||||
offset += self_len
|
||||
if isinstance(text, StyledText):
|
||||
for tag in text.tags:
|
||||
tag.ranges = [(start + offset, end + offset)
|
||||
ntag = copy(tag)
|
||||
ntag.ranges = [(start + offset, end + offset)
|
||||
for (start, end) in tag.ranges]
|
||||
new_tags += [tag]
|
||||
|
||||
offset = offset + len(str(text)) + self_len
|
||||
new_tags += [ntag]
|
||||
offset += len(str(text))
|
||||
not_first = True
|
||||
|
||||
return self.__class__(new_string, new_tags)
|
||||
|
||||
@ -366,6 +379,7 @@ if __name__ == '__main__':
|
||||
from .styledtexttagtype import StyledTextTagType
|
||||
T1 = StyledTextTag(StyledTextTagType(1), 'v1', [(0, 2), (2, 4), (4, 6)])
|
||||
T2 = StyledTextTag(StyledTextTagType(2), 'v2', [(1, 3), (3, 5), (0, 7)])
|
||||
T3 = StyledTextTag(StyledTextTagType(0), 'v3', [(0, 1)])
|
||||
|
||||
A = StyledText('123X456', [T1])
|
||||
B = StyledText("abcXdef", [T2])
|
||||
@ -376,7 +390,7 @@ if __name__ == '__main__':
|
||||
|
||||
C = C.join([A, S, B])
|
||||
L = C.split()
|
||||
C = C.replace('X', StyledText('_'))
|
||||
C = C.replace('X', StyledText('_', [T3]))
|
||||
A = A + B
|
||||
|
||||
print(A)
|
||||
|
82
gramps/gen/lib/test/styledtext_test.py
Normal file
82
gramps/gen/lib/test/styledtext_test.py
Normal file
@ -0,0 +1,82 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Paul Culley
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
""" unittest for styledtext """
|
||||
|
||||
import unittest
|
||||
from copy import deepcopy
|
||||
from ..styledtext import StyledText
|
||||
from ..styledtexttag import StyledTextTag
|
||||
from ..styledtexttagtype import StyledTextTagType
|
||||
|
||||
|
||||
class Test1(unittest.TestCase):
|
||||
T1 = StyledTextTag(StyledTextTagType(1), 'v1', [(0, 2), (2, 4), (4, 6)])
|
||||
T2 = StyledTextTag(StyledTextTagType(2), 'v2', [(1, 3), (3, 5), (0, 7)])
|
||||
T3 = StyledTextTag(StyledTextTagType(0), 'v3', [(0, 1)])
|
||||
T4 = StyledTextTag(StyledTextTagType(2), 'v2',
|
||||
[(8, 10), (10, 12), (7, 14)])
|
||||
T5 = StyledTextTag(StyledTextTagType(2), 'v2',
|
||||
[(19, 21), (21, 23), (18, 25)])
|
||||
|
||||
A = StyledText('123X456', [T1])
|
||||
B = StyledText("abcXdef", [T2])
|
||||
|
||||
C = StyledText('\n')
|
||||
|
||||
S = 'cleartext'
|
||||
|
||||
# some basic tests
|
||||
# because the StyledText.__eq__ method doesn't work very well (tags don't
|
||||
# compare when they are equivalent, but not equal) we have to use
|
||||
# serialize for comparisons.
|
||||
def test_join(self):
|
||||
C = self.C.join([self.A, self.S, deepcopy(self.B)])
|
||||
_C = StyledText('123X456\ncleartext\nabcXdef', [self.T1, self.T5])
|
||||
self.assertEqual(C.serialize(), _C.serialize())
|
||||
|
||||
def test_split(self):
|
||||
C = self.C.join([self.A, self.S, deepcopy(self.B)])
|
||||
L = C.split()
|
||||
_L = [self.A, self.S, self.B]
|
||||
self.assertEqual(L[0].serialize(), self.A.serialize())
|
||||
self.assertEqual(str(L[1]), self.S)
|
||||
self.assertEqual(L[2].serialize(), self.B.serialize())
|
||||
|
||||
def test_replace(self):
|
||||
C = self.C.join([self.A, self.S, deepcopy(self.B)])
|
||||
C = C.replace('X', StyledText('_', [self.T3]))
|
||||
_C = ('123_456\ncleartext\nabc_def',
|
||||
[((1, ''), 'v1', [(0, 2), (2, 3)]),
|
||||
((0, ''), 'v3', [(3, 4)]),
|
||||
((1, ''), 'v1', [(4, 6)]),
|
||||
((2, ''), 'v2', [(19, 21), (18, 21)]),
|
||||
((0, ''), 'v3', [(21, 22)]),
|
||||
((2, ''), 'v2', [(22, 23), (22, 25)])])
|
||||
self.assertEqual(C.serialize(), _C)
|
||||
|
||||
def test_add(self):
|
||||
A = deepcopy(self.A) + deepcopy(self.B)
|
||||
_A = StyledText('123X456abcXdef', [self.T1, self.T4])
|
||||
self.assertEqual(A.serialize(), _A.serialize())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -399,7 +399,6 @@ class Gramplet:
|
||||
self.update()
|
||||
|
||||
def _no_db(self):
|
||||
if self.dbstate.db.is_open():
|
||||
self.disconnect_all() # clear the old signals
|
||||
|
||||
def get_option_widget(self, label):
|
||||
|
@ -25,6 +25,7 @@ Provide a simplified database access interface to the Gramps database.
|
||||
"""
|
||||
from ..lib import (Person, Family, Event, Source, Place, Citation,
|
||||
Media, Repository, Note, Date, Tag)
|
||||
from ..errors import HandleError
|
||||
from ..datehandler import displayer
|
||||
from ..utils.string import gender as gender_map
|
||||
from ..utils.db import get_birth_or_fallback, get_death_or_fallback
|
||||
@ -961,8 +962,12 @@ class SimpleAccess:
|
||||
:param value: gramps_id or handle.
|
||||
"""
|
||||
if object_class in self.dbase.get_table_names():
|
||||
obj = self.dbase.get_table_metadata(object_class)\
|
||||
[prop + "_func"](value)
|
||||
try:
|
||||
obj = self.dbase.get_table_metadata(
|
||||
object_class)[prop + "_func"](value)
|
||||
except HandleError:
|
||||
# Deals with deleted objects referenced in Note Links
|
||||
obj = None
|
||||
if obj:
|
||||
if isinstance(obj, Person):
|
||||
return "%s: %s [%s]" % (_(object_class),
|
||||
|
@ -311,8 +311,7 @@ class Callback:
|
||||
for key in keymap:
|
||||
self.__callback_map[signal_name].remove(key)
|
||||
self.__callback_map[signal_name] = None
|
||||
self.__callback_map = None
|
||||
del self.__callback_map
|
||||
self.__callback_map = {}
|
||||
|
||||
def emit(self, signal_name, args=tuple()):
|
||||
"""
|
||||
|
@ -117,6 +117,7 @@ def make_unknown(class_arg, explanation, class_func, commit_func, transaction,
|
||||
obj.set_type(EventType.UNKNOWN)
|
||||
elif isinstance(obj, Place):
|
||||
obj.set_title(_('Unknown'))
|
||||
obj.name.set_value(_('Unknown'))
|
||||
elif isinstance(obj, Source):
|
||||
obj.set_title(_('Unknown'))
|
||||
elif isinstance(obj, Citation):
|
||||
|
@ -288,6 +288,9 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
if not __debug__:
|
||||
self.convert_btn.set_visible(False)
|
||||
|
||||
if not _RCS_FOUND: # it's not in Windows
|
||||
self.rcs_btn.set_visible(False)
|
||||
|
||||
# if nothing is selected
|
||||
if not node:
|
||||
self.connect_btn.set_sensitive(False)
|
||||
@ -315,10 +318,10 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
self.rcs_btn.set_sensitive(True)
|
||||
else:
|
||||
self.close_btn.set_sensitive(False)
|
||||
backend_name = self.get_backend_name_from_dbid("bsddb")
|
||||
dbid = config.get('database.backend')
|
||||
backend_type = self.get_backend_name_from_dbid(dbid)
|
||||
if (store.get_value(node, ICON_COL) in [None, ""] and
|
||||
store.get_value(node,
|
||||
BACKEND_COL).startswith(backend_name)):
|
||||
store.get_value(node, BACKEND_COL) != backend_type):
|
||||
self.convert_btn.set_sensitive(True)
|
||||
else:
|
||||
self.convert_btn.set_sensitive(False)
|
||||
@ -422,9 +425,7 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
for items in self.current_names:
|
||||
data = list(items[:8])
|
||||
backend_type = self.get_backend_name_from_dbid(data[BACKEND_COL])
|
||||
version = str(".".join([str(v) for v in items[8]]))
|
||||
node = self.model.append(None, data[:-1] + [backend_type + ", "
|
||||
+ version])
|
||||
node = self.model.append(None, data[:-1] + [backend_type])
|
||||
# For already loaded database, set current_node:
|
||||
if self.dbstate.is_open() and \
|
||||
self.dbstate.db.get_save_path() == data[1]:
|
||||
@ -434,7 +435,7 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
last_accessed_node = node
|
||||
for rdata in find_revisions(os.path.join(items[1], ARCHIVE_V)):
|
||||
data = [rdata[2], rdata[0], items[1], rdata[1], 0, False, "",
|
||||
backend_type + ", " + version]
|
||||
backend_type]
|
||||
self.model.append(node, data)
|
||||
if self._current_node is None:
|
||||
self._current_node = last_accessed_node
|
||||
@ -464,6 +465,7 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
or the path and name if something has been selected
|
||||
"""
|
||||
self.show()
|
||||
self.__update_buttons(self.selection)
|
||||
while True:
|
||||
value = self.top.run()
|
||||
if value == Gtk.ResponseType.OK:
|
||||
@ -762,21 +764,23 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
|
||||
def __convert_db_ask(self, obj):
|
||||
"""
|
||||
Ask to convert a closed BSDDB tree into a new DB-API
|
||||
tree.
|
||||
Ask to convert a closed family tree into the default database backend.
|
||||
"""
|
||||
store, node = self.selection.get_selected()
|
||||
name = store[node][0]
|
||||
dirname = store[node][1]
|
||||
dbid = config.get('database.backend')
|
||||
backend_type = self.get_backend_name_from_dbid(dbid)
|
||||
QuestionDialog(
|
||||
_("Convert the '%s' database?") % name,
|
||||
_("You wish to convert this database into the new DB-API format?"),
|
||||
_("Do you wish to convert this family tree into a "
|
||||
"%(database_type)s database?") % {'database_type': backend_type},
|
||||
_("Convert"),
|
||||
lambda: self.__convert_db(name, dirname), parent=self.top)
|
||||
|
||||
def __convert_db(self, name, dirname):
|
||||
"""
|
||||
Actually convert the db from BSDDB to DB-API.
|
||||
Actually convert the family tree into the default database backend.
|
||||
"""
|
||||
try:
|
||||
db = open_database(name)
|
||||
@ -809,7 +813,7 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
new_text = "%s %s" % (name, _("(Converted #%d)") % count)
|
||||
new_path, newname = self._create_new_db(new_text, edit_entry=False)
|
||||
## Create a new database of correct type:
|
||||
dbase = make_database("dbapi")
|
||||
dbase = make_database(config.get('database.backend'))
|
||||
dbase.write_version(new_path)
|
||||
dbase.load(new_path)
|
||||
## import from XML
|
||||
@ -981,13 +985,6 @@ class DbManager(CLIDbManager, ManagedWindow):
|
||||
parent=self.top)
|
||||
self.new_btn.set_sensitive(True)
|
||||
|
||||
def get_backend_name_from_dbid(self, dbid):
|
||||
pmgr = GuiPluginManager.get_instance()
|
||||
for plugin in pmgr.get_reg_databases():
|
||||
if plugin.id == dbid:
|
||||
return plugin._name
|
||||
return _("Unknown")
|
||||
|
||||
def _create_new_db(self, title=None, create_db=True, dbid=None,
|
||||
edit_entry=True):
|
||||
"""
|
||||
|
@ -206,8 +206,8 @@ class ErrorDialog(Gtk.MessageDialog):
|
||||
|
||||
Gtk.MessageDialog.__init__(self, transient_for=parent,
|
||||
modal=True,
|
||||
message_type=Gtk.MessageType.ERROR,
|
||||
buttons=Gtk.ButtonsType.CLOSE)
|
||||
message_type=Gtk.MessageType.ERROR)
|
||||
self.add_button(_('_Close'), Gtk.ResponseType.CLOSE)
|
||||
self.set_markup('<span weight="bold" size="larger">%s</span>' % str(msg1))
|
||||
self.format_secondary_text(msg2)
|
||||
self.set_icon(ICON)
|
||||
@ -252,8 +252,8 @@ class WarningDialog(Gtk.MessageDialog):
|
||||
|
||||
Gtk.MessageDialog.__init__(self, transient_for=parent,
|
||||
modal=True,
|
||||
message_type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE)
|
||||
message_type=Gtk.MessageType.WARNING)
|
||||
self.add_button(_('_Close'), Gtk.ResponseType.CLOSE)
|
||||
self.set_markup('<span weight="bold" size="larger">%s</span>' % msg1)
|
||||
self.format_secondary_markup(msg2)
|
||||
# FIXME: Hyper-links in the secondary text display as underlined text,
|
||||
@ -278,8 +278,8 @@ class OkDialog(Gtk.MessageDialog):
|
||||
|
||||
Gtk.MessageDialog.__init__(self, transient_for=parent,
|
||||
modal=True,
|
||||
message_type=Gtk.MessageType.INFO,
|
||||
buttons=Gtk.ButtonsType.CLOSE)
|
||||
message_type=Gtk.MessageType.INFO)
|
||||
self.add_button(_('_Close'), Gtk.ResponseType.CLOSE)
|
||||
self.set_markup('<span weight="bold" size="larger">%s</span>' % msg1)
|
||||
self.format_secondary_text(msg2)
|
||||
self.set_icon(ICON)
|
||||
|
@ -196,11 +196,15 @@ class EditPlace(EditPrimary):
|
||||
|
||||
def _validate_coordinate(self, widget, text, typedeg):
|
||||
if (typedeg == 'lat') and not conv_lat_lon(text, "0", "ISO-D"):
|
||||
return ValidationError(_("Invalid latitude (syntax: 18\u00b09'") +
|
||||
_('48.21"S, -18.2412 or -18:9:48.21)'))
|
||||
return ValidationError(
|
||||
# translators: translate the "S" too (and the "or" of course)
|
||||
_('Invalid latitude\n(syntax: '
|
||||
'18\u00b09\'48.21"S, -18.2412 or -18:9:48.21)'))
|
||||
elif (typedeg == 'lon') and not conv_lat_lon("0", text, "ISO-D"):
|
||||
return ValidationError(_("Invalid longitude (syntax: 18\u00b09'") +
|
||||
_('48.21"E, -18.2412 or -18:9:48.21)'))
|
||||
return ValidationError(
|
||||
# translators: translate the "E" too (and the "or" of course)
|
||||
_('Invalid longitude\n(syntax: '
|
||||
'18\u00b09\'48.21"E, -18.2412 or -18:9:48.21)'))
|
||||
|
||||
def update_title(self):
|
||||
new_title = place_displayer.display(self.db, self.obj)
|
||||
|
@ -189,11 +189,15 @@ class EditPlaceRef(EditReference):
|
||||
|
||||
def _validate_coordinate(self, widget, text, typedeg):
|
||||
if (typedeg == 'lat') and not conv_lat_lon(text, "0", "ISO-D"):
|
||||
return ValidationError(_("Invalid latitude (syntax: 18\u00b09'") +
|
||||
_('48.21"S, -18.2412 or -18:9:48.21)'))
|
||||
return ValidationError(
|
||||
# translators: translate the "S" too (and the "or" of course)
|
||||
_('Invalid latitude\n(syntax: '
|
||||
'18\u00b09\'48.21"S, -18.2412 or -18:9:48.21)'))
|
||||
elif (typedeg == 'lon') and not conv_lat_lon("0", text, "ISO-D"):
|
||||
return ValidationError(_("Invalid longitude (syntax: 18\u00b09'") +
|
||||
_('48.21"E, -18.2412 or -18:9:48.21)'))
|
||||
return ValidationError(
|
||||
# translators: translate the "E" too (and the "or" of course)
|
||||
_('Invalid longitude\n(syntax: '
|
||||
'18\u00b09\'48.21"E, -18.2412 or -18:9:48.21)'))
|
||||
|
||||
def update_title(self):
|
||||
new_title = place_displayer.display(self.db, self.source)
|
||||
|
@ -267,6 +267,7 @@ class MergePerson(ManagedWindow):
|
||||
if len(alist) > 0:
|
||||
self.add(tobj, title, _("Addresses"))
|
||||
for addr in alist:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
location = ", ".join([addr.get_street(), addr.get_city(),
|
||||
addr.get_state(), addr.get_country(),
|
||||
addr.get_postal_code(), addr.get_phone()])
|
||||
|
@ -176,6 +176,7 @@ class PluginDialog(ManagedWindow):
|
||||
self.title.set_text('<span weight="bold" size="larger">%s</span>' \
|
||||
% pdata.name)
|
||||
self.title.set_use_markup(1)
|
||||
# TODO for Arabic, should the next two lines' commas be translated?
|
||||
self.author_name.set_text(', '.join(pdata.authors))
|
||||
self.author_email.set_text(', '.join(pdata.authors_email))
|
||||
self.item = pdata
|
||||
|
@ -1209,6 +1209,7 @@ class UpdateAddons(ManagedWindow):
|
||||
if errors:
|
||||
OkDialog(_("Installation Errors"),
|
||||
_("The following addons had errors: ") +
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
", ".join(errors),
|
||||
parent=self.parent_window)
|
||||
if count:
|
||||
|
@ -120,10 +120,11 @@ class UndoHistory(ManagedWindow):
|
||||
scrolled_window.add(self.tree)
|
||||
self.window.vbox.pack_start(scrolled_window, True, True, 0)
|
||||
|
||||
self.sel_chng_hndlr = self.selection.connect('changed',
|
||||
self._selection_changed)
|
||||
self._build_model()
|
||||
self._update_ui()
|
||||
|
||||
self.selection.connect('changed', self._selection_changed)
|
||||
self.show()
|
||||
|
||||
def _selection_changed(self, obj):
|
||||
@ -226,6 +227,7 @@ class UndoHistory(ManagedWindow):
|
||||
)
|
||||
|
||||
def _build_model(self):
|
||||
self.selection.handler_block(self.sel_chng_hndlr)
|
||||
self.model.clear()
|
||||
fg = bg = None
|
||||
|
||||
@ -243,6 +245,7 @@ class UndoHistory(ManagedWindow):
|
||||
mod_text = txn.get_description()
|
||||
self.model.append(row=[time_text, mod_text, fg, bg])
|
||||
path = (self.undodb.undo_count,)
|
||||
self.selection.handler_unblock(self.sel_chng_hndlr)
|
||||
self.selection.select_path(path)
|
||||
|
||||
def update(self):
|
||||
|
@ -129,6 +129,7 @@ class CitationBaseModel:
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[COLUMN_TAGS]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
||||
def citation_tag_color(self, data):
|
||||
@ -241,6 +242,7 @@ class CitationBaseModel:
|
||||
try:
|
||||
source = self.db.get_source_from_handle(source_handle)
|
||||
tag_list = list(map(self.get_tag_name, source.get_tag_list()))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
value = ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
except:
|
||||
value = ''
|
||||
@ -288,6 +290,7 @@ class CitationBaseModel:
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[COLUMN2_TAGS]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
||||
def source_src_tag_color(self, data):
|
||||
|
@ -224,4 +224,5 @@ class EventModel(FlatBaseModel):
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[COLUMN_TAGS]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
@ -236,4 +236,5 @@ class FamilyModel(FlatBaseModel):
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[13]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
@ -187,4 +187,5 @@ class MediaModel(FlatBaseModel):
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[11]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
@ -166,4 +166,5 @@ class NoteModel(FlatBaseModel):
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[Note.POS_TAGS]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
@ -559,6 +559,7 @@ class PeopleBaseModel(BaseModel):
|
||||
cached, value = self.get_cached_value(handle, "TAGS")
|
||||
if not cached:
|
||||
tag_list = list(map(self.get_tag_name, data[COLUMN_TAGS]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
value = ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
self.set_cached_value(handle, "TAGS", value)
|
||||
return value
|
||||
|
@ -219,6 +219,7 @@ class PlaceBaseModel:
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[16]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
@ -269,4 +269,5 @@ class RepositoryModel(FlatBaseModel):
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[8]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
@ -161,4 +161,5 @@ class SourceModel(FlatBaseModel):
|
||||
Return the sorted list of tags.
|
||||
"""
|
||||
tag_list = list(map(self.get_tag_name, data[11]))
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||
|
@ -83,7 +83,7 @@ def gramps_upgrade_19(self):
|
||||
"""
|
||||
default_handle = self.metadata.get(b'default')
|
||||
with BSDDBTxn(self.env, self.metadata) as txn:
|
||||
if default_handle is not None:
|
||||
if isinstance(default_handle, bytes):
|
||||
default_handle = default_handle.decode('utf-8')
|
||||
txn.put(b'default', default_handle)
|
||||
txn.put(b'version', 19)
|
||||
@ -201,6 +201,7 @@ def gramps_upgrade_17(self):
|
||||
n -= 1
|
||||
while n > level:
|
||||
if loc[n]:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
title = ', '.join([item for item in loc[n:] if item])
|
||||
parent_handle = add_place(self, loc[n], n, parent_handle, title)
|
||||
locations[tuple([''] * n + loc[n:])] = parent_handle
|
||||
|
@ -1655,7 +1655,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
|
||||
bsddb
|
||||
"""
|
||||
self.surname_list = sorted(
|
||||
[s.decode('utf-8') for s in self.surnames.keys()],
|
||||
[s.decode('utf-8') for s in set(self.surnames.keys())],
|
||||
key=glocale.sort_key)
|
||||
|
||||
def add_to_surname_list(self, person, batch_transaction):
|
||||
@ -2290,7 +2290,6 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
|
||||
else:
|
||||
bsddb_version = _("Unknown")
|
||||
return {
|
||||
_("DB-API version"): "n/a",
|
||||
_("Number of people"): self.get_number_of_people(),
|
||||
_("Number of families"): self.get_number_of_families(),
|
||||
_("Number of sources"): self.get_number_of_sources(),
|
||||
@ -2301,8 +2300,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
|
||||
_("Number of repositories"): self.get_number_of_repositories(),
|
||||
_("Number of notes"): self.get_number_of_notes(),
|
||||
_("Number of tags"): self.get_number_of_tags(),
|
||||
_("Data version"): schema_version,
|
||||
_("Database db version"): bsddb_version,
|
||||
_("Schema version"): schema_version,
|
||||
_("Database version"): bsddb_version,
|
||||
}
|
||||
|
||||
def _mkname(path, name):
|
||||
|
@ -25,11 +25,8 @@
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import sys
|
||||
import pickle
|
||||
from operator import itemgetter
|
||||
import logging
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
@ -82,46 +79,12 @@ class DBAPI(DbGeneric):
|
||||
version_file.write(str(self.VERSION[0]))
|
||||
|
||||
versionpath = os.path.join(directory, str(DBBACKEND))
|
||||
_LOG.debug("Write database backend file to 'dbapi'")
|
||||
_LOG.debug("Write database backend file")
|
||||
with open(versionpath, "w") as version_file:
|
||||
version_file.write("dbapi")
|
||||
# Write settings.py and settings.ini:
|
||||
settings_py = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"settings.py")
|
||||
settings_ini = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"settings.ini")
|
||||
LOG.debug("Copy settings.py from: " + settings_py)
|
||||
LOG.debug("Copy settings.ini from: " + settings_py)
|
||||
shutil.copy2(settings_py, directory)
|
||||
shutil.copy2(settings_ini, directory)
|
||||
version_file.write(self.__class__.__name__.lower())
|
||||
|
||||
def _initialize(self, directory):
|
||||
# Run code from directory
|
||||
from gramps.gen.utils.configmanager import ConfigManager
|
||||
config_file = os.path.join(directory, 'settings.ini')
|
||||
config_mgr = ConfigManager(config_file)
|
||||
config_mgr.register('database.dbtype', 'sqlite')
|
||||
config_mgr.register('database.dbname', 'gramps')
|
||||
config_mgr.register('database.host', 'localhost')
|
||||
config_mgr.register('database.user', 'user')
|
||||
config_mgr.register('database.password', 'password')
|
||||
config_mgr.register('database.port', 'port')
|
||||
config_mgr.load() # load from settings.ini
|
||||
settings = {
|
||||
"__file__":
|
||||
os.path.join(directory, "settings.py"),
|
||||
"config": config_mgr
|
||||
}
|
||||
settings_file = os.path.join(directory, "settings.py")
|
||||
with open(settings_file) as fp:
|
||||
code = compile(fp.read(), settings_file, 'exec')
|
||||
exec(code, globals(), settings)
|
||||
self.dbapi = settings["dbapi"]
|
||||
|
||||
# We use the existence of the person table as a proxy for the database
|
||||
# being new
|
||||
if not self.dbapi.table_exists("person"):
|
||||
self._create_schema()
|
||||
raise NotImplementedError
|
||||
|
||||
def _create_schema(self):
|
||||
"""
|
||||
@ -261,6 +224,7 @@ class DBAPI(DbGeneric):
|
||||
Lowlevel interface to the backend transaction.
|
||||
Executes a db BEGIN;
|
||||
"""
|
||||
if self.transaction == None:
|
||||
_LOG.debug(" DBAPI %s transaction begin", hex(id(self)))
|
||||
self.dbapi.begin()
|
||||
|
||||
@ -269,6 +233,7 @@ class DBAPI(DbGeneric):
|
||||
Lowlevel interface to the backend transaction.
|
||||
Executes a db END;
|
||||
"""
|
||||
if self.transaction == None:
|
||||
_LOG.debug(" DBAPI %s transaction commit", hex(id(self)))
|
||||
self.dbapi.commit()
|
||||
|
||||
@ -277,6 +242,7 @@ class DBAPI(DbGeneric):
|
||||
Lowlevel interface to the backend transaction.
|
||||
Executes a db ROLLBACK;
|
||||
"""
|
||||
if self.transaction == None:
|
||||
self.dbapi.rollback()
|
||||
|
||||
def transaction_begin(self, transaction):
|
||||
@ -995,16 +961,3 @@ class DBAPI(DbGeneric):
|
||||
in the appropriate type.
|
||||
"""
|
||||
return [v if not isinstance(v, bool) else int(v) for v in values]
|
||||
|
||||
def get_summary(self):
|
||||
"""
|
||||
Returns dictionary of summary item.
|
||||
Should include, if possible:
|
||||
|
||||
_("Number of people")
|
||||
_("Version")
|
||||
_("Schema version")
|
||||
"""
|
||||
summary = super().get_summary()
|
||||
summary.update(self.dbapi.__class__.get_summary())
|
||||
return summary
|
||||
|
@ -1,58 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2016 Douglas S. Blank <doug.blank@gmail.com>
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
from gramps.plugins.db.dbapi.dbapi import DBAPI
|
||||
from gramps.plugins.db.dbapi.sqlite import Sqlite
|
||||
from gramps.gen.db import DBBACKEND
|
||||
from gramps.gen.db.generic import DbGeneric, LOG
|
||||
import os
|
||||
import glob
|
||||
|
||||
class InMemoryDB(DBAPI):
|
||||
"""
|
||||
A DB-API 2.0 In-memory SQL database.
|
||||
"""
|
||||
def _initialize(self, directory):
|
||||
"""
|
||||
Create an in-memory sqlite database.
|
||||
"""
|
||||
self.dbapi = Sqlite(":memory:")
|
||||
self._create_schema()
|
||||
|
||||
def write_version(self, directory):
|
||||
"""Write files for a newly created DB."""
|
||||
versionpath = os.path.join(directory, DBBACKEND)
|
||||
LOG.debug("Write database backend file to 'inmemorydb'")
|
||||
with open(versionpath, "w") as version_file:
|
||||
version_file.write("inmemorydb")
|
||||
|
||||
def load(self, directory, callback=None, mode=None,
|
||||
force_schema_upgrade=False,
|
||||
force_bsddb_upgrade=False,
|
||||
force_bsddb_downgrade=False,
|
||||
force_python_upgrade=False,
|
||||
update=True):
|
||||
DbGeneric.load(self, directory,
|
||||
callback,
|
||||
mode,
|
||||
force_schema_upgrade,
|
||||
force_bsddb_upgrade,
|
||||
force_bsddb_downgrade,
|
||||
force_python_upgrade)
|
@ -21,16 +21,23 @@ from gramps.gen.plug._pluginreg import register, STABLE, DATABASE
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
try:
|
||||
import psycopg2
|
||||
available = True
|
||||
except (ImportError, ValueError):
|
||||
available = False
|
||||
|
||||
if available:
|
||||
register(DATABASE,
|
||||
id = 'inmemorydb',
|
||||
name = _("In-Memory"),
|
||||
name_accell = _("In-_Memory Database"),
|
||||
description = _("In-Memory Database"),
|
||||
id='postgresql',
|
||||
name=_('PostgreSQL'),
|
||||
name_accell=_('_PostgreSQL Database'),
|
||||
description=_('PostgreSQL Database'),
|
||||
version='1.0.0',
|
||||
gramps_target_version = "5.1",
|
||||
gramps_target_version='5.1',
|
||||
status=STABLE,
|
||||
fname = 'inmemorydb.py',
|
||||
databaseclass = 'InMemoryDB',
|
||||
fname='postgresql.py',
|
||||
databaseclass='PostgreSQL',
|
||||
authors=['Doug Blank'],
|
||||
authors_email=["doug.blank@gmail.com"],
|
||||
authors_email=['doug.blank@gmail.com']
|
||||
)
|
@ -2,7 +2,7 @@
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2015-2016 Douglas S. Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2016 Nick Hall
|
||||
# Copyright (C) 2016-2017 Nick Hall
|
||||
#
|
||||
# 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
|
||||
@ -19,12 +19,17 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Backend for PostgreSQL database.
|
||||
"""
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import psycopg2
|
||||
import os
|
||||
import re
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -32,27 +37,57 @@ import re
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.plugins.db.dbapi.dbapi import DBAPI
|
||||
from gramps.gen.utils.configmanager import ConfigManager
|
||||
from gramps.gen.db.dbconst import ARRAYSIZE
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
psycopg2.paramstyle = 'format'
|
||||
|
||||
class Postgresql:
|
||||
@classmethod
|
||||
def get_summary(cls):
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# PostgreSQL class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class PostgreSQL(DBAPI):
|
||||
|
||||
def get_summary(self):
|
||||
"""
|
||||
Return a diction of information about this database
|
||||
backend.
|
||||
"""
|
||||
summary = {
|
||||
"DB-API version": "2.0",
|
||||
"Database SQL type": cls.__name__,
|
||||
"Database SQL module": "psycopg2",
|
||||
"Database SQL module version": psycopg2.__version__,
|
||||
"Database SQL module location": psycopg2.__file__,
|
||||
}
|
||||
summary = super().get_summary()
|
||||
summary.update({
|
||||
_("Database version"): psycopg2.__version__,
|
||||
_("Database module location"): psycopg2.__file__,
|
||||
})
|
||||
return summary
|
||||
|
||||
def _initialize(self, directory):
|
||||
config_file = os.path.join(directory, 'settings.ini')
|
||||
config_mgr = ConfigManager(config_file)
|
||||
config_mgr.register('database.dbname', 'gramps')
|
||||
config_mgr.register('database.host', 'localhost')
|
||||
config_mgr.register('database.user', 'user')
|
||||
config_mgr.register('database.password', 'password')
|
||||
config_mgr.register('database.port', 'port')
|
||||
config_mgr.load()
|
||||
|
||||
dbkwargs = {}
|
||||
for key in config_mgr.get_section_settings('database'):
|
||||
dbkwargs[key] = config_mgr.get('database.' + key)
|
||||
|
||||
self.dbapi = Connection(**dbkwargs)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Connection class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Connection:
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.__connection = psycopg2.connect(*args, **kwargs)
|
||||
self.__connection.autocommit = True
|
||||
@ -140,6 +175,11 @@ class Postgresql:
|
||||
return Cursor(self.__connection)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Cursor class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Cursor:
|
||||
def __init__(self, connection):
|
||||
self.__connection = connection
|
||||
|
@ -1,11 +0,0 @@
|
||||
;; Gramps key file
|
||||
;; Automatically created at 2016/07/12 13:06:48
|
||||
|
||||
[database]
|
||||
;;dbname='gramps'
|
||||
;;dbtype='sqlite'
|
||||
;;host='localhost'
|
||||
;;password='password'
|
||||
;;port='port'
|
||||
;;user='user'
|
||||
|
@ -1,52 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2015-2016 Douglas S. Blank <doug.blank@gmail.com>
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
import os
|
||||
|
||||
## This file is copied from gramps/plugins/db/dbapi/settings.py
|
||||
## into each grampsdb/*/ directory. You can edit each copy
|
||||
## to connect to different databases, or with different
|
||||
## parameters.
|
||||
|
||||
## The database options are saved in settings.ini.
|
||||
# NOTE: config is predefined
|
||||
|
||||
# NOTE: you can override this in settings.ini or here:
|
||||
#from gramps.gen.config import config
|
||||
dbtype = config.get('database.dbtype')
|
||||
|
||||
if dbtype == "sqlite":
|
||||
from gramps.plugins.db.dbapi.sqlite import Sqlite
|
||||
path_to_db = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'sqlite.db')
|
||||
dbapi = Sqlite(path_to_db)
|
||||
else:
|
||||
# NOTE: you can override these settings here or in settings.ini:
|
||||
dbkwargs = {}
|
||||
for key in config.get_section_settings('database'):
|
||||
# Use all parameters except dbtype as keyword arguments
|
||||
if key == 'dbtype':
|
||||
continue
|
||||
dbkwargs[key] = config.get('database.' + key)
|
||||
if dbtype == "postgresql":
|
||||
from gramps.plugins.db.dbapi.postgresql import Postgresql
|
||||
dbapi = Postgresql(**dbkwargs)
|
||||
else:
|
||||
raise AttributeError(("invalid DB-API dbtype: '%s'. " +
|
||||
"Should be 'sqlite' or 'postgresql'") % dbtype)
|
@ -22,15 +22,15 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
register(DATABASE,
|
||||
id = 'dbapi',
|
||||
name = _("DB-API"),
|
||||
name_accell = _("DB-_API Database"),
|
||||
description = _("DB-API Database"),
|
||||
version = '1.0.32',
|
||||
gramps_target_version = "5.1",
|
||||
id='sqlite',
|
||||
name=_('SQLite'),
|
||||
name_accell=_('_SQLite Database'),
|
||||
description=_('SQLite Database'),
|
||||
version='1.0.0',
|
||||
gramps_target_version='5.1',
|
||||
status=STABLE,
|
||||
fname = 'dbapi.py',
|
||||
databaseclass = 'DBAPI',
|
||||
fname='sqlite.py',
|
||||
databaseclass='SQLite',
|
||||
authors=['Doug Blank'],
|
||||
authors_email=["doug.blank@gmail.com"],
|
||||
authors_email=['doug.blank@gmail.com']
|
||||
)
|
@ -2,7 +2,7 @@
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2015-2016 Douglas S. Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2016 Nick Hall
|
||||
# Copyright (C) 2016-2017 Nick Hall
|
||||
#
|
||||
# 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
|
||||
@ -20,7 +20,7 @@
|
||||
#
|
||||
|
||||
"""
|
||||
Backend for sqlite database.
|
||||
Backend for SQLite database.
|
||||
"""
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -29,43 +29,59 @@ Backend for sqlite database.
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import sqlite3
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.plugins.db.dbapi.dbapi import DBAPI
|
||||
from gramps.gen.db.dbconst import ARRAYSIZE
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
sqlite3.paramstyle = 'qmark'
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Sqlite class
|
||||
# SQLite class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Sqlite:
|
||||
class SQLite(DBAPI):
|
||||
|
||||
def get_summary(self):
|
||||
"""
|
||||
Return a dictionary of information about this database backend.
|
||||
"""
|
||||
summary = super().get_summary()
|
||||
summary.update({
|
||||
_("Database version"): sqlite3.sqlite_version,
|
||||
_("Database module version"): sqlite3.version,
|
||||
_("Database module location"): sqlite3.__file__,
|
||||
})
|
||||
return summary
|
||||
|
||||
def _initialize(self, directory):
|
||||
if directory == ':memory:':
|
||||
path_to_db = ':memory:'
|
||||
else:
|
||||
path_to_db = os.path.join(directory, 'sqlite.db')
|
||||
self.dbapi = Connection(path_to_db)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Connection class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Connection:
|
||||
"""
|
||||
The Sqlite class is an interface between the DBAPI class which is the Gramps
|
||||
backend for the DBAPI interface and the sqlite3 python module.
|
||||
"""
|
||||
@classmethod
|
||||
def get_summary(cls):
|
||||
"""
|
||||
Return a dictionary of information about this database backend.
|
||||
"""
|
||||
summary = {
|
||||
"DB-API version": "2.0",
|
||||
"Database SQL type": cls.__name__,
|
||||
"Database SQL module": "sqlite3",
|
||||
"Database SQL Python module version": sqlite3.version,
|
||||
"Database SQL module version": sqlite3.sqlite_version,
|
||||
"Database SQL module location": sqlite3.__file__,
|
||||
}
|
||||
return summary
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
|
@ -47,8 +47,8 @@ class DbRandomTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.db = make_database("inmemorydb")
|
||||
cls.db.load(None)
|
||||
cls.db = make_database("sqlite")
|
||||
cls.db.load(":memory:")
|
||||
|
||||
def setUp(self):
|
||||
self.handles = {'Person': [], 'Family': [], 'Event': [], 'Place': [],
|
||||
@ -709,8 +709,8 @@ class DbEmptyTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.db = make_database("inmemorydb")
|
||||
cls.db.load(None)
|
||||
cls.db = make_database("sqlite")
|
||||
cls.db.load(":memory:")
|
||||
|
||||
################################################################
|
||||
#
|
||||
@ -806,8 +806,8 @@ class DbPersonTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.db = make_database("inmemorydb")
|
||||
cls.db.load(None)
|
||||
cls.db = make_database("sqlite")
|
||||
cls.db.load(":memory:")
|
||||
|
||||
def __add_person(self, gender, first_name, surname, trans):
|
||||
person = Person()
|
||||
|
@ -281,6 +281,7 @@ def write_index(index, doc):
|
||||
doc.start_cell('IDX-Cell')
|
||||
doc.start_paragraph('IDX-Entry')
|
||||
pages = [str(page_nr) for page_nr in index[key]]
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
doc.write_text(', '.join(pages))
|
||||
doc.end_paragraph()
|
||||
doc.end_cell()
|
||||
|
@ -359,7 +359,7 @@ class FanChart(Report):
|
||||
else:
|
||||
name = p_pn.get_first_name() + p_pn.get_surname()
|
||||
if (name != "") and (val != ""):
|
||||
string = name + ", " + val
|
||||
string = name + self._(", ") + val # Arabic OK
|
||||
else:
|
||||
string = name + val
|
||||
return [string]
|
||||
@ -375,7 +375,7 @@ class FanChart(Report):
|
||||
return [name, val]
|
||||
else:
|
||||
if (name != "") and (val != ""):
|
||||
string = name + ", " + val
|
||||
string = name + self._(", ") + val # Arabic OK
|
||||
else:
|
||||
string = name + val
|
||||
return [string]
|
||||
|
@ -44,12 +44,10 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.lib import (AttributeType, ChildRefType, Citation, Date,
|
||||
EventRoleType, EventType, LdsOrd, NameType,
|
||||
PlaceType, NoteType, Person, UrlType,
|
||||
SrcAttributeType)
|
||||
PlaceType, NoteType, Person, UrlType)
|
||||
from gramps.version import VERSION
|
||||
import gramps.plugins.lib.libgedcom as libgedcom
|
||||
from gramps.gen.errors import DatabaseError
|
||||
from gramps.gui.plug.export import WriterOptionBox
|
||||
from gramps.gen.updatecallback import UpdateCallback
|
||||
from gramps.gen.utils.file import media_path_full
|
||||
from gramps.gen.utils.place import conv_lat_lon
|
||||
@ -91,14 +89,12 @@ LDS_STATUS = {
|
||||
}
|
||||
|
||||
LANGUAGES = {
|
||||
'cs' : 'Czech', 'da' : 'Danish', 'nl' : 'Dutch',
|
||||
'en' : 'English', 'eo' : 'Esperanto', 'fi' : 'Finnish',
|
||||
'fr' : 'French', 'de' : 'German', 'hu' : 'Hungarian',
|
||||
'it' : 'Italian', 'lt' : 'Latvian', 'lv' : 'Lithuanian',
|
||||
'no' : 'Norwegian', 'po' : 'Polish', 'pt' : 'Portuguese',
|
||||
'ro' : 'Romanian', 'sk' : 'Slovak', 'es' : 'Spanish',
|
||||
'sv' : 'Swedish', 'ru' : 'Russian',
|
||||
}
|
||||
'cs' : 'Czech', 'da' : 'Danish', 'nl' : 'Dutch', 'en' : 'English',
|
||||
'eo' : 'Esperanto', 'fi' : 'Finnish', 'fr' : 'French', 'de' : 'German',
|
||||
'hu' : 'Hungarian', 'it' : 'Italian', 'lt' : 'Latvian',
|
||||
'lv' : 'Lithuanian', 'no' : 'Norwegian', 'po' : 'Polish',
|
||||
'pt' : 'Portuguese', 'ro' : 'Romanian', 'sk' : 'Slovak',
|
||||
'es' : 'Spanish', 'sv' : 'Swedish', 'ru' : 'Russian', }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -130,6 +126,8 @@ PEDIGREE_TYPES = {
|
||||
}
|
||||
|
||||
NOTES_PER_PERSON = 104 # fudge factor to make progress meter a bit smoother
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# sort_handles_by_id
|
||||
@ -152,6 +150,7 @@ def sort_handles_by_id(handle_list, handle_to_object):
|
||||
sorted_list.sort()
|
||||
return sorted_list
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# breakup
|
||||
@ -170,8 +169,8 @@ def breakup(txt, limit):
|
||||
# look for non-space pair to break between
|
||||
# do not break within a UTF-8 byte sequence, i. e. first char >127
|
||||
idx = limit
|
||||
while (idx>0 and (txt[idx-1].isspace() or txt[idx].isspace()
|
||||
or ord(txt[idx-1]) > 127)):
|
||||
while (idx > 0 and (txt[idx - 1].isspace() or txt[idx].isspace() or
|
||||
ord(txt[idx - 1]) > 127)):
|
||||
idx -= 1
|
||||
if idx == 0:
|
||||
#no words to break on, just break at limit anyway
|
||||
@ -191,6 +190,7 @@ def breakup(txt, limit):
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def event_has_subordinate_data(event, event_ref):
|
||||
""" determine if event is empty or not """
|
||||
if event and event_ref:
|
||||
return (event.get_description().strip() or
|
||||
not event.get_date_object().is_empty() or
|
||||
@ -273,11 +273,14 @@ class GedcomWriter(UpdateCallback):
|
||||
into multiple lines using CONC.
|
||||
|
||||
"""
|
||||
assert(token)
|
||||
assert token
|
||||
if textlines:
|
||||
# break the line into multiple lines if a newline is found
|
||||
textlines = textlines.replace('\n\r', '\n')
|
||||
textlines = textlines.replace('\r', '\n')
|
||||
# Need to double '@' See Gedcom 5.5 spec 'any_char'
|
||||
if not textlines.startswith('@'): # avoid xrefs
|
||||
textlines = textlines.replace('@', '@@')
|
||||
textlist = textlines.split('\n')
|
||||
token_level = level
|
||||
for text in textlist:
|
||||
@ -288,7 +291,8 @@ class GedcomWriter(UpdateCallback):
|
||||
txt = prefix.join(breakup(text, limit))
|
||||
else:
|
||||
txt = text
|
||||
self.gedcom_file.write("%d %s %s\n" % (token_level, token, txt))
|
||||
self.gedcom_file.write("%d %s %s\n" %
|
||||
(token_level, token, txt))
|
||||
token_level = level + 1
|
||||
token = "CONT"
|
||||
else:
|
||||
@ -551,7 +555,8 @@ class GedcomWriter(UpdateCallback):
|
||||
adop_written = False
|
||||
for event_ref in person.get_event_ref_list():
|
||||
event = self.dbase.get_event_from_handle(event_ref.ref)
|
||||
if not event: continue
|
||||
if not event:
|
||||
continue
|
||||
self._process_person_event(person, event, event_ref)
|
||||
if not adop_written:
|
||||
self._adoption_records(person, adop_written)
|
||||
@ -854,7 +859,8 @@ class GedcomWriter(UpdateCallback):
|
||||
for cref in child_ref_list]
|
||||
|
||||
for gid in child_list:
|
||||
if gid is None: continue
|
||||
if gid is None:
|
||||
continue
|
||||
self._writeln(1, 'CHIL', '@%s@' % gid)
|
||||
|
||||
def _family_reference(self, token, person_handle):
|
||||
@ -880,7 +886,8 @@ class GedcomWriter(UpdateCallback):
|
||||
"""
|
||||
for event_ref in family.get_event_ref_list():
|
||||
event = self.dbase.get_event_from_handle(event_ref.ref)
|
||||
if event is None: continue
|
||||
if event is None:
|
||||
continue
|
||||
self._process_family_event(event, event_ref)
|
||||
self._dump_event_stats(event, event_ref)
|
||||
|
||||
@ -973,7 +980,8 @@ class GedcomWriter(UpdateCallback):
|
||||
for (source_id, handle) in sorted_list:
|
||||
self.update()
|
||||
source = self.dbase.get_source_from_handle(handle)
|
||||
if source is None: continue
|
||||
if source is None:
|
||||
continue
|
||||
self._writeln(0, '@%s@' % source_id, 'SOUR')
|
||||
if source.get_title():
|
||||
self._writeln(1, 'TITL', source.get_title())
|
||||
@ -1011,7 +1019,8 @@ class GedcomWriter(UpdateCallback):
|
||||
self.update()
|
||||
note_cnt += 1
|
||||
note = self.dbase.get_note_from_handle(note_handle)
|
||||
if note is None: continue
|
||||
if note is None:
|
||||
continue
|
||||
self._note_record(note)
|
||||
|
||||
def _note_record(self, note):
|
||||
@ -1025,7 +1034,8 @@ class GedcomWriter(UpdateCallback):
|
||||
+1 <<CHANGE_DATE>> {0:1}
|
||||
"""
|
||||
if note:
|
||||
self._writeln(0, '@%s@' % note.get_gramps_id(), 'NOTE ' + note.get())
|
||||
self._writeln(0, '@%s@' % note.get_gramps_id(),
|
||||
'NOTE ' + note.get())
|
||||
|
||||
def _repos(self):
|
||||
"""
|
||||
@ -1050,7 +1060,8 @@ class GedcomWriter(UpdateCallback):
|
||||
for (repo_id, handle) in sorted_list:
|
||||
self.update()
|
||||
repo = self.dbase.get_repository_from_handle(handle)
|
||||
if repo is None: continue
|
||||
if repo is None:
|
||||
continue
|
||||
self._writeln(0, '@%s@' % repo_id, 'REPO')
|
||||
if repo.get_name():
|
||||
self._writeln(1, 'NAME', repo.get_name())
|
||||
@ -1138,8 +1149,7 @@ class GedcomWriter(UpdateCallback):
|
||||
if int(attr.get_type()) == AttributeType.TIME]
|
||||
# Not legal, but inserted by PhpGedView
|
||||
if len(times) > 0:
|
||||
time = times[0]
|
||||
self._writeln(3, 'TIME', time)
|
||||
self._writeln(3, 'TIME', times[0])
|
||||
|
||||
place = None
|
||||
|
||||
@ -1220,11 +1230,13 @@ class GedcomWriter(UpdateCallback):
|
||||
family_handle = lds_ord.get_family_handle()
|
||||
family = self.dbase.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
self._writeln(index+1, 'FAMC', '@%s@' % family.get_gramps_id())
|
||||
self._writeln(index + 1, 'FAMC', '@%s@' %
|
||||
family.get_gramps_id())
|
||||
if lds_ord.get_temple():
|
||||
self._writeln(index + 1, 'TEMP', lds_ord.get_temple())
|
||||
if lds_ord.get_place_handle():
|
||||
place = self.dbase.get_place_from_handle(lds_ord.get_place_handle())
|
||||
place = self.dbase.get_place_from_handle(
|
||||
lds_ord.get_place_handle())
|
||||
self._place(place, lds_ord.get_date_object(), 2)
|
||||
if lds_ord.get_status() != LdsOrd.STATUS_NONE:
|
||||
self._writeln(2, 'STAT', LDS_STATUS[lds_ord.get_status()])
|
||||
@ -1358,7 +1370,6 @@ class GedcomWriter(UpdateCallback):
|
||||
self._writeln(level + 1, 'PAGE', citation.get_page()[0:248],
|
||||
limit=248)
|
||||
|
||||
|
||||
conf = min(citation.get_confidence_level(),
|
||||
Citation.CONF_VERY_HIGH)
|
||||
if conf != Citation.CONF_NORMAL and conf != -1:
|
||||
@ -1443,7 +1454,8 @@ class GedcomWriter(UpdateCallback):
|
||||
+2 LONG <PLACE_LONGITUDE> {1:1}
|
||||
+1 <<NOTE_STRUCTURE>> {0:M}
|
||||
"""
|
||||
if place is None: return
|
||||
if place is None:
|
||||
return
|
||||
place_name = _pd.display(self.dbase, place, dateobj)
|
||||
self._writeln(level, "PLAC", place_name.replace('\r', ' '), limit=120)
|
||||
longitude = place.get_longitude()
|
||||
@ -1466,7 +1478,7 @@ class GedcomWriter(UpdateCallback):
|
||||
country = location.get(PlaceType.COUNTRY)
|
||||
postal_code = place.get_code()
|
||||
|
||||
if (street or locality or city or state or postal_code or country):
|
||||
if street or locality or city or state or postal_code or country:
|
||||
self._writeln(level, "ADDR", street)
|
||||
if street:
|
||||
self._writeln(level + 1, 'ADR1', street)
|
||||
@ -1535,6 +1547,7 @@ class GedcomWriter(UpdateCallback):
|
||||
if addr.get_country():
|
||||
self._writeln(level + 1, 'CTRY', addr.get_country())
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
|
@ -228,6 +228,7 @@ class PersonDetails(Gramplet):
|
||||
if attr.get_type() == attr_key:
|
||||
values.append(attr.get_value())
|
||||
if values:
|
||||
# translators: needed for Arabic, ignore otherwise
|
||||
self.add_row(attr_key, _(', ').join(values))
|
||||
|
||||
def display_type(self, active_person, event_type):
|
||||
|
@ -367,7 +367,7 @@ class WhatNextGramplet(Gramplet):
|
||||
if missingbits:
|
||||
self.link(name, 'Person', person.get_handle())
|
||||
self.append_text(_(": %(list)s\n") % {
|
||||
'list': _(", ").join(missingbits)})
|
||||
'list': _(", ").join(missingbits)}) # Arabic OK
|
||||
self.__counter += 1
|
||||
|
||||
append_list.append(person)
|
||||
@ -389,7 +389,7 @@ class WhatNextGramplet(Gramplet):
|
||||
if missingbits:
|
||||
self.link(name, 'Person', person.get_handle())
|
||||
self.append_text(_(": %(list)s\n") % {
|
||||
'list': _(", ").join(missingbits)})
|
||||
'list': _(", ").join(missingbits)}) # Arabic OK
|
||||
self.__counter += 1
|
||||
|
||||
|
||||
@ -437,7 +437,7 @@ class WhatNextGramplet(Gramplet):
|
||||
if missingbits:
|
||||
self.link(name, 'Family', family.get_handle())
|
||||
self.append_text(_(": %(list)s\n") % {
|
||||
'list': _(", ").join(missingbits)})
|
||||
'list': _(", ").join(missingbits)}) # Arabic OK
|
||||
self.__counter += 1
|
||||
|
||||
append_list.append((family, person1, person2))
|
||||
@ -470,7 +470,7 @@ class WhatNextGramplet(Gramplet):
|
||||
if missingbits:
|
||||
self.link(name, 'Family', family.get_handle())
|
||||
self.append_text(_(": %(list)s\n") % {
|
||||
'list': _(", ").join(missingbits)})
|
||||
'list': _(", ").join(missingbits)}) # Arabic OK
|
||||
self.__counter += 1
|
||||
|
||||
|
||||
@ -492,7 +492,7 @@ class WhatNextGramplet(Gramplet):
|
||||
# translators: needed for French, ignore otherwise
|
||||
return [_("%(str1)s: %(str2)s"
|
||||
) % {'str1' : event.get_type(),
|
||||
'str2' : _(", ").join(missingbits)}]
|
||||
'str2' : _(", ").join(missingbits)}] # Arabic OK
|
||||
else:
|
||||
return []
|
||||
|
||||
|
@ -657,7 +657,7 @@ class RelGraphReport(Report):
|
||||
label += '%s' % desc
|
||||
if place:
|
||||
if date or desc:
|
||||
label += ', '
|
||||
label += self._(', ') # Arabic OK
|
||||
label += '%s' % place
|
||||
label += ')'
|
||||
|
||||
|
@ -676,6 +676,7 @@ class GeneWebParser:
|
||||
except IndexError: # not all parts are written all the time
|
||||
pass
|
||||
if tnth: # Append title numer to title
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
ttitle += ", " + tnth
|
||||
title = self.create_event(
|
||||
EventType.NOB_TITLE, ttitle, tstart, tplace)
|
||||
|
@ -2515,8 +2515,10 @@ class GrampsParser(UpdateCallback):
|
||||
date_value = self.name.get_date_object()
|
||||
elif self.event:
|
||||
date_value = self.event.get_date_object()
|
||||
else:
|
||||
elif self.placeref:
|
||||
date_value = self.placeref.get_date_object()
|
||||
else:
|
||||
date_value = self.place_name.get_date_object()
|
||||
|
||||
date_value.set_as_text(attrs['val'])
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -88,6 +88,7 @@ class PlaceImport:
|
||||
n -= 1
|
||||
while n > type_num:
|
||||
if loc[n]:
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
title = ', '.join([item for item in loc[n:] if item])
|
||||
parent = self.__add_place(loc[n], n, parent, title, trans)
|
||||
self.loc2handle[tuple([''] * n + loc[n:])] = parent
|
||||
|
@ -1063,6 +1063,7 @@ class GeoGraphyView(OsmGps, NavigationView):
|
||||
if gids == "":
|
||||
gids = plce.gramps_id
|
||||
else:
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
gids = gids + ", " + plce.gramps_id
|
||||
if nb_places > 1:
|
||||
from gramps.gui.dialog import WarningDialog
|
||||
|
@ -77,10 +77,13 @@ def _build_title(db, place):
|
||||
if descr:
|
||||
title_descr += descr.strip()
|
||||
if parish:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
title_descr += ', ' + parish.strip() + _(" parish")
|
||||
if city:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
title_descr += ', ' + city.strip()
|
||||
if state:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
title_descr += ', ' + state.strip() + _(" state")
|
||||
return _strip_leading_comma(title_descr)
|
||||
|
||||
@ -91,6 +94,7 @@ def _build_city(db, place):
|
||||
# Build a title description string that will work for Eniro
|
||||
city_descr = _build_area(db, place)
|
||||
if county:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
city_descr += ', ' + county
|
||||
return _strip_leading_comma(city_descr)
|
||||
|
||||
@ -104,6 +108,7 @@ def _build_area(db, place):
|
||||
if street:
|
||||
area_descr += street.strip()
|
||||
if city:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
area_descr += ', ' + city
|
||||
return _strip_leading_comma(area_descr)
|
||||
|
||||
|
@ -315,6 +315,7 @@ class AllRelReport:
|
||||
if isinstance(fam, list):
|
||||
famstr = str(fam[0]+1)
|
||||
for val in fam[1:] :
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
famstr += ', ' + str(val+1)
|
||||
else:
|
||||
famstr = str(fam+1)
|
||||
|
@ -225,6 +225,7 @@ def add_rem(remark, text):
|
||||
""" Allow for extension of remark, return new remark string
|
||||
"""
|
||||
if remark:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return remark + ', ' + text
|
||||
else:
|
||||
return text
|
||||
|
@ -361,7 +361,7 @@ class DbTestClassBase(object):
|
||||
msg="Callback Manager disconnect cb check")
|
||||
|
||||
|
||||
params = [('BsdDb', 'bsddb'), ('DbApi', 'dbapi')]
|
||||
params = [('BsdDb', 'bsddb'), ('SQLite', 'sqlite')]
|
||||
|
||||
for name, param in params:
|
||||
cls_name = "TestMyTestClass_%s" % (name, )
|
||||
|
@ -266,7 +266,7 @@ class Printinfo:
|
||||
tmp = self.__date_place(
|
||||
get_death_or_fallback(self.database, person))
|
||||
if string and tmp:
|
||||
string += ", "
|
||||
string += self._(", ") # Arabic OK
|
||||
string += tmp
|
||||
|
||||
if string:
|
||||
@ -276,13 +276,13 @@ class Printinfo:
|
||||
tmp = self.__date_place(
|
||||
get_marriage_or_fallback(self.database, family))
|
||||
if tmp:
|
||||
string += ", " + tmp
|
||||
string += self._(", ") + tmp # Arabic OK
|
||||
|
||||
if family and self.showdivorce:
|
||||
tmp = self.__date_place(
|
||||
get_divorce_or_fallback(self.database, family))
|
||||
if tmp:
|
||||
string += ", " + tmp
|
||||
string += self._(", ") + tmp # Arabic OK
|
||||
|
||||
if family and self.want_ids:
|
||||
string += ' (%s)' % family.get_gramps_id()
|
||||
|
@ -484,6 +484,7 @@ class NotRelated(tool.ActivePersonTool, ManagedWindow) :
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
tags.append(tag.get_name())
|
||||
tags.sort(key=glocale.sort_key)
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ', '.join(tags)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
|
@ -232,6 +232,7 @@ class RelCalc(tool.Tool, ManagedWindow):
|
||||
for person_handle in common:
|
||||
person = self.db.get_person_from_handle(person_handle)
|
||||
if index:
|
||||
# TODO for Arabic, should the next comma be translated?
|
||||
commontext += ", "
|
||||
commontext += name_displayer.display(person)
|
||||
index += 1
|
||||
|
120
gramps/plugins/webreport/addressbook.py
Normal file
120
gramps/plugins/webreport/addressbook.py
Normal file
@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
AddressBookPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.plug.report import Bibliography
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import FULLCLEAR
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
class AddressBookPage(BasePage):
|
||||
"""
|
||||
Create one page for one Address
|
||||
"""
|
||||
def __init__(self, report, title, person_handle, has_add, has_res, has_url):
|
||||
"""
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: person_handle -- the url, address and residence to use
|
||||
for the report
|
||||
@param: has_add -- the address to use for the report
|
||||
@param: has_res -- the residence to use for the report
|
||||
@param: has_url -- the url to use for the report
|
||||
"""
|
||||
person = report.database.get_person_from_handle(person_handle)
|
||||
BasePage.__init__(self, report, title, person.gramps_id)
|
||||
self.bibli = Bibliography()
|
||||
|
||||
self.uplink = True
|
||||
|
||||
# set the file name and open file
|
||||
output_file, sio = self.report.create_file(person_handle, "addr")
|
||||
addressbookpage, head, body = self.write_header(_("Address Book"))
|
||||
|
||||
# begin address book page division and section title
|
||||
with Html("div", class_="content",
|
||||
id="AddressBookDetail") as addressbookdetail:
|
||||
body += addressbookdetail
|
||||
|
||||
link = self.new_person_link(person_handle, uplink=True,
|
||||
person=person)
|
||||
addressbookdetail += Html("h3", link)
|
||||
|
||||
# individual has an address
|
||||
if has_add:
|
||||
addressbookdetail += self.display_addr_list(has_add, None)
|
||||
|
||||
# individual has a residence
|
||||
if has_res:
|
||||
addressbookdetail.extend(
|
||||
self.dump_residence(res)
|
||||
for res in has_res
|
||||
)
|
||||
|
||||
# individual has a url
|
||||
if has_url:
|
||||
addressbookdetail += self.display_url_list(has_url)
|
||||
|
||||
# add fullclear for proper styling
|
||||
# and footer section to page
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(addressbookpage, output_file, sio, 0)
|
170
gramps/plugins/webreport/addressbooklist.py
Normal file
170
gramps/plugins/webreport/addressbooklist.py
Normal file
@ -0,0 +1,170 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
AddressBookListPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import FULLCLEAR
|
||||
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
_ = glocale.translation.sgettext
|
||||
getcontext().prec = 8
|
||||
|
||||
class AddressBookListPage(BasePage):
|
||||
"""
|
||||
Create the index for addresses.
|
||||
"""
|
||||
def __init__(self, report, title, has_url_addr_res):
|
||||
"""
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: has_url_addr_res -- The url, address and residence to use
|
||||
for the report
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
# Name the file, and create it
|
||||
output_file, sio = self.report.create_file("addressbook")
|
||||
|
||||
# Add xml, doctype, meta and stylesheets
|
||||
addressbooklistpage, head, body = self.write_header(_("Address Book"))
|
||||
|
||||
# begin AddressBookList division
|
||||
with Html("div", class_="content",
|
||||
id="AddressBookList") as addressbooklist:
|
||||
body += addressbooklist
|
||||
|
||||
# Address Book Page message
|
||||
msg = _("This page contains an index of all the individuals in "
|
||||
"the database, sorted by their surname, with one of the "
|
||||
"following: Address, Residence, or Web Links. "
|
||||
"Selecting the person’s name will take you "
|
||||
"to their individual Address Book page.")
|
||||
addressbooklist += Html("p", msg, id="description")
|
||||
|
||||
# begin Address Book table
|
||||
with Html("table",
|
||||
class_="infolist primobjlist addressbook") as table:
|
||||
addressbooklist += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow.extend(
|
||||
Html("th", label, class_=colclass, inline=True)
|
||||
for (label, colclass) in [
|
||||
[" ", "ColumnRowLabel"],
|
||||
[_("Full Name"), "ColumnName"],
|
||||
[_("Address"), "ColumnAddress"],
|
||||
[_("Residence"), "ColumnResidence"],
|
||||
[_("Web Links"), "ColumnWebLinks"]
|
||||
]
|
||||
)
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
index = 1
|
||||
for (sort_name, person_handle,
|
||||
has_add, has_res,
|
||||
has_url) in has_url_addr_res:
|
||||
|
||||
address = None
|
||||
residence = None
|
||||
weblinks = None
|
||||
|
||||
# has address but no residence event
|
||||
if has_add and not has_res:
|
||||
address = "X"
|
||||
|
||||
# has residence, but no addresses
|
||||
elif has_res and not has_add:
|
||||
residence = "X"
|
||||
|
||||
# has residence and addresses too
|
||||
elif has_add and has_res:
|
||||
address = "X"
|
||||
residence = "X"
|
||||
|
||||
# has Web Links
|
||||
if has_url:
|
||||
weblinks = "X"
|
||||
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
trow.extend(
|
||||
Html("td", data or " ", class_=colclass,
|
||||
inline=True)
|
||||
for (colclass, data) in [
|
||||
["ColumnRowLabel", index],
|
||||
["ColumnName",
|
||||
self.addressbook_link(person_handle)],
|
||||
["ColumnAddress", address],
|
||||
["ColumnResidence", residence],
|
||||
["ColumnWebLinks", weblinks]
|
||||
]
|
||||
)
|
||||
index += 1
|
||||
|
||||
# Add footer and clearline
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send the page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(addressbooklistpage, output_file, sio, 0)
|
2796
gramps/plugins/webreport/basepage.py
Normal file
2796
gramps/plugins/webreport/basepage.py
Normal file
File diff suppressed because it is too large
Load Diff
78
gramps/plugins/webreport/citation.py
Normal file
78
gramps/plugins/webreport/citation.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
CitationPages - dummy
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# Passes citations through to the Sources page
|
||||
#
|
||||
#################################################
|
||||
class CitationPages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Citation'
|
||||
database objects. It passes this information to the 'Sources' tab. It is
|
||||
told by the 'add_instances' call which 'Citation's to display.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
|
||||
def display_pages(self, title):
|
||||
pass
|
862
gramps/plugins/webreport/common.py
Normal file
862
gramps/plugins/webreport/common.py
Normal file
@ -0,0 +1,862 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
This module is used to share variables, enums and functions between all modules
|
||||
|
||||
"""
|
||||
|
||||
from unicodedata import normalize
|
||||
from collections import defaultdict
|
||||
from hashlib import md5
|
||||
import re
|
||||
import logging
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.display.name import displayer as _nd
|
||||
from gramps.gen.display.place import displayer as _pd
|
||||
from gramps.gen.utils.db import get_death_or_fallback
|
||||
from gramps.gen.lib import (EventType, Date)
|
||||
from gramps.gen.plug import BasePluginManager
|
||||
from gramps.plugins.lib.libgedcom import make_gedcom_date, DATE_QUALITY
|
||||
from gramps.gen.plug.report import utils
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
|
||||
# define clear blank line for proper styling
|
||||
FULLCLEAR = Html("div", class_="fullclear", inline=True)
|
||||
# define all possible web page filename extensions
|
||||
_WEB_EXT = ['.html', '.htm', '.shtml', '.php', '.php3', '.cgi']
|
||||
# used to select secured web site or not
|
||||
HTTP = "http://"
|
||||
HTTPS = "https://"
|
||||
|
||||
GOOGLE_MAPS = 'https://maps.googleapis.com/maps/'
|
||||
# javascript code for marker path
|
||||
MARKER_PATH = """
|
||||
var marker_png = '%s'
|
||||
"""
|
||||
|
||||
# javascript code for Google's FamilyLinks...
|
||||
FAMILYLINKS = """
|
||||
var tracelife = %s
|
||||
|
||||
function initialize() {
|
||||
var myLatLng = new google.maps.LatLng(%s, %s);
|
||||
|
||||
var mapOptions = {
|
||||
scaleControl: true,
|
||||
panControl: true,
|
||||
backgroundColor: '#000000',
|
||||
zoom: %d,
|
||||
center: myLatLng,
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP
|
||||
};
|
||||
var map = new google.maps.Map(document.getElementById("map_canvas"),
|
||||
mapOptions);
|
||||
|
||||
var flightPath = new google.maps.Polyline({
|
||||
path: tracelife,
|
||||
strokeColor: "#FF0000",
|
||||
strokeOpacity: 1.0,
|
||||
strokeWeight: 2
|
||||
});
|
||||
|
||||
flightPath.setMap(map);
|
||||
}"""
|
||||
|
||||
# javascript for Google's Drop Markers...
|
||||
DROPMASTERS = """
|
||||
var markers = [];
|
||||
var iterator = 0;
|
||||
|
||||
var tracelife = %s
|
||||
var map;
|
||||
var myLatLng = new google.maps.LatLng(%s, %s);
|
||||
|
||||
function initialize() {
|
||||
var mapOptions = {
|
||||
scaleControl: true,
|
||||
zoomControl: true,
|
||||
zoom: %d,
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
||||
center: myLatLng,
|
||||
};
|
||||
map = new google.maps.Map(document.getElementById("map_canvas"),
|
||||
mapOptions);
|
||||
};
|
||||
|
||||
function drop() {
|
||||
for (var i = 0; i < tracelife.length; i++) {
|
||||
setTimeout(function() {
|
||||
addMarker();
|
||||
}, i * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function addMarker() {
|
||||
var location = tracelife[iterator];
|
||||
var myLatLng = new google.maps.LatLng(location[1], location[2]);
|
||||
|
||||
markers.push(new google.maps.Marker({
|
||||
position: myLatLng,
|
||||
map: map,
|
||||
draggable: true,
|
||||
title: location[0],
|
||||
animation: google.maps.Animation.DROP
|
||||
}));
|
||||
iterator++;
|
||||
}"""
|
||||
|
||||
# javascript for Google's Markers...
|
||||
MARKERS = """
|
||||
var tracelife = %s
|
||||
var map;
|
||||
var myLatLng = new google.maps.LatLng(%s, %s);
|
||||
|
||||
function initialize() {
|
||||
var mapOptions = {
|
||||
scaleControl: true,
|
||||
panControl: true,
|
||||
backgroundColor: '#000000',
|
||||
zoom: %d,
|
||||
center: myLatLng,
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP
|
||||
};
|
||||
map = new google.maps.Map(document.getElementById("map_canvas"),
|
||||
mapOptions);
|
||||
addMarkers();
|
||||
}
|
||||
|
||||
function addMarkers() {
|
||||
var bounds = new google.maps.LatLngBounds();
|
||||
|
||||
for (var i = 0; i < tracelife.length; i++) {
|
||||
var location = tracelife[i];
|
||||
var myLatLng = new google.maps.LatLng(location[1], location[2]);
|
||||
|
||||
var marker = new google.maps.Marker({
|
||||
position: myLatLng,
|
||||
draggable: true,
|
||||
title: location[0],
|
||||
map: map,
|
||||
zIndex: location[3]
|
||||
});
|
||||
bounds.extend(myLatLng);
|
||||
if ( i > 1 ) { map.fitBounds(bounds); };
|
||||
}
|
||||
}"""
|
||||
|
||||
# javascript for OpenStreetMap's markers...
|
||||
OSM_MARKERS = """
|
||||
function initialize(){
|
||||
var map;
|
||||
var tracelife = %s;
|
||||
var iconStyle = new ol.style.Style({
|
||||
image: new ol.style.Icon(({
|
||||
opacity: 1.0,
|
||||
src: marker_png
|
||||
}))
|
||||
});
|
||||
var markerSource = new ol.source.Vector({
|
||||
});
|
||||
for (var i = 0; i < tracelife.length; i++) {
|
||||
var loc = tracelife[i];
|
||||
var iconFeature = new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.transform([loc[0], loc[1]],
|
||||
'EPSG:4326', 'EPSG:3857')),
|
||||
name: loc[2],
|
||||
});
|
||||
iconFeature.setStyle(iconStyle);
|
||||
markerSource.addFeature(iconFeature);
|
||||
}
|
||||
markerLayer = new ol.layer.Vector({
|
||||
source: markerSource,
|
||||
style: iconStyle
|
||||
});
|
||||
var centerCoord = new ol.proj.transform([%s, %s], 'EPSG:4326', 'EPSG:3857');
|
||||
map= new ol.Map({
|
||||
target: 'map_canvas',
|
||||
layers: [new ol.layer.Tile({ source: new ol.source.OSM() }),
|
||||
markerLayer],
|
||||
view: new ol.View({ center: centerCoord, zoom: %d })
|
||||
});
|
||||
var element = document.getElementById('popup');
|
||||
var tooltip = new ol.Overlay({
|
||||
element: element,
|
||||
positioning: 'bottom-center',
|
||||
stopEvent: false
|
||||
});
|
||||
map.addOverlay(tooltip);
|
||||
var displayFeatureInfo = function(pixel) {
|
||||
var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
|
||||
return feature;
|
||||
});
|
||||
var info = document.getElementById('popup');
|
||||
if (feature) {
|
||||
var geometry = feature.getGeometry();
|
||||
var coord = geometry.getCoordinates();
|
||||
tooltip.setPosition(coord);
|
||||
$(element).siblings('.popover').css({ width: '250px' });
|
||||
$(element).siblings('.popover').css({ background: '#aaa' });
|
||||
$(info).popover({
|
||||
'placement': 'auto',
|
||||
'html': true,
|
||||
'content': feature.get('name')
|
||||
});
|
||||
$(info).popover('show');
|
||||
} else {
|
||||
// TODO : some warning with firebug here
|
||||
$(info).popover('destroy');
|
||||
$('.popover').remove();
|
||||
}
|
||||
};
|
||||
map.on('pointermove', function(evt) {
|
||||
if (evt.dragging) {
|
||||
return;
|
||||
}
|
||||
var pixel = map.getEventPixel(evt.originalEvent);
|
||||
displayFeatureInfo(pixel);
|
||||
});
|
||||
map.on('click', function(evt) {
|
||||
displayFeatureInfo(evt.pixel);
|
||||
});
|
||||
};
|
||||
"""
|
||||
|
||||
# variables for alphabet_navigation()
|
||||
_KEYPERSON, _KEYPLACE, _KEYEVENT, _ALPHAEVENT = 0, 1, 2, 3
|
||||
|
||||
COLLATE_LANG = glocale.collation
|
||||
|
||||
_NAME_STYLE_SHORT = 2
|
||||
_NAME_STYLE_DEFAULT = 1
|
||||
_NAME_STYLE_FIRST = 0
|
||||
_NAME_STYLE_SPECIAL = None
|
||||
|
||||
PLUGMAN = BasePluginManager.get_instance()
|
||||
CSS = PLUGMAN.process_plugin_data('WEBSTUFF')
|
||||
|
||||
#_NAME_COL = 3
|
||||
|
||||
_WRONGMEDIAPATH = []
|
||||
|
||||
_HTML_DBL_QUOTES = re.compile(r'([^"]*) " ([^"]*) " (.*)', re.VERBOSE)
|
||||
_HTML_SNG_QUOTES = re.compile(r"([^']*) ' ([^']*) ' (.*)", re.VERBOSE)
|
||||
|
||||
# Events that are usually a family event
|
||||
_EVENTMAP = set([EventType.MARRIAGE, EventType.MARR_ALT,
|
||||
EventType.MARR_SETTL, EventType.MARR_LIC,
|
||||
EventType.MARR_CONTR, EventType.MARR_BANNS,
|
||||
EventType.ENGAGEMENT, EventType.DIVORCE,
|
||||
EventType.DIV_FILING])
|
||||
|
||||
# Names for stylesheets
|
||||
_NARRATIVESCREEN = "narrative-screen.css"
|
||||
_NARRATIVEPRINT = "narrative-print.css"
|
||||
|
||||
def sort_people(dbase, handle_list, rlocale=glocale):
|
||||
"""
|
||||
will sort the database people by surname
|
||||
"""
|
||||
sname_sub = defaultdict(list)
|
||||
sortnames = {}
|
||||
|
||||
for person_handle in handle_list:
|
||||
person = dbase.get_person_from_handle(person_handle)
|
||||
primary_name = person.get_primary_name()
|
||||
|
||||
if primary_name.group_as:
|
||||
surname = primary_name.group_as
|
||||
else:
|
||||
group_map = _nd.primary_surname(primary_name)
|
||||
surname = dbase.get_name_group_mapping(group_map)
|
||||
|
||||
# Treat people who have no name with those whose name is just
|
||||
# 'whitespace'
|
||||
if surname is None or surname.isspace():
|
||||
surname = ''
|
||||
sortnames[person_handle] = _nd.sort_string(primary_name)
|
||||
sname_sub[surname].append(person_handle)
|
||||
|
||||
sorted_lists = []
|
||||
temp_list = sorted(sname_sub, key=rlocale.sort_key)
|
||||
|
||||
for name in temp_list:
|
||||
if isinstance(name, bytes):
|
||||
name = name.decode('utf-8')
|
||||
slist = sorted(((sortnames[x], x) for x in sname_sub[name]),
|
||||
key=lambda x: rlocale.sort_key(x[0]))
|
||||
entries = [x[1] for x in slist]
|
||||
sorted_lists.append((name, entries))
|
||||
|
||||
return sorted_lists
|
||||
|
||||
def sort_event_types(dbase, event_types, event_handle_list, rlocale):
|
||||
"""
|
||||
sort a list of event types and their associated event handles
|
||||
|
||||
@param: dbase -- report database
|
||||
@param: event_types -- a dict of event types
|
||||
@param: event_handle_list -- all event handles in this database
|
||||
"""
|
||||
event_dict = dict((evt_type, list()) for evt_type in event_types)
|
||||
|
||||
for event_handle in event_handle_list:
|
||||
|
||||
event = dbase.get_event_from_handle(event_handle)
|
||||
event_type = rlocale.translation.sgettext(event.get_type().xml_str())
|
||||
|
||||
# add (gramps_id, date, handle) from this event
|
||||
if event_type in event_dict:
|
||||
sort_value = event.get_date_object().get_sort_value()
|
||||
event_dict[event_type].append((sort_value, event_handle))
|
||||
|
||||
for tup_list in event_dict.values():
|
||||
tup_list.sort()
|
||||
|
||||
# return a list of sorted tuples, one per event
|
||||
retval = [(event_type, event_list) for (event_type,
|
||||
event_list) in event_dict.items()]
|
||||
retval.sort(key=lambda item: str(item[0]))
|
||||
|
||||
return retval
|
||||
|
||||
# Modified _get_regular_surname from WebCal.py to get prefix, first name,
|
||||
# and suffix
|
||||
def _get_short_name(gender, name):
|
||||
""" Will get suffix for all people passed through it """
|
||||
|
||||
short_name = name.get_first_name()
|
||||
suffix = name.get_suffix()
|
||||
if suffix:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
short_name = short_name + ", " + suffix
|
||||
return short_name
|
||||
|
||||
def __get_person_keyname(dbase, handle):
|
||||
""" .... """
|
||||
|
||||
person = dbase.get_person_from_handle(handle)
|
||||
return _nd.sort_string(person.get_primary_name())
|
||||
|
||||
def __get_place_keyname(dbase, handle):
|
||||
""" ... """
|
||||
|
||||
return utils.place_name(dbase, handle)
|
||||
|
||||
# See : http://www.gramps-project.org/bugs/view.php?id = 4423
|
||||
|
||||
# Contraction data taken from CLDR 22.1. Only the default variant is considered.
|
||||
# The languages included below are, by no means, all the langauges that have
|
||||
# contractions - just a sample of langauges that have been supported
|
||||
|
||||
# At the time of writing (Feb 2013), the following langauges have greater that
|
||||
# 50% coverage of translation of Gramps: bg Bulgarian, ca Catalan, cs Czech, da
|
||||
# Danish, de German, el Greek, en_GB, es Spanish, fi Finish, fr French, he
|
||||
# Hebrew, hr Croation, hu Hungarian, it Italian, ja Japanese, lt Lithuanian, nb
|
||||
# Noregian Bokmål, nn Norwegian Nynorsk, nl Dutch, pl Polish, pt_BR Portuguese
|
||||
# (Brazil), pt_P Portugeuse (Portugal), ru Russian, sk Slovak, sl Slovenian, sv
|
||||
# Swedish, vi Vietnamese, zh_CN Chinese.
|
||||
|
||||
# Key is the language (or language and country), Value is a list of
|
||||
# contractions. Each contraction consists of a tuple. First element of the
|
||||
# tuple is the list of characters, second element is the string to use as the
|
||||
# index entry.
|
||||
|
||||
# The DUCET contractions (e.g. LATIN CAPIAL LETTER L, MIDDLE DOT) are ignored,
|
||||
# as are the supresscontractions in some locales.
|
||||
|
||||
CONTRACTIONS_DICT = {
|
||||
# bg Bulgarian validSubLocales="bg_BG" no contractions
|
||||
# ca Catalan validSubLocales="ca_AD ca_ES"
|
||||
"ca" : [(("l·", "L·"), "L")],
|
||||
# Czech, validSubLocales="cs_CZ" Czech_Czech Republic
|
||||
"cs" : [(("ch", "cH", "Ch", "CH"), "CH")],
|
||||
# Danish validSubLocales="da_DK" Danish_Denmark
|
||||
"da" : [(("aa", "Aa", "AA"), "Å")],
|
||||
# de German validSubLocales="de_AT de_BE de_CH de_DE de_LI de_LU" no
|
||||
# contractions in standard collation.
|
||||
# el Greek validSubLocales="el_CY el_GR" no contractions.
|
||||
# es Spanish validSubLocales="es_419 es_AR es_BO es_CL es_CO es_CR es_CU
|
||||
# es_DO es_EA es_EC es_ES es_GQ es_GT es_HN es_IC es_MX es_NI es_PA es_PE
|
||||
# es_PH es_PR es_PY es_SV es_US es_UY es_VE" no contractions in standard
|
||||
# collation.
|
||||
# fi Finish validSubLocales="fi_FI" no contractions in default (phonebook)
|
||||
# collation.
|
||||
# fr French no collation data.
|
||||
# he Hebrew validSubLocales="he_IL" no contractions
|
||||
# hr Croation validSubLocales="hr_BA hr_HR"
|
||||
"hr" : [(("dž", "Dž"), "dž"),
|
||||
(("lj", "Lj", 'LJ'), "LJ"),
|
||||
(("Nj", "NJ", "nj"), "NJ")],
|
||||
# Hungarian hu_HU for two and three character contractions.
|
||||
"hu" : [(("cs", "Cs", "CS"), "CS"),
|
||||
(("dzs", "Dzs", "DZS"), "DZS"), # order is important
|
||||
(("dz", "Dz", "DZ"), "DZ"),
|
||||
(("gy", "Gy", "GY"), "GY"),
|
||||
(("ly", "Ly", "LY"), "LY"),
|
||||
(("ny", "Ny", "NY"), "NY"),
|
||||
(("sz", "Sz", "SZ"), "SZ"),
|
||||
(("ty", "Ty", "TY"), "TY"),
|
||||
(("zs", "Zs", "ZS"), "ZS")
|
||||
],
|
||||
# it Italian no collation data.
|
||||
# ja Japanese unable to process the data as it is too complex.
|
||||
# lt Lithuanian no contractions.
|
||||
# Norwegian Bokmål
|
||||
"nb" : [(("aa", "Aa", "AA"), "Å")],
|
||||
# nn Norwegian Nynorsk validSubLocales="nn_NO"
|
||||
"nn" : [(("aa", "Aa", "AA"), "Å")],
|
||||
# nl Dutch no collation data.
|
||||
# pl Polish validSubLocales="pl_PL" no contractions
|
||||
# pt Portuguese no collation data.
|
||||
# ru Russian validSubLocales="ru_BY ru_KG ru_KZ ru_MD ru_RU ru_UA" no
|
||||
# contractions
|
||||
# Slovak, validSubLocales="sk_SK" Slovak_Slovakia
|
||||
# having DZ in Slovak as a contraction was rejected in
|
||||
# http://unicode.org/cldr/trac/ticket/2968
|
||||
"sk" : [(("ch", "cH", "Ch", "CH"), "Ch")],
|
||||
# sl Slovenian validSubLocales="sl_SI" no contractions
|
||||
# sv Swedish validSubLocales="sv_AX sv_FI sv_SE" default collation is
|
||||
# "reformed" no contractions.
|
||||
# vi Vietnamese validSubLocales="vi_VN" no contractions.
|
||||
# zh Chinese validSubLocales="zh_Hans zh_Hans_CN zh_Hans_SG" no contractions
|
||||
# in Latin characters the others are too complex.
|
||||
}
|
||||
|
||||
# The comment below from the glibc locale sv_SE in
|
||||
# localedata/locales/sv_SE :
|
||||
#
|
||||
# % The letter w is normally not present in the Swedish alphabet. It
|
||||
# % exists in some names in Swedish and foreign words, but is accounted
|
||||
# % for as a variant of 'v'. Words and names with 'w' are in Swedish
|
||||
# % ordered alphabetically among the words and names with 'v'. If two
|
||||
# % words or names are only to be distinguished by 'v' or % 'w', 'v' is
|
||||
# % placed before 'w'.
|
||||
#
|
||||
# See : http://www.gramps-project.org/bugs/view.php?id = 2933
|
||||
#
|
||||
|
||||
# HOWEVER: the characters V and W in Swedish are not considered as a special
|
||||
# case for several reasons. (1) The default collation for Swedish (called the
|
||||
# 'reformed' collation type) regards the difference between 'v' and 'w' as a
|
||||
# primary difference. (2) 'v' and 'w' in the 'standard' (non-default) collation
|
||||
# type are not a contraction, just a case where the difference is secondary
|
||||
# rather than primary. (3) There are plenty of other languages where a
|
||||
# difference that is primary in other languages is secondary, and those are not
|
||||
# specially handled.
|
||||
|
||||
def first_letter(string, rlocale=glocale):
|
||||
"""
|
||||
Receives a string and returns the first letter
|
||||
"""
|
||||
if string is None or len(string) < 1:
|
||||
return ' '
|
||||
|
||||
norm_unicode = normalize('NFKC', str(string))
|
||||
contractions = CONTRACTIONS_DICT.get(COLLATE_LANG)
|
||||
if contractions is None:
|
||||
contractions = CONTRACTIONS_DICT.get(COLLATE_LANG.split("_")[0])
|
||||
|
||||
if contractions is not None:
|
||||
for contraction in contractions:
|
||||
count = len(contraction[0][0])
|
||||
if (len(norm_unicode) >= count and
|
||||
norm_unicode[:count] in contraction[0]):
|
||||
return contraction[1]
|
||||
|
||||
# no special case
|
||||
return norm_unicode[0].upper()
|
||||
|
||||
try:
|
||||
import PyICU # pylint : disable=wrong-import-position
|
||||
PRIM_COLL = PyICU.Collator.createInstance(PyICU.Locale(COLLATE_LANG))
|
||||
PRIM_COLL.setStrength(PRIM_COLL.PRIMARY)
|
||||
|
||||
def primary_difference(prev_key, new_key, rlocale=glocale):
|
||||
"""
|
||||
Try to use the PyICU collation.
|
||||
"""
|
||||
|
||||
return PRIM_COLL.compare(prev_key, new_key) != 0
|
||||
|
||||
except:
|
||||
def primary_difference(prev_key, new_key, rlocale=glocale):
|
||||
"""
|
||||
The PyICU collation is not available.
|
||||
|
||||
Returns true if there is a primary difference between the two parameters
|
||||
See http://www.gramps-project.org/bugs/view.php?id=2933#c9317 if
|
||||
letter[i]+'a' < letter[i+1]+'b' and letter[i+1]+'a' < letter[i]+'b' is
|
||||
true then the letters should be grouped together
|
||||
|
||||
The test characters here must not be any that are used in contractions.
|
||||
"""
|
||||
|
||||
return rlocale.sort_key(prev_key + "e") >= \
|
||||
rlocale.sort_key(new_key + "f") or \
|
||||
rlocale.sort_key(new_key + "e") >= \
|
||||
rlocale.sort_key(prev_key + "f")
|
||||
|
||||
def get_first_letters(dbase, handle_list, key, rlocale=glocale):
|
||||
"""
|
||||
get the first letters of the handle_list
|
||||
|
||||
@param: handle_list -- One of a handle list for either person or
|
||||
place handles or an evt types list
|
||||
@param: key -- Either a person, place, or event type
|
||||
|
||||
The first letter (or letters if there is a contraction) are extracted from
|
||||
all the objects in the handle list. There may be duplicates, and there may
|
||||
be letters where there is only a secondary or tertiary difference, not a
|
||||
primary difference. The list is sorted in collation order. For each group
|
||||
with secondary or tertiary differences, the first in collation sequence is
|
||||
retained. For example, assume the default collation sequence (DUCET) and
|
||||
names Ånström and Apple. These will sort in the order shown. Å and A have a
|
||||
secondary difference. If the first letter from these names was chosen then
|
||||
the inex entry would be Å. This is not desirable. Instead, the initial
|
||||
letters are extracted (Å and A). These are sorted, which gives A and Å. Then
|
||||
the first of these is used for the index entry.
|
||||
"""
|
||||
index_list = []
|
||||
|
||||
for handle in handle_list:
|
||||
if key == _KEYPERSON:
|
||||
keyname = __get_person_keyname(dbase, handle)
|
||||
|
||||
elif key == _KEYPLACE:
|
||||
keyname = __get_place_keyname(dbase, handle)
|
||||
|
||||
else:
|
||||
if rlocale != glocale:
|
||||
keyname = rlocale.translation.sgettext(handle)
|
||||
else:
|
||||
keyname = handle
|
||||
ltr = first_letter(keyname)
|
||||
|
||||
index_list.append(ltr)
|
||||
|
||||
# Now remove letters where there is not a primary difference
|
||||
index_list.sort(key=rlocale.sort_key)
|
||||
first = True
|
||||
prev_index = None
|
||||
for key in index_list[:]: #iterate over a slice copy of the list
|
||||
if first or primary_difference(prev_index, key, rlocale):
|
||||
first = False
|
||||
prev_index = key
|
||||
else:
|
||||
index_list.remove(key)
|
||||
|
||||
# return menu set letters for alphabet_navigation
|
||||
return index_list
|
||||
|
||||
def get_index_letter(letter, index_list, rlocale=glocale):
|
||||
"""
|
||||
This finds the letter in the index_list that has no primary difference from
|
||||
the letter provided. See the discussion in get_first_letters above.
|
||||
Continuing the example, if letter is Å and index_list is A, then this would
|
||||
return A.
|
||||
"""
|
||||
for index in index_list:
|
||||
if not primary_difference(letter, index, rlocale):
|
||||
return index
|
||||
|
||||
LOG.warning("Initial letter '%s' not found in alphabetic navigation list",
|
||||
letter)
|
||||
LOG.debug("filtered sorted index list %s", index_list)
|
||||
return letter
|
||||
|
||||
def alphabet_navigation(index_list, rlocale=glocale):
|
||||
"""
|
||||
Will create the alphabet navigation bar for classes IndividualListPage,
|
||||
SurnameListPage, PlaceListPage, and EventList
|
||||
|
||||
@param: index_list -- a dictionary of either letters or words
|
||||
"""
|
||||
sorted_set = defaultdict(int)
|
||||
|
||||
for menu_item in index_list:
|
||||
sorted_set[menu_item] += 1
|
||||
|
||||
# remove the number of each occurance of each letter
|
||||
sorted_alpha_index = sorted(sorted_set, key=rlocale.sort_key)
|
||||
|
||||
# if no letters, return None to its callers
|
||||
if not sorted_alpha_index:
|
||||
return None
|
||||
|
||||
num_ltrs = len(sorted_alpha_index)
|
||||
num_of_cols = 26
|
||||
num_of_rows = ((num_ltrs // num_of_cols) + 1)
|
||||
|
||||
# begin alphabet navigation division
|
||||
with Html("div", id="alphanav") as alphabetnavigation:
|
||||
|
||||
index = 0
|
||||
for row in range(num_of_rows):
|
||||
unordered = Html("ul")
|
||||
|
||||
cols = 0
|
||||
while cols <= num_of_cols and index < num_ltrs:
|
||||
menu_item = sorted_alpha_index[index]
|
||||
if menu_item == ' ':
|
||||
menu_item = ' '
|
||||
# adding title to hyperlink menu for screen readers and
|
||||
# braille writers
|
||||
title_txt = "Alphabet Menu: %s" % menu_item
|
||||
title_str = rlocale.translation.sgettext(title_txt)
|
||||
hyper = Html("a", menu_item, title=title_str,
|
||||
href="#%s" % menu_item)
|
||||
unordered.extend(Html("li", hyper, inline=True))
|
||||
|
||||
index += 1
|
||||
cols += 1
|
||||
num_of_rows -= 1
|
||||
|
||||
alphabetnavigation += unordered
|
||||
|
||||
return alphabetnavigation
|
||||
|
||||
def _has_webpage_extension(url):
|
||||
"""
|
||||
determine if a filename has an extension or not...
|
||||
|
||||
@param: url -- filename to be checked
|
||||
"""
|
||||
return any(url.endswith(ext) for ext in _WEB_EXT)
|
||||
|
||||
def add_birthdate(dbase, ppl_handle_list, rlocale):
|
||||
"""
|
||||
This will sort a list of child handles in birth order
|
||||
For each entry in the list, we'll have :
|
||||
birth date
|
||||
The transtated birth date for the configured locale
|
||||
The transtated death date for the configured locale
|
||||
The handle for the child
|
||||
|
||||
@param: dbase -- The database to use
|
||||
@param: ppl_handle_list -- the handle for the people
|
||||
@param: rlocale -- the locale for date translation
|
||||
"""
|
||||
sortable_individuals = []
|
||||
for person_handle in ppl_handle_list:
|
||||
birth_date = 0 # dummy value in case none is found
|
||||
person = dbase.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
birth_ref = person.get_birth_ref()
|
||||
birth1 = ""
|
||||
if birth_ref:
|
||||
birth = dbase.get_event_from_handle(birth_ref.ref)
|
||||
if birth:
|
||||
birth1 = rlocale.get_date(birth.get_date_object())
|
||||
birth_date = birth.get_date_object().get_sort_value()
|
||||
death_event = get_death_or_fallback(dbase, person)
|
||||
if death_event:
|
||||
death = rlocale.get_date(death_event.get_date_object())
|
||||
else:
|
||||
death = ""
|
||||
sortable_individuals.append((birth_date, birth1, death, person_handle))
|
||||
|
||||
# return a list of handles with the individual's birthdate attached
|
||||
return sortable_individuals
|
||||
|
||||
def _find_birth_date(dbase, individual):
|
||||
"""
|
||||
will look for a birth date within the person's events
|
||||
|
||||
@param: dbase -- The database to use
|
||||
@param: individual -- The individual for who we want to find the birth date
|
||||
"""
|
||||
date_out = None
|
||||
birth_ref = individual.get_birth_ref()
|
||||
if birth_ref:
|
||||
birth = dbase.get_event_from_handle(birth_ref.ref)
|
||||
if birth:
|
||||
date_out = birth.get_date_object()
|
||||
date_out.fallback = False
|
||||
else:
|
||||
person_evt_ref_list = individual.get_primary_event_ref_list()
|
||||
if person_evt_ref_list:
|
||||
for evt_ref in person_evt_ref_list:
|
||||
event = dbase.get_event_from_handle(evt_ref.ref)
|
||||
if event:
|
||||
if event.get_type().is_birth_fallback():
|
||||
date_out = event.get_date_object()
|
||||
date_out.fallback = True
|
||||
LOG.debug("setting fallback to true for '%s'", event)
|
||||
break
|
||||
return date_out
|
||||
|
||||
def _find_death_date(dbase, individual):
|
||||
"""
|
||||
will look for a death date within a person's events
|
||||
|
||||
@param: dbase -- The database to use
|
||||
@param: individual -- The individual for who we want to find the death date
|
||||
"""
|
||||
date_out = None
|
||||
death_ref = individual.get_death_ref()
|
||||
if death_ref:
|
||||
death = dbase.get_event_from_handle(death_ref.ref)
|
||||
if death:
|
||||
date_out = death.get_date_object()
|
||||
date_out.fallback = False
|
||||
else:
|
||||
person_evt_ref_list = individual.get_primary_event_ref_list()
|
||||
if person_evt_ref_list:
|
||||
for evt_ref in person_evt_ref_list:
|
||||
event = dbase.get_event_from_handle(evt_ref.ref)
|
||||
if event:
|
||||
if event.get_type().is_death_fallback():
|
||||
date_out = event.get_date_object()
|
||||
date_out.fallback = True
|
||||
LOG.debug("setting fallback to true for '%s'", event)
|
||||
break
|
||||
return date_out
|
||||
|
||||
def build_event_data_by_individuals(dbase, ppl_handle_list):
|
||||
"""
|
||||
creates a list of event handles and event types for this database
|
||||
|
||||
@param: dbase -- The database to use
|
||||
@param: ppl_handle_list -- the handle for the people
|
||||
"""
|
||||
event_handle_list = []
|
||||
event_types = []
|
||||
|
||||
for person_handle in ppl_handle_list:
|
||||
person = dbase.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
|
||||
evt_ref_list = person.get_event_ref_list()
|
||||
if evt_ref_list:
|
||||
for evt_ref in evt_ref_list:
|
||||
event = dbase.get_event_from_handle(evt_ref.ref)
|
||||
if event:
|
||||
|
||||
event_types.append(str(event.get_type()))
|
||||
event_handle_list.append(evt_ref.ref)
|
||||
|
||||
person_family_handle_list = person.get_family_handle_list()
|
||||
if person_family_handle_list:
|
||||
for family_handle in person_family_handle_list:
|
||||
family = dbase.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
|
||||
family_evt_ref_list = family.get_event_ref_list()
|
||||
if family_evt_ref_list:
|
||||
for evt_ref in family_evt_ref_list:
|
||||
event = dbase.get_event_from_handle(evt_ref.ref)
|
||||
if event:
|
||||
event_types.append(str(event.type))
|
||||
event_handle_list.append(evt_ref.ref)
|
||||
|
||||
# return event_handle_list and event types to its caller
|
||||
return event_handle_list, event_types
|
||||
|
||||
def name_to_md5(text):
|
||||
"""This creates an MD5 hex string to be used as filename."""
|
||||
|
||||
return md5(text.encode('utf-8')).hexdigest()
|
||||
|
||||
def get_gendex_data(database, event_ref):
|
||||
"""
|
||||
Given an event, return the date and place a strings
|
||||
|
||||
@param: database -- The database
|
||||
@param: event_ref -- The event reference
|
||||
"""
|
||||
doe = "" # date of event
|
||||
poe = "" # place of event
|
||||
if event_ref and event_ref.ref:
|
||||
event = database.get_event_from_handle(event_ref.ref)
|
||||
if event:
|
||||
date = event.get_date_object()
|
||||
doe = format_date(date)
|
||||
if event.get_place_handle():
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = database.get_place_from_handle(place_handle)
|
||||
if place:
|
||||
poe = _pd.display(database, place, date)
|
||||
return doe, poe
|
||||
|
||||
def format_date(date):
|
||||
"""
|
||||
Format the date
|
||||
"""
|
||||
start = date.get_start_date()
|
||||
if start != Date.EMPTY:
|
||||
cal = date.get_calendar()
|
||||
mod = date.get_modifier()
|
||||
quality = date.get_quality()
|
||||
if quality in DATE_QUALITY:
|
||||
qual_text = DATE_QUALITY[quality] + " "
|
||||
else:
|
||||
qual_text = ""
|
||||
if mod == Date.MOD_SPAN:
|
||||
val = "%sFROM %s TO %s" % (
|
||||
qual_text,
|
||||
make_gedcom_date(start, cal, mod, None),
|
||||
make_gedcom_date(date.get_stop_date(), cal, mod, None))
|
||||
elif mod == Date.MOD_RANGE:
|
||||
val = "%sBET %s AND %s" % (
|
||||
qual_text,
|
||||
make_gedcom_date(start, cal, mod, None),
|
||||
make_gedcom_date(date.get_stop_date(), cal, mod, None))
|
||||
else:
|
||||
val = make_gedcom_date(start, cal, mod, quality)
|
||||
return val
|
||||
return ""
|
||||
|
||||
# This command then defines the 'html_escape' option for escaping
|
||||
# special characters for presentation in HTML based on the above list.
|
||||
def html_escape(text):
|
||||
"""Convert the text and replace some characters with a &# variant."""
|
||||
|
||||
# First single characters, no quotes
|
||||
text = escape(text)
|
||||
|
||||
# Deal with double quotes.
|
||||
match = _HTML_DBL_QUOTES.match(text)
|
||||
while match:
|
||||
text = "%s" "“" "%s" "”" "%s" % match.groups()
|
||||
match = _HTML_DBL_QUOTES.match(text)
|
||||
# Replace remaining double quotes.
|
||||
text = text.replace('"', '"')
|
||||
|
||||
# Deal with single quotes.
|
||||
text = text.replace("'s ", '’s ')
|
||||
match = _HTML_SNG_QUOTES.match(text)
|
||||
while match:
|
||||
text = "%s" "‘" "%s" "’" "%s" % match.groups()
|
||||
match = _HTML_SNG_QUOTES.match(text)
|
||||
# Replace remaining single quotes.
|
||||
text = text.replace("'", ''')
|
||||
|
||||
return text
|
||||
|
139
gramps/plugins/webreport/contact.py
Normal file
139
gramps/plugins/webreport/contact.py
Normal file
@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
ContactPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.utils.config import get_researcher
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import FULLCLEAR
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
class ContactPage(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Researcher'
|
||||
"""
|
||||
def __init__(self, report, title):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for this report
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
output_file, sio = self.report.create_file("contact")
|
||||
contactpage, head, body = self.write_header(self._('Contact'))
|
||||
|
||||
# begin contact division
|
||||
with Html("div", class_="content", id="Contact") as section:
|
||||
body += section
|
||||
|
||||
# begin summaryarea division
|
||||
with Html("div", id='summaryarea') as summaryarea:
|
||||
section += summaryarea
|
||||
|
||||
contactimg = self.add_image('contactimg', 200)
|
||||
if contactimg is not None:
|
||||
summaryarea += contactimg
|
||||
|
||||
# get researcher information
|
||||
res = get_researcher()
|
||||
|
||||
with Html("div", id='researcher') as researcher:
|
||||
summaryarea += researcher
|
||||
|
||||
if res.name:
|
||||
res.name = res.name.replace(',,,', '')
|
||||
researcher += Html("h3", res.name, inline=True)
|
||||
if res.addr:
|
||||
researcher += Html("span", res.addr,
|
||||
id='streetaddress', inline=True)
|
||||
if res.locality:
|
||||
researcher += Html("span", res.locality,
|
||||
id="locality", inline=True)
|
||||
text = "".join([res.city, res.state, res.postal])
|
||||
if text:
|
||||
city = Html("span", res.city, id='city', inline=True)
|
||||
state = Html("span", res.state, id='state', inline=True)
|
||||
postal = Html("span", res.postal, id='postalcode',
|
||||
inline=True)
|
||||
researcher += (city, state, postal)
|
||||
if res.country:
|
||||
researcher += Html("span", res.country,
|
||||
id='country', inline=True)
|
||||
if res.email:
|
||||
researcher += Html("span", id='email') + (
|
||||
Html("a", res.email,
|
||||
href='mailto:%s' % res.email, inline=True)
|
||||
)
|
||||
|
||||
# add clear line for proper styling
|
||||
summaryarea += FULLCLEAR
|
||||
|
||||
note_id = report.options['contactnote']
|
||||
if note_id:
|
||||
note = self.r_db.get_note_from_gramps_id(note_id)
|
||||
note_text = self.get_note_format(note, False)
|
||||
|
||||
# attach note
|
||||
summaryarea += note_text
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for porcessing
|
||||
# and close the file
|
||||
self.xhtml_writer(contactpage, output_file, sio, 0)
|
195
gramps/plugins/webreport/download.py
Normal file
195
gramps/plugins/webreport/download.py
Normal file
@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
DownloadPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
import os
|
||||
import datetime
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (FULLCLEAR, html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
class DownloadPage(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the Download page
|
||||
"""
|
||||
def __init__(self, report, title):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for this report
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
# do NOT include a Download Page
|
||||
if not self.report.inc_download:
|
||||
return
|
||||
|
||||
# menu options for class
|
||||
# download and description #1
|
||||
|
||||
dlfname1 = self.report.dl_fname1
|
||||
dldescr1 = self.report.dl_descr1
|
||||
|
||||
# download and description #2
|
||||
dlfname2 = self.report.dl_fname2
|
||||
dldescr2 = self.report.dl_descr2
|
||||
|
||||
# if no filenames at all, return???
|
||||
if dlfname1 or dlfname2:
|
||||
|
||||
output_file, sio = self.report.create_file("download")
|
||||
downloadpage, head, body = self.write_header(self._('Download'))
|
||||
|
||||
# begin download page and table
|
||||
with Html("div", class_="content", id="Download") as download:
|
||||
body += download
|
||||
|
||||
msg = self._("This page is for the user/ creator "
|
||||
"of this Family Tree/ Narrative website "
|
||||
"to share a couple of files with you "
|
||||
"regarding their family. If there are "
|
||||
"any files listed "
|
||||
"below, clicking on them will allow you "
|
||||
"to download them. The "
|
||||
"download page and files have the same "
|
||||
"copyright as the remainder "
|
||||
"of these web pages.")
|
||||
download += Html("p", msg, id="description")
|
||||
|
||||
# begin download table and table head
|
||||
with Html("table", class_="infolist download") as table:
|
||||
download += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow.extend(
|
||||
Html("th", label, class_="Column" + colclass,
|
||||
inline=True)
|
||||
for (label, colclass) in [
|
||||
(self._("File Name"), "Filename"),
|
||||
(self._("Description"), "Description"),
|
||||
(self._("Last Modified"), "Modified")])
|
||||
# table body
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
# if dlfname1 is not None, show it???
|
||||
if dlfname1:
|
||||
|
||||
trow = Html("tr", id='Row01')
|
||||
tbody += trow
|
||||
|
||||
fname = os.path.basename(dlfname1)
|
||||
# TODO dlfname1 is filename, convert disk path to URL
|
||||
tcell = Html("td", class_="ColumnFilename") + (
|
||||
Html("a", fname, href=dlfname1,
|
||||
title=html_escape(dldescr1))
|
||||
)
|
||||
trow += tcell
|
||||
|
||||
dldescr1 = dldescr1 or " "
|
||||
trow += Html("td", dldescr1,
|
||||
class_="ColumnDescription", inline=True)
|
||||
|
||||
tcell = Html("td", class_="ColumnModified", inline=True)
|
||||
trow += tcell
|
||||
if os.path.exists(dlfname1):
|
||||
modified = os.stat(dlfname1).st_mtime
|
||||
last_mod = datetime.datetime.fromtimestamp(modified)
|
||||
tcell += last_mod
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# if download filename #2, show it???
|
||||
if dlfname2:
|
||||
|
||||
# begin row #2
|
||||
trow = Html("tr", id='Row02')
|
||||
tbody += trow
|
||||
|
||||
fname = os.path.basename(dlfname2)
|
||||
tcell = Html("td", class_="ColumnFilename") + (
|
||||
Html("a", fname, href=dlfname2,
|
||||
title=html_escape(dldescr2))
|
||||
)
|
||||
trow += tcell
|
||||
|
||||
dldescr2 = dldescr2 or " "
|
||||
trow += Html("td", dldescr2,
|
||||
class_="ColumnDescription", inline=True)
|
||||
|
||||
tcell = Html("td", id='Col04',
|
||||
class_="ColumnModified", inline=True)
|
||||
trow += tcell
|
||||
if os.path.exists(dlfname2):
|
||||
modified = os.stat(dlfname2).st_mtime
|
||||
last_mod = datetime.datetime.fromtimestamp(modified)
|
||||
tcell += last_mod
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# clear line for proper styling
|
||||
# create footer section
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(downloadpage, output_file, sio, 0)
|
448
gramps/plugins/webreport/event.py
Normal file
448
gramps/plugins/webreport/event.py
Normal file
@ -0,0 +1,448 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
EventPage - Event index page and individual Event pages
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import (Date, Event)
|
||||
from gramps.gen.plug.report import Bibliography
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (get_first_letters, _ALPHAEVENT,
|
||||
_EVENTMAP, alphabet_navigation,
|
||||
FULLCLEAR, sort_event_types,
|
||||
primary_difference,
|
||||
get_index_letter)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# creates the Event List Page and EventPages
|
||||
#
|
||||
#################################################
|
||||
class EventPages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Person'
|
||||
database objects. It displays this information under the 'Events'
|
||||
tab. It is told by the 'add_instances' call which 'Person's to display,
|
||||
and remembers the list of persons. A single call to 'display_pages'
|
||||
displays both the Event List (Index) page and all the Event
|
||||
pages.
|
||||
|
||||
The base class 'BasePage' is initialised once for each page that is
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
self.event_handle_list = []
|
||||
self.event_types = []
|
||||
self.event_dict = defaultdict(set)
|
||||
|
||||
def display_pages(self, title):
|
||||
"""
|
||||
Generate and output the pages under the Event tab, namely the event
|
||||
index and the individual event pages.
|
||||
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
LOG.debug("obj_dict[Event]")
|
||||
for item in self.report.obj_dict[Event].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
event_handle_list = self.report.obj_dict[Event].keys()
|
||||
event_types = []
|
||||
for event_handle in event_handle_list:
|
||||
event = self.r_db.get_event_from_handle(event_handle)
|
||||
event_types.append(self._(event.get_type().xml_str()))
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating event pages"),
|
||||
len(event_handle_list) + 1
|
||||
) as step:
|
||||
self.eventlistpage(self.report, title, event_types,
|
||||
event_handle_list)
|
||||
|
||||
for event_handle in event_handle_list:
|
||||
step()
|
||||
self.eventpage(self.report, title, event_handle)
|
||||
|
||||
|
||||
def eventlistpage(self, report, title, event_types, event_handle_list):
|
||||
"""
|
||||
Will create the event list page
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: event_types -- A list of the type in the events database
|
||||
@param: event_handle_list -- A list of event handles
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
ldatec = 0
|
||||
prev_letter = " "
|
||||
|
||||
output_file, sio = self.report.create_file("events")
|
||||
eventslistpage, head, body = self.write_header(self._("Events"))
|
||||
|
||||
# begin events list division
|
||||
with Html("div", class_="content", id="EventList") as eventlist:
|
||||
body += eventlist
|
||||
|
||||
msg = self._("This page contains an index of all the events in the "
|
||||
"database, sorted by their type and date (if one is "
|
||||
"present). Clicking on an event’s Gramps ID "
|
||||
"will open a page for that event.")
|
||||
eventlist += Html("p", msg, id="description")
|
||||
|
||||
# get alphabet navigation...
|
||||
index_list = get_first_letters(self.r_db, event_types,
|
||||
_ALPHAEVENT)
|
||||
alpha_nav = alphabet_navigation(index_list, self.rlocale)
|
||||
if alpha_nav:
|
||||
eventlist += alpha_nav
|
||||
|
||||
# begin alphabet event table
|
||||
with Html("table",
|
||||
class_="infolist primobjlist alphaevent") as table:
|
||||
eventlist += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow.extend(
|
||||
Html("th", label, class_=colclass, inline=True)
|
||||
for (label, colclass) in [(self._("Letter"),
|
||||
"ColumnRowLabel"),
|
||||
(self._("Type"), "ColumnType"),
|
||||
(self._("Date"), "ColumnDate"),
|
||||
(self._("Gramps ID"),
|
||||
"ColumnGRAMPSID"),
|
||||
(self._("Person"), "ColumnPerson")
|
||||
]
|
||||
)
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
# separate events by their type and then thier event handles
|
||||
for (evt_type,
|
||||
data_list) in sort_event_types(self.r_db,
|
||||
event_types,
|
||||
event_handle_list,
|
||||
self.rlocale):
|
||||
first = True
|
||||
_event_displayed = []
|
||||
|
||||
# sort datalist by date of event and by event handle...
|
||||
data_list = sorted(data_list, key=itemgetter(0, 1))
|
||||
first_event = True
|
||||
|
||||
for (sort_value, event_handle) in data_list:
|
||||
event = self.r_db.get_event_from_handle(event_handle)
|
||||
_type = event.get_type()
|
||||
gid = event.get_gramps_id()
|
||||
if event.get_change_time() > ldatec:
|
||||
ldatec = event.get_change_time()
|
||||
|
||||
# check to see if we have listed this gramps_id yet?
|
||||
if gid not in _event_displayed:
|
||||
|
||||
# family event
|
||||
if int(_type) in _EVENTMAP:
|
||||
handle_list = set(
|
||||
self.r_db.find_backlink_handles(
|
||||
event_handle,
|
||||
include_classes=['Family', 'Person']))
|
||||
else:
|
||||
handle_list = set(
|
||||
self.r_db.find_backlink_handles(
|
||||
event_handle,
|
||||
include_classes=['Person']))
|
||||
if handle_list:
|
||||
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
# set up hyperlinked letter for
|
||||
# alphabet_navigation
|
||||
tcell = Html("td", class_="ColumnLetter",
|
||||
inline=True)
|
||||
trow += tcell
|
||||
|
||||
if evt_type and not evt_type.isspace():
|
||||
letter = get_index_letter(
|
||||
self._(str(evt_type)[0].capitalize()),
|
||||
index_list, self.rlocale)
|
||||
else:
|
||||
letter = " "
|
||||
|
||||
if first or primary_difference(letter,
|
||||
prev_letter,
|
||||
self.rlocale):
|
||||
first = False
|
||||
prev_letter = letter
|
||||
t_a = 'class = "BeginLetter BeginType"'
|
||||
trow.attr = t_a
|
||||
ttle = self._("Event types beginning "
|
||||
"with letter %s") % letter
|
||||
tcell += Html("a", letter, name=letter,
|
||||
id_=letter, title=ttle,
|
||||
inline=True)
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# display Event type if first in the list
|
||||
tcell = Html("td", class_="ColumnType",
|
||||
title=self._(evt_type),
|
||||
inline=True)
|
||||
trow += tcell
|
||||
if first_event:
|
||||
tcell += self._(evt_type)
|
||||
if trow.attr == "":
|
||||
trow.attr = 'class = "BeginType"'
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# event date
|
||||
tcell = Html("td", class_="ColumnDate",
|
||||
inline=True)
|
||||
trow += tcell
|
||||
date = Date.EMPTY
|
||||
if event:
|
||||
date = event.get_date_object()
|
||||
if date and date is not Date.EMPTY:
|
||||
tcell += self.rlocale.get_date(date)
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# Gramps ID
|
||||
trow += Html("td", class_="ColumnGRAMPSID") + (
|
||||
self.event_grampsid_link(event_handle,
|
||||
gid, None)
|
||||
)
|
||||
|
||||
# Person(s) column
|
||||
tcell = Html("td", class_="ColumnPerson")
|
||||
trow += tcell
|
||||
|
||||
# classname can either be a person or a family
|
||||
first_person = True
|
||||
|
||||
# get person(s) for ColumnPerson
|
||||
sorted_list = sorted(handle_list)
|
||||
self.complete_people(tcell, first_person,
|
||||
sorted_list,
|
||||
uplink=False)
|
||||
|
||||
_event_displayed.append(gid)
|
||||
first_event = False
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page ut for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(eventslistpage, output_file, sio, ldatec)
|
||||
|
||||
def _geteventdate(self, event_handle):
|
||||
"""
|
||||
Get the event date
|
||||
|
||||
@param: event_handle -- The handle for the event to use
|
||||
"""
|
||||
event_date = Date.EMPTY
|
||||
event = self.r_db.get_event_from_handle(event_handle)
|
||||
if event:
|
||||
date = event.get_date_object()
|
||||
if date:
|
||||
|
||||
# returns the date in YYYY-MM-DD format
|
||||
return Date(date.get_year_calendar("Gregorian"),
|
||||
date.get_month(), date.get_day())
|
||||
|
||||
# return empty date string
|
||||
return event_date
|
||||
|
||||
def event_grampsid_link(self, handle, grampsid, uplink):
|
||||
"""
|
||||
Create a hyperlink from event handle, but show grampsid
|
||||
|
||||
@param: handle -- The handle for the event
|
||||
@param: grampsid -- The gramps ID to display
|
||||
@param: uplink -- If True, then "../../../" is inserted in front of
|
||||
the result.
|
||||
"""
|
||||
url = self.report.build_url_fname_html(handle, "evt", uplink)
|
||||
|
||||
# return hyperlink to its caller
|
||||
return Html("a", grampsid, href=url, title=grampsid, inline=True)
|
||||
|
||||
def eventpage(self, report, title, event_handle):
|
||||
"""
|
||||
Creates the individual event page
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: event_handle -- The event handle for the database
|
||||
"""
|
||||
event = report.database.get_event_from_handle(event_handle)
|
||||
BasePage.__init__(self, report, title, event.get_gramps_id())
|
||||
if not event:
|
||||
return None
|
||||
|
||||
ldatec = event.get_change_time()
|
||||
event_media_list = event.get_media_list()
|
||||
|
||||
self.uplink = True
|
||||
subdirs = True
|
||||
evt_type = self._(event.get_type().xml_str())
|
||||
self.page_title = "%(eventtype)s" % {'eventtype' : evt_type}
|
||||
self.bibli = Bibliography()
|
||||
|
||||
output_file, sio = self.report.create_file(event_handle, "evt")
|
||||
eventpage, head, body = self.write_header(self._("Events"))
|
||||
|
||||
# start event detail division
|
||||
with Html("div", class_="content", id="EventDetail") as eventdetail:
|
||||
body += eventdetail
|
||||
|
||||
thumbnail = self.disp_first_img_as_thumbnail(event_media_list,
|
||||
event)
|
||||
if thumbnail is not None:
|
||||
eventdetail += thumbnail
|
||||
|
||||
# display page title
|
||||
eventdetail += Html("h3", self.page_title, inline=True)
|
||||
|
||||
# begin eventdetail table
|
||||
with Html("table", class_="infolist eventlist") as table:
|
||||
eventdetail += table
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
evt_gid = event.get_gramps_id()
|
||||
if not self.noid and evt_gid:
|
||||
trow = Html("tr") + (
|
||||
Html("td", self._("Gramps ID"),
|
||||
class_="ColumnAttribute", inline=True),
|
||||
Html("td", evt_gid,
|
||||
class_="ColumnGRAMPSID", inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
# get event data
|
||||
#
|
||||
# for more information: see get_event_data()
|
||||
#
|
||||
event_data = self.get_event_data(event, event_handle,
|
||||
subdirs, evt_gid)
|
||||
|
||||
for (label, colclass, data) in event_data:
|
||||
if data:
|
||||
trow = Html("tr") + (
|
||||
Html("td", label, class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html('td', data, class_="Column" + colclass)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
# Narrative subsection
|
||||
notelist = event.get_note_list()
|
||||
notelist = self.display_note_list(notelist)
|
||||
if notelist is not None:
|
||||
eventdetail += notelist
|
||||
|
||||
# get attribute list
|
||||
attrlist = event.get_attribute_list()
|
||||
if attrlist:
|
||||
attrsection, attrtable = self.display_attribute_header()
|
||||
self.display_attr_list(attrlist, attrtable)
|
||||
eventdetail += attrsection
|
||||
|
||||
# event source references
|
||||
srcrefs = self.display_ind_sources(event)
|
||||
if srcrefs is not None:
|
||||
eventdetail += srcrefs
|
||||
|
||||
# display additional images as gallery
|
||||
if self.create_media:
|
||||
addgallery = self.disp_add_img_as_gallery(event_media_list,
|
||||
event)
|
||||
if addgallery:
|
||||
eventdetail += addgallery
|
||||
|
||||
# References list
|
||||
ref_list = self.display_bkref_list(Event, event_handle)
|
||||
if ref_list is not None:
|
||||
eventdetail += ref_list
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the page
|
||||
self.xhtml_writer(eventpage, output_file, sio, ldatec)
|
399
gramps/plugins/webreport/family.py
Normal file
399
gramps/plugins/webreport/family.py
Normal file
@ -0,0 +1,399 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
FamilyPage - Family index page and individual Family pages
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from collections import defaultdict
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import (EventType, Family)
|
||||
from gramps.gen.plug.report import Bibliography
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (get_first_letters, _KEYPERSON,
|
||||
alphabet_navigation, sort_people,
|
||||
primary_difference, first_letter,
|
||||
FULLCLEAR, get_index_letter)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# creates the Family List Page and Family Pages
|
||||
#
|
||||
#################################################
|
||||
class FamilyPages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Family'
|
||||
database objects. It displays this information under the 'Families'
|
||||
tab. It is told by the 'add_instances' call which 'Family's to display,
|
||||
and remembers the list of Family. A single call to 'display_pages'
|
||||
displays both the Family List (Index) page and all the Family
|
||||
pages.
|
||||
|
||||
The base class 'BasePage' is initialised once for each page that is
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
self.family_dict = defaultdict(set)
|
||||
self.person = None
|
||||
self.familymappages = None
|
||||
|
||||
def display_pages(self, title):
|
||||
"""
|
||||
Generate and output the pages under the Family tab, namely the family
|
||||
index and the individual family pages.
|
||||
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
LOG.debug("obj_dict[Family]")
|
||||
for item in self.report.obj_dict[Family].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating family pages..."),
|
||||
len(self.report.obj_dict[Family]) + 1
|
||||
) as step:
|
||||
self.familylistpage(self.report, title,
|
||||
self.report.obj_dict[Family].keys())
|
||||
|
||||
for family_handle in self.report.obj_dict[Family]:
|
||||
step()
|
||||
self.familypage(self.report, title, family_handle)
|
||||
|
||||
def familylistpage(self, report, title, fam_list):
|
||||
"""
|
||||
Create a family index
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: fam_list -- The handle for the place to add
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
output_file, sio = self.report.create_file("families")
|
||||
familieslistpage, head, body = self.write_header(self._("Families"))
|
||||
ldatec = 0
|
||||
prev_letter = " "
|
||||
|
||||
# begin Family Division
|
||||
with Html("div", class_="content", id="Relationships") as relationlist:
|
||||
body += relationlist
|
||||
|
||||
# Families list page message
|
||||
msg = self._("This page contains an index of all the "
|
||||
"families/ relationships in the "
|
||||
"database, sorted by their family name/ surname. "
|
||||
"Clicking on a person’s "
|
||||
"name will take you to their "
|
||||
"family/ relationship’s page.")
|
||||
relationlist += Html("p", msg, id="description")
|
||||
|
||||
# go through all the families, and construct a dictionary of all the
|
||||
# people and the families thay are involved in. Note that the people
|
||||
# in the list may be involved in OTHER families, that are not listed
|
||||
# because they are not in the original family list.
|
||||
pers_fam_dict = defaultdict(list)
|
||||
for family_handle in fam_list:
|
||||
family = self.r_db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
if family.get_change_time() > ldatec:
|
||||
ldatec = family.get_change_time()
|
||||
husband_handle = family.get_father_handle()
|
||||
spouse_handle = family.get_mother_handle()
|
||||
if husband_handle:
|
||||
pers_fam_dict[husband_handle].append(family)
|
||||
if spouse_handle:
|
||||
pers_fam_dict[spouse_handle].append(family)
|
||||
|
||||
# add alphabet navigation
|
||||
index_list = get_first_letters(self.r_db, pers_fam_dict.keys(),
|
||||
_KEYPERSON, rlocale=self.rlocale)
|
||||
alpha_nav = alphabet_navigation(index_list, self.rlocale)
|
||||
if alpha_nav:
|
||||
relationlist += alpha_nav
|
||||
|
||||
# begin families table and table head
|
||||
with Html("table", class_="infolist relationships") as table:
|
||||
relationlist += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
# set up page columns
|
||||
trow.extend(
|
||||
Html("th", trans, class_=colclass, inline=True)
|
||||
for trans, colclass in [(self._("Letter"),
|
||||
"ColumnRowLabel"),
|
||||
(self._("Person"), "ColumnPartner"),
|
||||
(self._("Family"), "ColumnPartner"),
|
||||
(self._("Marriage"), "ColumnDate"),
|
||||
(self._("Divorce"), "ColumnDate")]
|
||||
)
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
# begin displaying index list
|
||||
ppl_handle_list = sort_people(self.r_db, pers_fam_dict.keys(),
|
||||
self.rlocale)
|
||||
first = True
|
||||
for (surname, handle_list) in ppl_handle_list:
|
||||
|
||||
if surname and not surname.isspace():
|
||||
letter = get_index_letter(first_letter(surname),
|
||||
index_list,
|
||||
self.rlocale)
|
||||
else:
|
||||
letter = ' '
|
||||
|
||||
# get person from sorted database list
|
||||
for person_handle in sorted(
|
||||
handle_list, key=self.sort_on_name_and_grampsid):
|
||||
person = self.r_db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
family_list = person.get_family_handle_list()
|
||||
first_family = True
|
||||
for family_handle in family_list:
|
||||
get_family = self.r_db.get_family_from_handle
|
||||
family = get_family(family_handle)
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
tcell = Html("td", class_="ColumnRowLabel")
|
||||
trow += tcell
|
||||
|
||||
if first or primary_difference(letter,
|
||||
prev_letter,
|
||||
self.rlocale):
|
||||
first = False
|
||||
prev_letter = letter
|
||||
trow.attr = 'class="BeginLetter"'
|
||||
ttle = self._("Families beginning with "
|
||||
"letter ")
|
||||
tcell += Html("a", letter, name=letter,
|
||||
title=ttle + letter,
|
||||
inline=True)
|
||||
else:
|
||||
tcell += ' '
|
||||
|
||||
tcell = Html("td", class_="ColumnPartner")
|
||||
trow += tcell
|
||||
|
||||
if first_family:
|
||||
trow.attr = 'class ="BeginFamily"'
|
||||
|
||||
tcell += self.new_person_link(
|
||||
person_handle, uplink=self.uplink)
|
||||
|
||||
first_family = False
|
||||
else:
|
||||
tcell += ' '
|
||||
|
||||
tcell = Html("td", class_="ColumnPartner")
|
||||
trow += tcell
|
||||
|
||||
tcell += self.family_link(
|
||||
family.get_handle(),
|
||||
self.report.get_family_name(family),
|
||||
family.get_gramps_id(), self.uplink)
|
||||
|
||||
# family events; such as marriage and divorce
|
||||
# events
|
||||
fam_evt_ref_list = family.get_event_ref_list()
|
||||
tcell1 = Html("td", class_="ColumnDate",
|
||||
inline=True)
|
||||
tcell2 = Html("td", class_="ColumnDate",
|
||||
inline=True)
|
||||
trow += (tcell1, tcell2)
|
||||
|
||||
if fam_evt_ref_list:
|
||||
fam_evt_srt_ref_list = sorted(
|
||||
fam_evt_ref_list,
|
||||
key=self.sort_on_grampsid)
|
||||
for evt_ref in fam_evt_srt_ref_list:
|
||||
evt = self.r_db.get_event_from_handle(
|
||||
evt_ref.ref)
|
||||
if evt:
|
||||
evt_type = evt.get_type()
|
||||
if evt_type in [EventType.MARRIAGE,
|
||||
EventType.DIVORCE]:
|
||||
|
||||
cell = self.rlocale.get_date(
|
||||
evt.get_date_object())
|
||||
if (evt_type ==
|
||||
EventType.MARRIAGE):
|
||||
tcell1 += cell
|
||||
else:
|
||||
tcell1 += ' '
|
||||
|
||||
if (evt_type ==
|
||||
EventType.DIVORCE):
|
||||
tcell2 += cell
|
||||
else:
|
||||
tcell2 += ' '
|
||||
else:
|
||||
tcell1 += ' '
|
||||
tcell2 += ' '
|
||||
first_family = False
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(familieslistpage, output_file, sio, ldatec)
|
||||
|
||||
def familypage(self, report, title, family_handle):
|
||||
"""
|
||||
Create a family page
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: family_handle -- The handle for the family to add
|
||||
"""
|
||||
family = report.database.get_family_from_handle(family_handle)
|
||||
if not family:
|
||||
return
|
||||
BasePage.__init__(self, report, title, family.get_gramps_id())
|
||||
ldatec = family.get_change_time()
|
||||
|
||||
self.bibli = Bibliography()
|
||||
self.uplink = True
|
||||
family_name = self.report.get_family_name(family)
|
||||
self.page_title = family_name
|
||||
|
||||
self.familymappages = report.options["familymappages"]
|
||||
|
||||
output_file, sio = self.report.create_file(family.get_handle(), "fam")
|
||||
familydetailpage, head, body = self.write_header(family_name)
|
||||
|
||||
# begin FamilyDetaill division
|
||||
with Html("div", class_="content",
|
||||
id="RelationshipDetail") as relationshipdetail:
|
||||
body += relationshipdetail
|
||||
|
||||
# family media list for initial thumbnail
|
||||
if self.create_media:
|
||||
media_list = family.get_media_list()
|
||||
# If Event pages are not being created, then we need to display
|
||||
# the family event media here
|
||||
if not self.inc_events:
|
||||
for evt_ref in family.get_event_ref_list():
|
||||
event = self.r_db.get_event_from_handle(evt_ref.ref)
|
||||
media_list += event.get_media_list()
|
||||
thumbnail = self.disp_first_img_as_thumbnail(media_list,
|
||||
family)
|
||||
if thumbnail:
|
||||
relationshipdetail += thumbnail
|
||||
|
||||
self.person = None # no longer used
|
||||
|
||||
relationshipdetail += Html(
|
||||
"h2", self.page_title, inline=True) + (
|
||||
Html('sup') + (Html('small') +
|
||||
self.get_citation_links(
|
||||
family.get_citation_list())))
|
||||
|
||||
# display relationships
|
||||
families = self.display_family_relationships(family, None)
|
||||
if families is not None:
|
||||
relationshipdetail += families
|
||||
|
||||
# display additional images as gallery
|
||||
if self.create_media and media_list:
|
||||
addgallery = self.disp_add_img_as_gallery(media_list, family)
|
||||
if addgallery:
|
||||
relationshipdetail += addgallery
|
||||
|
||||
# Narrative subsection
|
||||
notelist = family.get_note_list()
|
||||
if notelist:
|
||||
relationshipdetail += self.display_note_list(notelist)
|
||||
|
||||
# display family LDS ordinance...
|
||||
family_lds_ordinance_list = family.get_lds_ord_list()
|
||||
if family_lds_ordinance_list:
|
||||
relationshipdetail += self.display_lds_ordinance(family)
|
||||
|
||||
# get attribute list
|
||||
attrlist = family.get_attribute_list()
|
||||
if attrlist:
|
||||
attrsection, attrtable = self.display_attribute_header()
|
||||
self.display_attr_list(attrlist, attrtable)
|
||||
relationshipdetail += attrsection
|
||||
|
||||
# source references
|
||||
srcrefs = self.display_ind_sources(family)
|
||||
if srcrefs:
|
||||
relationshipdetail += srcrefs
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(familydetailpage, output_file, sio, ldatec)
|
105
gramps/plugins/webreport/home.py
Normal file
105
gramps/plugins/webreport/home.py
Normal file
@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
HomePage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import FULLCLEAR
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
class HomePage(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the Home page.
|
||||
"""
|
||||
def __init__(self, report, title):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
ldatec = 0
|
||||
|
||||
output_file, sio = self.report.create_file("index")
|
||||
homepage, head, body = self.write_header(self._('Home'))
|
||||
|
||||
# begin home division
|
||||
with Html("div", class_="content", id="Home") as section:
|
||||
body += section
|
||||
|
||||
homeimg = self.add_image('homeimg')
|
||||
if homeimg is not None:
|
||||
section += homeimg
|
||||
|
||||
note_id = report.options['homenote']
|
||||
if note_id:
|
||||
note = self.r_db.get_note_from_gramps_id(note_id)
|
||||
note_text = self.get_note_format(note, False)
|
||||
|
||||
# attach note
|
||||
section += note_text
|
||||
|
||||
# last modification of this note
|
||||
ldatec = note.get_change_time()
|
||||
|
||||
# create clear line for proper styling
|
||||
# create footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(homepage, output_file, sio, ldatec)
|
106
gramps/plugins/webreport/introduction.py
Normal file
106
gramps/plugins/webreport/introduction.py
Normal file
@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
IntroductionPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import FULLCLEAR
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
class IntroductionPage(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information
|
||||
about the introduction page.
|
||||
"""
|
||||
def __init__(self, report, title):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
ldatec = 0
|
||||
|
||||
output_file, sio = self.report.create_file(report.intro_fname)
|
||||
intropage, head, body = self.write_header(self._('Introduction'))
|
||||
|
||||
# begin Introduction division
|
||||
with Html("div", class_="content", id="Introduction") as section:
|
||||
body += section
|
||||
|
||||
introimg = self.add_image('introimg')
|
||||
if introimg is not None:
|
||||
section += introimg
|
||||
|
||||
note_id = report.options['intronote']
|
||||
if note_id:
|
||||
note = self.r_db.get_note_from_gramps_id(note_id)
|
||||
note_text = self.get_note_format(note, False)
|
||||
|
||||
# attach note
|
||||
section += note_text
|
||||
|
||||
# last modification of this note
|
||||
ldatec = note.get_change_time()
|
||||
|
||||
# add clearline for proper styling
|
||||
# create footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(intropage, output_file, sio, ldatec)
|
641
gramps/plugins/webreport/media.py
Normal file
641
gramps/plugins/webreport/media.py
Normal file
@ -0,0 +1,641 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
MediaPage - Media index page and individual Media pages
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
import gc
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from collections import defaultdict
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import (Date, Media)
|
||||
from gramps.gen.plug.report import Bibliography
|
||||
from gramps.gen.utils.file import media_path_full
|
||||
from gramps.gen.utils.thumbnails import run_thumbnailer
|
||||
from gramps.gen.utils.image import image_size # , resize_to_jpeg_buffer
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (FULLCLEAR, _WRONGMEDIAPATH,
|
||||
html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# creates the Media List Page and Media Pages
|
||||
#
|
||||
#################################################
|
||||
class MediaPages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Media'
|
||||
database objects. It displays this information under the 'Individuals'
|
||||
tab. It is told by the 'add_instances' call which 'Media's to display,
|
||||
and remembers the list of persons. A single call to 'display_pages'
|
||||
displays both the Individual List (Index) page and all the Individual
|
||||
pages.
|
||||
|
||||
The base class 'BasePage' is initialised once for each page that is
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
self.media_dict = defaultdict(set)
|
||||
|
||||
def display_pages(self, title):
|
||||
"""
|
||||
Generate and output the pages under the Media tab, namely the media
|
||||
index and the individual media pages.
|
||||
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
LOG.debug("obj_dict[Media]")
|
||||
for item in self.report.obj_dict[Media].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating media pages"),
|
||||
len(self.report.obj_dict[Media]) + 1
|
||||
) as step:
|
||||
# bug 8950 : it seems it's better to sort on desc + gid.
|
||||
def sort_by_desc_and_gid(obj):
|
||||
"""
|
||||
Sort by media description and gramps ID
|
||||
"""
|
||||
return (obj.desc.lower(), obj.gramps_id)
|
||||
|
||||
sorted_media_handles = sorted(
|
||||
self.report.obj_dict[Media].keys(),
|
||||
key=lambda x: sort_by_desc_and_gid(
|
||||
self.r_db.get_media_from_handle(x)))
|
||||
self.medialistpage(self.report, title, sorted_media_handles)
|
||||
|
||||
prev = None
|
||||
total = len(sorted_media_handles)
|
||||
index = 1
|
||||
for handle in sorted_media_handles:
|
||||
gc.collect() # Reduce memory usage when there are many images.
|
||||
next_ = None if index == total else sorted_media_handles[index]
|
||||
step()
|
||||
self.mediapage(self.report, title,
|
||||
handle, (prev, next_, index, total))
|
||||
prev = handle
|
||||
index += 1
|
||||
|
||||
def medialistpage(self, report, title, sorted_media_handles):
|
||||
"""
|
||||
Generate and output the Media index page.
|
||||
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: sorted_media_handles -- A list of the handles of the media to be
|
||||
displayed sorted by the media title
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
output_file, sio = self.report.create_file("media")
|
||||
medialistpage, head, body = self.write_header(self._('Media'))
|
||||
|
||||
ldatec = 0
|
||||
# begin gallery division
|
||||
with Html("div", class_="content", id="Gallery") as medialist:
|
||||
body += medialist
|
||||
|
||||
msg = self._("This page contains an index of all the media objects "
|
||||
"in the database, sorted by their title. Clicking on "
|
||||
"the title will take you to that "
|
||||
"media object’s page. "
|
||||
"If you see media size dimensions "
|
||||
"above an image, click on the "
|
||||
"image to see the full sized version. ")
|
||||
medialist += Html("p", msg, id="description")
|
||||
|
||||
# begin gallery table and table head
|
||||
with Html("table",
|
||||
class_="infolist primobjlist gallerylist") as table:
|
||||
medialist += table
|
||||
|
||||
# begin table head
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow.extend(
|
||||
Html("th", trans, class_=colclass, inline=True)
|
||||
for trans, colclass in [(" ", "ColumnRowLabel"),
|
||||
(self._("Media | Name"),
|
||||
"ColumnName"),
|
||||
(self._("Date"), "ColumnDate"),
|
||||
(self._("Mime Type"), "ColumnMime")]
|
||||
)
|
||||
|
||||
# begin table body
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
index = 1
|
||||
for media_handle in sorted_media_handles:
|
||||
media = self.r_db.get_media_from_handle(media_handle)
|
||||
if media:
|
||||
if media.get_change_time() > ldatec:
|
||||
ldatec = media.get_change_time()
|
||||
title = media.get_description() or "[untitled]"
|
||||
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
media_data_row = [
|
||||
[index, "ColumnRowLabel"],
|
||||
[self.media_ref_link(media_handle,
|
||||
title), "ColumnName"],
|
||||
[self.rlocale.get_date(media.get_date_object()),
|
||||
"ColumnDate"],
|
||||
[media.get_mime_type(), "ColumnMime"]]
|
||||
|
||||
trow.extend(
|
||||
Html("td", data, class_=colclass)
|
||||
for data, colclass in media_data_row
|
||||
)
|
||||
index += 1
|
||||
|
||||
def sort_by_desc_and_gid(obj):
|
||||
"""
|
||||
Sort by media description and gramps ID
|
||||
"""
|
||||
return (obj.desc, obj.gramps_id)
|
||||
|
||||
unused_media_handles = []
|
||||
if self.create_unused_media:
|
||||
# add unused media
|
||||
media_list = self.r_db.get_media_handles()
|
||||
for media_ref in media_list:
|
||||
if media_ref not in self.report.obj_dict[Media]:
|
||||
unused_media_handles.append(media_ref)
|
||||
unused_media_handles = sorted(
|
||||
unused_media_handles,
|
||||
key=lambda x: sort_by_desc_and_gid(
|
||||
self.r_db.get_media_from_handle(x)))
|
||||
|
||||
idx = 1
|
||||
prev = None
|
||||
total = len(unused_media_handles)
|
||||
if total > 0:
|
||||
trow += Html("tr")
|
||||
trow.extend(
|
||||
Html("td", Html("h4", " "), inline=True) +
|
||||
Html("td",
|
||||
Html("h4",
|
||||
self._("Below unused media objects"),
|
||||
inline=True),
|
||||
class_="") +
|
||||
Html("td", Html("h4", " "), inline=True) +
|
||||
Html("td", Html("h4", " "), inline=True)
|
||||
)
|
||||
for media_handle in unused_media_handles:
|
||||
media = self.r_db.get_media_from_handle(media_handle)
|
||||
gc.collect() # Reduce memory usage when many images.
|
||||
next_ = None if idx == total else unused_media_handles[idx]
|
||||
trow += Html("tr")
|
||||
media_data_row = [
|
||||
[index, "ColumnRowLabel"],
|
||||
[self.media_ref_link(media_handle,
|
||||
media.get_description()),
|
||||
"ColumnName"],
|
||||
[self.rlocale.get_date(media.get_date_object()),
|
||||
"ColumnDate"],
|
||||
[media.get_mime_type(), "ColumnMime"]]
|
||||
trow.extend(
|
||||
Html("td", data, class_=colclass)
|
||||
for data, colclass in media_data_row
|
||||
)
|
||||
self.mediapage(self.report, title,
|
||||
media_handle, (prev, next_, index, total))
|
||||
prev = media_handle
|
||||
index += 1
|
||||
idx += 1
|
||||
|
||||
# add footer section
|
||||
# add clearline for proper styling
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(medialistpage, output_file, sio, ldatec)
|
||||
|
||||
def media_ref_link(self, handle, name, uplink=False):
|
||||
"""
|
||||
Create a reference link to a media
|
||||
|
||||
@param: handle -- The media handle
|
||||
@param: name -- The name to use for the link
|
||||
@param: uplink -- If True, then "../../../" is inserted in front of the
|
||||
result.
|
||||
"""
|
||||
# get media url
|
||||
url = self.report.build_url_fname_html(handle, "img", uplink)
|
||||
|
||||
# get name
|
||||
name = html_escape(name)
|
||||
|
||||
# begin hyper link
|
||||
hyper = Html("a", name, href=url, title=name)
|
||||
|
||||
# return hyperlink to its callers
|
||||
return hyper
|
||||
|
||||
def mediapage(self, report, title, media_handle, info):
|
||||
"""
|
||||
Generate and output an individual Media page.
|
||||
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: media_handle -- The media handle to use
|
||||
@param: info -- A tuple containing the media handle for the
|
||||
next and previous media, the current page
|
||||
number, and the total number of media pages
|
||||
"""
|
||||
media = report.database.get_media_from_handle(media_handle)
|
||||
BasePage.__init__(self, report, title, media.gramps_id)
|
||||
(prev, next_, page_number, total_pages) = info
|
||||
|
||||
ldatec = media.get_change_time()
|
||||
|
||||
# get media rectangles
|
||||
_region_items = self.media_ref_rect_regions(media_handle)
|
||||
|
||||
output_file, sio = self.report.create_file(media_handle, "img")
|
||||
self.uplink = True
|
||||
|
||||
self.bibli = Bibliography()
|
||||
|
||||
# get media type to be used primarily with "img" tags
|
||||
mime_type = media.get_mime_type()
|
||||
#mtype = get_description(mime_type)
|
||||
|
||||
if mime_type:
|
||||
#note_only = False
|
||||
newpath = self.copy_source_file(media_handle, media)
|
||||
target_exists = newpath is not None
|
||||
else:
|
||||
#note_only = True
|
||||
target_exists = False
|
||||
|
||||
self.copy_thumbnail(media_handle, media)
|
||||
self.page_title = media.get_description()
|
||||
esc_page_title = html_escape(self.page_title)
|
||||
(mediapage, head,
|
||||
body) = self.write_header("%s - %s" % (self._("Media"),
|
||||
self.page_title))
|
||||
|
||||
# if there are media rectangle regions, attach behaviour style sheet
|
||||
if _region_items:
|
||||
|
||||
fname = "/".join(["css", "behaviour.css"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
|
||||
# begin MediaDetail division
|
||||
with Html("div", class_="content", id="GalleryDetail") as mediadetail:
|
||||
body += mediadetail
|
||||
|
||||
# media navigation
|
||||
with Html("div", id="GalleryNav", role="navigation") as medianav:
|
||||
mediadetail += medianav
|
||||
if prev:
|
||||
medianav += self.media_nav_link(prev,
|
||||
self._("Previous"), True)
|
||||
data = self._('%(strong1_strt)s%(page_number)d%(strong_end)s '
|
||||
'of %(strong2_strt)s%(total_pages)d%(strong_end)s'
|
||||
) % {'strong1_strt' :
|
||||
'<strong id="GalleryCurrent">',
|
||||
'strong2_strt' : '<strong id="GalleryTotal">',
|
||||
'strong_end' : '</strong>',
|
||||
'page_number' : page_number,
|
||||
'total_pages' : total_pages}
|
||||
medianav += Html("span", data, id="GalleryPages")
|
||||
if next_:
|
||||
medianav += self.media_nav_link(next_, self._("Next"), True)
|
||||
|
||||
# missing media error message
|
||||
errormsg = self._("The file has been moved or deleted.")
|
||||
|
||||
# begin summaryarea division
|
||||
with Html("div", id="summaryarea") as summaryarea:
|
||||
mediadetail += summaryarea
|
||||
if mime_type:
|
||||
if mime_type.startswith("image"):
|
||||
if not target_exists:
|
||||
with Html("div", id="MediaDisplay") as mediadisplay:
|
||||
summaryarea += mediadisplay
|
||||
mediadisplay += Html("span", errormsg,
|
||||
class_="MissingImage")
|
||||
|
||||
else:
|
||||
# Check how big the image is relative to the
|
||||
# requested 'initial' image size.
|
||||
# If it's significantly bigger, scale it down to
|
||||
# improve the site's responsiveness. We don't want
|
||||
# the user to have to await a large download
|
||||
# unnecessarily. Either way, set the display image
|
||||
# size as requested.
|
||||
orig_image_path = media_path_full(self.r_db,
|
||||
media.get_path())
|
||||
#mtime = os.stat(orig_image_path).st_mtime
|
||||
(width, height) = image_size(orig_image_path)
|
||||
max_width = self.report.options[
|
||||
'maxinitialimagewidth']
|
||||
max_height = self.report.options[
|
||||
'maxinitialimageheight']
|
||||
if width != 0 and height != 0:
|
||||
scale_w = (float(max_width)/width) or 1
|
||||
# the 'or 1' is so that a max of
|
||||
# zero is ignored
|
||||
scale_h = (float(max_height)/height) or 1
|
||||
else:
|
||||
scale_w = 1.0
|
||||
scale_h = 1.0
|
||||
scale = min(scale_w, scale_h, 1.0)
|
||||
new_width = int(width*scale)
|
||||
new_height = int(height*scale)
|
||||
|
||||
# TODO. Convert disk path to URL.
|
||||
url = self.report.build_url_fname(orig_image_path,
|
||||
None, self.uplink)
|
||||
with Html("div", id="GalleryDisplay",
|
||||
style='width: %dpx; height: %dpx' % (
|
||||
new_width,
|
||||
new_height)) as mediadisplay:
|
||||
summaryarea += mediadisplay
|
||||
|
||||
# Feature #2634; display the mouse-selectable
|
||||
# regions. See the large block at the top of
|
||||
# this function where the various regions are
|
||||
# stored in _region_items
|
||||
if _region_items:
|
||||
ordered = Html("ol", class_="RegionBox")
|
||||
mediadisplay += ordered
|
||||
while len(_region_items) > 0:
|
||||
(name, coord_x, coord_y,
|
||||
width, height, linkurl
|
||||
) = _region_items.pop()
|
||||
ordered += Html(
|
||||
"li",
|
||||
style="left:%d%%; "
|
||||
"top:%d%%; "
|
||||
"width:%d%%; "
|
||||
"height:%d%%;" % (
|
||||
coord_x, coord_y,
|
||||
width, height)) + (
|
||||
Html("a", name,
|
||||
href=linkurl)
|
||||
)
|
||||
|
||||
# display the image
|
||||
if orig_image_path != newpath:
|
||||
url = self.report.build_url_fname(
|
||||
newpath, None, self.uplink)
|
||||
mediadisplay += Html("a", href=url) + (
|
||||
Html("img", width=new_width,
|
||||
height=new_height, src=url,
|
||||
alt=esc_page_title)
|
||||
)
|
||||
else:
|
||||
dirname = tempfile.mkdtemp()
|
||||
thmb_path = os.path.join(dirname, "document.png")
|
||||
if run_thumbnailer(mime_type,
|
||||
media_path_full(self.r_db,
|
||||
media.get_path()),
|
||||
thmb_path, 320):
|
||||
try:
|
||||
path = self.report.build_path(
|
||||
"preview", media.get_handle())
|
||||
npath = os.path.join(path, media.get_handle())
|
||||
npath += ".png"
|
||||
self.report.copy_file(thmb_path, npath)
|
||||
path = npath
|
||||
os.unlink(thmb_path)
|
||||
except EnvironmentError:
|
||||
path = os.path.join("images", "document.png")
|
||||
else:
|
||||
path = os.path.join("images", "document.png")
|
||||
os.rmdir(dirname)
|
||||
|
||||
with Html("div", id="GalleryDisplay") as mediadisplay:
|
||||
summaryarea += mediadisplay
|
||||
|
||||
img_url = self.report.build_url_fname(path,
|
||||
None,
|
||||
self.uplink)
|
||||
if target_exists:
|
||||
# TODO. Convert disk path to URL
|
||||
url = self.report.build_url_fname(newpath,
|
||||
None,
|
||||
self.uplink)
|
||||
hyper = Html("a", href=url,
|
||||
title=esc_page_title) + (
|
||||
Html("img", src=img_url,
|
||||
alt=esc_page_title)
|
||||
)
|
||||
mediadisplay += hyper
|
||||
else:
|
||||
mediadisplay += Html("span", errormsg,
|
||||
class_="MissingImage")
|
||||
else:
|
||||
with Html("div", id="GalleryDisplay") as mediadisplay:
|
||||
summaryarea += mediadisplay
|
||||
url = self.report.build_url_image("document.png",
|
||||
"images", self.uplink)
|
||||
mediadisplay += Html("img", src=url,
|
||||
alt=esc_page_title,
|
||||
title=esc_page_title)
|
||||
|
||||
# media title
|
||||
title = Html("h3", html_escape(self.page_title.strip()),
|
||||
inline=True)
|
||||
summaryarea += title
|
||||
|
||||
# begin media table
|
||||
with Html("table", class_="infolist gallery") as table:
|
||||
summaryarea += table
|
||||
|
||||
# Gramps ID
|
||||
media_gid = media.gramps_id
|
||||
if not self.noid and media_gid:
|
||||
trow = Html("tr") + (
|
||||
Html("td", self._("Gramps ID"),
|
||||
class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", media_gid, class_="ColumnValue",
|
||||
inline=True)
|
||||
)
|
||||
table += trow
|
||||
|
||||
# mime type
|
||||
if mime_type:
|
||||
trow = Html("tr") + (
|
||||
Html("td", self._("File Type"),
|
||||
class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", mime_type, class_="ColumnValue",
|
||||
inline=True)
|
||||
)
|
||||
table += trow
|
||||
|
||||
# media date
|
||||
date = media.get_date_object()
|
||||
if date and date is not Date.EMPTY:
|
||||
trow = Html("tr") + (
|
||||
Html("td", self._("Date"), class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", self.rlocale.get_date(date),
|
||||
class_="ColumnValue",
|
||||
inline=True)
|
||||
)
|
||||
table += trow
|
||||
|
||||
# get media notes
|
||||
notelist = self.display_note_list(media.get_note_list())
|
||||
if notelist is not None:
|
||||
mediadetail += notelist
|
||||
|
||||
# get attribute list
|
||||
attrlist = media.get_attribute_list()
|
||||
if attrlist:
|
||||
attrsection, attrtable = self.display_attribute_header()
|
||||
self.display_attr_list(attrlist, attrtable)
|
||||
mediadetail += attrsection
|
||||
|
||||
# get media sources
|
||||
srclist = self.display_media_sources(media)
|
||||
if srclist is not None:
|
||||
mediadetail += srclist
|
||||
|
||||
# get media references
|
||||
reflist = self.display_bkref_list(Media, media_handle)
|
||||
if reflist is not None:
|
||||
mediadetail += reflist
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(mediapage, output_file, sio, ldatec)
|
||||
|
||||
def media_nav_link(self, handle, name, uplink=False):
|
||||
"""
|
||||
Creates the Media Page Navigation hyperlinks for Next and Prev
|
||||
"""
|
||||
url = self.report.build_url_fname_html(handle, "img", uplink)
|
||||
name = html_escape(name)
|
||||
return Html("a", name, name=name, id=name, href=url,
|
||||
title=name, inline=True)
|
||||
|
||||
def display_media_sources(self, photo):
|
||||
"""
|
||||
Display media sources
|
||||
|
||||
@param: photo -- The source object (image, pdf, ...)
|
||||
"""
|
||||
list(map(
|
||||
lambda i: self.bibli.add_reference(
|
||||
self.r_db.get_citation_from_handle(i)),
|
||||
photo.get_citation_list()))
|
||||
sourcerefs = self.display_source_refs(self.bibli)
|
||||
|
||||
# return source references to its caller
|
||||
return sourcerefs
|
||||
|
||||
def copy_source_file(self, handle, photo):
|
||||
"""
|
||||
Copy source file in the web tree.
|
||||
|
||||
@param: handle -- Handle of the source
|
||||
@param: photo -- The source object (image, pdf, ...)
|
||||
"""
|
||||
ext = os.path.splitext(photo.get_path())[1]
|
||||
to_dir = self.report.build_path('images', handle)
|
||||
newpath = os.path.join(to_dir, handle) + ext
|
||||
|
||||
fullpath = media_path_full(self.r_db, photo.get_path())
|
||||
if not os.path.isfile(fullpath):
|
||||
_WRONGMEDIAPATH.append([photo.get_gramps_id(), fullpath])
|
||||
return None
|
||||
try:
|
||||
mtime = os.stat(fullpath).st_mtime
|
||||
if self.report.archive:
|
||||
self.report.archive.add(fullpath, str(newpath))
|
||||
else:
|
||||
to_dir = os.path.join(self.html_dir, to_dir)
|
||||
if not os.path.isdir(to_dir):
|
||||
os.makedirs(to_dir)
|
||||
new_file = os.path.join(self.html_dir, newpath)
|
||||
shutil.copyfile(fullpath, new_file)
|
||||
os.utime(new_file, (mtime, mtime))
|
||||
return newpath
|
||||
except (IOError, OSError) as msg:
|
||||
error = _("Missing media object:"
|
||||
) + "%s (%s)" % (photo.get_description(),
|
||||
photo.get_gramps_id())
|
||||
self.r_user.warn(error, str(msg))
|
||||
return None
|
File diff suppressed because it is too large
Load Diff
1790
gramps/plugins/webreport/person.py
Normal file
1790
gramps/plugins/webreport/person.py
Normal file
File diff suppressed because it is too large
Load Diff
451
gramps/plugins/webreport/place.py
Normal file
451
gramps/plugins/webreport/place.py
Normal file
@ -0,0 +1,451 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
PlacePage - Place index page and individual Place pages
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from collections import defaultdict
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import (PlaceType, Place)
|
||||
from gramps.gen.plug.report import Bibliography
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
from gramps.gen.utils.place import conv_lat_lon
|
||||
from gramps.gen.utils.location import get_main_location
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (get_first_letters, first_letter,
|
||||
alphabet_navigation, GOOGLE_MAPS,
|
||||
primary_difference, _KEYPLACE,
|
||||
get_index_letter, FULLCLEAR,
|
||||
MARKER_PATH, OSM_MARKERS, MARKERS,
|
||||
html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
######################################################
|
||||
# #
|
||||
# Place Pages #
|
||||
# #
|
||||
######################################################
|
||||
class PlacePages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Person'
|
||||
database objects. It displays this information under the 'Events'
|
||||
tab. It is told by the 'add_instances' call which 'Person's to display,
|
||||
and remembers the list of persons. A single call to 'display_pages'
|
||||
displays both the Event List (Index) page and all the Event
|
||||
pages.
|
||||
|
||||
The base class 'BasePage' is initialised once for each page that is
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
self.place_dict = defaultdict(set)
|
||||
self.placemappages = None
|
||||
self.mapservice = None
|
||||
self.person = None
|
||||
self.familymappages = None
|
||||
self.googlemapkey = None
|
||||
|
||||
def display_pages(self, title):
|
||||
"""
|
||||
Generate and output the pages under the Place tab, namely the place
|
||||
index and the individual place pages.
|
||||
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
LOG.debug("obj_dict[Place]")
|
||||
for item in self.report.obj_dict[Place].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating place pages"),
|
||||
len(self.report.obj_dict[Place]) + 1
|
||||
) as step:
|
||||
|
||||
self.placelistpage(self.report, title,
|
||||
self.report.obj_dict[Place].keys())
|
||||
|
||||
for place_handle in self.report.obj_dict[Place]:
|
||||
step()
|
||||
self.placepage(self.report, title, place_handle)
|
||||
|
||||
def placelistpage(self, report, title, place_handles):
|
||||
"""
|
||||
Create a place index
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: place_handles -- The handle for the place to add
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
output_file, sio = self.report.create_file("places")
|
||||
placelistpage, head, body = self.write_header(self._("Places"))
|
||||
ldatec = 0
|
||||
prev_letter = " "
|
||||
|
||||
# begin places division
|
||||
with Html("div", class_="content", id="Places") as placelist:
|
||||
body += placelist
|
||||
|
||||
# place list page message
|
||||
msg = self._("This page contains an index of all the places in the "
|
||||
"database, sorted by their title. "
|
||||
"Clicking on a place’s "
|
||||
"title will take you to that place’s page.")
|
||||
placelist += Html("p", msg, id="description")
|
||||
|
||||
# begin alphabet navigation
|
||||
index_list = get_first_letters(self.r_db, place_handles,
|
||||
_KEYPLACE, rlocale=self.rlocale)
|
||||
alpha_nav = alphabet_navigation(index_list, self.rlocale)
|
||||
if alpha_nav is not None:
|
||||
placelist += alpha_nav
|
||||
|
||||
# begin places table and table head
|
||||
with Html("table",
|
||||
class_="infolist primobjlist placelist") as table:
|
||||
placelist += table
|
||||
|
||||
# begin table head
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow.extend(
|
||||
Html("th", label, class_=colclass, inline=True)
|
||||
for (label, colclass) in [
|
||||
[self._("Letter"), "ColumnLetter"],
|
||||
[self._("Place Name | Name"), "ColumnName"],
|
||||
[self._("State/ Province"), "ColumnState"],
|
||||
[self._("Country"), "ColumnCountry"],
|
||||
[self._("Latitude"), "ColumnLatitude"],
|
||||
[self._("Longitude"), "ColumnLongitude"]
|
||||
]
|
||||
)
|
||||
|
||||
# bug 9495 : incomplete display of place hierarchy labels
|
||||
def sort_by_place_name(obj):
|
||||
""" sort by lower case place name. """
|
||||
name = self.report.obj_dict[Place][obj][1]
|
||||
return name.lower()
|
||||
|
||||
handle_list = sorted(place_handles,
|
||||
key=lambda x: sort_by_place_name(x))
|
||||
first = True
|
||||
|
||||
# begin table body
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
for place_handle in handle_list:
|
||||
place = self.r_db.get_place_from_handle(place_handle)
|
||||
if place:
|
||||
if place.get_change_time() > ldatec:
|
||||
ldatec = place.get_change_time()
|
||||
plc_title = self.report.obj_dict[Place][place_handle][1]
|
||||
main_location = get_main_location(self.r_db, place)
|
||||
|
||||
if plc_title and plc_title != " ":
|
||||
letter = get_index_letter(first_letter(plc_title),
|
||||
index_list,
|
||||
self.rlocale)
|
||||
else:
|
||||
letter = ' '
|
||||
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
tcell = Html("td", class_="ColumnLetter", inline=True)
|
||||
trow += tcell
|
||||
if first or primary_difference(letter, prev_letter,
|
||||
self.rlocale):
|
||||
first = False
|
||||
prev_letter = letter
|
||||
trow.attr = 'class = "BeginLetter"'
|
||||
|
||||
ttle = self._("Places beginning "
|
||||
"with letter %s") % letter
|
||||
tcell += Html("a", letter, name=letter, title=ttle)
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
trow += Html("td",
|
||||
self.place_link(
|
||||
place.get_handle(),
|
||||
plc_title, place.get_gramps_id()),
|
||||
class_="ColumnName")
|
||||
|
||||
trow.extend(
|
||||
Html("td", data or " ", class_=colclass,
|
||||
inline=True)
|
||||
for (colclass, data) in [
|
||||
["ColumnState",
|
||||
main_location.get(PlaceType.STATE, '')],
|
||||
["ColumnCountry",
|
||||
main_location.get(PlaceType.COUNTRY, '')]
|
||||
]
|
||||
)
|
||||
|
||||
tcell1 = Html("td", class_="ColumnLatitude",
|
||||
inline=True)
|
||||
tcell2 = Html("td", class_="ColumnLongitude",
|
||||
inline=True)
|
||||
trow += (tcell1, tcell2)
|
||||
|
||||
if place.lat and place.long:
|
||||
latitude, longitude = conv_lat_lon(place.lat,
|
||||
place.long,
|
||||
"DEG")
|
||||
tcell1 += latitude
|
||||
tcell2 += longitude
|
||||
else:
|
||||
tcell1 += ' '
|
||||
tcell2 += ' '
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(placelistpage, output_file, sio, ldatec)
|
||||
|
||||
def placepage(self, report, title, place_handle):
|
||||
"""
|
||||
Create a place page
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: place_handle -- The handle for the place to add
|
||||
"""
|
||||
place = report.database.get_place_from_handle(place_handle)
|
||||
if not place:
|
||||
return None
|
||||
BasePage.__init__(self, report, title, place.get_gramps_id())
|
||||
self.bibli = Bibliography()
|
||||
place_name = self.report.obj_dict[Place][place_handle][1]
|
||||
ldatec = place.get_change_time()
|
||||
|
||||
output_file, sio = self.report.create_file(place_handle, "plc")
|
||||
self.uplink = True
|
||||
self.page_title = place_name
|
||||
placepage, head, body = self.write_header(_("Places"))
|
||||
|
||||
self.placemappages = self.report.options['placemappages']
|
||||
self.mapservice = self.report.options['mapservice']
|
||||
self.googlemapkey = self.report.options['googlemapkey']
|
||||
|
||||
# begin PlaceDetail Division
|
||||
with Html("div", class_="content", id="PlaceDetail") as placedetail:
|
||||
body += placedetail
|
||||
|
||||
if self.create_media:
|
||||
media_list = place.get_media_list()
|
||||
thumbnail = self.disp_first_img_as_thumbnail(media_list,
|
||||
place)
|
||||
if thumbnail is not None:
|
||||
placedetail += thumbnail
|
||||
|
||||
# add section title
|
||||
placedetail += Html("h3",
|
||||
html_escape(place_name),
|
||||
inline=True)
|
||||
|
||||
# begin summaryarea division and places table
|
||||
with Html("div", id='summaryarea') as summaryarea:
|
||||
placedetail += summaryarea
|
||||
|
||||
with Html("table", class_="infolist place") as table:
|
||||
summaryarea += table
|
||||
|
||||
# list the place fields
|
||||
self.dump_place(place, table)
|
||||
|
||||
# place gallery
|
||||
if self.create_media:
|
||||
placegallery = self.disp_add_img_as_gallery(media_list, place)
|
||||
if placegallery is not None:
|
||||
placedetail += placegallery
|
||||
|
||||
# place notes
|
||||
notelist = self.display_note_list(place.get_note_list())
|
||||
if notelist is not None:
|
||||
placedetail += notelist
|
||||
|
||||
# place urls
|
||||
urllinks = self.display_url_list(place.get_url_list())
|
||||
if urllinks is not None:
|
||||
placedetail += urllinks
|
||||
|
||||
# add place map here
|
||||
# Link to Gramps marker
|
||||
fname = "/".join(['images', 'marker.png'])
|
||||
marker_path = self.report.build_url_image("marker.png",
|
||||
"images", self.uplink)
|
||||
|
||||
if self.placemappages:
|
||||
if place and (place.lat and place.long):
|
||||
latitude, longitude = conv_lat_lon(place.get_latitude(),
|
||||
place.get_longitude(),
|
||||
"D.D8")
|
||||
placetitle = place_name
|
||||
|
||||
# add narrative-maps CSS...
|
||||
fname = "/".join(["css", "narrative-maps.css"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
|
||||
# add MapService specific javascript code
|
||||
src_js = GOOGLE_MAPS + "api/js?sensor=false"
|
||||
if self.mapservice == "Google":
|
||||
if self.googlemapkey:
|
||||
src_js += "&key=" + self.googlemapkey
|
||||
head += Html("script", type="text/javascript",
|
||||
src=src_js, inline=True)
|
||||
else:
|
||||
url = self.secure_mode
|
||||
url += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
|
||||
"css/bootstrap.min.css")
|
||||
head += Html("link", href=url, type="text/javascript",
|
||||
rel="stylesheet")
|
||||
src_js = self.secure_mode
|
||||
src_js += ("ajax.googleapis.com/ajax/libs/jquery/1.9.1/"
|
||||
"jquery.min.js")
|
||||
head += Html("script", type="text/javascript",
|
||||
src=src_js, inline=True)
|
||||
src_js = self.secure_mode
|
||||
src_js += "openlayers.org/en/v3.17.1/build/ol.js"
|
||||
head += Html("script", type="text/javascript",
|
||||
src=src_js, inline=True)
|
||||
url = self.secure_mode
|
||||
url += "openlayers.org/en/v3.17.1/css/ol.css"
|
||||
head += Html("link", href=url, type="text/javascript",
|
||||
rel="stylesheet")
|
||||
src_js = self.secure_mode
|
||||
src_js += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
|
||||
"js/bootstrap.min.js")
|
||||
head += Html("script", type="text/javascript",
|
||||
src=src_js, inline=True)
|
||||
|
||||
# section title
|
||||
placedetail += Html("h4", self._("Place Map"), inline=True)
|
||||
|
||||
# begin map_canvas division
|
||||
with Html("div", id="map_canvas", inline=True) as canvas:
|
||||
placedetail += canvas
|
||||
|
||||
# Begin inline javascript code because jsc is a
|
||||
# docstring, it does NOT have to be properly indented
|
||||
if self.mapservice == "Google":
|
||||
with Html("script", type="text/javascript",
|
||||
indent=False) as jsc:
|
||||
head += jsc
|
||||
|
||||
# Google adds Latitude/ Longitude to its maps...
|
||||
plce = placetitle.replace("'", "\\'")
|
||||
jsc += MARKER_PATH % marker_path
|
||||
jsc += MARKERS % ([[plce,
|
||||
latitude,
|
||||
longitude,
|
||||
1]],
|
||||
latitude, longitude,
|
||||
10)
|
||||
|
||||
else:
|
||||
# OpenStreetMap (OSM) adds Longitude/ Latitude
|
||||
# to its maps, and needs a country code in
|
||||
# lowercase letters...
|
||||
with Html("script", type="text/javascript") as jsc:
|
||||
canvas += jsc
|
||||
#param1 = xml_lang()[3:5].lower()
|
||||
jsc += MARKER_PATH % marker_path
|
||||
jsc += OSM_MARKERS % ([[float(longitude),
|
||||
float(latitude),
|
||||
placetitle]],
|
||||
longitude, latitude, 10)
|
||||
|
||||
# add javascript function call to body element
|
||||
body.attr += ' onload = "initialize();" '
|
||||
|
||||
# add div for popups.
|
||||
with Html("div", id="popup", inline=True) as popup:
|
||||
placedetail += popup
|
||||
|
||||
# source references
|
||||
srcrefs = self.display_ind_sources(place)
|
||||
if srcrefs is not None:
|
||||
placedetail += srcrefs
|
||||
|
||||
# References list
|
||||
ref_list = self.display_bkref_list(Place, place_handle)
|
||||
if ref_list is not None:
|
||||
placedetail += ref_list
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(placepage, output_file, sio, ldatec)
|
287
gramps/plugins/webreport/repository.py
Normal file
287
gramps/plugins/webreport/repository.py
Normal file
@ -0,0 +1,287 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
RepositoryPage - Repository index page and individual Repository pages
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from collections import defaultdict
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import Repository
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (FULLCLEAR, html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# creates the Repository List Page and Repository Pages
|
||||
#
|
||||
#################################################
|
||||
class RepositoryPages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Repository'
|
||||
database objects. It displays this information under the 'Individuals'
|
||||
tab. It is told by the 'add_instances' call which 'Repository's to display,
|
||||
and remembers the list of persons. A single call to 'display_pages'
|
||||
displays both the Individual List (Index) page and all the Individual
|
||||
pages.
|
||||
|
||||
The base class 'BasePage' is initialised once for each page that is
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
self.repos_dict = defaultdict(set)
|
||||
|
||||
def display_pages(self, title):
|
||||
"""
|
||||
Generate and output the pages under the Repository tab, namely the
|
||||
repository index and the individual repository pages.
|
||||
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
LOG.debug("obj_dict[Person]")
|
||||
for item in self.report.obj_dict[Repository].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
|
||||
# set progress bar pass for Repositories
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_('Creating repository pages'),
|
||||
len(self.report.obj_dict[Repository]) + 1
|
||||
) as step:
|
||||
# Sort the repositories
|
||||
repos_dict = {}
|
||||
for repo_handle in self.report.obj_dict[Repository]:
|
||||
repository = self.r_db.get_repository_from_handle(repo_handle)
|
||||
key = repository.get_name() + str(repository.get_gramps_id())
|
||||
repos_dict[key] = (repository, repo_handle)
|
||||
|
||||
keys = sorted(repos_dict, key=self.rlocale.sort_key)
|
||||
|
||||
# RepositoryListPage Class
|
||||
self.repositorylistpage(self.report, title, repos_dict, keys)
|
||||
|
||||
for index, key in enumerate(keys):
|
||||
(repo, handle) = repos_dict[key]
|
||||
|
||||
step()
|
||||
self.repositorypage(self.report, title, repo, handle)
|
||||
|
||||
def repositorylistpage(self, report, title, repos_dict, keys):
|
||||
"""
|
||||
Create Index for repositories
|
||||
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: repos_dict -- The dictionary for all repositories
|
||||
@param: keys -- The keys used to access repositories
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
#inc_repos = self.report.options["inc_repository"]
|
||||
|
||||
output_file, sio = self.report.create_file("repositories")
|
||||
repolistpage, head, body = self.write_header(_("Repositories"))
|
||||
|
||||
ldatec = 0
|
||||
# begin RepositoryList division
|
||||
with Html("div", class_="content",
|
||||
id="RepositoryList") as repositorylist:
|
||||
body += repositorylist
|
||||
|
||||
msg = self._("This page contains an index of "
|
||||
"all the repositories in the "
|
||||
"database, sorted by their title. "
|
||||
"Clicking on a repositories’s title "
|
||||
"will take you to that repositories’s page.")
|
||||
repositorylist += Html("p", msg, id="description")
|
||||
|
||||
# begin repositories table and table head
|
||||
with Html("table", class_="infolist primobjlist repolist") as table:
|
||||
repositorylist += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr") + (
|
||||
Html("th", " ", class_="ColumnRowLabel", inline=True),
|
||||
Html("th", self._("Type"), class_="ColumnType",
|
||||
inline=True),
|
||||
Html("th", self._("Repository |Name"), class_="ColumnName",
|
||||
inline=True)
|
||||
)
|
||||
thead += trow
|
||||
|
||||
# begin table body
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
for index, key in enumerate(keys):
|
||||
(repo, handle) = repos_dict[key]
|
||||
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
# index number
|
||||
trow += Html("td", index + 1, class_="ColumnRowLabel",
|
||||
inline=True)
|
||||
|
||||
# repository type
|
||||
rtype = self._(repo.type.xml_str())
|
||||
trow += Html("td", rtype, class_="ColumnType", inline=True)
|
||||
|
||||
# repository name and hyperlink
|
||||
if repo.get_name():
|
||||
trow += Html("td",
|
||||
self.repository_link(handle,
|
||||
repo.get_name(),
|
||||
repo.get_gramps_id(),
|
||||
self.uplink),
|
||||
class_="ColumnName")
|
||||
ldatec = repo.get_change_time()
|
||||
else:
|
||||
trow += Html("td", "[ untitled ]", class_="ColumnName")
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(repolistpage, output_file, sio, ldatec)
|
||||
|
||||
def repositorypage(self, report, title, repo, handle):
|
||||
"""
|
||||
Create one page for one repository.
|
||||
|
||||
@param: report -- The instance of the main report class for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: repo -- the repository to use
|
||||
@param: handle -- the handle to use
|
||||
"""
|
||||
gid = repo.get_gramps_id()
|
||||
BasePage.__init__(self, report, title, gid)
|
||||
ldatec = repo.get_change_time()
|
||||
|
||||
output_file, sio = self.report.create_file(handle, 'repo')
|
||||
self.uplink = True
|
||||
repositorypage, head, body = self.write_header(_('Repositories'))
|
||||
|
||||
# begin RepositoryDetail division and page title
|
||||
with Html("div", class_="content",
|
||||
id="RepositoryDetail") as repositorydetail:
|
||||
body += repositorydetail
|
||||
|
||||
# repository name
|
||||
repositorydetail += Html("h3", html_escape(repo.name),
|
||||
inline=True)
|
||||
|
||||
# begin repository table
|
||||
with Html("table", class_="infolist repolist") as table:
|
||||
repositorydetail += table
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
if not self.noid and gid:
|
||||
trow = Html("tr") + (
|
||||
Html("td", self._("Gramps ID"),
|
||||
class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", gid, class_="ColumnValue", inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
trow = Html("tr") + (
|
||||
Html("td", self._("Type"), class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", self._(repo.get_type().xml_str()),
|
||||
class_="ColumnValue",
|
||||
inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
# repository: address(es)...
|
||||
# repository addresses do NOT have Sources
|
||||
repo_address = self.display_addr_list(repo.get_address_list(),
|
||||
False)
|
||||
if repo_address is not None:
|
||||
repositorydetail += repo_address
|
||||
|
||||
# repository: urllist
|
||||
urllist = self.display_url_list(repo.get_url_list())
|
||||
if urllist is not None:
|
||||
repositorydetail += urllist
|
||||
|
||||
# reposity: notelist
|
||||
notelist = self.display_note_list(repo.get_note_list())
|
||||
if notelist is not None:
|
||||
repositorydetail += notelist
|
||||
|
||||
# display Repository Referenced Sources...
|
||||
ref_list = self.display_bkref_list(Repository, repo.get_handle())
|
||||
if ref_list is not None:
|
||||
repositorydetail += ref_list
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(repositorypage, output_file, sio, ldatec)
|
306
gramps/plugins/webreport/source.py
Normal file
306
gramps/plugins/webreport/source.py
Normal file
@ -0,0 +1,306 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
SourcePage - Source index page and individual Source pages
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from collections import defaultdict
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import Source
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (FULLCLEAR, html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb.source")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# creates the Source List Page and Source Pages
|
||||
#
|
||||
#################################################
|
||||
class SourcePages(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about the 'Source'
|
||||
database objects. It displays this information under the 'Sources'
|
||||
tab. It is told by the 'add_instances' call which 'Source's to display,
|
||||
and remembers the list of persons. A single call to 'display_pages'
|
||||
displays both the Individual List (Index) page and all the Individual
|
||||
pages.
|
||||
|
||||
The base class 'BasePage' is initialised once for each page that is
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, report):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
"""
|
||||
BasePage.__init__(self, report, title="")
|
||||
self.source_dict = defaultdict(set)
|
||||
self.navigation = None
|
||||
self.citationreferents = None
|
||||
|
||||
def display_pages(self, title):
|
||||
"""
|
||||
Generate and output the pages under the Sources tab, namely the sources
|
||||
index and the individual sources pages.
|
||||
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
LOG.debug("obj_dict[Source]")
|
||||
for item in self.report.obj_dict[Source].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating source pages"),
|
||||
len(self.report.obj_dict[Source]) + 1
|
||||
) as step:
|
||||
self.sourcelistpage(self.report, title,
|
||||
self.report.obj_dict[Source].keys())
|
||||
|
||||
for source_handle in self.report.obj_dict[Source]:
|
||||
step()
|
||||
self.sourcepage(self.report, title, source_handle)
|
||||
|
||||
def sourcelistpage(self, report, title, source_handles):
|
||||
"""
|
||||
Generate and output the Sources index page.
|
||||
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: source_handles -- A list of the handles of the sources to be
|
||||
displayed
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
source_dict = {}
|
||||
|
||||
output_file, sio = self.report.create_file("sources")
|
||||
sourcelistpage, head, body = self.write_header(self._("Sources"))
|
||||
|
||||
# begin source list division
|
||||
with Html("div", class_="content", id="Sources") as sourceslist:
|
||||
body += sourceslist
|
||||
|
||||
# Sort the sources
|
||||
for handle in source_handles:
|
||||
source = self.r_db.get_source_from_handle(handle)
|
||||
if source is not None:
|
||||
key = source.get_title() + source.get_author()
|
||||
key += str(source.get_gramps_id())
|
||||
source_dict[key] = (source, handle)
|
||||
|
||||
keys = sorted(source_dict, key=self.rlocale.sort_key)
|
||||
|
||||
msg = self._("This page contains an index of all the sources "
|
||||
"in the database, sorted by their title. "
|
||||
"Clicking on a source’s "
|
||||
"title will take you to that source’s page.")
|
||||
sourceslist += Html("p", msg, id="description")
|
||||
|
||||
# begin sourcelist table and table head
|
||||
with Html("table",
|
||||
class_="infolist primobjlist sourcelist") as table:
|
||||
sourceslist += table
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
header_row = [
|
||||
(self._("Number"), "ColumnRowLabel"),
|
||||
(self._("Author"), "ColumnAuthor"),
|
||||
(self._("Source Name|Name"), "ColumnName")]
|
||||
|
||||
trow.extend(
|
||||
Html("th", label or " ", class_=colclass, inline=True)
|
||||
for (label, colclass) in header_row
|
||||
)
|
||||
|
||||
# begin table body
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
for index, key in enumerate(keys):
|
||||
source, source_handle = source_dict[key]
|
||||
|
||||
trow = Html("tr") + (
|
||||
Html("td", index + 1, class_="ColumnRowLabel",
|
||||
inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
trow.extend(
|
||||
Html("td", source.get_author(), class_="ColumnAuthor",
|
||||
inline=True)
|
||||
)
|
||||
trow.extend(
|
||||
Html("td", self.source_link(source_handle,
|
||||
source.get_title(),
|
||||
source.get_gramps_id()),
|
||||
class_="ColumnName")
|
||||
)
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(sourcelistpage, output_file, sio, 0)
|
||||
|
||||
def sourcepage(self, report, title, source_handle):
|
||||
"""
|
||||
Generate and output an individual Source page.
|
||||
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: source_handle -- The handle of the source to be output
|
||||
"""
|
||||
source = report.database.get_source_from_handle(source_handle)
|
||||
BasePage.__init__(self, report, title, source.get_gramps_id())
|
||||
if not source:
|
||||
return
|
||||
|
||||
self.page_title = source.get_title()
|
||||
|
||||
inc_repositories = self.report.options["inc_repository"]
|
||||
self.navigation = self.report.options['navigation']
|
||||
self.citationreferents = self.report.options['citationreferents']
|
||||
|
||||
output_file, sio = self.report.create_file(source_handle, "src")
|
||||
self.uplink = True
|
||||
sourcepage, head, body = self.write_header(
|
||||
"%s - %s" % (self._('Sources'), self.page_title))
|
||||
|
||||
ldatec = 0
|
||||
# begin source detail division
|
||||
with Html("div", class_="content", id="SourceDetail") as sourcedetail:
|
||||
body += sourcedetail
|
||||
|
||||
media_list = source.get_media_list()
|
||||
if self.create_media and media_list:
|
||||
thumbnail = self.disp_first_img_as_thumbnail(media_list,
|
||||
source)
|
||||
if thumbnail is not None:
|
||||
sourcedetail += thumbnail
|
||||
|
||||
# add section title
|
||||
sourcedetail += Html("h3", html_escape(source.get_title()),
|
||||
inline=True)
|
||||
|
||||
# begin sources table
|
||||
with Html("table", class_="infolist source") as table:
|
||||
sourcedetail += table
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
source_gid = False
|
||||
if not self.noid and self.gid:
|
||||
source_gid = source.get_gramps_id()
|
||||
|
||||
# last modification of this source
|
||||
ldatec = source.get_change_time()
|
||||
|
||||
for (label, value) in [(self._("Gramps ID"), source_gid),
|
||||
(self._("Author"), source.get_author()),
|
||||
(self._("Abbreviation"),
|
||||
source.get_abbreviation()),
|
||||
(self._("Publication information"),
|
||||
source.get_publication_info())]:
|
||||
if value:
|
||||
trow = Html("tr") + (
|
||||
Html("td", label, class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", value, class_="ColumnValue", inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
# Source notes
|
||||
notelist = self.display_note_list(source.get_note_list())
|
||||
if notelist is not None:
|
||||
sourcedetail += notelist
|
||||
|
||||
# additional media from Source (if any?)
|
||||
if self.create_media and media_list:
|
||||
sourcemedia = self.disp_add_img_as_gallery(media_list, source)
|
||||
if sourcemedia is not None:
|
||||
sourcedetail += sourcemedia
|
||||
|
||||
# Source Data Map...
|
||||
src_data_map = self.write_srcattr(source.get_attribute_list())
|
||||
if src_data_map is not None:
|
||||
sourcedetail += src_data_map
|
||||
|
||||
# Source Repository list
|
||||
if inc_repositories:
|
||||
repo_list = self.dump_repository_ref_list(
|
||||
source.get_reporef_list())
|
||||
if repo_list is not None:
|
||||
sourcedetail += repo_list
|
||||
|
||||
# Source references list
|
||||
ref_list = self.display_bkref_list(Source, source_handle)
|
||||
if ref_list is not None:
|
||||
sourcedetail += ref_list
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(sourcepage, output_file, sio, ldatec)
|
243
gramps/plugins/webreport/statistics.py
Normal file
243
gramps/plugins/webreport/statistics.py
Normal file
@ -0,0 +1,243 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
StatisticsPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import (Person, Family, Event, Place, Source,
|
||||
Citation, Repository)
|
||||
from gramps.gen.plug.report import Bibliography
|
||||
from gramps.gen.utils.file import media_path_full
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import FULLCLEAR
|
||||
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
_ = glocale.translation.sgettext
|
||||
|
||||
class StatisticsPage(BasePage):
|
||||
"""
|
||||
Create one page for statistics
|
||||
"""
|
||||
def __init__(self, report, title, step):
|
||||
"""
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
"""
|
||||
import posixpath
|
||||
BasePage.__init__(self, report, title)
|
||||
self.bibli = Bibliography()
|
||||
self.uplink = False
|
||||
self.report = report
|
||||
# set the file name and open file
|
||||
output_file, sio = self.report.create_file("statistics")
|
||||
addressbookpage, head, body = self.write_header(_("Statistics"))
|
||||
(males,
|
||||
females,
|
||||
unknown) = self.get_gender(report.database.iter_person_handles())
|
||||
|
||||
mobjects = report.database.get_number_of_media()
|
||||
npersons = report.database.get_number_of_people()
|
||||
nfamilies = report.database.get_number_of_families()
|
||||
nsurnames = len(set(report.database.surname_list))
|
||||
notfound = []
|
||||
total_media = 0
|
||||
mbytes = "0"
|
||||
chars = 0
|
||||
for media in report.database.iter_media():
|
||||
total_media += 1
|
||||
fullname = media_path_full(report.database, media.get_path())
|
||||
try:
|
||||
chars += posixpath.getsize(fullname)
|
||||
length = len(str(chars))
|
||||
if chars <= 999999:
|
||||
mbytes = _("less than 1")
|
||||
else:
|
||||
mbytes = str(chars)[:(length-6)]
|
||||
except OSError:
|
||||
notfound.append(media.get_path())
|
||||
|
||||
|
||||
with Html("div", class_="content", id='EventDetail') as section:
|
||||
section += Html("h3", self._("Database overview"), inline=True)
|
||||
body += section
|
||||
with Html("div", class_="content", id='subsection narrative') as sec11:
|
||||
sec11 += Html("h4", self._("Individuals"), inline=True)
|
||||
body += sec11
|
||||
with Html("div", class_="content", id='subsection narrative') as sec1:
|
||||
sec1 += Html("br", self._("Number of individuals") + self.colon +
|
||||
"%d" % npersons, inline=True)
|
||||
sec1 += Html("br", self._("Males") + self.colon +
|
||||
"%d" % males, inline=True)
|
||||
sec1 += Html("br", self._("Females") + self.colon +
|
||||
"%d" % females, inline=True)
|
||||
sec1 += Html("br", self._("Individuals with unknown gender") +
|
||||
self.colon + "%d" % unknown, inline=True)
|
||||
body += sec1
|
||||
with Html("div", class_="content", id='subsection narrative') as sec2:
|
||||
sec2 += Html("h4", self._("Family Information"), inline=True)
|
||||
sec2 += Html("br", self._("Number of families") + self.colon +
|
||||
"%d" % nfamilies, inline=True)
|
||||
sec2 += Html("br", self._("Unique surnames") + self.colon +
|
||||
"%d" % nsurnames, inline=True)
|
||||
body += sec2
|
||||
with Html("div", class_="content", id='subsection narrative') as sec3:
|
||||
sec3 += Html("h4", self._("Media Objects"), inline=True)
|
||||
sec3 += Html("br",
|
||||
self._("Total number of media object references") +
|
||||
self.colon + "%d" % total_media, inline=True)
|
||||
sec3 += Html("br", self._("Number of unique media objects") +
|
||||
self.colon + "%d" % mobjects, inline=True)
|
||||
sec3 += Html("br", self._("Total size of media objects") +
|
||||
self.colon +
|
||||
"%8s %s" % (mbytes, self._("Megabyte|MB")),
|
||||
inline=True)
|
||||
sec3 += Html("br", self._("Missing Media Objects") +
|
||||
self.colon + "%d" % len(notfound), inline=True)
|
||||
body += sec3
|
||||
with Html("div", class_="content", id='subsection narrative') as sec4:
|
||||
sec4 += Html("h4", self._("Miscellaneous"), inline=True)
|
||||
sec4 += Html("br", self._("Number of events") + self.colon +
|
||||
"%d" % report.database.get_number_of_events(),
|
||||
inline=True)
|
||||
sec4 += Html("br", self._("Number of places") + self.colon +
|
||||
"%d" % report.database.get_number_of_places(),
|
||||
inline=True)
|
||||
nsources = report.database.get_number_of_sources()
|
||||
sec4 += Html("br", self._("Number of sources") +
|
||||
self.colon + "%d" % nsources,
|
||||
inline=True)
|
||||
ncitations = report.database.get_number_of_citations()
|
||||
sec4 += Html("br", self._("Number of citations") +
|
||||
self.colon + "%d" % ncitations,
|
||||
inline=True)
|
||||
nrepo = report.database.get_number_of_repositories()
|
||||
sec4 += Html("br", self._("Number of repositories") +
|
||||
self.colon + "%d" % nrepo,
|
||||
inline=True)
|
||||
body += sec4
|
||||
|
||||
(males,
|
||||
females,
|
||||
unknown) = self.get_gender(self.report.bkref_dict[Person].keys())
|
||||
|
||||
origin = " :<br/>" + report.filter.get_name(self.rlocale)
|
||||
with Html("div", class_="content", id='EventDetail') as section:
|
||||
section += Html("h3",
|
||||
self._("Narrative web content report for") + origin,
|
||||
inline=True)
|
||||
body += section
|
||||
with Html("div", class_="content", id='subsection narrative') as sec5:
|
||||
sec5 += Html("h4", self._("Individuals"), inline=True)
|
||||
sec5 += Html("br", self._("Number of individuals") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Person]),
|
||||
inline=True)
|
||||
sec5 += Html("br", self._("Males") + self.colon +
|
||||
"%d" % males, inline=True)
|
||||
sec5 += Html("br", self._("Females") + self.colon +
|
||||
"%d" % females, inline=True)
|
||||
sec5 += Html("br", self._("Individuals with unknown gender") +
|
||||
self.colon + "%d" % unknown, inline=True)
|
||||
body += sec5
|
||||
with Html("div", class_="content", id='subsection narrative') as sec6:
|
||||
sec6 += Html("h4", self._("Family Information"), inline=True)
|
||||
sec6 += Html("br", self._("Number of families") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Family]),
|
||||
inline=True)
|
||||
body += sec6
|
||||
with Html("div", class_="content", id='subsection narrative') as sec7:
|
||||
sec7 += Html("h4", self._("Miscellaneous"), inline=True)
|
||||
sec7 += Html("br", self._("Number of events") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Event]),
|
||||
inline=True)
|
||||
sec7 += Html("br", self._("Number of places") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Place]),
|
||||
inline=True)
|
||||
sec7 += Html("br", self._("Number of sources") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Source]),
|
||||
inline=True)
|
||||
sec7 += Html("br", self._("Number of citations") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Citation]),
|
||||
inline=True)
|
||||
sec7 += Html("br", self._("Number of repositories") + self.colon +
|
||||
"%d" % len(self.report.bkref_dict[Repository]),
|
||||
inline=True)
|
||||
body += sec7
|
||||
|
||||
# add fullclear for proper styling
|
||||
# and footer section to page
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(addressbookpage, output_file, sio, 0)
|
||||
|
||||
def get_gender(self, person_list):
|
||||
"""
|
||||
This function return the number of males, females and unknown gender
|
||||
from a person list.
|
||||
"""
|
||||
males = 0
|
||||
females = 0
|
||||
unknown = 0
|
||||
for person_handle in person_list:
|
||||
person = self.report.database.get_person_from_handle(person_handle)
|
||||
gender = person.get_gender()
|
||||
if gender == Person.MALE:
|
||||
males += 1
|
||||
elif gender == Person.FEMALE:
|
||||
females += 1
|
||||
else:
|
||||
unknown += 1
|
||||
return (males, females, unknown)
|
265
gramps/plugins/webreport/surname.py
Normal file
265
gramps/plugins/webreport/surname.py
Normal file
@ -0,0 +1,265 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
SurnamePage - creates list of individuals with same surname
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.plug.report import utils
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (name_to_md5, _NAME_STYLE_FIRST,
|
||||
_find_birth_date, _find_death_date,
|
||||
FULLCLEAR, html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# create the page from SurnameListPage
|
||||
#
|
||||
#################################################
|
||||
class SurnamePage(BasePage):
|
||||
"""
|
||||
This will create a list of individuals with the same surname
|
||||
"""
|
||||
def __init__(self, report, title, surname, ppl_handle_list):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: surname -- The surname to use
|
||||
@param: ppl_handle_list -- The list of people for whom we need to create
|
||||
a page.
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
|
||||
# module variables
|
||||
showbirth = report.options['showbirth']
|
||||
showdeath = report.options['showdeath']
|
||||
showpartner = report.options['showpartner']
|
||||
showparents = report.options['showparents']
|
||||
|
||||
if surname == '':
|
||||
surname = self._("<absent>")
|
||||
|
||||
output_file, sio = self.report.create_file(name_to_md5(surname), "srn")
|
||||
self.uplink = True
|
||||
(surnamepage, head,
|
||||
body) = self.write_header("%s - %s" % (self._("Surname"), surname))
|
||||
ldatec = 0
|
||||
|
||||
# begin SurnameDetail division
|
||||
with Html("div", class_="content", id="SurnameDetail") as surnamedetail:
|
||||
body += surnamedetail
|
||||
|
||||
# section title
|
||||
surnamedetail += Html("h3", html_escape(surname), inline=True)
|
||||
|
||||
# feature request 2356: avoid genitive form
|
||||
msg = self._("This page contains an index of all the individuals "
|
||||
"in the database with the surname of %s. "
|
||||
"Selecting the person’s name "
|
||||
"will take you to that person’s "
|
||||
"individual page.") % html_escape(surname)
|
||||
surnamedetail += Html("p", msg, id="description")
|
||||
|
||||
# begin surname table and thead
|
||||
with Html("table", class_="infolist primobjlist surname") as table:
|
||||
surnamedetail += table
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
# Name Column
|
||||
trow += Html("th", self._("Given Name"), class_="ColumnName",
|
||||
inline=True)
|
||||
|
||||
if showbirth:
|
||||
trow += Html("th", self._("Birth"), class_="ColumnDate",
|
||||
inline=True)
|
||||
|
||||
if showdeath:
|
||||
trow += Html("th", self._("Death"), class_="ColumnDate",
|
||||
inline=True)
|
||||
|
||||
if showpartner:
|
||||
trow += Html("th", self._("Partner"),
|
||||
class_="ColumnPartner",
|
||||
inline=True)
|
||||
|
||||
if showparents:
|
||||
trow += Html("th", self._("Parents"),
|
||||
class_="ColumnParents",
|
||||
inline=True)
|
||||
|
||||
# begin table body
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
for person_handle in sorted(ppl_handle_list,
|
||||
key=self.sort_on_name_and_grampsid):
|
||||
|
||||
person = self.r_db.get_person_from_handle(person_handle)
|
||||
if person.get_change_time() > ldatec:
|
||||
ldatec = person.get_change_time()
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
# firstname column
|
||||
link = self.new_person_link(person_handle, uplink=True,
|
||||
person=person,
|
||||
name_style=_NAME_STYLE_FIRST)
|
||||
trow += Html("td", link, class_="ColumnName")
|
||||
|
||||
# birth column
|
||||
if showbirth:
|
||||
tcell = Html("td", class_="ColumnBirth", inline=True)
|
||||
trow += tcell
|
||||
|
||||
birth_date = _find_birth_date(self.r_db, person)
|
||||
if birth_date is not None:
|
||||
if birth_date.fallback:
|
||||
tcell += Html('em',
|
||||
self.rlocale.get_date(birth_date),
|
||||
inline=True)
|
||||
else:
|
||||
tcell += self.rlocale.get_date(birth_date)
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# death column
|
||||
if showdeath:
|
||||
tcell = Html("td", class_="ColumnDeath", inline=True)
|
||||
trow += tcell
|
||||
|
||||
death_date = _find_death_date(self.r_db, person)
|
||||
if death_date is not None:
|
||||
if death_date.fallback:
|
||||
tcell += Html('em',
|
||||
self.rlocale.get_date(death_date),
|
||||
inline=True)
|
||||
else:
|
||||
tcell += self.rlocale.get_date(death_date)
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# partner column
|
||||
if showpartner:
|
||||
tcell = Html("td", class_="ColumnPartner")
|
||||
trow += tcell
|
||||
family_list = person.get_family_handle_list()
|
||||
if family_list:
|
||||
fam_count = 0
|
||||
for family_handle in family_list:
|
||||
fam_count += 1
|
||||
family = self.r_db.get_family_from_handle(
|
||||
family_handle)
|
||||
partner_handle = utils.find_spouse(
|
||||
person, family)
|
||||
if partner_handle:
|
||||
link = self.new_person_link(partner_handle,
|
||||
uplink=True)
|
||||
if fam_count < len(family_list):
|
||||
if isinstance(link, Html):
|
||||
link.inside += ","
|
||||
else:
|
||||
link += ','
|
||||
tcell += link
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# parents column
|
||||
if showparents:
|
||||
parent_hdl_list = person.get_parent_family_handle_list()
|
||||
if parent_hdl_list:
|
||||
parent_hdl = parent_hdl_list[0]
|
||||
fam = self.r_db.get_family_from_handle(parent_hdl)
|
||||
f_id = fam.get_father_handle()
|
||||
m_id = fam.get_mother_handle()
|
||||
mother = father = None
|
||||
if f_id:
|
||||
father = self.r_db.get_person_from_handle(f_id)
|
||||
if father:
|
||||
father_name = self.get_name(father)
|
||||
if m_id:
|
||||
mother = self.r_db.get_person_from_handle(m_id)
|
||||
if mother:
|
||||
mother_name = self.get_name(mother)
|
||||
if mother and father:
|
||||
tcell = Html("span", father_name,
|
||||
class_="father fatherNmother")
|
||||
tcell += Html("span", mother_name,
|
||||
class_="mother")
|
||||
elif mother:
|
||||
tcell = Html("span", mother_name,
|
||||
class_="mother", inline=True)
|
||||
elif father:
|
||||
tcell = Html("span", father_name,
|
||||
class_="father", inline=True)
|
||||
samerow = False
|
||||
else:
|
||||
tcell = " " # pylint: disable=R0204
|
||||
samerow = True
|
||||
trow += Html("td", tcell,
|
||||
class_="ColumnParents", inline=samerow)
|
||||
|
||||
# add clearline for proper styling
|
||||
# add footer section
|
||||
footer = self.write_footer(ldatec)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(surnamepage, output_file, sio, ldatec)
|
249
gramps/plugins/webreport/surnamelist.py
Normal file
249
gramps/plugins/webreport/surnamelist.py
Normal file
@ -0,0 +1,249 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
SurnameListPage - Index for first letters of surname
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (get_first_letters, _KEYPERSON,
|
||||
alphabet_navigation, html_escape,
|
||||
sort_people, name_to_md5,
|
||||
first_letter, get_index_letter,
|
||||
primary_difference, FULLCLEAR)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
#################################################
|
||||
#
|
||||
# Creates the Surname List page
|
||||
#
|
||||
#################################################
|
||||
class SurnameListPage(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying the list of Surnames
|
||||
"""
|
||||
ORDER_BY_NAME = 0
|
||||
ORDER_BY_COUNT = 1
|
||||
|
||||
def __init__(self, report, title, ppl_handle_list,
|
||||
order_by=ORDER_BY_NAME, filename="surnames"):
|
||||
"""
|
||||
@param: report -- The instance of the main report class for
|
||||
this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: ppl_handle_list -- The list of people for whom we need to create
|
||||
a page.
|
||||
@param: order_by -- The way to sort surnames :
|
||||
Surnames or Surnames count
|
||||
@param: filename -- The name to use for the Surnames page
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
prev_surname = ""
|
||||
prev_letter = " "
|
||||
|
||||
if order_by == self.ORDER_BY_NAME:
|
||||
output_file, sio = self.report.create_file(filename)
|
||||
surnamelistpage, head, body = self.write_header(self._('Surnames'))
|
||||
else:
|
||||
output_file, sio = self.report.create_file("surnames_count")
|
||||
(surnamelistpage, head,
|
||||
body) = self.write_header(self._('Surnames by person count'))
|
||||
|
||||
# begin surnames division
|
||||
with Html("div", class_="content", id="surnames") as surnamelist:
|
||||
body += surnamelist
|
||||
|
||||
# page message
|
||||
msg = self._('This page contains an index of all the '
|
||||
'surnames in the database. Selecting a link '
|
||||
'will lead to a list of individuals in the '
|
||||
'database with this same surname.')
|
||||
surnamelist += Html("p", msg, id="description")
|
||||
|
||||
# add alphabet navigation...
|
||||
# only if surname list not surname count
|
||||
if order_by == self.ORDER_BY_NAME:
|
||||
index_list = get_first_letters(self.r_db, ppl_handle_list,
|
||||
_KEYPERSON, rlocale=self.rlocale)
|
||||
alpha_nav = alphabet_navigation(index_list, self.rlocale)
|
||||
if alpha_nav is not None:
|
||||
surnamelist += alpha_nav
|
||||
|
||||
if order_by == self.ORDER_BY_COUNT:
|
||||
table_id = 'SortByCount'
|
||||
else:
|
||||
table_id = 'SortByName'
|
||||
|
||||
# begin surnamelist table and table head
|
||||
with Html("table", class_="infolist primobjlist surnamelist",
|
||||
id=table_id) as table:
|
||||
surnamelist += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow += Html("th", self._("Letter"), class_="ColumnLetter",
|
||||
inline=True)
|
||||
|
||||
# create table header surname hyperlink
|
||||
fname = self.report.surname_fname + self.ext
|
||||
tcell = Html("th", class_="ColumnSurname", inline=True)
|
||||
trow += tcell
|
||||
hyper = Html("a", self._("Surname"),
|
||||
href=fname, title=self._("Surnames"))
|
||||
tcell += hyper
|
||||
|
||||
# create table header number of people hyperlink
|
||||
fname = "surnames_count" + self.ext
|
||||
tcell = Html("th", class_="ColumnQuantity", inline=True)
|
||||
trow += tcell
|
||||
num_people = self._("Number of People")
|
||||
hyper = Html("a", num_people, href=fname, title=num_people)
|
||||
tcell += hyper
|
||||
|
||||
# begin table body
|
||||
with Html("tbody") as tbody:
|
||||
table += tbody
|
||||
|
||||
ppl_handle_list = sort_people(self.r_db, ppl_handle_list,
|
||||
self.rlocale)
|
||||
if order_by == self.ORDER_BY_COUNT:
|
||||
temp_list = {}
|
||||
for (surname, data_list) in ppl_handle_list:
|
||||
index_val = "%90d_%s" % (999999999-len(data_list),
|
||||
surname)
|
||||
temp_list[index_val] = (surname, data_list)
|
||||
|
||||
lkey = self.rlocale.sort_key
|
||||
ppl_handle_list = (temp_list[key]
|
||||
for key in sorted(temp_list,
|
||||
key=lkey))
|
||||
|
||||
first = True
|
||||
first_surname = True
|
||||
|
||||
for (surname, data_list) in ppl_handle_list:
|
||||
|
||||
if surname and not surname.isspace():
|
||||
letter = first_letter(surname)
|
||||
if order_by == self.ORDER_BY_NAME:
|
||||
# There will only be an alphabetic index list if
|
||||
# the ORDER_BY_NAME page is being generated
|
||||
letter = get_index_letter(letter, index_list,
|
||||
self.rlocale)
|
||||
else:
|
||||
letter = ' '
|
||||
surname = self._("<absent>")
|
||||
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
tcell = Html("td", class_="ColumnLetter", inline=True)
|
||||
trow += tcell
|
||||
|
||||
if first or primary_difference(letter, prev_letter,
|
||||
self.rlocale):
|
||||
first = False
|
||||
prev_letter = letter
|
||||
trow.attr = 'class = "BeginLetter"'
|
||||
ttle = self._("Surnames beginning with "
|
||||
"letter %s") % letter
|
||||
hyper = Html("a", letter, name=letter,
|
||||
title=ttle, inline=True)
|
||||
tcell += hyper
|
||||
elif first_surname or surname != prev_surname:
|
||||
first_surname = False
|
||||
tcell += " "
|
||||
prev_surname = surname
|
||||
|
||||
trow += Html("td",
|
||||
self.surname_link(name_to_md5(surname),
|
||||
#html_escape(surname)),
|
||||
surname),
|
||||
class_="ColumnSurname", inline=True)
|
||||
|
||||
trow += Html("td", len(data_list),
|
||||
class_="ColumnQuantity", inline=True)
|
||||
|
||||
# create footer section
|
||||
# add clearline for proper styling
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(surnamelistpage,
|
||||
output_file, sio, 0) # 0 => current date modification
|
||||
|
||||
def surname_link(self, fname, name, opt_val=None, uplink=False):
|
||||
"""
|
||||
Create a link to the surname page.
|
||||
|
||||
@param: fname -- Path to the file name
|
||||
@param: name -- Name to see in the link
|
||||
@param: opt_val -- Option value to use
|
||||
@param: uplink -- If True, then "../../../" is inserted in front of
|
||||
the result.
|
||||
"""
|
||||
url = self.report.build_url_fname_html(fname, "srn", uplink)
|
||||
hyper = Html("a", html_escape(name), href=url,
|
||||
title=name, inline=True)
|
||||
if opt_val is not None:
|
||||
hyper += opt_val
|
||||
|
||||
# return hyperlink to its caller
|
||||
return hyper
|
277
gramps/plugins/webreport/thumbnail.py
Normal file
277
gramps/plugins/webreport/thumbnail.py
Normal file
@ -0,0 +1,277 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
||||
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
||||
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
||||
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
||||
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2017 Serge Noiraud
|
||||
# Copyright (C) 2011 Tim G L Lyons
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2016 Allen Crider
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""
|
||||
Narrative Web Page generator.
|
||||
|
||||
Classe:
|
||||
ThumbnailPreviewPage
|
||||
"""
|
||||
#------------------------------------------------
|
||||
# python modules
|
||||
#------------------------------------------------
|
||||
from decimal import getcontext
|
||||
import logging
|
||||
|
||||
#------------------------------------------------
|
||||
# Gramps module
|
||||
#------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.lib import Media
|
||||
from gramps.plugins.lib.libhtml import Html
|
||||
|
||||
#------------------------------------------------
|
||||
# specific narrative web import
|
||||
#------------------------------------------------
|
||||
from gramps.plugins.webreport.basepage import BasePage
|
||||
from gramps.plugins.webreport.common import (FULLCLEAR, html_escape)
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
LOG = logging.getLogger(".NarrativeWeb")
|
||||
getcontext().prec = 8
|
||||
|
||||
class ThumbnailPreviewPage(BasePage):
|
||||
"""
|
||||
This class is responsible for displaying information about
|
||||
the Thumbnails page.
|
||||
"""
|
||||
def __init__(self, report, title, cb_progress):
|
||||
"""
|
||||
@param: report -- The instance of the main report class
|
||||
for this report
|
||||
@param: title -- Is the title of the web page
|
||||
@param: cb_progress -- The step used for the progress bar.
|
||||
"""
|
||||
BasePage.__init__(self, report, title)
|
||||
self.create_thumbs_only = report.options['create_thumbs_only']
|
||||
# bug 8950 : it seems it's better to sort on desc + gid.
|
||||
def sort_by_desc_and_gid(obj):
|
||||
"""
|
||||
Sort by media description and gramps ID
|
||||
"""
|
||||
return (obj.desc, obj.gramps_id)
|
||||
|
||||
self.photo_keys = sorted(self.report.obj_dict[Media],
|
||||
key=lambda x: sort_by_desc_and_gid(
|
||||
self.r_db.get_media_from_handle(x)))
|
||||
|
||||
if self.create_unused_media:
|
||||
# add unused media
|
||||
media_list = self.r_db.get_media_handles()
|
||||
for media_ref in media_list:
|
||||
if media_ref not in self.report.obj_dict[Media]:
|
||||
self.photo_keys.append(media_ref)
|
||||
|
||||
media_list = []
|
||||
for person_handle in self.photo_keys:
|
||||
photo = self.r_db.get_media_from_handle(person_handle)
|
||||
if photo:
|
||||
if photo.get_mime_type().startswith("image"):
|
||||
media_list.append((photo.get_description(), person_handle,
|
||||
photo))
|
||||
|
||||
if self.create_thumbs_only:
|
||||
self.copy_thumbnail(person_handle, photo)
|
||||
|
||||
media_list.sort(key=lambda x: self.rlocale.sort_key(x[0]))
|
||||
|
||||
# Create thumbnail preview page...
|
||||
output_file, sio = self.report.create_file("thumbnails")
|
||||
thumbnailpage, head, body = self.write_header(self._("Thumbnails"))
|
||||
|
||||
with Html("div", class_="content", id="Preview") as previewpage:
|
||||
body += previewpage
|
||||
|
||||
msg = self._("This page displays a indexed list "
|
||||
"of all the media objects "
|
||||
"in this database. It is sorted by media title. "
|
||||
"There is an index "
|
||||
"of all the media objects in this database. "
|
||||
"Clicking on a thumbnail "
|
||||
"will take you to that image’s page.")
|
||||
previewpage += Html("p", msg, id="description")
|
||||
|
||||
with Html("table", class_="calendar") as table:
|
||||
previewpage += table
|
||||
|
||||
thead = Html("thead")
|
||||
table += thead
|
||||
|
||||
# page title...
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
trow += Html("th", self._("Thumbnail Preview"),
|
||||
class_="monthName", colspan=7, inline=True)
|
||||
|
||||
# table header cells...
|
||||
trow = Html("tr")
|
||||
thead += trow
|
||||
|
||||
ltrs = [" ", " ", " ",
|
||||
" ", " ", " ", " "]
|
||||
for ltr in ltrs:
|
||||
trow += Html("th", ltr, class_="weekend", inline=True)
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
index, indexpos = 1, 0
|
||||
num_of_images = len(media_list)
|
||||
num_of_rows = ((num_of_images // 7) + 1)
|
||||
num_of_cols = 7
|
||||
grid_row = 0
|
||||
while grid_row < num_of_rows:
|
||||
trow = Html("tr", id="RowNumber: %08d" % grid_row)
|
||||
tbody += trow
|
||||
|
||||
cols = 0
|
||||
while cols < num_of_cols and indexpos < num_of_images:
|
||||
ptitle = media_list[indexpos][0]
|
||||
person_handle = media_list[indexpos][1]
|
||||
photo = media_list[indexpos][2]
|
||||
|
||||
# begin table cell and attach to table row(trow)...
|
||||
tcell = Html("td", class_="highlight weekend")
|
||||
trow += tcell
|
||||
|
||||
# attach index number...
|
||||
numberdiv = Html("div", class_="date")
|
||||
tcell += numberdiv
|
||||
|
||||
# attach anchor name to date cell in upper right
|
||||
# corner of grid...
|
||||
numberdiv += Html("a", index, name=index, title=index,
|
||||
inline=True)
|
||||
|
||||
# begin unordered list and
|
||||
# attach to table cell(tcell)...
|
||||
unordered = Html("ul")
|
||||
tcell += unordered
|
||||
|
||||
# create thumbnail
|
||||
(real_path,
|
||||
newpath) = self.report.prepare_copy_media(photo)
|
||||
newpath = self.report.build_url_fname(newpath)
|
||||
|
||||
list_html = Html("li")
|
||||
unordered += list_html
|
||||
|
||||
# attach thumbnail to list...
|
||||
list_html += self.thumb_hyper_image(newpath, "img",
|
||||
person_handle,
|
||||
ptitle)
|
||||
|
||||
index += 1
|
||||
indexpos += 1
|
||||
cols += 1
|
||||
grid_row += 1
|
||||
|
||||
# if last row is incomplete, finish it off?
|
||||
if grid_row == num_of_rows and cols < num_of_cols:
|
||||
for emptycols in range(cols, num_of_cols):
|
||||
trow += Html("td", class_="emptyDays", inline=True)
|
||||
|
||||
# begin Thumbnail Reference section...
|
||||
with Html("div", class_="subsection", id="references") as section:
|
||||
body += section
|
||||
section += Html("h4", self._("References"), inline=True)
|
||||
|
||||
with Html("table", class_="infolist") as table:
|
||||
section += table
|
||||
|
||||
tbody = Html("tbody")
|
||||
table += tbody
|
||||
|
||||
index = 1
|
||||
for ptitle, person_handle, photo in media_list:
|
||||
trow = Html("tr")
|
||||
tbody += trow
|
||||
|
||||
tcell1 = Html("td",
|
||||
self.thumbnail_link(ptitle, index),
|
||||
class_="ColumnRowLabel")
|
||||
tcell2 = Html("td", ptitle, class_="ColumnName")
|
||||
trow += (tcell1, tcell2)
|
||||
|
||||
# increase index for row number...
|
||||
index += 1
|
||||
|
||||
# increase progress meter...
|
||||
cb_progress()
|
||||
|
||||
# add body id element
|
||||
body.attr = 'id ="ThumbnailPreview"'
|
||||
|
||||
# add footer section
|
||||
# add clearline for proper styling
|
||||
footer = self.write_footer(None)
|
||||
body += (FULLCLEAR, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.xhtml_writer(thumbnailpage, output_file, sio, 0)
|
||||
|
||||
|
||||
def thumbnail_link(self, name, index):
|
||||
"""
|
||||
creates a hyperlink for Thumbnail Preview Reference...
|
||||
"""
|
||||
return Html("a", index, title=html_escape(name),
|
||||
href="#%d" % index)
|
||||
|
||||
def thumb_hyper_image(self, thumbnail_url, subdir, fname, name):
|
||||
"""
|
||||
eplaces media_link() because it doesn't work for this instance
|
||||
"""
|
||||
name = html_escape(name)
|
||||
url = "/".join(self.report.build_subdirs(subdir,
|
||||
fname) + [fname]) + self.ext
|
||||
|
||||
with Html("div", class_="content", id="ThumbnailPreview") as section:
|
||||
with Html("div", class_="snapshot") as snapshot:
|
||||
section += snapshot
|
||||
|
||||
with Html("div", class_="thumbnail") as thumbnail:
|
||||
snapshot += thumbnail
|
||||
|
||||
if not self.create_thumbs_only:
|
||||
thumbnail_link = Html("a", href=url, title=name) + (
|
||||
Html("img", src=thumbnail_url, alt=name)
|
||||
)
|
||||
else:
|
||||
thumbnail_link = Html("img", src=thumbnail_url,
|
||||
alt=name)
|
||||
thumbnail += thumbnail_link
|
||||
return section
|
@ -1901,6 +1901,7 @@ def _regular_surname(sex, name):
|
||||
surname = name.get_surname()
|
||||
suffix = name.get_suffix()
|
||||
if suffix:
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
surname = surname + ", " + suffix
|
||||
return surname
|
||||
|
||||
|
@ -34,13 +34,13 @@ plg.id = 'navwebpage'
|
||||
plg.name = _("Narrated Web Site")
|
||||
plg.description = _("Produces web (HTML) pages for individuals, or a set of "
|
||||
"individuals")
|
||||
plg.version = '1.0'
|
||||
plg.version = '2.0'
|
||||
plg.gramps_target_version = MODULE_VERSION
|
||||
plg.status = STABLE
|
||||
plg.fname = 'narrativeweb.py'
|
||||
plg.ptype = REPORT
|
||||
plg.authors = ["Donald N. Allingham", "Rob G. Healey"]
|
||||
plg.authors_email = ["don@gramps-project.org", "robhealey1@gmail.com"]
|
||||
plg.authors = ["Donald N. Allingham", "Rob G. Healey", "Serge Noiraud"]
|
||||
plg.authors_email = ["don@gramps-project.org", "serge.noiraud@free.fr"]
|
||||
plg.category = CATEGORY_WEB
|
||||
plg.reportclass = 'NavWebReport'
|
||||
plg.optionclass = 'NavWebOptions'
|
||||
|
@ -108,8 +108,8 @@ gramps/gen/filters/rules/family/_hassourcecount.py
|
||||
gramps/gen/filters/rules/family/_hassourceof.py
|
||||
gramps/gen/filters/rules/family/_hastag.py
|
||||
gramps/gen/filters/rules/family/_hastwins.py
|
||||
gramps/gen/filters/rules/family/_isbookmarked.py
|
||||
gramps/gen/filters/rules/family/_isancestorof.py
|
||||
gramps/gen/filters/rules/family/_isbookmarked.py
|
||||
gramps/gen/filters/rules/family/_isdescendantof.py
|
||||
gramps/gen/filters/rules/family/_matchesfilter.py
|
||||
gramps/gen/filters/rules/family/_matchessourceconfidence.py
|
||||
@ -280,7 +280,10 @@ gramps/gen/filters/rules/source/_matchesrepositoryfilter.py
|
||||
gramps/gen/filters/rules/source/_matchestitlesubstringof.py
|
||||
gramps/gen/filters/rules/source/_regexpidof.py
|
||||
gramps/gen/filters/rules/source/_sourceprivate.py
|
||||
gramps/gen/lib/address.py
|
||||
gramps/gen/lib/attribute.py
|
||||
gramps/gen/lib/attrtype.py
|
||||
gramps/gen/lib/childref.py
|
||||
gramps/gen/lib/childreftype.py
|
||||
gramps/gen/lib/citation.py
|
||||
gramps/gen/lib/date.py
|
||||
@ -292,26 +295,35 @@ gramps/gen/lib/family.py
|
||||
gramps/gen/lib/familyreltype.py
|
||||
gramps/gen/lib/grampstype.py
|
||||
gramps/gen/lib/ldsord.py
|
||||
gramps/gen/lib/location.py
|
||||
gramps/gen/lib/markertype.py
|
||||
gramps/gen/lib/media.py
|
||||
gramps/gen/lib/mediaref.py
|
||||
gramps/gen/lib/name.py
|
||||
gramps/gen/lib/nameorigintype.py
|
||||
gramps/gen/lib/nametype.py
|
||||
gramps/gen/lib/note.py
|
||||
gramps/gen/lib/notetype.py
|
||||
gramps/gen/lib/person.py
|
||||
gramps/gen/lib/personref.py
|
||||
gramps/gen/lib/place.py
|
||||
gramps/gen/lib/placename.py
|
||||
gramps/gen/lib/placeref.py
|
||||
gramps/gen/lib/placetype.py
|
||||
gramps/gen/lib/repo.py
|
||||
gramps/gen/lib/reporef.py
|
||||
gramps/gen/lib/repotype.py
|
||||
gramps/gen/lib/src.py
|
||||
gramps/gen/lib/srcmediatype.py
|
||||
gramps/gen/lib/srcattribute.py
|
||||
gramps/gen/lib/srcattrtype.py
|
||||
gramps/gen/lib/srcmediatype.py
|
||||
gramps/gen/lib/styledtext.py
|
||||
gramps/gen/lib/styledtexttag.py
|
||||
gramps/gen/lib/styledtexttagtype.py
|
||||
gramps/gen/lib/surname.py
|
||||
gramps/gen/lib/surnamebase.py
|
||||
gramps/gen/lib/tag.py
|
||||
gramps/gen/lib/url.py
|
||||
gramps/gen/lib/urltype.py
|
||||
gramps/gen/merge/diff.py
|
||||
gramps/gen/merge/mergecitationquery.py
|
||||
@ -350,8 +362,8 @@ gramps/gen/utils/cast.py
|
||||
gramps/gen/utils/configmanager.py
|
||||
gramps/gen/utils/db.py
|
||||
gramps/gen/utils/docgen/odstab.py
|
||||
gramps/gen/utils/image.py
|
||||
gramps/gen/utils/grampslocale.py
|
||||
gramps/gen/utils/image.py
|
||||
gramps/gen/utils/keyword.py
|
||||
gramps/gen/utils/lds.py
|
||||
gramps/gen/utils/place.py
|
||||
@ -552,21 +564,23 @@ gramps/gui/widgets/reorderfam.py
|
||||
gramps/gui/widgets/styledtextbuffer.py
|
||||
gramps/gui/widgets/styledtexteditor.py
|
||||
gramps/gui/widgets/validatedmaskedentry.py
|
||||
gramps/plugins/db/dbapi/dbapi.gpr.py
|
||||
gramps/plugins/db/bsddb/bsddb.gpr.py
|
||||
gramps/plugins/db/bsddb/read.py
|
||||
gramps/plugins/db/bsddb/undoredo.py
|
||||
gramps/plugins/db/bsddb/upgrade.py
|
||||
gramps/plugins/db/bsddb/write.py
|
||||
gramps/plugins/db/dbapi/inmemorydb.gpr.py
|
||||
gramps/plugins/db/dbapi/postgresql.gpr.py
|
||||
gramps/plugins/db/dbapi/postgresql.py
|
||||
gramps/plugins/db/dbapi/sqlite.gpr.py
|
||||
gramps/plugins/db/dbapi/sqlite.py
|
||||
gramps/plugins/docgen/asciidoc.py
|
||||
gramps/plugins/docgen/cairodoc.py
|
||||
gramps/plugins/docgen/docgen.gpr.py
|
||||
gramps/plugins/docgen/gtkprint.glade
|
||||
gramps/plugins/docgen/gtkprint.py
|
||||
gramps/plugins/docgen/htmldoc.py
|
||||
gramps/plugins/docgen/latexdoc.py
|
||||
gramps/plugins/docgen/odfdoc.py
|
||||
gramps/plugins/docgen/cairodoc.py
|
||||
gramps/plugins/docgen/rtfdoc.py
|
||||
gramps/plugins/docgen/svgdrawdoc.py
|
||||
gramps/plugins/drawreport/ancestortree.py
|
||||
@ -650,7 +664,6 @@ gramps/plugins/lib/libplugins.gpr.py
|
||||
gramps/plugins/lib/libprogen.py
|
||||
gramps/plugins/lib/librecords.py
|
||||
gramps/plugins/lib/libsubstkeyword.py
|
||||
#gramps/plugins/lib/libtranslate.py
|
||||
gramps/plugins/lib/libtreebase.py
|
||||
gramps/plugins/lib/maps/geography.py
|
||||
gramps/plugins/lib/maps/osmgps.py
|
||||
@ -671,8 +684,8 @@ gramps/plugins/quickview/reporef.py
|
||||
gramps/plugins/quickview/samesurnames.py
|
||||
gramps/plugins/quickview/siblings.py
|
||||
gramps/plugins/rel/relplugins.gpr.py
|
||||
gramps/plugins/sidebar/sidebar.gpr.py
|
||||
gramps/plugins/sidebar/dropdownsidebar.py
|
||||
gramps/plugins/sidebar/sidebar.gpr.py
|
||||
gramps/plugins/textreport/alphabeticalindex.py
|
||||
gramps/plugins/textreport/ancestorreport.py
|
||||
gramps/plugins/textreport/birthdayreport.py
|
||||
@ -758,7 +771,25 @@ gramps/plugins/view/relview.py
|
||||
gramps/plugins/view/repoview.py
|
||||
gramps/plugins/view/sourceview.py
|
||||
gramps/plugins/view/view.gpr.py
|
||||
gramps/plugins/webreport/addressbook.py
|
||||
gramps/plugins/webreport/addressbooklist.py
|
||||
gramps/plugins/webreport/basepage.py
|
||||
gramps/plugins/webreport/contact.py
|
||||
gramps/plugins/webreport/download.py
|
||||
gramps/plugins/webreport/event.py
|
||||
gramps/plugins/webreport/family.py
|
||||
gramps/plugins/webreport/home.py
|
||||
gramps/plugins/webreport/introduction.py
|
||||
gramps/plugins/webreport/media.py
|
||||
gramps/plugins/webreport/narrativeweb.py
|
||||
gramps/plugins/webreport/person.py
|
||||
gramps/plugins/webreport/place.py
|
||||
gramps/plugins/webreport/repository.py
|
||||
gramps/plugins/webreport/source.py
|
||||
gramps/plugins/webreport/statistics.py
|
||||
gramps/plugins/webreport/surname.py
|
||||
gramps/plugins/webreport/surnamelist.py
|
||||
gramps/plugins/webreport/thumbnail.py
|
||||
gramps/plugins/webreport/webcal.py
|
||||
gramps/plugins/webreport/webplugins.gpr.py
|
||||
gramps/plugins/webstuff/webstuff.gpr.py
|
||||
|
385
po/POTFILES.skip
385
po/POTFILES.skip
@ -3,23 +3,34 @@
|
||||
# Python files
|
||||
#
|
||||
Gramps.py
|
||||
setup.py
|
||||
#
|
||||
# gramps
|
||||
#
|
||||
gramps/__init__.py
|
||||
gramps/version.py
|
||||
#
|
||||
# cli
|
||||
#
|
||||
gramps/cli/__init__.py
|
||||
#
|
||||
# cli.test package
|
||||
#
|
||||
gramps/cli/test/argparser_test.py
|
||||
gramps/cli/test/cli_test.py
|
||||
gramps/cli/test/user_test.py
|
||||
#
|
||||
# gen
|
||||
#
|
||||
gramps/gen/__init__.py
|
||||
gramps/gen/constfunc.py
|
||||
gramps/gen/dbstate.py
|
||||
gramps/gen/errors.py
|
||||
gramps/gen/git_revision.py
|
||||
gramps/gen/__init__.py
|
||||
gramps/gen/sort.py
|
||||
gramps/gen/soundex.py
|
||||
gramps/gen/updatecallback.py
|
||||
gramps/gen/ggettext.py
|
||||
gramps/gen/user.py
|
||||
#
|
||||
# gen.datehandler package
|
||||
#
|
||||
@ -33,9 +44,11 @@ gramps/gen/datehandler/_date_el.py
|
||||
gramps/gen/datehandler/_date_es.py
|
||||
gramps/gen/datehandler/_date_fi.py
|
||||
gramps/gen/datehandler/_date_fr.py
|
||||
gramps/gen/datehandler/_datehandler.py
|
||||
gramps/gen/datehandler/_date_hr.py
|
||||
gramps/gen/datehandler/_date_hu.py
|
||||
gramps/gen/datehandler/_date_is.py
|
||||
gramps/gen/datehandler/_date_it.py
|
||||
gramps/gen/datehandler/_date_ja.py
|
||||
gramps/gen/datehandler/_date_lt.py
|
||||
gramps/gen/datehandler/_date_nb.py
|
||||
gramps/gen/datehandler/_date_nl.py
|
||||
@ -46,18 +59,25 @@ gramps/gen/datehandler/_date_sk.py
|
||||
gramps/gen/datehandler/_date_sl.py
|
||||
gramps/gen/datehandler/_date_sr.py
|
||||
gramps/gen/datehandler/_date_sv.py
|
||||
gramps/gen/datehandler/_date_zh.py
|
||||
gramps/gen/datehandler/_date_uk.py
|
||||
gramps/gen/datehandler/_date_zh_CN.py
|
||||
gramps/gen/datehandler/_date_zh_TW.py
|
||||
gramps/gen/datehandler/_datehandler.py
|
||||
gramps/gen/datehandler/_grampslocale.py
|
||||
#
|
||||
# gen.datehandler.test package
|
||||
#
|
||||
gramps/gen/datehandler/test/datedisplay_test.py
|
||||
gramps/gen/datehandler/test/datehandler_test.py
|
||||
gramps/gen/datehandler/test/dateparser_test.py
|
||||
gramps/gen/datehandler/test/datestrings_test.py
|
||||
#
|
||||
# gen db API
|
||||
#
|
||||
gramps/gen/db/__init__.py
|
||||
gramps/gen/db/backup.py
|
||||
gramps/gen/db/bookmarks.py
|
||||
gramps/gen/db/bsddbtxn.py
|
||||
gramps/gen/db/cursor.py
|
||||
gramps/gen/db/dbconst.py
|
||||
gramps/gen/db/test/db_test.py
|
||||
gramps/gen/db/dummydb.py
|
||||
gramps/gen/db/txn.py
|
||||
gramps/gen/db/undoredo.py
|
||||
gramps/gen/db/utils.py
|
||||
@ -65,18 +85,57 @@ gramps/gen/db/utils.py
|
||||
# gen.display package
|
||||
#
|
||||
gramps/gen/display/__init__.py
|
||||
gramps/gen/display/place.py
|
||||
#
|
||||
# gen.filters package
|
||||
#
|
||||
gramps/gen/filters/_filterlist.py
|
||||
gramps/gen/filters/__init__.py
|
||||
gramps/gen/filters/_filterlist.py
|
||||
gramps/gen/filters/_paramfilter.py
|
||||
gramps/gen/filters/_searchfilter.py
|
||||
#
|
||||
# gen.filters.rules package
|
||||
#
|
||||
gramps/gen/filters/rules/_hastextmatchingregexpof.py
|
||||
gramps/gen/filters/rules/__init__.py
|
||||
gramps/gen/filters/rules/_hastextmatchingregexpof.py
|
||||
#
|
||||
# gen.filters.rules.citation package
|
||||
#
|
||||
gramps/gen/filters/rules/citation/__init__.py
|
||||
#
|
||||
# gen.filters.rules.event package
|
||||
#
|
||||
gramps/gen/filters/rules/event/__init__.py
|
||||
#
|
||||
# gen.filters.rules.family package
|
||||
#
|
||||
gramps/gen/filters/rules/family/__init__.py
|
||||
gramps/gen/filters/rules/family/_memberbase.py
|
||||
#
|
||||
# gen.filters.rules.media package
|
||||
#
|
||||
gramps/gen/filters/rules/media/__init__.py
|
||||
#
|
||||
# gen.filters.rules.note package
|
||||
#
|
||||
gramps/gen/filters/rules/note/__init__.py
|
||||
#
|
||||
# gen.filters.rules.person package
|
||||
#
|
||||
gramps/gen/filters/rules/person/__init__.py
|
||||
gramps/gen/filters/rules/person/_hastextmatchingregexpof.py
|
||||
#
|
||||
# gen.filters.rules.place package
|
||||
#
|
||||
gramps/gen/filters/rules/place/__init__.py
|
||||
#
|
||||
# gen.filters.rules.repository package
|
||||
#
|
||||
gramps/gen/filters/rules/repository/__init__.py
|
||||
#
|
||||
# gen.filters.rules.source package
|
||||
#
|
||||
gramps/gen/filters/rules/source/__init__.py
|
||||
#
|
||||
# gen.filters.rules.test package
|
||||
#
|
||||
@ -88,85 +147,39 @@ gramps/gen/filters/rules/test/person_rules_test.py
|
||||
gramps/gen/filters/rules/test/place_rules_test.py
|
||||
gramps/gen/filters/rules/test/repository_rules_test.py
|
||||
#
|
||||
# gen.filters.rules.person package
|
||||
#
|
||||
gramps/gen/filters/rules/person/__init__.py
|
||||
gramps/gen/filters/rules/person/_hastextmatchingregexpof.py
|
||||
#
|
||||
# gen.filters.rules.family package
|
||||
#
|
||||
gramps/gen/filters/rules/family/__init__.py
|
||||
gramps/gen/filters/rules/family/_memberbase.py
|
||||
#
|
||||
# gen.filters.rules.event package
|
||||
#
|
||||
gramps/gen/filters/rules/event/__init__.py
|
||||
#
|
||||
# gen.filters.rules.place package
|
||||
#
|
||||
gramps/gen/filters/rules/place/__init__.py
|
||||
#
|
||||
# gen.filters.rules.source package
|
||||
#
|
||||
gramps/gen/filters/rules/source/__init__.py
|
||||
#
|
||||
# gen.filters.rules.citation package
|
||||
#
|
||||
gramps/gen/filters/rules/citation/__init__.py
|
||||
#
|
||||
# gen.filters.rules.media package
|
||||
#
|
||||
gramps/gen/filters/rules/media/__init__.py
|
||||
#
|
||||
# gen.filters.rules.repository package
|
||||
#
|
||||
gramps/gen/filters/rules/repository/__init__.py
|
||||
#
|
||||
# gen.filters.rules.note package
|
||||
#
|
||||
gramps/gen/filters/rules/note/__init__.py
|
||||
#
|
||||
# gen lib API
|
||||
#
|
||||
gramps/gen/lib/__init__.py
|
||||
gramps/gen/lib/address.py
|
||||
gramps/gen/lib/addressbase.py
|
||||
gramps/gen/lib/attrbase.py
|
||||
gramps/gen/lib/attribute.py
|
||||
gramps/gen/lib/baseobj.py
|
||||
gramps/gen/lib/gcalendar.py
|
||||
gramps/gen/lib/childref.py
|
||||
gramps/gen/lib/citationbase.py
|
||||
gramps/gen/lib/const.py
|
||||
gramps/gen/lib/datebase.py
|
||||
gramps/gen/lib/gcalendar.py
|
||||
gramps/gen/lib/genderstats.py
|
||||
gramps/gen/lib/ldsordbase.py
|
||||
gramps/gen/lib/location.py
|
||||
gramps/gen/lib/locationbase.py
|
||||
gramps/gen/lib/mediaobj.py
|
||||
gramps/gen/lib/mediabase.py
|
||||
gramps/gen/lib/mediaref.py
|
||||
gramps/gen/lib/notebase.py
|
||||
gramps/gen/lib/personref.py
|
||||
gramps/gen/lib/placebase.py
|
||||
gramps/gen/lib/primaryobj.py
|
||||
gramps/gen/lib/privacybase.py
|
||||
gramps/gen/lib/refbase.py
|
||||
gramps/gen/lib/reporef.py
|
||||
gramps/gen/lib/researcher.py
|
||||
gramps/gen/lib/secondaryobj.py
|
||||
gramps/gen/lib/serialize.py
|
||||
gramps/gen/lib/srcbase.py
|
||||
gramps/gen/lib/srcref.py
|
||||
gramps/gen/lib/styledtexttag.py
|
||||
gramps/gen/lib/tableobj.py
|
||||
gramps/gen/lib/tagbase.py
|
||||
gramps/gen/lib/urlbase.py
|
||||
#
|
||||
# gen lib test API
|
||||
#
|
||||
gramps/gen/lib/test/date_test.py
|
||||
gramps/gen/lib/test/grampstype_test.py
|
||||
gramps/gen/lib/test/merge_test.py
|
||||
gramps/gen/lib/test/schema_test.py
|
||||
gramps/gen/lib/url.py
|
||||
gramps/gen/lib/urlbase.py
|
||||
gramps/gen/lib/test/serialize_test.py
|
||||
#
|
||||
# gen.merge package
|
||||
#
|
||||
@ -176,46 +189,58 @@ gramps/gen/merge/__init__.py
|
||||
#
|
||||
gramps/gen/mime/__init__.py
|
||||
#
|
||||
# gen plugin API
|
||||
# gen plug API
|
||||
#
|
||||
gramps/gen/plug/__init__.py
|
||||
gramps/gen/plug/_docgenplugin.py
|
||||
gramps/gen/plug/_export.py
|
||||
gramps/gen/plug/_import.py
|
||||
gramps/gen/plug/_plugin.py
|
||||
gramps/gen/plug/menu/_booleanlist.py
|
||||
gramps/gen/plug/menu/_boolean.py
|
||||
gramps/gen/plug/menu/_color.py
|
||||
gramps/gen/plug/menu/_destination.py
|
||||
gramps/gen/plug/menu/_family.py
|
||||
gramps/gen/plug/menu/_filter.py
|
||||
gramps/gen/plug/menu/__init__.py
|
||||
gramps/gen/plug/menu/_media.py
|
||||
gramps/gen/plug/menu/_menu.py
|
||||
gramps/gen/plug/menu/_note.py
|
||||
gramps/gen/plug/menu/_number.py
|
||||
gramps/gen/plug/menu/_option.py
|
||||
gramps/gen/plug/menu/_personlist.py
|
||||
gramps/gen/plug/menu/_person.py
|
||||
gramps/gen/plug/menu/_placelist.py
|
||||
gramps/gen/plug/menu/_string.py
|
||||
gramps/gen/plug/menu/_style.py
|
||||
gramps/gen/plug/menu/_surnamecolor.py
|
||||
gramps/gen/plug/menu/_text.py
|
||||
gramps/gen/plug/docbackend/cairobackend.py
|
||||
#
|
||||
# gen.plug.docbackend
|
||||
#
|
||||
gramps/gen/plug/docbackend/__init__.py
|
||||
gramps/gen/plug/docbackend/cairobackend.py
|
||||
#
|
||||
# gen.plug.docgen
|
||||
#
|
||||
gramps/gen/plug/docgen/__init__.py
|
||||
gramps/gen/plug/docgen/basedoc.py
|
||||
gramps/gen/plug/docgen/drawdoc.py
|
||||
gramps/gen/plug/docgen/fontscale.py
|
||||
gramps/gen/plug/docgen/fontstyle.py
|
||||
gramps/gen/plug/docgen/graphicstyle.py
|
||||
gramps/gen/plug/docgen/__init__.py
|
||||
gramps/gen/plug/docgen/paragraphstyle.py
|
||||
gramps/gen/plug/docgen/stylesheet.py
|
||||
gramps/gen/plug/docgen/tablestyle.py
|
||||
gramps/gen/plug/docgen/textdoc.py
|
||||
gramps/gen/plug/report/_bibliography.py
|
||||
#
|
||||
# gen.plug.menu
|
||||
#
|
||||
gramps/gen/plug/menu/__init__.py
|
||||
gramps/gen/plug/menu/_boolean.py
|
||||
gramps/gen/plug/menu/_booleanlist.py
|
||||
gramps/gen/plug/menu/_color.py
|
||||
gramps/gen/plug/menu/_destination.py
|
||||
gramps/gen/plug/menu/_family.py
|
||||
gramps/gen/plug/menu/_filter.py
|
||||
gramps/gen/plug/menu/_media.py
|
||||
gramps/gen/plug/menu/_menu.py
|
||||
gramps/gen/plug/menu/_note.py
|
||||
gramps/gen/plug/menu/_number.py
|
||||
gramps/gen/plug/menu/_option.py
|
||||
gramps/gen/plug/menu/_person.py
|
||||
gramps/gen/plug/menu/_personlist.py
|
||||
gramps/gen/plug/menu/_placelist.py
|
||||
gramps/gen/plug/menu/_string.py
|
||||
gramps/gen/plug/menu/_style.py
|
||||
gramps/gen/plug/menu/_surnamecolor.py
|
||||
gramps/gen/plug/menu/_text.py
|
||||
#
|
||||
# gen.plug.report
|
||||
#
|
||||
gramps/gen/plug/report/__init__.py
|
||||
gramps/gen/plug/report/_bibliography.py
|
||||
gramps/gen/plug/report/_options.py
|
||||
gramps/gen/plug/report/_paper.py
|
||||
gramps/gen/plug/report/_reportbase.py
|
||||
@ -223,6 +248,7 @@ gramps/gen/plug/report/_reportbase.py
|
||||
# gen proxy API
|
||||
#
|
||||
gramps/gen/proxy/__init__.py
|
||||
gramps/gen/proxy/cache.py
|
||||
gramps/gen/proxy/filter.py
|
||||
gramps/gen/proxy/living.py
|
||||
gramps/gen/proxy/proxybase.py
|
||||
@ -232,7 +258,11 @@ gramps/gen/proxy/referencedbyselection.py
|
||||
#
|
||||
gramps/gen/simple/__init__.py
|
||||
gramps/gen/simple/_simpledoc.py
|
||||
#gramps/gen/simple/_simpletable.py
|
||||
#
|
||||
# gen test API
|
||||
#
|
||||
gramps/gen/test/config_test.py
|
||||
gramps/gen/test/constfunc_test.py
|
||||
#
|
||||
# gen utils API
|
||||
#
|
||||
@ -244,8 +274,11 @@ gramps/gen/utils/debug.py
|
||||
gramps/gen/utils/file.py
|
||||
gramps/gen/utils/id.py
|
||||
gramps/gen/utils/libformatting.py
|
||||
gramps/gen/utils/mactrans.py
|
||||
gramps/gen/utils/test/callback_test.py
|
||||
gramps/gen/utils/location.py
|
||||
gramps/gen/utils/lru.py
|
||||
gramps/gen/utils/maclocale.py
|
||||
gramps/gen/utils/resourcepath.py
|
||||
gramps/gen/utils/thumbnails.py
|
||||
#
|
||||
# gen.utils.docgen
|
||||
#
|
||||
@ -253,9 +286,18 @@ gramps/gen/utils/docgen/__init__.py
|
||||
gramps/gen/utils/docgen/csvtab.py
|
||||
gramps/gen/utils/docgen/tabbeddoc.py
|
||||
#
|
||||
# gen.utils.test
|
||||
#
|
||||
gramps/gen/utils/test/callback_test.py
|
||||
gramps/gen/utils/test/file_test.py
|
||||
gramps/gen/utils/test/grampslocale_test.py
|
||||
gramps/gen/utils/test/keyword_test.py
|
||||
gramps/gen/utils/test/place_test.py
|
||||
#
|
||||
# gui - GUI code
|
||||
#
|
||||
gramps/gui/__init__.py
|
||||
gramps/gui/actiongroup.py
|
||||
gramps/gui/basesidebar.py
|
||||
gramps/gui/dbguielement.py
|
||||
gramps/gui/ddtargets.py
|
||||
@ -265,9 +307,8 @@ gramps/gui/listmodel.py
|
||||
gramps/gui/managedwindow.py
|
||||
gramps/gui/navigator.py
|
||||
gramps/gui/pluginmanager.py
|
||||
gramps/gui/thumbnails.py
|
||||
gramps/gui/user.py
|
||||
gramps/gui/glade/catalog/grampswidgets.py
|
||||
gramps/gui/utilscairo.py
|
||||
#
|
||||
# gui/editors - the GUI editors package
|
||||
#
|
||||
@ -278,12 +319,10 @@ gramps/gui/editors/editsecondary.py
|
||||
#
|
||||
gramps/gui/editors/displaytabs/__init__.py
|
||||
gramps/gui/editors/displaytabs/addressmodel.py
|
||||
gramps/gui/editors/displaytabs/altnamemodel.py
|
||||
gramps/gui/editors/displaytabs/attrmodel.py
|
||||
gramps/gui/editors/displaytabs/childmodel.py
|
||||
gramps/gui/editors/displaytabs/citationbackreflist.py
|
||||
gramps/gui/editors/displaytabs/citationrefmodel.py
|
||||
gramps/gui/editors/displaytabs/datamodel.py
|
||||
gramps/gui/editors/displaytabs/eventattrembedlist.py
|
||||
gramps/gui/editors/displaytabs/eventbackreflist.py
|
||||
gramps/gui/editors/displaytabs/familyattrembedlist.py
|
||||
@ -295,38 +334,64 @@ gramps/gui/editors/displaytabs/mediaattrembedlist.py
|
||||
gramps/gui/editors/displaytabs/mediabackreflist.py
|
||||
gramps/gui/editors/displaytabs/notebackreflist.py
|
||||
gramps/gui/editors/displaytabs/notemodel.py
|
||||
gramps/gui/editors/displaytabs/personbackreflist.py
|
||||
gramps/gui/editors/displaytabs/personrefmodel.py
|
||||
gramps/gui/editors/displaytabs/placebackreflist.py
|
||||
gramps/gui/editors/displaytabs/placenamemodel.py
|
||||
gramps/gui/editors/displaytabs/placerefmodel.py
|
||||
gramps/gui/editors/displaytabs/reporefmodel.py
|
||||
gramps/gui/editors/displaytabs/sourcebackreflist.py
|
||||
gramps/gui/editors/displaytabs/surnamemodel.py
|
||||
gramps/gui/editors/displaytabs/webmodel.py
|
||||
#
|
||||
# gui.editors.test package
|
||||
#
|
||||
gramps/gui/editors/test/editreference_test.py
|
||||
#
|
||||
# gui.filters package
|
||||
#
|
||||
gramps/gui/filters/__init__.py
|
||||
gramps/gui/filters/_filtercombobox.py
|
||||
gramps/gui/filters/_filtermenu.py
|
||||
#gramps/gui/filters/_filterstore.py
|
||||
gramps/gui/filters/__init__.py
|
||||
#
|
||||
# gui.filters.sidebar package
|
||||
#
|
||||
gramps/gui/filters/sidebar/__init__.py
|
||||
#
|
||||
# gui.glade.catalog package
|
||||
#
|
||||
gramps/gui/glade/catalog/grampswidgets.py
|
||||
#
|
||||
# gui.logger package
|
||||
#
|
||||
gramps/gui/logger/__init__.py
|
||||
gramps/gui/logger/_gtkhandler.py
|
||||
gramps/gui/logger/_rotatehandler.py
|
||||
#
|
||||
# gui.logger.test package
|
||||
#
|
||||
gramps/gui/logger/test/rotate_handler_test.py
|
||||
#
|
||||
# gui.merge package
|
||||
#
|
||||
gramps/gui/merge/__init__.py
|
||||
#
|
||||
# gui.plug package
|
||||
#
|
||||
gramps/gui/plug/export/__init__.py
|
||||
gramps/gui/plug/__init__.py
|
||||
#
|
||||
# gui.plug.export package
|
||||
#
|
||||
gramps/gui/plug/export/__init__.py
|
||||
#
|
||||
# gui.plug.quick package
|
||||
#
|
||||
gramps/gui/plug/quick/__init__.py
|
||||
gramps/gui/plug/report/_drawreportdialog.py
|
||||
#
|
||||
# gui.plug.report package
|
||||
#
|
||||
gramps/gui/plug/report/__init__.py
|
||||
gramps/gui/plug/report/_drawreportdialog.py
|
||||
gramps/gui/plug/report/_textreportdialog.py
|
||||
gramps/gui/plug/report/_webreportdialog.py
|
||||
#
|
||||
@ -337,6 +402,10 @@ gramps/gui/selectors/baseselector.py
|
||||
gramps/gui/selectors/selectorexceptions.py
|
||||
gramps/gui/selectors/selectorfactory.py
|
||||
#
|
||||
# gui.test package
|
||||
#
|
||||
gramps/gui/test/user_test.py
|
||||
#
|
||||
# gui/views - the GUI views package
|
||||
#
|
||||
gramps/gui/views/__init__.py
|
||||
@ -344,25 +413,33 @@ gramps/gui/views/__init__.py
|
||||
# gui/views/treemodels - the GUI views package
|
||||
#
|
||||
gramps/gui/views/treemodels/__init__.py
|
||||
gramps/gui/views/treemodels/basemodel.py
|
||||
gramps/gui/views/treemodels/citationlistmodel.py
|
||||
gramps/gui/views/treemodels/eventmodel.py
|
||||
gramps/gui/views/treemodels/familymodel.py
|
||||
gramps/gui/views/treemodels/flatbasemodel.py
|
||||
gramps/gui/views/treemodels/lru.py
|
||||
gramps/gui/views/treemodels/notemodel.py
|
||||
gramps/gui/views/treemodels/repomodel.py
|
||||
gramps/gui/views/treemodels/sourcemodel.py
|
||||
#
|
||||
# gui.views.treemodels.test package
|
||||
#
|
||||
gramps/gui/views/treemodels/test/node_test.py
|
||||
#
|
||||
# gui/widgets - the GUI widgets package
|
||||
#
|
||||
gramps/gui/widgets/__init__.py
|
||||
gramps/gui/widgets/basicentry.py
|
||||
gramps/gui/widgets/dateentry.py
|
||||
gramps/gui/widgets/fanchart2way.py
|
||||
gramps/gui/widgets/fanchartdesc.py
|
||||
gramps/gui/widgets/grabbers.py
|
||||
gramps/gui/widgets/interactivesearchbox.py
|
||||
gramps/gui/widgets/linkbox.py
|
||||
gramps/gui/widgets/menuitem.py
|
||||
gramps/gui/widgets/multitreeview.py
|
||||
gramps/gui/widgets/placeentry.py
|
||||
gramps/gui/widgets/selectionwidget.py
|
||||
gramps/gui/widgets/shortlistcomboentry.py
|
||||
gramps/gui/widgets/springseparator.py
|
||||
gramps/gui/widgets/statusbar.py
|
||||
@ -374,28 +451,72 @@ gramps/gui/widgets/validatedcomboentry.py
|
||||
gramps/gui/widgets/valueaction.py
|
||||
gramps/gui/widgets/valuetoolitem.py
|
||||
#
|
||||
# plugins .gpr.py
|
||||
# plugins
|
||||
#
|
||||
gramps/plugins/__init__.py
|
||||
#
|
||||
# plugins/db directory
|
||||
# plugins/db/bsddb directory
|
||||
#
|
||||
gramps/plugins/db/dbapi/inmemorydb.py
|
||||
gramps/plugins/db/bsddb/__init__.py
|
||||
gramps/plugins/db/bsddb/bsddb.py
|
||||
gramps/plugins/db/bsddb/bsddbtxn.py
|
||||
gramps/plugins/db/bsddb/cursor.py
|
||||
gramps/plugins/db/bsddb/summary.py
|
||||
#
|
||||
# plugins/db/bsddb/test directory
|
||||
#
|
||||
gramps/plugins/db/bsddb/test/cursor_test.py
|
||||
gramps/plugins/db/bsddb/test/db_test.py
|
||||
gramps/plugins/db/bsddb/test/grampsdbtestbase.py
|
||||
gramps/plugins/db/bsddb/test/reference_map_test.py
|
||||
#
|
||||
# plugins/db/dbapi directory
|
||||
#
|
||||
gramps/plugins/db/dbapi/__init__.py
|
||||
gramps/plugins/db/dbapi/dbapi.py
|
||||
#
|
||||
# plugins/db/dbapi/test directory
|
||||
#
|
||||
gramps/plugins/db/dbapi/test/db_test.py
|
||||
#
|
||||
# plugins/docgen directory
|
||||
#
|
||||
gramps/plugins/docgen/__init__.py
|
||||
#
|
||||
# plugins/draw directory
|
||||
# plugins/drawreport directory
|
||||
#
|
||||
gramps/plugins/drawreport/__init__.py
|
||||
#
|
||||
# plugins/export directory
|
||||
#
|
||||
gramps/plugins/export/exportftree.py
|
||||
gramps/plugins/export/__init__.py
|
||||
gramps/plugins/export/exportftree.py
|
||||
#
|
||||
# plugins/export/test directory
|
||||
#
|
||||
gramps/plugins/export/test/exportvcard_test.py
|
||||
#
|
||||
# plugins/gramplet directory
|
||||
#
|
||||
gramps/plugins/gramplet/__init__.py
|
||||
gramps/plugins/gramplet/filter.py
|
||||
gramps/plugins/gramplet/gallery.py
|
||||
gramps/plugins/gramplet/mediapreview.py
|
||||
gramps/plugins/gramplet/metadataviewer.py
|
||||
#
|
||||
# plugins/graph directory
|
||||
#
|
||||
gramps/plugins/graph/__init__.py
|
||||
#
|
||||
# plugins/importer directory
|
||||
#
|
||||
gramps/plugins/importer/__init__.py
|
||||
#
|
||||
# plugins/importer/test directory
|
||||
#
|
||||
gramps/plugins/importer/test/importgeneweb_test.py
|
||||
gramps/plugins/importer/test/importvcard_test.py
|
||||
#
|
||||
# plugins/lib directory
|
||||
#
|
||||
gramps/plugins/lib/__init__.py
|
||||
@ -404,14 +525,19 @@ gramps/plugins/lib/libhtml.py
|
||||
gramps/plugins/lib/libmapservice.py
|
||||
gramps/plugins/lib/libmixin.py
|
||||
gramps/plugins/lib/libodfbackend.py
|
||||
gramps/plugins/lib/libplaceimport.py
|
||||
gramps/plugins/lib/librecurse.py
|
||||
#
|
||||
# plugins/map directory
|
||||
# plugins/lib/maps directory
|
||||
#
|
||||
gramps/plugins/lib/maps/__init__.py
|
||||
gramps/plugins/lib/maps/cairoprint.py
|
||||
gramps/plugins/lib/maps/constants.py
|
||||
gramps/plugins/lib/maps/datelayer.py
|
||||
gramps/plugins/lib/maps/dummylayer.py
|
||||
gramps/plugins/lib/maps/dummynogps.py
|
||||
gramps/plugins/lib/maps/__init__.py
|
||||
gramps/plugins/lib/maps/kmllayer.py
|
||||
gramps/plugins/lib/maps/libkml.py
|
||||
gramps/plugins/lib/maps/lifewaylayer.py
|
||||
gramps/plugins/lib/maps/markerlayer.py
|
||||
gramps/plugins/lib/maps/messagelayer.py
|
||||
@ -419,35 +545,14 @@ gramps/plugins/lib/maps/selectionlayer.py
|
||||
#
|
||||
# plugins/mapservices directory
|
||||
#
|
||||
gramps/plugins/mapservices/googlemap.py
|
||||
gramps/plugins/mapservices/__init__.py
|
||||
gramps/plugins/mapservices/googlemap.py
|
||||
gramps/plugins/mapservices/openstreetmap.py
|
||||
#
|
||||
# plugins/quickview directory
|
||||
#
|
||||
gramps/plugins/quickview/__init__.py
|
||||
#
|
||||
# plugins/import directory
|
||||
#
|
||||
gramps/plugins/importer/__init__.py
|
||||
gramps/plugins/importer/test/importvcard_test.py
|
||||
#
|
||||
# plugins/gramplet directory
|
||||
#
|
||||
gramps/plugins/gramplet/filter.py
|
||||
gramps/plugins/gramplet/gallery.py
|
||||
gramps/plugins/gramplet/__init__.py
|
||||
gramps/plugins/gramplet/mediapreview.py
|
||||
gramps/plugins/gramplet/metadataviewer.py
|
||||
gramps/plugins/gramplet/notegramplet.py
|
||||
gramps/plugins/gramplet/populategramplet.py
|
||||
gramps/plugins/gramplet/populategramplet.gpr.py
|
||||
gramps/plugins/gramplet/pythongramplet.py
|
||||
#
|
||||
# plugins/graph directory
|
||||
#
|
||||
gramps/plugins/graph/__init__.py
|
||||
#
|
||||
# plugins/rel directory
|
||||
#
|
||||
gramps/plugins/rel/__init__.py
|
||||
@ -460,6 +565,7 @@ gramps/plugins/rel/rel_fi.py
|
||||
gramps/plugins/rel/rel_fr.py
|
||||
gramps/plugins/rel/rel_hr.py
|
||||
gramps/plugins/rel/rel_hu.py
|
||||
gramps/plugins/rel/rel_is.py
|
||||
gramps/plugins/rel/rel_it.py
|
||||
gramps/plugins/rel/rel_nl.py
|
||||
gramps/plugins/rel/rel_no.py
|
||||
@ -469,12 +575,21 @@ gramps/plugins/rel/rel_ru.py
|
||||
gramps/plugins/rel/rel_sk.py
|
||||
gramps/plugins/rel/rel_sl.py
|
||||
gramps/plugins/rel/rel_sv.py
|
||||
gramps/plugins/rel/rel_uk.py
|
||||
#
|
||||
# plugins/sidebar directory
|
||||
#
|
||||
gramps/plugins/sidebar/__init__.py
|
||||
gramps/plugins/sidebar/categorysidebar.py
|
||||
gramps/plugins/sidebar/expandersidebar.py
|
||||
gramps/plugins/sidebar/__init__.py
|
||||
#
|
||||
# plugins/test directory
|
||||
#
|
||||
gramps/plugins/test/db_undo_and_signals_test.py
|
||||
gramps/plugins/test/exports_test.py
|
||||
gramps/plugins/test/imports_test.py
|
||||
gramps/plugins/test/reports_test.py
|
||||
gramps/plugins/test/tools_test.py
|
||||
#
|
||||
# Development tools
|
||||
#
|
||||
@ -484,21 +599,25 @@ gramps/plugins/tool/__init__.py
|
||||
#
|
||||
gramps/plugins/view/__init__.py
|
||||
#
|
||||
# plugins/web directory
|
||||
# plugins/webreport directory
|
||||
#
|
||||
gramps/plugins/webreport/__init__.py
|
||||
gramps/plugins/webreport/citation.py
|
||||
gramps/plugins/webreport/common.py
|
||||
#
|
||||
# plugins/webstuff directory
|
||||
#
|
||||
gramps/plugins/webstuff/__init__.py
|
||||
#
|
||||
# test
|
||||
#
|
||||
gramps/test/config_test.py
|
||||
gramps/test/gramps_cli_test.py
|
||||
gramps/test/__init__.py
|
||||
gramps/test/regrtest.py
|
||||
gramps/test/test/gedread_util_test.py
|
||||
gramps/test/test/test_util_test.py
|
||||
gramps/test/test_util.py
|
||||
gramps/test/utils_test.py
|
||||
#
|
||||
# test/test
|
||||
#
|
||||
gramps/test/test/test_util_test.py
|
||||
#
|
||||
# Glade files
|
||||
#
|
||||
|
3804
po/gramps.pot
3804
po/gramps.pot
File diff suppressed because it is too large
Load Diff
8
po/it.po
8
po/it.po
@ -43225,14 +43225,6 @@ msgstr "Nessun foglio di stile"
|
||||
#~ msgid "Ancestors of \"%s\""
|
||||
#~ msgstr "Ascendenti di \"%s\""
|
||||
|
||||
#~ msgid "Generation %d has 1 individual. (%3.2f%%)\n"
|
||||
#~ msgstr "La generazione %d comprende 1 persona. (%3.2f%%)\n"
|
||||
|
||||
#~ msgid "Total ancestors in generations 2 to %d is %d. (%3.2f%%)\n"
|
||||
#~ msgstr ""
|
||||
#~ "Il numero totale di ascendenti dalla generazione 2 alla generazione %d è "
|
||||
#~ "%d. (%3.2f%%)\n"
|
||||
|
||||
#~ msgid "Whether to compress tree."
|
||||
#~ msgstr "Indica se comprimere l'albero."
|
||||
|
||||
|
7
po/mk.po
7
po/mk.po
@ -32154,13 +32154,6 @@ msgstr "Неуспешно"
|
||||
msgid "Number of Ancestors for %s"
|
||||
msgstr "Број на предци"
|
||||
|
||||
#: ../gramps/plugins/textreport/numberofancestorsreport.py:125
|
||||
#, fuzzy, python-brace-format
|
||||
msgid "Generation {number} has {count} individual. {percent}"
|
||||
msgid_plural "Generation {number} has {count} individuals. {percent}"
|
||||
msgstr[0] "Генерацијата %d има %d лица. (%3.2f%%)\n"
|
||||
msgstr[1] "Генерацијата %d има %d лица. (%3.2f%%)\n"
|
||||
|
||||
#. TC # English return something like:
|
||||
#. Total ancestors in generations 2 to 3 is 4. (66.67%)
|
||||
#: ../gramps/plugins/textreport/numberofancestorsreport.py:167
|
||||
|
6
po/pl.po
6
po/pl.po
@ -42397,12 +42397,6 @@ msgstr "Bez arkusza stylów"
|
||||
#~ msgid "Filtering"
|
||||
#~ msgstr "Filtrowanie"
|
||||
|
||||
#~ msgid "Generation %d has 1 individual. (%3.2f%%)"
|
||||
#~ msgstr "Pokolenie %d ma 1 osobę. (%3.2f%%)"
|
||||
|
||||
#~ msgid "Total ancestors in generations 2 to %d is %d. (%3.2f%%)"
|
||||
#~ msgstr "Łączna ilość przodków w pokoleniach od 2 do %d wynosi %d. (%3.2f%%)"
|
||||
|
||||
#~ msgid "Easter"
|
||||
#~ msgstr "Wielkanoc"
|
||||
|
||||
|
2739
po/pt_BR.po
2739
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
11585
po/pt_PT.po
11585
po/pt_PT.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user