Added new Gramplet: AgeStats.py that creates textual distribution graphs where each row can be double-clicked to show matching groups
svn: r11499
This commit is contained in:
parent
48dc331f3e
commit
e10cf8deb1
@ -237,6 +237,7 @@ src/Selectors/__init__.py
|
||||
|
||||
# plugins directory
|
||||
src/plugins/AgeOnDate.py
|
||||
src/plugins/AgeStats.py
|
||||
src/plugins/all_events.py
|
||||
src/plugins/all_relations.py
|
||||
src/plugins/AncestorReport.py
|
||||
|
203
src/plugins/AgeStats.py
Normal file
203
src/plugins/AgeStats.py
Normal file
@ -0,0 +1,203 @@
|
||||
from DataViews import register, Gramplet
|
||||
|
||||
class AgeStatsGramplet(Gramplet):
|
||||
def on_load(self):
|
||||
self.no_wrap()
|
||||
tag = self.gui.buffer.create_tag("fixed")
|
||||
tag.set_property("font", "Courier 8")
|
||||
|
||||
def db_changed(self):
|
||||
self.update()
|
||||
|
||||
def main(self):
|
||||
self.clear_text()
|
||||
age_dict = {}
|
||||
mother_dict = {}
|
||||
father_dict = {}
|
||||
age_handles = [[] for age in range(120)]
|
||||
mother_handles = [[] for age in range(60)]
|
||||
father_handles = [[] for age in range(60)]
|
||||
text = ""
|
||||
handles = self.dbstate.db.get_person_handles(sort_handles=False)
|
||||
for h in handles:
|
||||
yield True
|
||||
p = self.dbstate.db.get_person_from_handle(h)
|
||||
# if birth_date and death_date, compute age
|
||||
birth_ref = p.get_birth_ref()
|
||||
birth_date = None
|
||||
if birth_ref:
|
||||
birth_event = self.dbstate.db.get_event_from_handle(birth_ref.ref)
|
||||
birth_date = birth_event.get_date_object()
|
||||
death_ref = p.get_death_ref()
|
||||
death_date = None
|
||||
if death_ref:
|
||||
death_event = self.dbstate.db.get_event_from_handle(death_ref.ref)
|
||||
death_date = death_event.get_date_object()
|
||||
if death_date and birth_date and birth_date.get_year() != 0:
|
||||
age = death_date.get_year() - birth_date.get_year()
|
||||
if age >= 0 and age < 120:
|
||||
age_dict[age] = age_dict.get(age, 0) + 1
|
||||
age_handles[age].append(h)
|
||||
#else:
|
||||
# print "Age out of range: %d for %s" % (age,
|
||||
# p.get_primary_name().get_first_name()
|
||||
# + " " + p.get_primary_name().get_surname())
|
||||
# for each parent m/f:
|
||||
family_list = p.get_parent_family_handle_list()
|
||||
if len(family_list) > 0:
|
||||
family = self.dbstate.db.get_family_from_handle(family_list[0]) # first is primary, I think
|
||||
if family:
|
||||
f_handle = family.get_father_handle()
|
||||
m_handle = family.get_mother_handle()
|
||||
# if they have a birth_date, compute difference each m/f
|
||||
if f_handle:
|
||||
f = self.dbstate.db.get_person_from_handle(f_handle)
|
||||
bref = f.get_birth_ref()
|
||||
if bref:
|
||||
bevent = self.dbstate.db.get_event_from_handle(bref.ref)
|
||||
bdate = bevent.get_date_object()
|
||||
if bdate and birth_date and birth_date.get_year() != 0:
|
||||
diff = birth_date.get_year() - bdate.get_year()
|
||||
if diff >= 0 and diff < 60:
|
||||
father_dict[diff] = father_dict.get(diff, 0) + 1
|
||||
father_handles[diff].append(f_handle)
|
||||
#else:
|
||||
# print "Father diff out of range: %d for %s" % (diff,
|
||||
# p.get_primary_name().get_first_name()
|
||||
# + " " + p.get_primary_name().get_surname())
|
||||
if m_handle:
|
||||
m = self.dbstate.db.get_person_from_handle(m_handle)
|
||||
bref = m.get_birth_ref()
|
||||
if bref:
|
||||
bevent = self.dbstate.db.get_event_from_handle(bref.ref)
|
||||
bdate = bevent.get_date_object()
|
||||
if bdate and birth_date and birth_date.get_year() != 0:
|
||||
diff = birth_date.get_year() - bdate.get_year()
|
||||
if diff >= 0 and diff < 60:
|
||||
mother_dict[diff] = mother_dict.get(diff, 0) + 1
|
||||
mother_handles[diff].append(m_handle)
|
||||
#else:
|
||||
# print "Mother diff out of range: %d for %s" % (diff,
|
||||
# p.get_primary_name().get_first_name()
|
||||
# + " " + p.get_primary_name().get_surname())
|
||||
width = 60
|
||||
graph_width = width - 8
|
||||
self.create_bargraph(age_dict, age_handles, "Lifespan Age Distribution", "Age", graph_width, 5, 120)
|
||||
self.create_bargraph(father_dict, father_handles, "Father - Child Age Diff Distribution", "Diff", graph_width, 5, 60)
|
||||
self.create_bargraph(mother_dict, mother_handles, "Mother - Child Age Diff Distribution", "Diff", graph_width, 5, 60)
|
||||
start, end = self.gui.buffer.get_bounds()
|
||||
self.gui.buffer.apply_tag_by_name("fixed", start, end)
|
||||
self.append_text("", scroll_to="begin")
|
||||
|
||||
def ticks(self, width, start = 0, stop = 100, fill = " "):
|
||||
""" Returns the tickmark numbers for a graph axis """
|
||||
count = int(width / 10.0)
|
||||
retval = "%-3d" % start
|
||||
space = int((width - count * 3) / float(count - 1))
|
||||
incr = (stop - start) / float(count - 1)
|
||||
lastincr = 0
|
||||
for i in range(count - 2):
|
||||
retval += " " * space
|
||||
newincr = int(start + (i + 1) * incr)
|
||||
if newincr != lastincr:
|
||||
retval += "%3d" % newincr
|
||||
else:
|
||||
retval += " | "
|
||||
lastincr = newincr
|
||||
rest = width - len(retval) - 3 + 1
|
||||
retval += " " * rest
|
||||
retval += "%3d" % int(stop)
|
||||
return retval
|
||||
|
||||
def format(self, text, width, align = "left", borders = "||", fill = " "):
|
||||
""" Returns a formatted string for nice, fixed-font display """
|
||||
if align == "center":
|
||||
text = text.center(width, fill)
|
||||
elif align == "left":
|
||||
text = (text + (fill * width))[:width]
|
||||
elif align == "right":
|
||||
text = ((fill * width) + text)[-width:]
|
||||
if borders[0] != None:
|
||||
text = borders[0] + text
|
||||
if borders[1] != None:
|
||||
text = text + borders[1]
|
||||
return text
|
||||
|
||||
def compute_stats(self, hash):
|
||||
""" Returns the statistics of a dictionary of data """
|
||||
hashkeys = hash.keys()
|
||||
hashkeys.sort()
|
||||
count = sum(hash.values())
|
||||
sumval = sum([k * hash[k] for k in hash])
|
||||
minval = min(hashkeys)
|
||||
maxval = max(hashkeys)
|
||||
median = 0
|
||||
average = 0
|
||||
if count > 0:
|
||||
current = 0
|
||||
for k in hashkeys:
|
||||
if current + hash[k] > count/2:
|
||||
median = k
|
||||
break
|
||||
current += hash[k]
|
||||
average = sumval/float(count)
|
||||
retval = "Statistics:\n"
|
||||
retval += " Total : %d\n" % count
|
||||
retval += " Minimum: %d\n" % minval
|
||||
retval += " Average: %.2f\n" % average
|
||||
retval += " Median : %d\n" % median
|
||||
retval += " Maximum: %d\n" % maxval
|
||||
return retval
|
||||
|
||||
def make_handles_set(self, min, max, handles):
|
||||
retval = []
|
||||
for i in range(min, max):
|
||||
try:
|
||||
retval.extend(handles[i])
|
||||
except:
|
||||
pass
|
||||
return retval
|
||||
|
||||
def create_bargraph(self, hash, handles, title, column, graph_width, bin_size, max_val):
|
||||
"""
|
||||
Create a bargraph based on the data in hash. hash is a dict, like:
|
||||
hash = {12: 4, 20: 6, 35: 13, 50: 5}
|
||||
where the key is the age, and the value stored is the count.
|
||||
"""
|
||||
# first, binify:
|
||||
bin = [0] * (max_val/bin_size)
|
||||
for value in hash.keys():
|
||||
bin[value/bin_size] += hash[value]
|
||||
text = ""
|
||||
max_bin = float(max(bin))
|
||||
if max_bin != 0:
|
||||
i = 0
|
||||
self.append_text("--------" + self.format("", graph_width, fill = "-", borders="++") + "\n")
|
||||
self.append_text(column.center(8) + self.format(title, graph_width, align="center") + "\n")
|
||||
self.append_text("--------" + self.format("", graph_width, fill = "-", borders="++") + "\n")
|
||||
for bin in bin:
|
||||
self.append_text((" %3d-%3d" % (i * 5, (i+1)* 5,)))
|
||||
selected = self.make_handles_set(i * 5, (i+1) *5, handles)
|
||||
self.link(self.format("X" * int(bin/max_bin * graph_width), graph_width),
|
||||
'PersonList',
|
||||
selected,
|
||||
tooltip=_("Double-click to see %d people" % len(selected)))
|
||||
self.append_text("\n")
|
||||
i += 1
|
||||
self.append_text("--------" + self.format("", graph_width, fill = "-", borders="++") + "\n")
|
||||
self.append_text(" Counts " + self.ticks(graph_width, start = 0, stop = int(max_bin)) + "\n\n")
|
||||
self.append_text(self.compute_stats(hash))
|
||||
self.append_text("\n")
|
||||
|
||||
register(type="gramplet",
|
||||
name = "Age Stats Gramplet",
|
||||
tname = _("Age Stats Gramplet"),
|
||||
height=300,
|
||||
expand=True,
|
||||
content = AgeStatsGramplet,
|
||||
title=_("Age Stats"),
|
||||
detached_width = 600,
|
||||
detached_height = 450,
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,7 @@ pkgdata_PYTHON = \
|
||||
all_events.py\
|
||||
all_relations.py\
|
||||
AgeOnDate.py\
|
||||
AgeStats.py\
|
||||
AncestorTree.py\
|
||||
AncestorReport.py\
|
||||
BookReport.py\
|
||||
|
Loading…
Reference in New Issue
Block a user