1# -*- coding: utf-8 -*-
2#
3# Gramps - a GTK+/GNOME based genealogy program
4#
5# Copyright (C) 2000-2006  Martin Hawlisch, Donald N. Allingham
6# Copyright (C) 2008       Brian G. Matherly
7# Copyright (C) 2010       Jakim Friant
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22#
23
24"""
25Validate localized date parser and displayer.
26
27Tools/Debug/Check Localized Date Parser and Displayer
28"""
29
30#-------------------------------------------------------------------------
31#
32# standard python modules
33#
34#-------------------------------------------------------------------------
35import traceback
36import sys
37from gramps.gen.const import GRAMPS_LOCALE as glocale
38_ = glocale.translation.gettext
39
40#-------------------------------------------------------------------------
41#
42# Gramps modules
43#
44#-------------------------------------------------------------------------
45from gramps.gen.lib import Date, Event, EventRef, EventType, Name, Person, Surname, Tag
46from gramps.gen.db import DbTxn
47from gramps.gui.plug import tool
48from gramps.gui.utils import ProgressMeter
49from gramps.gui.dialog import QuestionDialog
50from gramps.gen.datehandler import parser as _dp
51from gramps.gen.datehandler import displayer as _dd
52
53#-------------------------------------------------------------------------
54#
55#
56#
57#-------------------------------------------------------------------------
58class DateParserDisplayTest(tool.Tool):
59
60    def __init__(self, dbstate, user, options_class, name, callback=None):
61        uistate = user.uistate
62
63        tool.Tool.__init__(self, dbstate, options_class, name)
64        if uistate:
65            # Running with gui -> Show message
66            self.parent_window = uistate.window
67            QuestionDialog(_("Start date test?"),
68                _("This test will create many persons and events " \
69                    "in the current database. Do you really want to " \
70                    "run this test?"),
71                _("Run test"),
72                self.run_tool,
73                parent=self.parent_window)
74        else:
75            self.parent_window = None
76            self.run_tool()
77
78
79    def run_tool(self):
80        self.progress = ProgressMeter(_('Running Date Test'), '',
81                                      parent=self.parent_window)
82        self.progress.set_pass(_('Generating dates'),
83                               4)
84        dates = []
85        # first some valid dates
86        calendar = Date.CAL_GREGORIAN
87        for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED,
88                        Date.QUAL_CALCULATED):
89            for modifier in (Date.MOD_NONE, Date.MOD_BEFORE,
90                             Date.MOD_AFTER, Date.MOD_ABOUT):
91                for slash1 in (False,True):
92                    for month in range(0,13):
93                        for day in (0,5,27):
94                            if not month and day:
95                                continue
96                            d = Date()
97                            d.set(quality,modifier,calendar,(day,month,1789,slash1),"Text comment")
98                            dates.append( d)
99            for modifier in (Date.MOD_RANGE, Date.MOD_SPAN):
100                for slash1 in (False,True):
101                    for slash2 in (False,True):
102                        for month in range(0,13):
103                            for day in (0,5,27):
104                                if not month and day:
105                                    continue
106
107                                d = Date()
108                                d.set(quality,modifier,calendar,(day,month,1789,slash1,day,month,1876,slash2),"Text comment")
109                                dates.append( d)
110
111                                if not month:
112                                    continue
113
114                                d = Date()
115                                d.set(quality,modifier,calendar,(day,month,1789,slash1,day,13-month,1876,slash2),"Text comment")
116                                dates.append( d)
117
118                                if not day:
119                                    continue
120
121                                d = Date()
122                                d.set(quality,modifier,calendar,(day,month,1789,slash1,32-day,month,1876,slash2),"Text comment")
123                                dates.append( d)
124                                d = Date()
125                                d.set(quality,modifier,calendar,(day,month,1789,slash1,32-day,13-month,1876,slash2),"Text comment")
126                                dates.append( d)
127            modifier = Date.MOD_TEXTONLY
128            d = Date()
129            d.set(quality,modifier,calendar,Date.EMPTY,
130                  "This is a textual date")
131            dates.append( d)
132            self.progress.step()
133
134        # test invalid dates
135        #dateval = (4,7,1789,False,5,8,1876,False)
136        #for l in range(1,len(dateval)):
137        #    d = Date()
138        #    try:
139        #        d.set(Date.QUAL_NONE,Date.MOD_NONE,
140        #              Date.CAL_GREGORIAN,dateval[:l],"Text comment")
141        #        dates.append( d)
142        #    except DateError, e:
143        #        d.set_as_text("Date identified value correctly as invalid.\n%s" % e)
144        #        dates.append( d)
145        #    except:
146        #        d = Date()
147        #        d.set_as_text("Date.set Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),))
148        #        dates.append( d)
149        #for l in range(1,len(dateval)):
150        #    d = Date()
151        #    try:
152        #        d.set(Date.QUAL_NONE,Date.MOD_SPAN,Date.CAL_GREGORIAN,dateval[:l],"Text comment")
153        #        dates.append( d)
154        #    except DateError, e:
155        #        d.set_as_text("Date identified value correctly as invalid.\n%s" % e)
156        #        dates.append( d)
157        #    except:
158        #        d = Date()
159        #        d.set_as_text("Date.set Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),))
160        #        dates.append( d)
161        #self.progress.step()
162        #d = Date()
163        #d.set(Date.QUAL_NONE,Date.MOD_NONE,
164        #      Date.CAL_GREGORIAN,(44,7,1789,False),"Text comment")
165        #dates.append( d)
166        #d = Date()
167        #d.set(Date.QUAL_NONE,Date.MOD_NONE,
168        #      Date.CAL_GREGORIAN,(4,77,1789,False),"Text comment")
169        #dates.append( d)
170        #d = Date()
171        #d.set(Date.QUAL_NONE,Date.MOD_SPAN,
172        #      Date.CAL_GREGORIAN,
173        #      (4,7,1789,False,55,8,1876,False),"Text comment")
174        #dates.append( d)
175        #d = Date()
176        #d.set(Date.QUAL_NONE,Date.MOD_SPAN,
177        #      Date.CAL_GREGORIAN,
178        #      (4,7,1789,False,5,88,1876,False),"Text comment")
179        #dates.append( d)
180
181        with DbTxn(_("Date Test Plugin"), self.db, batch=True) as self.trans:
182            self.db.disable_signals()
183            self.progress.set_pass(_('Generating dates'),
184                                   len(dates))
185
186            # create pass and fail tags
187            pass_handle = self.create_tag(_('Pass'), '#0000FFFF0000')
188            fail_handle = self.create_tag(_('Fail'), '#FFFF00000000')
189
190            # now add them as birth to new persons
191            i = 1
192            for dateval in dates:
193                person = Person()
194                surname = Surname()
195                surname.set_surname("DateTest")
196                name = Name()
197                name.add_surname(surname)
198                name.set_first_name("Test %d" % i)
199                person.set_primary_name(name)
200                self.db.add_person(person, self.trans)
201                bevent = Event()
202                bevent.set_type(EventType.BIRTH)
203                bevent.set_date_object(dateval)
204                bevent.set_description("Date Test %d (source)" % i)
205                bevent_h = self.db.add_event(bevent, self.trans)
206                bevent_ref = EventRef()
207                bevent_ref.set_reference_handle(bevent_h)
208                # for the death event display the date as text and parse it back to a new date
209                ndate = None
210                try:
211                    datestr = _dd.display( dateval)
212                    try:
213                        ndate = _dp.parse( datestr)
214                        if not ndate:
215                            ndate = Date()
216                            ndate.set_as_text("DateParser None")
217                            person.add_tag(fail_handle)
218                        else:
219                            person.add_tag(pass_handle)
220                    except:
221                        ndate = Date()
222                        ndate.set_as_text("DateParser Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),))
223                        person.add_tag(fail_handle)
224                    else:
225                        person.add_tag(pass_handle)
226                except:
227                    ndate = Date()
228                    ndate.set_as_text("DateDisplay Exception: %s" % ("".join(traceback.format_exception(*sys.exc_info())),))
229                    person.add_tag(fail_handle)
230
231                if dateval.get_modifier() != Date.MOD_TEXTONLY \
232                       and ndate.get_modifier() == Date.MOD_TEXTONLY:
233                    # parser was unable to correctly parse the string
234                    ndate.set_as_text( "TEXTONLY: "+ndate.get_text())
235                    person.add_tag(fail_handle)
236                if dateval.get_modifier() == Date.MOD_TEXTONLY \
237                        and dateval.get_text().count("Traceback") \
238                        and pass_handle in person.get_tag_list():
239                    person.add_tag(fail_handle)
240
241                devent = Event()
242                devent.set_type(EventType.DEATH)
243                devent.set_date_object(ndate)
244                devent.set_description("Date Test %d (result)" % i)
245                devent_h = self.db.add_event(devent, self.trans)
246                devent_ref = EventRef()
247                devent_ref.set_reference_handle(devent_h)
248                person.set_birth_ref(bevent_ref)
249                person.set_death_ref(devent_ref)
250                self.db.commit_person(person, self.trans)
251                i = i + 1
252                self.progress.step()
253        self.db.enable_signals()
254        self.db.request_rebuild()
255        self.progress.close()
256
257    def create_tag(self, tag_name, tag_color):
258        """
259        Create a tag if it doesn't already exist.
260        """
261        tag = self.db.get_tag_from_name(tag_name)
262        if tag is None:
263            tag = Tag()
264            tag.set_name(tag_name)
265            if tag_color is not None:
266                tag.set_color(tag_color)
267            tag.set_priority(self.db.get_number_of_tags())
268            tag_handle = self.db.add_tag(tag, self.trans)
269        else:
270            tag_handle = tag.get_handle()
271        return tag_handle
272
273#------------------------------------------------------------------------
274#
275# DateParserDisplayTestOptions
276#
277#------------------------------------------------------------------------
278class DateParserDisplayTestOptions(tool.ToolOptions):
279    """
280    Defines options and provides handling interface.
281    """
282    def __init__(self, name, person_id=None):
283        """ Initialize the options class """
284        tool.ToolOptions.__init__(self, name, person_id)
285