#!/usr/bin/python2.7
# -*- coding: iso-8859-15 -*-
# $Id$
# $HeadURL$
"""This file contains "validators" that convert between string and python (database) representation
of properties used in the "Rodelbahnbox" and "Gasthausbox".
The "to_python" method has to get a unicode argument.
"""
import datetime
import re
import xml.dom.minidom as minidom
from xml.parsers.expat import ExpatError
import formencode
import formencode.national
class NoneValidator(formencode.FancyValidator):
"""Takes a validator and makes it possible that empty strings are mapped to None."""
def __init__(self, validator, python_none=None):
self.validator = validator
self.python_none = python_none
def to_python(self, value):
self.assert_string(value, None)
if value == u'': return self.python_none
return self.validator.to_python(value)
def from_python(self, value):
if value == self.python_none: return u''
return self.validator.from_python(value)
class NeinValidator(formencode.FancyValidator):
"""Take an arbitrary validator and adds the possibility that the
string can be u'Nein'.
Example together with an UnsignedNone validator:
>>> v = NeinValidator(UnsignedNone())
>>> v.to_python(u'')
None
>>> v.to_python(u'34')
34
>>> v.to_python(u'Nein')
u'Nein'
"""
def __init__(self, validator, python_no=u'Nein'):
self.validator = validator
self.python_no = python_no
def to_python(self, value):
self.assert_string(value, None)
if value == u'Nein': return self.python_no
return self.validator.to_python(value)
def from_python(self, value):
if value == self.python_no: return u'Nein'
return self.validator.from_python(value)
class Unicode(formencode.FancyValidator):
"""Converts an unicode string to an unicode string:
u'any string' <=> u'any string'"""
def to_python(self, value):
self.assert_string(value, None)
return unicode(value)
def from_python(self, value):
return unicode(value)
class UnicodeNone(NoneValidator):
"""Converts an unicode string to an unicode string:
u'' <=> None
u'any string' <=> u'any string'"""
def __init__(self):
NoneValidator.__init__(self, Unicode())
class Unsigned(formencode.FancyValidator):
"""Converts an unsigned number to a string and vice versa:
u'0' <=> 0
u'1' <=> 1
u'45' <=> 45
"""
def __init__(self, max=None):
self.iv = formencode.validators.Int(min=0, max=max)
def to_python(self, value):
self.assert_string(value, None)
return self.iv.to_python(value)
def from_python(self, value):
return unicode(value)
class UnsignedNone(NoneValidator):
"""Converts an unsigned number to a string and vice versa:
u'' <=> None
u'0' <=> 0
u'1' <=> 1
u'45' <=> 45
"""
def __init__(self, max=None):
NoneValidator.__init__(self, Unsigned(max))
class UnsignedNeinNone(NoneValidator):
""" Translates a number of Nein to a number.
u'' <=> None
u'Nein' <=> 0
u'1' <=> 1
u'2' <=> 2
...
"""
def __init__(self):
NoneValidator.__init__(self, UnsignedNone())
class Loop(formencode.FancyValidator):
"""Takes a validator and calls from_python(to_python(value))."""
def __init__(self, validator):
self.validator = validator
def to_python(self, value):
self.assert_string(value, None)
return self.validator.from_python(self.validator.to_python(value))
def from_python(self, value):
# we don't call self.validator.to_python(self.validator.from_python(value))
# here because our to_python implementation basically leaves the input untouched
# and so should from_python do.
return self.validator.from_python(self.validator.to_python(value))
class DictValidator(formencode.FancyValidator):
"""Translates strings to other values via a python directory.
>>> boolValidator = DictValidator({u'': None, u'Ja': True, u'Nein': False})
>>> boolValidator.to_python(u'')
None
>>> boolValidator.to_python(u'Ja')
True
"""
def __init__(self, dict):
self.dict = dict
def to_python(self, value):
self.assert_string(value, None)
if not self.dict.has_key(value): raise formencode.Invalid("Key not found in dict.", value, None)
return self.dict[value]
def from_python(self, value):
for k, v in self.dict.iteritems():
if v == value:
return k
raise formencode.Invalid('Invalid value', value, None)
class GermanBoolNone(DictValidator):
"""Converts German bool values to the python bool type:
u'' <=> None
u'Ja' <=> True
u'Nein' <=> False
"""
def __init__(self):
DictValidator.__init__(self, {u'': None, u'Ja': True, u'Nein': False})
class GermanTristateTuple(DictValidator):
"""Does the following conversion:
u'' <=> (None, None)
u'Ja' <=> (True, False)
u'Teilweise' <=> (True, True)
u'Nein' <=> (False, True)"""
def __init__(self, yes_python = (True, False), no_python = (False, True), partly_python = (True, True), none_python = (None, None)):
DictValidator.__init__(self, {u'': none_python, u'Ja': yes_python, u'Nein': no_python, u'Teilweise': partly_python})
class GermanTristateFloat(GermanTristateTuple):
"""Converts the a property with the possible values 0.0, 0.5, 1.0 or None
to a German text:
u'' <=> None
u'Ja' <=> 1.0
u'Teilweise' <=> 0.5
u'Nein' <=> 0.0"""
def __init__(self):
GermanTristateTuple.__init__(self, yes_python=1.0, no_python=0.0, partly_python=0.5, none_python=None)
class ValueComment(formencode.FancyValidator):
"""Converts value with a potentially optional comment to a python tuple. If a comment is present, the
closing bracket has to be the rightmost character.
u'' <=> (None, None)
u'value' <=> (u'value', None)
u'value (comment)' <=> (u'value', u'comment')
u'[[link (linkcomment)]]' <=> (u'[[link (linkcomment)]]', None)
u'[[link (linkcomment)]] (comment)' <=> (u'[[link (linkcomment)]]', comment)
"""
def __init__(self, value_validator=UnicodeNone(), comment_validator=UnicodeNone(), comment_is_optional=True):
self.value_validator = value_validator
self.comment_validator = comment_validator
self.comment_is_optional = comment_is_optional
def to_python(self, value):
self.assert_string(value, None)
if value == u'':
v = value
c = value
else:
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. "1090.57"
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'%.2f' % (latitude, longitude, height))
else: result.append(u'' % (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())
class UnsignedCommentNone(NoneValidator):
"""Converts the a property with unsigned values an optional comment
in parenthesis to a text:
u'' <=> (None, None)
u'2 (Mo, Di)' <=> (2, u'Mo, Di')
u'7' <=> (7, None)
u'0' <=> (0, None)
"""
def __init__(self, max=None):
NoneValidator.__init__(self, ValueComment(Unsigned(max=max)), (None, None))
class GermanCachet(formencode.FancyValidator):
"""Converts a "Gütesiegel":
u'' <=> None
u'Nein' <=> 'Nein'
u'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel' <=> u'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'"""
def to_python(self, value):
self.assert_string(value, None)
if value == u'': return None
elif value == u'Nein': return value
elif value.startswith(u'Tiroler Naturrodelbahn-Gütesiegel '):
p = value.split(" ")
Unsigned().to_python(p[2]) # check if year can be parsed
if not p[3] in ['leicht', 'mittel', 'schwer']: raise formencode.Invalid("Unbekannter Schwierigkeitsgrad", value, None)
return value
else: raise formencode.Invalid(u"Unbekanntes Gütesiegel", value, None)
def from_python(self, value):
if value == None: return u''
assert value != u''
return self.to_python(value)
class Url(formencode.FancyValidator):
"""Validates an URL. In contrast to fromencode.validators.URL, umlauts are allowed."""
urlv = formencode.validators.URL()
def to_python(self, value):
self.assert_string(value, None)
v = value
v = v.replace(u'ä', u'a')
v = v.replace(u'ö', u'o')
v = v.replace(u'ü', u'u')
v = v.replace(u'ß', u'ss')
v = self.urlv.to_python(v)
return value
def from_python(self, value):
return value
class UrlNeinNone(NoneValidator):
"""Validates an URL. In contrast to fromencode.validators.URL, umlauts are allowed.
The special value u"Nein" is allowed."""
def __init__(self):
NoneValidator.__init__(self, NeinValidator(Url()))
class ValueCommentListNeinLoopNone(NoneValidator):
"""Translates a semicolon separated list of values with optional comments in paranthesis or u'Nein' to itself.
An empty string is translated to None:
u'' <=> None
u'Nein' <=> u'Nein'
u'T-Mobile (gut); A1' <=> u'T-Mobile (gut); A1'"""
def __init__(self):
NoneValidator.__init__(self, NeinValidator(Loop(ValueCommentList())))
class PhoneNumber(formencode.FancyValidator):
"""Telefonnumber in international format, e.g. u'+43-699-1234567'"""
def __init__(self, default_cc=43):
self.validator = formencode.national.InternationalPhoneNumber(default_cc=lambda: default_cc)
def to_python(self, value):
return unicode(self.validator.to_python(value))
def from_python(self, value):
return self.validator.from_python(value)
class PhoneCommentListNeinLoopNone(NoneValidator):
"""List with semicolon-separated phone numbers in international format with optional comment or 'Nein' as string:
u'' <=> None
u'Nein' <=> u'Nein'
u'+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456' <=> u'+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456'
"""
def __init__(self, comments_are_optional):
NoneValidator.__init__(self, NeinValidator(Loop(ValueCommentList(PhoneNumber(default_cc=43), comments_are_optional=comments_are_optional))))
class MaskedEmail(formencode.FancyValidator):
"""A masked email address as defined here is an email address that has the `@` character replacted by the text `(at)`.
So instead of `abd.def@example.com` it would be `abc.def(at)example.com`.
This validator takes either a normal or a masked email address in it's to_python method and returns the normal email address as well
as a bool indicating whether the email address was masked.
u'' <=> (None, None)
u'abc.def@example.com' <=> (u'abc.def@example.com', False)
u'abc.def(at)example.com' <=> (u'abc.def@example.com', True)
"""
def __init__(self, *args, **kw):
if not kw.has_key('strip'): kw['strip'] = True
if not kw.has_key('not_empty'): kw['not_empty'] = False
if not kw.has_key('if_empty'): kw['if_empty'] = (None, None)
self.at = '(at)'
formencode.FancyValidator.__init__(self, *args, **kw)
def _to_python(self, value, state):
email = value.replace(self.at, '@')
masked = value != email
val_email = formencode.validators.Email()
return val_email.to_python(email), masked
def _from_python(self, value, state):
email, masked = value
if email is None: return u''
val_email = formencode.validators.Email()
email = val_email.from_python(email)
if masked: email = email.replace('@', self.at)
return email
class EmailCommentListNeinLoopNone(NoneValidator):
"""Converts a semicolon-separated list of email addresses with optional comments to itself.
The special value of u'Nein' indicates that there are no email addresses.
The empty string translates to None:
u'' <=> None
u'Nein' <=> u'Nein'
u'first@example.com' <=> u'first@example.com'
u'first@example.com (Nur Winter); second@example.com' <=> u'first@example.com (Nur Winter); second@example.com'
If the parameter allow_masked_email is true, the following gives no error:
u'abc.def(at)example.com (comment)' <=> u'abc.def(at)example.com (comment)'
"""
def __init__(self, allow_masked_email=False):
NoneValidator.__init__(self, NeinValidator(Loop(ValueCommentList(MaskedEmail() if allow_masked_email else formencode.validators.Email()))))
class WikiPage(formencode.FancyValidator):
"""Validates wiki page name like u'[[Birgitzer Alm]]'.
The page is not checked for existance.
An empty string is an error.
u'[[Birgitzer Alm]]' <=> u'[[Birgitzer Alm]]'
"""
def to_python(self, value):
self.assert_string(value, None)
if not value.startswith('[[') or not value.endswith(']]'):
raise formencode.Invalid('No valid wiki page name', value, None)
return value
def from_python(self, value):
return value
class WikiPageList(SemicolonList):
"""Validates a list of wiki pages like u'[[Birgitzer Alm]]; [[Kemater Alm]]'.
u'[[Birgitzer Alm]]; [[Kemater Alm]]' <=> [u'[[Birgitzer Alm]]', u'[[Kemater Alm]]']
u'[[Birgitzer Alm]]' <=> [u'[[Birgitzer Alm]]']
u'' <=> []
"""
def __init__(self):
SemicolonList.__init__(self, WikiPage())
class WikiPageListLoopNone(NoneValidator):
"""Validates a list of wiki pages like u'[[Birgitzer Alm]]; [[Kemater Alm]]' as string.
u'[[Birgitzer Alm]]; [[Kemater Alm]]' <=> u'[[Birgitzer Alm]]; [[Kemater Alm]]'
u'[[Birgitzer Alm]]' <=> u'[[Birgitzer Alm]]'
u'' <=> None
"""
def __init__(self):
NoneValidator.__init__(self, Loop(WikiPageList()))
class TupleSecondValidator(formencode.FancyValidator):
"""Does not really validate anything but puts the string through
a validator in the second part of a tuple.
Examples with an Unsigned() validator and the True argument:
u'6' <=> (True, 6)
u'2' <=> (True, 2)"""
def __init__(self, first=True, validator=UnicodeNone()):
self.first = first
self.validator = validator
def to_python(self, value):
self.assert_string(value, None)
return self.first, self.validator.to_python(value)
def from_python(self, value):
assert value[0] == self.first
return self.validator.from_python(value[1])
class BoolUnicodeTupleValidator(NoneValidator):
"""Translates an unparsed string or u'Nein' to a tuple:
u'' <=> (None, None)
u'Nein' <=> (False, None)
u'any text' <=> (True, u'any text')
"""
def __init__(self, validator=UnicodeNone()):
NoneValidator.__init__(self, NeinValidator(TupleSecondValidator(True, validator), (False, None)), (None, None))
class GermanLift(BoolUnicodeTupleValidator):
"""Checks a lift_details property. It is a value comment property with the following
values allowed:
u'Sessellift'
u'Gondel'
u'Linienbus'
u'Taxi'
u'Sonstige'
Alternatively, the value u'Nein' is allowed.
An empty string maps to (None, None).
Examples:
u'' <=> (None, None)
u'Nein' <=> (False, None)
u'Sessellift <=> (True, u'Sessellift')
u'Gondel (nur bis zur Hälfte)' <=> (True, u'Gondel (nur bis zur Hälfte)')
u'Sessellift; Taxi' <=> (True, u'Sessellift; Taxi')
u'Sessellift (Wochenende); Taxi (6 Euro)' <=> (True, u'Sessellift (Wochenende); Taxi (6 Euro)')
"""
def __init__(self):
BoolUnicodeTupleValidator.__init__(self, Loop(ValueCommentList(DictValidator({u'Sessellift': u'Sessellift', u'Gondel': u'Gondel', u'Linienbus': u'Linienbus', u'Taxi': u'Taxi', u'Sonstige': u'Sonstige'}))))
class SledRental(BoolUnicodeTupleValidator):
"""The value can be an empty string, u'Nein' or a comma-separated list of unicode strings with optional comments.
u'' <=> (None, None)
u'Nein' <=> (False, None)
u'Talstation (nur mit Ticket); Schneealm' <=> (True, u'Talstation (nur mit Ticket); Schneealm')"""
def __init__(self):
BoolUnicodeTupleValidator.__init__(self, Loop(ValueCommentList()))