Created function to translate between sledrun title names and urls.
[philipp/winterrodeln/wrpylib.git] / wrpylib / wrvalidators.py
index 2b43cab87897505f2f022b2b60d2e12718ed3f4b..d3114550dd9a67d12c01b94241e06c004d703066 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python2.7
+#!/usr/bin/python3.4
 # -*- coding: iso-8859-15 -*-
 # $Id$
 # $HeadURL$
@@ -10,10 +10,61 @@ import datetime
 import re
 import xml.dom.minidom as minidom
 from xml.parsers.expat import ExpatError
+import collections
 import formencode
 import formencode.national
 
 
+class OrderedSchema(formencode.Schema):
+    def _convert_to_python(self, value, state):
+        pre_validators = self.pre_validators
+        chained_validators = self.chained_validators
+        for validator in pre_validators:
+            value = validator.to_python(value, state)
+        self.pre_validators = []
+        self.chained_validators = []
+        try:
+            result = formencode.Schema._convert_to_python(self, value, state)
+            ordered_result = collections.OrderedDict()
+            for key in value.keys():
+                ordered_result[key] = result[key]
+            for validator in chained_validators:
+                ordered_result = validator.to_python(ordered_result, state)
+        finally:
+            self.pre_validators = pre_validators
+            self.chained_validators = chained_validators
+        return ordered_result
+
+    def _convert_from_python(self, value, state):
+        # store original pre- and chained validators
+        pre_validators = self.pre_validators
+        chained_validators = self.chained_validators[:]
+        # apply chained validators
+        chained = chained_validators[:]
+        chained.reverse()
+        for validator in chained:
+            value = validator.from_python(value, state)
+        # tempoarly remove pre- and chained validators
+        self.pre_validators = []
+        self.chained_validators = []
+        # apply original _convert_from_python method
+        try:
+            result = formencode.Schema._convert_from_python(self, value, state)
+            ordered_result = collections.OrderedDict()
+            for key in value.keys():
+                ordered_result[key] = result[key]
+            # apply pre_validators
+            pre = pre_validators[:]
+            pre.reverse()
+            for validator in pre:
+                ordered_result = validator.from_python(ordered_result, state)
+        finally:
+            # resore original pre- and chained_validators
+            self.pre_validators = pre_validators
+            self.chained_validators = chained_validators
+        return ordered_result
+
+
 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):
@@ -22,11 +73,11 @@ class NoneValidator(formencode.FancyValidator):
     
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        if value == u'': return self.python_none
+        if value == '': return self.python_none
         return self.validator.to_python(value, state)
     
     def from_python(self, value, state=None):
-        if value == self.python_none: return u''
+        if value == self.python_none: return ''
         return self.validator.from_python(value, state)
 
 
@@ -42,17 +93,17 @@ class NeinValidator(formencode.FancyValidator):
     >>> v.to_python(u'Nein')
     u'Nein'
     """
-    def __init__(self, validator, python_no=u'Nein'):
+    def __init__(self, validator, python_no='Nein'):
         self.validator = validator
         self.python_no = python_no
     
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        if value == u'Nein': return self.python_no
+        if value == 'Nein': return self.python_no
         return self.validator.to_python(value, state)
     
     def from_python(self, value, state=None):
-        if value == self.python_no: return u'Nein'
+        if value == self.python_no: return 'Nein'
         return self.validator.from_python(value, state)
 
 
@@ -61,10 +112,10 @@ class Unicode(formencode.FancyValidator):
     u'any string' <=> u'any string'"""
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        return unicode(value)
+        return str(value)
 
     def from_python(self, value, state=None):
-        return unicode(value)
+        return str(value)
 
 
 class UnicodeNone(NoneValidator):
@@ -89,7 +140,7 @@ class Unsigned(formencode.FancyValidator):
         return self.iv.to_python(value, state)
     
     def from_python(self, value, state=None):
-        return unicode(value)
+        return str(value)
 
 
 class UnsignedNone(NoneValidator):
@@ -144,11 +195,11 @@ class DictValidator(formencode.FancyValidator):
     
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        if not self.dict.has_key(value): raise formencode.Invalid("Key not found in dict.", value, state)
+        if value not in self.dict: raise formencode.Invalid("Key not found in dict.", value, state)
         return self.dict[value]
     
     def from_python(self, value, state=None):
