]> ToastFreeware Gitweb - philipp/winterrodeln/wradmin.git/blob - wradmin/wradmin/model/validators.py
9f555e854481166ede3aec4578630655ce54b212
[philipp/winterrodeln/wradmin.git] / wradmin / wradmin / model / validators.py
1 # -*- coding: iso-8859-15 -*-
2 import formencode
3 import datetime
4 import re
5 import xml.dom.minidom as minidom
6 from xml.parsers.expat import ExpatError
7
8
9 class GermanBool(formencode.FancyValidator):
10     "Converts German bool values to the python bool type. 'Ja' and 'Nein' are supported."
11     
12     def __init__(self, yes = [u'Ja'], no = [u'Nein'], **keywords):
13         "The yes and no arguments specify the valid possibilities. The first possibility is the default one."
14         formencode.FancyValidator.__init__(self, **keywords)
15         self.yes = yes
16         self.no = no
17     
18     def _to_python(self, value, state):
19         self.assert_string(value, state)
20         if value in self.yes: return True
21         if value in self.no: return False
22         all = self.yes[:]
23         all.extend(self.no)
24         raise formencode.Invalid(u"'%s' is not a valid boolean value, use one of %s" % (value, all), value, state)
25     
26     def from_python(self, value):
27         if value is None: return u''
28         if value: return self.yes[0]
29         return self.no[0]
30
31
32 class GenericDateTimeConverter(formencode.FancyValidator):
33     """Converts generic date/time information to datetime classes with a user defined format.
34     '2009-03-22 20:36:15' would be specified as '%Y-%m-%d %H:%M:%S'."""
35     
36     def __init__(self, date_time_format = '%Y-%m-%d %H:%M:%S', **keywords):
37         formencode.FancyValidator.__init__(self, **keywords)
38         self.date_time_format = date_time_format
39     
40     def _to_python(self, value, state):
41         self.assert_string(value, state)
42         try: return datetime.datetime.strptime(value, self.date_time_format)
43         except ValueError, e: raise formencode.Invalid(str(e), value, state)
44     
45     def _from_python(self, value, state):
46         if value is None: return u''
47         return value.strftime(self.date_time_format)
48
49
50 class DateTimeNoSecConverter(GenericDateTimeConverter):
51     def __init__(self, **keywords):
52         GenericDateTimeConverter.__init__(self, '%Y-%m-%d %H:%M', **keywords)
53
54
55 class DateConverter(GenericDateTimeConverter):
56     "Converts date information to date classes with the format '%Y-%m-%d'."
57     
58     def __init__(self, **keywords):
59         GenericDateTimeConverter.__init__(self, '%Y-%m-%d', **keywords)
60     
61     def _to_python(self, value, state):
62         GenericDateTimeConverter._to_python(self, value, state).date()
63
64
65 class GermanTristate(formencode.FancyValidator):
66     """Does the following conversion:
67     None         -> (None, None)
68     u'Ja'        -> (True, False)
69     u'Teilweise' -> (True,  True)
70     u'Nein'      -> (False, True)"""
71     
72     def __init__(self, yes_python = (True, False), no_python = (False, True), partly_python = (True, True), yes_text = [u'Ja'], no_text = [u'Nein'], partly_text = [u'Teilweise'], **keywords):
73         formencode.FancyValidator.__init__(self, if_empty = (None, None), **keywords)
74         self.yes_python = yes_python
75         self.no_python = no_python
76         self.partly_python = partly_python
77         self.yes_text = yes_text
78         self.no_text = no_text
79         self.partly_text = partly_text
80     
81     def _to_python(self, value, state):
82         self.assert_string(value, state)
83         if value in self.yes_text: return self.yes_python
84         if value in self.no_text: return self.no_python
85         if value in self.partly_text: return self.partly_python
86         all = self.yes_text[:]
87         all.extend(self.no_text)
88         all.extend(self.partly_text)
89         raise formencode.Invalid(u"'%s' is not a valid value, use one of %s" % (value, all), value, state)
90     
91     def _from_python(self, value, state):
92         if value == (None, None): return ''
93         if value == self.yes_python: return self.yes_text[0]
94         if value == self.no_python: return self.no_text[0]
95         if value == self.partly_python: return self.partly_text[0]
96         raise formencode.Invalid(u"Invalid representation of a tristate value: '%s'" % (value,), value, state)
97
98
99 class Geo(formencode.FancyValidator):
100     "Formats to coordinates '47.076207 N 11.453553 E' to the (latitude, longitude) tuplet."
101     
102     def __init__(self, **keywords):
103         formencode.FancyValidator.__init__(self, if_empty = (None, None), **keywords)
104     
105     def _to_python(self, value, state):
106         r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', value)
107         if r is None: raise formencode.Invalid(u"Coordinates '%s' have not a format like '47.076207 N 11.453553 E'" % value, value, state)
108         return (float(r.groups()[0]), float(r.groups()[1]))
109     
110     def _from_python(self, value, state):
111         if value == (None, None): return ''
112         latitude, longitude = value
113         return u'%.6f N %.6f E' % (latitude, longitude)
114
115
116 class MultiGeo(formencode.FancyValidator):
117     "Formats multiple coordinates, even in multiple lines to [(latitude, longitude, elevation), ...] or [(latitude, longitude, None), ...] tuplets."
118     
119     # Valid for input_format
120     FORMAT_GUESS = 0         # guesses the input format; default for input_format
121     FORMAT_NONE = -1          # indicates missing formats
122     
123     # Valid for input_format and output_format
124     FORMAT_GEOCACHING = 1    # e.g. "N 47° 13.692 E 011° 25.535"
125     FORMAT_WINTERRODELN = 2  # e.g. "47.222134 N 11.467211 E"
126     FORMAT_GMAPPLUGIN = 3    # e.g. "47.232922, 11.452239"
127     FORMAT_GPX = 4           # e.g. "<trkpt lat="47.181289" lon="11.408827"><ele>1090.57</ele></trkpt>"
128     
129     input_format = FORMAT_GUESS
130     output_format = FORMAT_WINTERRODELN
131     last_input_format = FORMAT_NONE
132
133     def __init__(self, input_format = FORMAT_GUESS, output_format = FORMAT_WINTERRODELN, **keywords):
134         self.input_format = input_format
135         self.output_format = output_format
136         formencode.FancyValidator.__init__(self, if_empty = (None, None, None), **keywords)
137     
138     def _to_python(self, value, state):
139         input_format = self.input_format
140         if not input_format in [self.FORMAT_GUESS, self.FORMAT_GEOCACHING, self.FORMAT_WINTERRODELN, self.FORMAT_GMAPPLUGIN, self.FORMAT_GPX]:
141             raise formencode.Invalid(u"input_format %d is not recognized" % input_format, value, state) # Shouldn't it be an other type of runtime error?
142         lines = [line.strip() for line in value.split("\n") if len(line.strip()) > 0]
143         
144         result = []
145         for line in lines:
146             if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GEOCACHING:
147                 r = re.match(u'N ?(\d+)° ?(\d+\.\d+) +E ?(\d+)° ?(\d+\.\d+)', line)
148                 if not r is None:
149                     g = r.groups()
150                     result.append((float(g[0]) + float(g[1])/60, float(g[2]) + float(g[3])/60, None))
151                     last_input_format = self.FORMAT_WINTERRODELN
152                     continue
153                     
154             if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_WINTERRODELN:
155                 r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', line)
156                 if not r is None:
157                     result.append((float(r.groups()[0]), float(r.groups()[1]), None))
158                     last_input_format = self.FORMAT_WINTERRODELN
159                     continue
160                 
161             if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GMAPPLUGIN:
162                 r = re.match(u'(\d+\.\d+), ?(\d+\.\d+)', line)
163                 if not r is None:
164                     result.append((float(r.groups()[0]), float(r.groups()[1]), None))
165                     last_input_format = self.FORMAT_GMAPPLUGIN
166                     continue
167                 
168             if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GPX:
169                 try:
170                     xml = minidom.parseString(line)
171                     coord = xml.documentElement
172                     lat = float(coord.getAttribute('lat'))
173                     lon = float(coord.getAttribute('lon'))
174                     try: ele = float(coord.childNodes[0].childNodes[0].nodeValue)
175                     except (IndexError, ValueError): ele = None
176                     result.append((lat, lon, ele))
177                     last_input_format = self.FORMAT_GPX
178                     continue
179                 except (ExpatError, IndexError, ValueError): pass
180
181             raise formencode.Invalid(u"Coordinates '%s' have no known format" % line, value, state)
182             
183         return result
184     
185     def _from_python(self, value, state):
186         output_format = self.output_format
187         result = []
188         for latitude, longitude, height in value:
189             if output_format == self.FORMAT_GEOCACHING:
190                 degree = latitude
191                 result.append(u'N %02d° %02.3f E %03d° %02.3f' % (latitude, latitude % 1 * 60, longitude, longitude % 1 * 60))
192                 
193             elif output_format == self.FORMAT_WINTERRODELN:
194                 result.append(u'%.6f N %.6f E' % (latitude, longitude))
195
196             elif output_format == self.FORMAT_GMAPPLUGIN:
197                 result.append(u'%.6f, %.6f' % (latitude, longitude))
198                 
199             elif output_format == self.FORMAT_GPX:
200                 if not height is None: result.append(u'<trkpt lat="%.6f" lon="%.6f"><ele>%.2f</ele></trkpt>' % (latitude, longitude, height))
201                 else: result.append(u'<trkpt lat="%.6f" lon="%.6f"/>' % (latitude, longitude))
202             
203             else:
204                 raise formencode.Invalid(u"output_format %d is not recognized" % output_format, value, state) # Shouldn't it be an other type of runtime error?
205             
206         return "\n".join(result)
207
208
209 class AustrianPhoneNumber(formencode.FancyValidator):
210     """
211     Validates and converts phone numbers to +##/###/####### or +##/###/#######-### (having an extension)
212     @param  default_cc      country code for prepending if none is provided, defaults to 43 (Austria)
213     ::
214         >>> v = AustrianPhoneNumber()
215         >>> v.to_python(u'0512/12345678')
216         u'+43/512/12345678'
217         >>> v.to_python(u'+43/512/12345678')
218         u'+43/512/12345678'
219         >>> v.to_python(u'0512/1234567-89') # 89 is the extension
220         u'+43/512/1234567-89'
221         >>> v.to_python(u'+43/512/1234567-89')
222         u'+43/512/1234567-89'
223         >>> v.to_python(u'0512 / 12345678') # Exception
224         >>> v.to_python(u'0512-12345678') # Exception
225     """
226     # Inspired by formencode.national.InternationalPhoneNumber
227
228     default_cc = 43 # Default country code
229     messages = {'phoneFormat': "'%%(value)s' is an invalid format. Please enter a number in the form +43/###/####### or 0###/########."}
230
231     def _to_python(self, value, state):
232         self.assert_string(value, state)
233         m = re.match(u'^(?:\+(\d+)/)?([\d/]+)(?:-(\d+))?$', value)
234         # This will separate 
235         #     u'+43/512/1234567-89'  => (u'43', u'512/1234567', u'89')
236         #     u'+43/512/1234/567-89' => (u'43', u'512/1234/567', u'89')
237         #     u'+43/512/1234/567'    => (u'43', u'512/1234/567', None)
238         #     u'0512/1234567'        => (None, u'0512/1234567', None)
239         if m is None: raise formencode.Invalid(self.message('phoneFormat', state) % {'value': value}, value, state)
240         (country, phone, extension) = m.groups()
241         
242         # Phone
243         if phone.find(u'//') > -1 or phone.count('/') == 0: raise formencode.Invalid(self.message('phoneFormat', state) % {'value': value}, value, state)
244         
245         # Country
246         if country is None:
247             if phone[0] != '0': raise formencode.Invalid(self.message('phoneFormat', state) % {'value': value}, value, state)
248             phone = phone[1:]
249             country = unicode(self.default_cc)
250         
251         if extension is None: return '+%s/%s' % (country, phone)
252         return '+%s/%s-%s' % (country, phone, extension)
253
254
255 class PhoneInfo(formencode.FancyValidator):
256     "Validates a info of the form '0644/1234567 (Schnee Alm)'"
257     messages = {'infoFormat': "'%%(value)s' is no valid format, please use a form like '0644/1234567 (Schnee Alm)'"}
258     
259     def _to_python(self, value, state):
260         self.assert_string(value, state)
261         m = re.match('^([-\d/\+]{5,}) \((.+)\)', value)
262         if m is None: raise formencode.Invalid(self.message('infoFormat', state) % {'value': value}, value, state)
263         (phone, info) = m.groups()
264         
265         # check phone
266         phone = AustrianPhoneNumber().to_python(phone)
267         
268         return "%s (%s)" % (phone, info)