- right = value.rfind(')')
- if right+1 != len(value):
- if not self.comment_is_optional: raise formencode.Invalid(u'Mandatory comment not present', value, None)
- v = value
- c = u''
- else:
- left = value.rfind('(')
- if left < 0: raise formencode.Invalid(u'Invalid format', value, None)
- v = value[:left].strip()
- c = value[left+1:right].strip()
- return self.value_validator.to_python(v), self.comment_validator.to_python(c)
-
- def from_python(self, value):
- assert len(value) == 2
- v = self.value_validator.from_python(value[0])
- c = self.comment_validator.from_python(value[1])
- if len(c) > 0:
- if len(v) > 0: return u'%s (%s)' % (v, c)
- else: return u'(%s)' % c
- return v
-
-
-class SemicolonList(formencode.FancyValidator):
- """Applies a given validator to a semicolon separated list of values and returns a python list.
- For an empty string an empty list is returned."""
- def __init__(self, validator=Unicode()):
- self.validator = validator
-
- def to_python(self, value):
- self.assert_string(value, None)
- return [self.validator.to_python(s.strip()) for s in value.split(';')]
-
- def from_python(self, value):
- return "; ".join([self.validator.from_python(s) for s in value])
-
-
-class ValueCommentList(SemicolonList):
- """A value-comment list looks like one of the following lines:
- value
- value (optional comment)
- value1; value2
- value1; value2 (optional comment)
- value1 (optional comment1); value2 (optional comment2); value3 (otional comment3)
- value1 (optional comment1); value2 (optional comment2); value3 (otional comment3)
- This function returns the value-comment list as list of tuples:
- [(u'value1', u'comment1'), (u'value2', None)]
- If no comment is present, None is specified.
- For an empty string, [] is returned."""
- def __init__(self, value_validator=Unicode(), comments_are_optional=True):
- SemicolonList.__init__(self, ValueComment(value_validator, comment_is_optional=comments_are_optional))
-
-
-class GenericDateTime(formencode.FancyValidator):
- """Converts a generic date/time information to a datetime class with a user defined format.
- '2009-03-22 20:36:15' would be specified as '%Y-%m-%d %H:%M:%S'."""
-
- def __init__(self, date_time_format = '%Y-%m-%d %H:%M:%S', **keywords):
- formencode.FancyValidator.__init__(self, **keywords)
- self.date_time_format = date_time_format
-
- def to_python(self, value, state=None):
- self.assert_string(value, None)
- try: return datetime.datetime.strptime(value, self.date_time_format)
- except ValueError, e: raise formencode.Invalid(str(e), value, None)
-
- def from_python(self, value, state=None):
- return value.strftime(self.date_time_format)
-
-
-class DateTimeNoSec(GenericDateTime):
- def __init__(self, **keywords):
- GenericDateTime.__init__(self, '%Y-%m-%d %H:%M', **keywords)
-
-
-class DateNone(NoneValidator):
- """Converts date information to date classes with the format '%Y-%m-%d' or None."""
- def __init__(self):
- NoneValidator.__init__(self, GenericDateTime('%Y-%m-%d'))
-
-
-class Geo(formencode.FancyValidator):
- """Formats to coordinates '47.076207 N 11.453553 E' to the (latitude, longitude) tuplet."""
- def to_python(self, value):
- self.assert_string(value, None)
- r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', value)
- if r is None: raise formencode.Invalid(u"Coordinates '%s' have not a format like '47.076207 N 11.453553 E'" % value, value, None)
- return (float(r.groups()[0]), float(r.groups()[1]))
-
- def from_python(self, value):
- latitude, longitude = value
- return u'%.6f N %.6f E' % (latitude, longitude)
-
-
-class GeoNone(NoneValidator):
- """Formats to coordinates '47.076207 N 11.453553 E' to the (latitude, longitude) tuplet."""
- def __init__(self):
- NoneValidator.__init__(self, Geo(), (None, None))
-
-
-class MultiGeo(formencode.FancyValidator):
- "Formats multiple coordinates, even in multiple lines to [(latitude, longitude, elevation), ...] or [(latitude, longitude, None), ...] tuplets."
-
- # Valid for input_format
- FORMAT_GUESS = 0 # guesses the input format; default for input_format
- FORMAT_NONE = -1 # indicates missing formats
-
- # Valid for input_format and output_format
- FORMAT_GEOCACHING = 1 # e.g. "N 47° 13.692 E 011° 25.535"
- FORMAT_WINTERRODELN = 2 # e.g. "47.222134 N 11.467211 E"
- FORMAT_GMAPPLUGIN = 3 # e.g. "47.232922, 11.452239"
- FORMAT_GPX = 4 # e.g. "<trkpt lat="47.181289" lon="11.408827"><ele>1090.57</ele></trkpt>"
-
- input_format = FORMAT_GUESS
- output_format = FORMAT_WINTERRODELN
- last_input_format = FORMAT_NONE
-
- def __init__(self, input_format = FORMAT_GUESS, output_format = FORMAT_WINTERRODELN, **keywords):
- self.input_format = input_format
- self.output_format = output_format
- formencode.FancyValidator.__init__(self, if_empty = (None, None, None), **keywords)
-
- def to_python(self, value):
- self.assert_string(value, None)
- input_format = self.input_format
- if not input_format in [self.FORMAT_GUESS, self.FORMAT_GEOCACHING, self.FORMAT_WINTERRODELN, self.FORMAT_GMAPPLUGIN, self.FORMAT_GPX]:
- raise formencode.Invalid(u"input_format %d is not recognized" % input_format, value, None) # Shouldn't it be an other type of runtime error?
- lines = [line.strip() for line in value.split("\n") if len(line.strip()) > 0]
-
- result = []
- for line in lines:
- if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GEOCACHING:
- r = re.match(u'N ?(\d+)° ?(\d+\.\d+) +E ?(\d+)° ?(\d+\.\d+)', line)
- if not r is None:
- g = r.groups()
- result.append((float(g[0]) + float(g[1])/60, float(g[2]) + float(g[3])/60, None))
- last_input_format = self.FORMAT_WINTERRODELN
- continue
-
- if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_WINTERRODELN:
- r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', line)
- if not r is None:
- result.append((float(r.groups()[0]), float(r.groups()[1]), None))
- last_input_format = self.FORMAT_WINTERRODELN
- continue
-
- if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GMAPPLUGIN:
- r = re.match(u'(\d+\.\d+), ?(\d+\.\d+)', line)
- if not r is None:
- result.append((float(r.groups()[0]), float(r.groups()[1]), None))
- last_input_format = self.FORMAT_GMAPPLUGIN
- continue
-
- if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_GPX:
- try:
- xml = minidom.parseString(line)
- coord = xml.documentElement
- lat = float(coord.getAttribute('lat'))
- lon = float(coord.getAttribute('lon'))
- try: ele = float(coord.childNodes[0].childNodes[0].nodeValue)
- except (IndexError, ValueError): ele = None
- result.append((lat, lon, ele))
- last_input_format = self.FORMAT_GPX
- continue
- except (ExpatError, IndexError, ValueError): pass
-
- raise formencode.Invalid(u"Coordinates '%s' have no known format" % line, value, None)
-
- return result
-
- def from_python(self, value):
- output_format = self.output_format
- result = []
- for latitude, longitude, height in value:
- if output_format == self.FORMAT_GEOCACHING:
- degree = latitude
- result.append(u'N %02d° %02.3f E %03d° %02.3f' % (latitude, latitude % 1 * 60, longitude, longitude % 1 * 60))
-
- elif output_format == self.FORMAT_WINTERRODELN:
- result.append(u'%.6f N %.6f E' % (latitude, longitude))
-
- elif output_format == self.FORMAT_GMAPPLUGIN:
- result.append(u'%.6f, %.6f' % (latitude, longitude))
-
- elif output_format == self.FORMAT_GPX:
- if not height is None: result.append(u'<trkpt lat="%.6f" lon="%.6f"><ele>%.2f</ele></trkpt>' % (latitude, longitude, height))
- else: result.append(u'<trkpt lat="%.6f" lon="%.6f"/>' % (latitude, longitude))
-
- else:
- raise formencode.Invalid(u"output_format %d is not recognized" % output_format, value, None) # Shouldn't it be an other type of runtime error?
-
- return "\n".join(result)
-
-
-# deprecated
-class AustrianPhoneNumber(formencode.FancyValidator):
- """
- Validates and converts phone numbers to +##/###/####### or +##/###/#######-### (having an extension)
- @param default_cc country code for prepending if none is provided, defaults to 43 (Austria)
- ::
- >>> v = AustrianPhoneNumber()
- >>> v.to_python(u'0512/12345678')
- u'+43/512/12345678'
- >>> v.to_python(u'+43/512/12345678')
- u'+43/512/12345678'
- >>> v.to_python(u'0512/1234567-89') # 89 is the extension
- u'+43/512/1234567-89'
- >>> v.to_python(u'+43/512/1234567-89')
- u'+43/512/1234567-89'
- >>> v.to_python(u'0512 / 12345678') # Exception
- >>> v.to_python(u'0512-12345678') # Exception
- """
- # Inspired by formencode.national.InternationalPhoneNumber
-
- default_cc = 43 # Default country code
- messages = {'phoneFormat': "'%%(value)s' is an invalid format. Please enter a number in the form +43/###/####### or 0###/########."}
-
- def to_python(self, value):
- self.assert_string(value, None)
- m = re.match(u'^(?:\+(\d+)/)?([\d/]+)(?:-(\d+))?$', value)
- # This will separate
- # u'+43/512/1234567-89' => (u'43', u'512/1234567', u'89')
- # u'+43/512/1234/567-89' => (u'43', u'512/1234/567', u'89')
- # u'+43/512/1234/567' => (u'43', u'512/1234/567', None)
- # u'0512/1234567' => (None, u'0512/1234567', None)
- if m is None: raise formencode.Invalid(self.message('phoneFormat', None) % {'value': value}, value, None)
- (country, phone, extension) = m.groups()
-
- # Phone
- if phone.find(u'//') > -1 or phone.count('/') == 0: raise formencode.Invalid(self.message('phoneFormat', None) % {'value': value}, value, None)
-
- # Country
- if country is None:
- if phone[0] != '0': raise formencode.Invalid(self.message('phoneFormat', None) % {'value': value}, value, None)
- phone = phone[1:]
- country = unicode(self.default_cc)
-
- if extension is None: return '+%s/%s' % (country, phone)
- return '+%s/%s-%s' % (country, phone, extension)
-
-
-# Deprecated
-class AustrianPhoneNumberNone(NoneValidator):
- def __init__(self):
- NoneValidator.__init__(self, AustrianPhoneNumber())
-
-
-# Deprecated
-class AustrianPhoneNumberCommentLoop(NoneValidator):
- def __init__(self):
- NoneValidator.__init__(self, Loop(ValueComment(AustrianPhoneNumber())))
-
-
-class GermanDifficulty(DictValidator):
- """Converts the difficulty represented in a number from 1 to 3 (or None)
- to a German representation:
- u'' <=> None
- u'leicht' <=> 1
- u'mittel' <=> 2
- u'schwer' <=> 3"""
- def __init__(self):
- DictValidator.__init__(self, {u'': None, u'leicht': 1, u'mittel': 2, u'schwer': 3})
-
-
-class GermanAvalanches(DictValidator):
- """Converts the avalanches property represented as number from 1 to 4 (or None)
- to a German representation:
- u'' <=> None
- u'kaum' <=> 1
- u'selten' <=> 2
- u'gelegentlich' <=> 3
- u'häufig' <=> 4"""
- def __init__(self):
- DictValidator.__init__(self, {u'': None, u'kaum': 1, u'selten': 2, u'gelegentlich': 3, u'häufig': 4})
-
-
-class GermanPublicTransport(DictValidator):
- """Converts the public_transport property represented as number from 1 to 6 (or None)
- to a German representation:
- u'' <=> None
- u'Sehr gut' <=> 1
- u'Gut' <=> 2
- u'Mittelmäßig' <=> 3
- u'Schlecht' <=> 4
- u'Nein' <=> 5
- u'Ja' <=> 6"""
- def __init__(self):
- DictValidator.__init__(self, {u'': None, u'Sehr gut': 1, u'Gut': 2, u'Mittelmäßig': 3, u'Schlecht': 4, u'Nein': 5, u'Ja': 6})
-
-
-class GermanTristateFloatComment(ValueComment):
- """Converts the a property with the possible values 0.0, 0.5, 1.0 or None and an optional comment
- in parenthesis to a German text:
- u'' <=> (None, None)
- u'Ja' <=> (1.0, None)
- u'Teilweise' <=> (0.5, None)
- u'Nein' <=> (0.0, None)
- u'Ja (aber schmal)' <=> (1.0, u'aber schmal')
- u'Teilweise (oben)' <=> (0.5, u'oben')
- u'Nein (aber breit)' <=> (0.0, u'aber breit')
- """
- def __init__(self):
- ValueComment.__init__(self, GermanTristateFloat())