-        for k, v in self.dict.iteritems():
+        for k, v in self.dict.items():
             if v == value:
                 return k
         raise formencode.Invalid('Invalid value', value, state)
@@ -161,7 +212,7 @@ class GermanBoolNone(DictValidator):
     u'Nein' <=> False
     """
     def __init__(self):
-        DictValidator.__init__(self, {u'': None, u'Ja': True, u'Nein': False})
+        DictValidator.__init__(self, {'': None, 'Ja': True, 'Nein': False})
 
 
 class GermanTristateTuple(DictValidator):
@@ -171,7 +222,7 @@ class GermanTristateTuple(DictValidator):
     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})
+        DictValidator.__init__(self, {'': none_python, 'Ja': yes_python, 'Nein': no_python, 'Teilweise': partly_python})
 
 
 class GermanTristateFloat(GermanTristateTuple):
@@ -201,18 +252,18 @@ class ValueComment(formencode.FancyValidator):
     
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        if value == u'':
+        if value == '':
             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, state)
+                if not self.comment_is_optional: raise formencode.Invalid('Mandatory comment not present', value, state)
                 v = value
-                c = u''
+                c = ''
             else:
                 left = value.rfind('(')
-                if left < 0: raise formencode.Invalid(u'Invalid format', value, state)
+                if left < 0: raise formencode.Invalid('Invalid format', value, state)
                 v = value[:left].strip()
                 c = value[left+1:right].strip()
         return self.value_validator.to_python(v, state), self.comment_validator.to_python(c, state)
@@ -222,8 +273,8 @@ class ValueComment(formencode.FancyValidator):
         v = self.value_validator.from_python(value[0], state)
         c = self.comment_validator.from_python(value[1], state)
         if len(c) > 0:
-            if len(v) > 0: return u'%s (%s)' % (v, c)
-            else: return u'(%s)' % c
+            if len(v) > 0: return '%s (%s)' % (v, c)
+            else: return '(%s)' % c
         return v
 
 
@@ -268,7 +319,7 @@ class GenericDateTime(formencode.FancyValidator):
     def to_python(self, value, state=None):
         self.assert_string(value, state)
         try: return datetime.datetime.strptime(value, self.date_time_format)
-        except ValueError, e: raise formencode.Invalid(str(e), value, state)
+        except ValueError as e: raise formencode.Invalid(str(e), value, state)
     
     def from_python(self, value, state=None):
         return value.strftime(self.date_time_format)
@@ -289,13 +340,13 @@ class Geo(formencode.FancyValidator):
     """Formats to coordinates '47.076207 N 11.453553 E' to the (latitude, longitude) tuplet."""
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        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, state)
+        r = re.match('(\d+\.\d+) N (\d+\.\d+) E', value)
+        if r is None: raise formencode.Invalid("Coordinates '%s' have not a format like '47.076207 N 11.453553 E'" % value, value, state)
         return (float(r.groups()[0]), float(r.groups()[1]))
     
     def from_python(self, value, state=None):
         latitude, longitude = value
-        return u'%.6f N %.6f E' % (latitude, longitude)
+        return '%.6f N %.6f E' % (latitude, longitude)
 
 
 class GeoNone(NoneValidator):
@@ -330,13 +381,13 @@ class MultiGeo(formencode.FancyValidator):
         self.assert_string(value, state)
         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, state) # Shouldn't it be an other type of runtime error?
+            raise formencode.Invalid("input_format %d is not recognized" % input_format, value, state) # 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)
+                r = re.match('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))
@@ -344,14 +395,14 @@ class MultiGeo(formencode.FancyValidator):
                     continue
                     
             if input_format == self.FORMAT_GUESS or input_format == self.FORMAT_WINTERRODELN:
-                r = re.match(u'(\d+\.\d+) N (\d+\.\d+) E', line)
+                r = re.match('(\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)
+                r = re.match('(\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
@@ -370,7 +421,7 @@ class MultiGeo(formencode.FancyValidator):
                     continue
                 except (ExpatError, IndexError, ValueError): pass
 
-            raise formencode.Invalid(u"Coordinates '%s' have no known format" % line, value, state)
+            raise formencode.Invalid("Coordinates '%s' have no known format" % line, value, state)
             
         return result
     
@@ -380,20 +431,20 @@ class MultiGeo(formencode.FancyValidator):
         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))
+                result.append('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))
+                result.append('%.6f N %.6f E' % (latitude, longitude))
 
             elif output_format == self.FORMAT_GMAPPLUGIN:
-                result.append(u'%.6f, %.6f' % (latitude, longitude))
+                result.append('%.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))
+                if not height is None: result.append('<trkpt lat="%.6f" lon="%.6f"><ele>%.2f</ele></trkpt>' % (latitude, longitude, height))
+                else: result.append('<trkpt lat="%.6f" lon="%.6f"/>' % (latitude, longitude))
             
             else:
-                raise formencode.Invalid(u"output_format %d is not recognized" % output_format, value, state) # Shouldn't it be an other type of runtime error?
+                raise formencode.Invalid("output_format %d is not recognized" % output_format, value, state) # Shouldn't it be an other type of runtime error?
             
         return "\n".join(result)
 
@@ -423,7 +474,7 @@ class AustrianPhoneNumber(formencode.FancyValidator):
 
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        m = re.match(u'^(?:\+(\d+)/)?([\d/]+)(?:-(\d+))?$', value)
+        m = re.match('^(?:\+(\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')
@@ -433,13 +484,13 @@ class AustrianPhoneNumber(formencode.FancyValidator):
         (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, state)
+        if phone.find('//') > -1 or phone.count('/') == 0: raise formencode.Invalid(self.message('phoneFormat', None) % {'value': value}, value, state)
         
         # Country
         if country is None:
             if phone[0] != '0': raise formencode.Invalid(self.message('phoneFormat', None) % {'value': value}, value, state)
             phone = phone[1:]
-            country = unicode(self.default_cc)
+            country = str(self.default_cc)
         
         if extension is None: return '+%s/%s' % (country, phone)
         return '+%s/%s-%s' % (country, phone, extension)
@@ -465,7 +516,7 @@ class GermanDifficulty(DictValidator):
     u'mittel' <=> 2
     u'schwer' <=> 3"""
     def __init__(self):
