1 # -*- coding: iso-8859-15 -*-
8 class GermanBool(formencode.FancyValidator):
9 "Converts German bool values to the python bool type. 'Ja' and 'Nein' are supported."
11 def __init__(self, yes = [u'Ja'], no = [u'Nein'], **keywords):
12 "The yes and no arguments specify the valid possibilities. The first possibility is the default one."
13 formencode.FancyValidator.__init__(self, **keywords)
17 def _to_python(self, value, state):
18 self.assert_string(value, state)
19 if value in self.yes: return True
20 if value in self.no: return False
23 raise formencode.Invalid(u"'%s' is not a valid boolean value, use one of %s" % (value, all), value, state)
25 def from_python(self, value):
26 if value is None: return u''
27 if value: return self.yes[0]
31 class GenericDateTimeConverter(formencode.FancyValidator):
32 """Converts generic date/time information to datetime classes with a user defined format.
33 '2009-03-22 20:36:15' would be specified as '%Y-%m-%d %H:%M:%S'."""
35 def __init__(self, date_time_format = '%Y-%m-%d %H:%M:%S', **keywords):
36 formencode.FancyValidator.__init__(self, **keywords)
37 self.date_time_format = date_time_format
39 def _to_python(self, value, state):
40 self.assert_string(value, state)
41 try: return datetime.datetime.strptime(value, self.date_time_format)
42 except ValueError, e: raise formencode.Invalid(str(e), value, state)
44 def _from_python(self, value, state):
45 if value is None: return u''
46 return value.strftime(self.date_time_format)
49 class DateTimeNoSecConverter(GenericDateTimeConverter):
50 def __init__(self, **keywords):
51 GenericDateTimeConverter.__init__(self, '%Y-%m-%d %H:%M', **keywords)
54 class DateConverter(GenericDateTimeConverter):
55 "Converts date information to date classes with the format '%Y-%m-%d'."
57 def __init__(self, **keywords):
58 GenericDateTimeConverter.__init__(self, '%Y-%m-%d', **keywords)
60 def _to_python(self, value, state):
61 GenericDateTimeConverter._to_python(self, value, state).date()
64 class GermanTristate(formencode.FancyValidator):
65 """Does the following conversion:
67 u'Ja' -> (True, False)
68 u'Teilweise' -> (True, True)
69 u'Nein' -> (False, True)"""
71 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):
72 formencode.FancyValidator.__init__(self, if_empty = (None, None), **keywords)
73 self.yes_python = yes_python
74 self.no_python = no_python
75 self.partly_python = partly_python
76 self.yes_text = yes_text
77 self.no_text = no_text
78 self.partly_text = partly_text
80 def _to_python(self, value, state):
81 self.assert_string(value, state)
82 if value in self.yes_text: return self.yes_python
83 if value in self.no_text: return self.no_python
84 if value in self.partly_text: return self.partly_python
85 all = self.yes_text[:]
86 all.extend(self.no_text)
87 all.extend(self.partly_text)
88 raise formencode.Invalid(u"'%s' is not a valid value, use one of %s" % (value, all), value, state)
90 def _from_python(self, value, state):
91 if value == (None, None): return ''
92 if value == self.yes_python: return self.yes_text[0]
93 if value == self.no_python: return self.no_text[0]
94 if value == self.partly_python: return self.partly_text[0]
95 raise formencode.Invalid(u"Invalid representation of a tristate value: '%s'" % (value,), value, state)
98 class Geo(formencode.FancyValidator):
99 "Formats to coordinates '47.076207 N 11.453553 E' to the (latitude, longitude) tuplet."
101 def __init__(self, **keywords):
102 formencode.FancyValidator.__init__(self, if_empty = (None, None), **keywords)
104 def _to_python(self, value, state):
105 r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', value)
106 if r is None: raise formencode.Invalid(u"Coordinates '%s' have not a format like '47.076207 N 11.453553 E'" % value, value, state)
107 return (float(r.groups()[0]), float(r.groups()[1]))
109 def _from_python(self, value, state):
110 if value == (None, None): return ''
111 latitude, longitude = value
112 return u'%.6f N %.6f E' % (latitude, longitude)
115 class MultiGeo(formencode.FancyValidator):
116 "Formats multiple coordinates, even in multiple lines to [(latitude, longitude, height), ...] or [(latitude, longitude, None), ...] tuplets."
118 # Valid for input_format
119 FORMAT_GUESS = 0 # guesses the input format; default for input_format
120 FORMAT_NONE = -1 # indicates missing formats
122 # Valid for input_format and output_format
123 FORMAT_GEOCACHING = 1 # e.g. "N 47° 13.692 E 011° 25.535"
124 FORMAT_WINTERRODELN = 2 # e.g. "47.222134 N 11.467211 E"
125 FORMAT_GMAPPLUGIN = 3 # e.g. "47.232922, 11.452239"
126 FORMAT_GPX = 4 # e.g. "<trkpt lat="47.181289" lon="11.408827"><ele>1090.57</ele></trkpt>"
128 input_format = FORMAT_GUESS
129 output_format = FORMAT_WINTERRODELN
130 last_input_format = FORMAT_NONE
132 def __init__(self, input_format = FORMAT_GUESS, output_format = FORMAT_WINTERRODELN, **keywords):
133 self.input_format = input_format
134 self.output_format = output_format
135 formencode.FancyValidator.__init__(self, if_empty = (None, None, None), **keywords)
137 def _to_python(self, value, state):
138 input_format = self.input_format
139 if not input_format in [self.FORMAT_GUESS, self.FORMAT_GEOCACHING, self.FORMAT_WINTERRODELN, self.FORMAT_GMAPPLUGIN, self.FORMAT_GPX]:
140 raise formencode.Invalid(u"input_format %d is not recognized" % input_format, value, state) # Shouldn't it be an other type of runtime error?
141 lines = [line.strip() for line in value.split("\n") if len(line.strip()) > 0]
145 if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GEOCACHING:
148 if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_WINTERRODELN:
149 r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', line)
151 result.append((float(r.groups()[0]), float(r.groups()[1]), None))
152 last_input_format = self.FORMAT_WINTERRODELN
155 if input_format == self.FORMAT_GUESS or input_format == FORMAT_GMAPPLUGIN:
158 if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GPX:
161 raise formencode.Invalid(u"Coordinates '%s' have no valid format" % value, value, state)
165 def _from_python(self, value, state):
166 output_format = self.output_format
168 for latitude, longitude, height in value:
169 if output_format == self.FORMAT_GEOCACHING:
171 result.append(u'N %02d° %02.3f E %03d° %02.3f' % (latitude, latitude % 1 * 60, longitude, longitude % 1 * 60))
173 elif output_format == self.FORMAT_WINTERRODELN:
174 result.append(u'%.6f N %.6f E' % (latitude, longitude))
176 elif output_format == FORMAT_GMAPPLUGIN:
179 elif output_format == self.FORMAT_GPX:
183 raise formencode.Invalid(u"output_format %d is not recognized" % output_format, value, state) # Shouldn't it be an other type of runtime error?
185 return "\n".join(result)
188 class AustrianPhoneNumber(formencode.FancyValidator):
190 Validates and converts phone numbers to +##/###/####### or +##/###/#######-### (having an extension)
191 @param default_cc country code for prepending if none is provided, defaults to 43 (Austria)
193 >>> v = AustrianPhoneNumber()
194 >>> v.to_python(u'0512/12345678')
196 >>> v.to_python(u'+43/512/12345678')
198 >>> v.to_python(u'0512/1234567-89') # 89 is the extension
199 u'+43/512/1234567-89'
200 >>> v.to_python(u'+43/512/1234567-89')
201 u'+43/512/1234567-89'
202 >>> v.to_python(u'0512 / 12345678') # Exception
203 >>> v.to_python(u'0512-12345678') # Exception
205 # Inspired by formencode.national.InternationalPhoneNumber
207 default_cc = 43 # Default country code
208 messages = {'phoneFormat': "'%%(value)s' is an invalid format. Please enter a number in the form +43/###/####### or 0###/########."}
210 def _to_python(self, value, state):
211 self.assert_string(value, state)
212 m = re.match(u'^(?:\+(\d+)/)?([\d/]+)(?:-(\d+))?$', value)
214 # u'+43/512/1234567-89' => (u'43', u'512/1234567', u'89')
215 # u'+43/512/1234/567-89' => (u'43', u'512/1234/567', u'89')
216 # u'+43/512/1234/567' => (u'43', u'512/1234/567', None)
217 # u'0512/1234567' => (None, u'0512/1234567', None)
218 if m is None: raise formencode.Invalid(self.message('phoneFormat', state) % {'value': value}, value, state)
219 (country, phone, extension) = m.groups()
222 if phone.find(u'//') > -1 or phone.count('/') == 0: raise formencode.Invalid(self.message('phoneFormat', state) % {'value': value}, value, state)
226 if phone[0] != '0': raise formencode.Invalid(self.message('phoneFormat', state) % {'value': value}, value, state)
228 country = unicode(self.default_cc)
230 if extension is None: return '+%s/%s' % (country, phone)
231 return '+%s/%s-%s' % (country, phone, extension)
234 class PhoneInfo(formencode.FancyValidator):
235 "Validates a info of the form '0644/1234567 (Schnee Alm)'"
236 messages = {'infoFormat': "'%%(value)s' is no valid format, please use a form like '0644/1234567 (Schnee Alm)'"}
238 def _to_python(self, value, state):
239 self.assert_string(value, state)
240 m = re.match('^([-\d/\+]{5,}) \((.+)\)', value)
241 if m is None: raise formencode.Invalid(self.message('infoFormat', state) % {'value': value}, value, state)
242 (phone, info) = m.groups()
245 phone = AustrianPhoneNumber().to_python(phone)
247 return "%s (%s)" % (phone, info)