1# Gramps - a GTK+/GNOME based genealogy program 2# 3# Copyright (C) 2009 Pander Musubi 4# Copyright (C) 2009 Douglas S. Blank 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19# 20# 21 22#------------------------------------------------------------------------- 23# 24# Python modules 25# 26#------------------------------------------------------------------------- 27from collections import defaultdict 28 29#------------------------------------------------------------------------- 30# 31# Gramps modules 32# 33#------------------------------------------------------------------------- 34from gramps.gen.plug import Gramplet 35from gramps.gen.config import config 36from gramps.gen.const import GRAMPS_LOCALE as glocale 37_ = glocale.translation.gettext 38 39_YIELD_INTERVAL = 350 40 41def make_tag_size(n, counts, mins=8, maxs=20): 42 # return font sizes mins to maxs 43 diff = maxs - mins 44 # based on counts (biggest to smallest) 45 if len(counts) > 1: 46 position = diff - (diff * (float(counts.index(n)) / (len(counts) - 1))) 47 else: 48 position = 0 49 return int(position) + mins 50 51class GivenNameCloudGramplet(Gramplet): 52 def init(self): 53 self.set_tooltip(_("Double-click given name for details")) 54 self.top_size = 100 # will be overwritten in load 55 self.set_text(_("No Family Tree loaded.")) 56 57 def db_changed(self): 58 self.connect(self.dbstate.db, 'person-add', self.update) 59 self.connect(self.dbstate.db, 'person-delete', self.update) 60 self.connect(self.dbstate.db, 'person-update', self.update) 61 62 def on_load(self): 63 if len(self.gui.data) > 0: 64 self.top_size = int(self.gui.data[0]) 65 66 def on_save(self): 67 self.gui.data = [self.top_size] 68 69 def main(self): 70 self.set_text(_("Processing...") + "\n") 71 yield True 72 givensubnames = defaultdict(int) 73 representative_handle = {} 74 75 cnt = 0 76 for person in self.dbstate.db.iter_people(): 77 allnames = [person.get_primary_name()] + person.get_alternate_names() 78 allnames = set(name.get_first_name().strip() for name in allnames) 79 for givenname in allnames: 80 nbsp = givenname.split('\u00A0') 81 if len(nbsp) > 1: # there was an NBSP, a non-breaking space 82 first_two = nbsp[0] + '\u00A0' + nbsp[1].split()[0] 83 givensubnames[first_two] += 1 84 representative_handle[first_two] = person.handle 85 givenname = ' '.join(nbsp[1].split()[1:]) 86 for givensubname in givenname.split(): 87 givensubnames[givensubname] += 1 88 representative_handle[givensubname] = person.handle 89 cnt += 1 90 if not cnt % _YIELD_INTERVAL: 91 yield True 92 93 total_people = cnt 94 givensubname_sort = [] 95 96 total = cnt = 0 97 for givensubname in givensubnames: 98 givensubname_sort.append((givensubnames[givensubname], 99 givensubname)) 100 total += givensubnames[givensubname] 101 cnt += 1 102 if not cnt % _YIELD_INTERVAL: 103 yield True 104 105 total_givensubnames = cnt 106 givensubname_sort.sort(reverse=True) 107 cloud_names = [] 108 cloud_values = [] 109 110 for count, givensubname in givensubname_sort: 111 cloud_names.append((count, givensubname)) 112 cloud_values.append(count) 113 114 cloud_names.sort(key=lambda k: k[1]) 115 counts = sorted(set(cloud_values), reverse=True) 116 line = 0 117 ### All done! 118 # Now, find out how many we can display without going over top_size: 119 totals = defaultdict(int) 120 for (count, givensubname) in cloud_names: # givensubname_sort: 121 totals[count] += 1 122 sums = sorted(totals, reverse=True) 123 total = 0 124 include_greater_than = 0 125 for s in sums: 126 if total + totals[s] <= self.top_size: 127 total += totals[s] 128 else: 129 include_greater_than = s 130 break 131 # Ok, now we can show those counts > include_greater_than: 132 133 self.set_text("") 134 showing = 0 135 for (count, givensubname) in cloud_names: # givensubname_sort: 136 if count > include_greater_than: 137 if len(givensubname) == 0: 138 text = config.get('preferences.no-surname-text') 139 else: 140 text = givensubname 141 size = make_tag_size(count, counts) 142 self.link(text, 'Given', text, size, 143 "%s, %.2f%% (%d)" % 144 (text, 145 (float(count)/total_people) * 100, 146 count)) 147 self.append_text(" ") 148 showing += 1 149 150 self.append_text(("\n\n" + _("Total unique given names") + ": %d\n") % 151 total_givensubnames) 152 self.append_text((_("Total given names showing") + ": %d\n") % showing) 153 self.append_text((_("Total people") + ": %d") % total_people, "begin") 154