-        DictValidator.__init__(self, {u'': None, u'leicht': 1, u'mittel': 2, u'schwer': 3})
+        DictValidator.__init__(self, {'': None, 'leicht': 1, 'mittel': 2, 'schwer': 3})
 
 
 class GermanAvalanches(DictValidator):
@@ -477,7 +528,7 @@ class GermanAvalanches(DictValidator):
     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})
+        DictValidator.__init__(self, {'': None, 'kaum': 1, 'selten': 2, 'gelegentlich': 3, 'häufig': 4})
 
 
 class GermanPublicTransport(DictValidator):
@@ -491,7 +542,7 @@ class GermanPublicTransport(DictValidator):
     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})
+        DictValidator.__init__(self, {'': None, 'Sehr gut': 1, 'Gut': 2, 'Mittelmäßig': 3, 'Schlecht': 4, 'Nein': 5, 'Ja': 6})
 
 
 class GermanTristateFloatComment(ValueComment):
@@ -528,18 +579,18 @@ class GermanCachet(formencode.FancyValidator):
     u'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel' <=> u'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'"""
     def to_python(self, value, state=None):
         self.assert_string(value, state)
-        if value == u'': return None
-        elif value == u'Nein': return value
-        elif value.startswith(u'Tiroler Naturrodelbahn-Gütesiegel '):
+        if value == '': return None
+        elif value == 'Nein': return value
+        elif value.startswith('Tiroler Naturrodelbahn-Gütesiegel '):
             p = value.split(" ")
             Unsigned().to_python(p[2], state) # check if year can be parsed
             if not p[3] in ['leicht', 'mittel', 'schwer']: raise formencode.Invalid("Unbekannter Schwierigkeitsgrad", value, state)
             return value
-        else: raise formencode.Invalid(u"Unbekanntes Gütesiegel", value, state)
+        else: raise formencode.Invalid("Unbekanntes Gütesiegel", value, state)
     
     def from_python(self, value, state=None):
-        if value is None: return u''
-        assert value != u''
+        if value is None: return ''
+        assert value != ''
         return self.to_python(value, state)
 
 
@@ -552,10 +603,10 @@ class Url(formencode.FancyValidator):
     def to_python(self, value, state=None):
         self.assert_string(value, state)
         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 = v.replace('ä', 'a')
+        v = v.replace('ö', 'o')
+        v = v.replace('ü', 'u')
+        v = v.replace('ß', 'ss')
         v = self.urlv.to_python(v, state)
         return value
     
@@ -586,7 +637,7 @@ class PhoneNumber(formencode.FancyValidator):
         self.validator = formencode.national.InternationalPhoneNumber(default_cc=lambda: default_cc)
 
     def to_python(self, value, state=None):
-        return unicode(self.validator.to_python(value, state))
+        return str(self.validator.to_python(value, state))
 
     def from_python(self, value, state=None):
         return self.validator.from_python(value, state)
@@ -613,9 +664,9 @@ class MaskedEmail(formencode.FancyValidator):
     
     """
     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)
