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