+        if 'strip' not in kw: kw['strip'] = True
+        if 'not_empty' not in kw: kw['not_empty'] = False
+        if 'if_empty' not in kw: kw['if_empty'] = (None, None)
         self.at = '(at)'
         formencode.FancyValidator.__init__(self, *args, **kw)
 
@@ -627,7 +678,7 @@ class MaskedEmail(formencode.FancyValidator):
 
     def _from_python(self, value, state=None):
         email, masked = value
-        if email is None: return u''
+        if email is None: return ''
         val_email = formencode.validators.Email()
         email = val_email.from_python(email, state)
         if masked: email = email.replace('@', self.at)
@@ -735,7 +786,7 @@ class GermanLift(BoolUnicodeTupleValidator):
     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'}))))
+        BoolUnicodeTupleValidator.__init__(self, Loop(ValueCommentList(DictValidator({'Sessellift': 'Sessellift', 'Gondel': 'Gondel', 'Linienbus': 'Linienbus', 'Taxi': 'Taxi', 'Sonstige': 'Sonstige'}))))
         
 
 class SledRental(BoolUnicodeTupleValidator):
@@ -747,29 +798,53 @@ class SledRental(BoolUnicodeTupleValidator):
         BoolUnicodeTupleValidator.__init__(self, Loop(ValueCommentList()))
 
 
-class RodelbahnboxValidator(formencode.Schema):
+class RodelbahnboxDictValidator(OrderedSchema):
     """Takes the fields of the Rodelbahnbox as dict of strings and returns them as dict of appropriet types."""
     def __init__(self):
-        self.add_field(u'Position', GeoNone()) # '47.583333 N 15.75 E'
-        self.add_field(u'Position oben', GeoNone()) # '47.583333 N 15.75 E'
-        self.add_field(u'Höhe oben', UnsignedNone()) # '2000'
-        self.add_field(u'Position unten', GeoNone()) # '47.583333 N 15.75 E'
-        self.add_field(u'Höhe unten', UnsignedNone()) # '1200'
-        self.add_field(u'Länge', UnsignedNone()) # 3500
-        self.add_field(u'Schwierigkeit', GermanDifficulty()) # 'mittel'
-        self.add_field(u'Lawinen', GermanAvalanches()) # 'kaum'
-        self.add_field(u'Betreiber', UnicodeNone()) # 'Max Mustermann'
-        self.add_field(u'Öffentliche Anreise', GermanPublicTransport()) # 'Mittelmäßig'
-        self.add_field(u'Aufstieg möglich', GermanBoolNone()) # 'Ja'
-        self.add_field(u'Aufstieg getrennt', GermanTristateFloatComment()) # 'Ja'
-        self.add_field(u'Gehzeit', UnsignedNone()) # 90
-        self.add_field(u'Aufstiegshilfe', GermanLift()) # 'Gondel (unterer Teil)'
-        self.add_field(u'Beleuchtungsanlage', GermanTristateFloatComment())
-        self.add_field(u'Beleuchtungstage', UnsignedCommentNone(7)) # '3 (Montag, Mittwoch, Freitag)'
-        self.add_field(u'Rodelverleih', SledRental()) # 'Talstation Serlesbahnan'
-        self.add_field(u'Gütesiegel', GermanCachet()) # 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'
-        self.add_field(u'Webauskunft', UrlNeinNone()) # 'http://www.nösslachhütte.at/page9.php'
-        self.add_field(u'Telefonauskunft', PhoneCommentListNeinLoopNone(comments_are_optional=False)) # '+43-664-5487520 (Mitterer Alm)'
-        self.add_field(u'Bild', UnicodeNone())
-        self.add_field(u'In Übersichtskarte', GermanBoolNone())
-        self.add_field(u'Forumid', UnsignedNeinNone())
+        self.add_field('Position', GeoNone()) # '47.583333 N 15.75 E'
+        self.add_field('Position oben', GeoNone()) # '47.583333 N 15.75 E'
+        self.add_field('Höhe oben', UnsignedNone()) # '2000'
+        self.add_field('Position unten', GeoNone()) # '47.583333 N 15.75 E'
+        self.add_field('Höhe unten', UnsignedNone()) # '1200'
+        self.add_field('Länge', UnsignedNone()) # 3500
+        self.add_field('Schwierigkeit', GermanDifficulty()) # 'mittel'
+        self.add_field('Lawinen', GermanAvalanches()) # 'kaum'
+        self.add_field('Betreiber', UnicodeNone()) # 'Max Mustermann'
+        self.add_field('Öffentliche Anreise', GermanPublicTransport()) # 'Mittelmäßig'
+        self.add_field('Aufstieg möglich', GermanBoolNone()) # 'Ja'
+        self.add_field('Aufstieg getrennt', GermanTristateFloatComment()) # 'Ja'
+        self.add_field('Gehzeit', UnsignedNone()) # 90
+        self.add_field('Aufstiegshilfe', GermanLift()) # 'Gondel (unterer Teil)'
+        self.add_field('Beleuchtungsanlage', GermanTristateFloatComment())
+        self.add_field('Beleuchtungstage', UnsignedCommentNone(7)) # '3 (Montag, Mittwoch, Freitag)'
+        self.add_field('Rodelverleih', SledRental()) # 'Talstation Serlesbahnan'
+        self.add_field('Gütesiegel', GermanCachet()) # 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'
+        self.add_field('Webauskunft', UrlNeinNone()) # 'http://www.nösslachhütte.at/page9.php'
+        self.add_field('Telefonauskunft', PhoneCommentListNeinLoopNone(comments_are_optional=False)) # '+43-664-5487520 (Mitterer Alm)'
+        self.add_field('Bild', UnicodeNone())
+        self.add_field('In Übersichtskarte', GermanBoolNone())
+        self.add_field('Forumid', UnsignedNeinNone())
+
+
+class GasthausboxDictValidator(OrderedSchema):
+    """Takes the fields of the Gasthausbox as dict of strings and returns them as dict of appropriet types."""
+    def __init__(self):
+        self.add_field('Position', GeoNone()) # '47.583333 N 15.75 E'
+        self.add_field('Höhe', UnsignedNone())
+        self.add_field('Betreiber', UnicodeNone())
+        self.add_field('Sitzplätze', UnsignedNone())
+        self.add_field('Übernachtung', BoolUnicodeTupleValidator())
+        self.add_field('Rauchfrei', GermanTristateTuple())
+        self.add_field('Rodelverleih', BoolUnicodeTupleValidator())
+        self.add_field('Handyempfang', ValueCommentListNeinLoopNone())
+        self.add_field('Homepage', UrlNeinNone())
+        self.add_field('E-Mail', EmailCommentListNeinLoopNone(allow_masked_email=True))
+        self.add_field('Telefon', PhoneCommentListNeinLoopNone(comments_are_optional=True))
+        self.add_field('Bild', UnicodeNone())
+        self.add_field('Rodelbahnen', WikiPageListLoopNone())
+
+
+def sledrun_page_title_to_pretty_url(page_title):
+    """Converts a page_title from the page_title column of wrsledruncache to name_url.
+    name_url is not used by MediaWiki but by new applications like wrweb."""
+    return page_title.lower().replace(' ', '-').replace('_', '-').replace('(', '').replace(')', '')