+#!/usr/bin/python2.5
+# -*- coding: iso-8859-15 -*-
+# $Id$
"MediaWiki communication functions"
+import datetime
+import re
from authkit.users import UsersReadOnly, md5
-import datetime
-import wradmin.model as model
-import wradmin.model.validators
import formencode, formencode.national
-
import logging
log = logging.getLogger(__name__)
+import wradmin.model as model
+import wradmin.model.validators
+
# Converter functions
# -------------------
return formencode.validators.Int(min=0).to_python(value)
+def from_unsigned(value):
+ """None -> ''
+ integer -> unicode"""
+ if value is None: return ''
+ return unicode(value)
+
+
+def to_string(value):
+ "Converts an empty string to None."
+ if len(value) == 0: return None
+ return value
+
+
def to_date(value):
"Parses a date in the form 'yyy-mm-dd'"
return model.validators.DateConverter().to_python(value)
def to_url(value):
- return formencode.validators.URL().to_python(value)
+ # return formencode.validators.URL().to_python(value) # does not accept URLs with accents
+ if value == '': return None
+ if not value.startswith('http'): raise formencode.Invalid('URL does not start with http.', value, None)
+ return value
+# deprecated
def to_phone(value):
return model.validators.AustrianPhoneNumber(messages={'phoneFormat': u"Telefonnummer %%(value)s muss das Format 0123/456789 oder +43/123/456789 haben"}).to_python(value)
+# deprecated
def to_phone_info(value):
return model.validators.PhoneInfo(messages={'phoneInfo': u"Bitte verwenden Sie ein Format wie '0512/123456 (Schnee Alm)'."}).to_python(value)
+def to_walkup_separate(value):
+ """'Ja', 'Nein', 'Teilweise' -> 1, 0, 0.5. A comment is allowed."""
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None: return None, None
+ if len(v) > 1: raise formencode.Invalid('Only one field is allowed.', value, None)
+ return formencode.validators.DictConverter({u'Ja': 1.0, u'Teilweise': 0.5, u'Nein': 0.0}).to_python(v[0][0]), v[0][1]
+
+
+def to_lift(value):
+ """Example 'Gondel (nur tagsüber); Taxi'"""
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None: return None, None
+ has_lift = None
+ for vv, comment in v:
+ if vv == u'Nein':
+ if len(vv) != 1: raise formencode.Invalid('"Nein" kann mit keiner anderen Aufstiegshilfe kombiniert werden.', value, None)
+ lift = False
+ elif not vv in [u'Sessellift', u'Gondel', u'Linienbus', u'Taxi', u'Sonstige']:
+ raise formencode.Invalid(u'"%s" ist keine gültige Aufstiegshilfe.' % vv, vv, None)
+ else: has_lift = True
+ return has_lift, value
+
+
+def to_night_light(value):
+ """Examle: 'Teilweise (im unteren Teil)'"""
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None: return None, None
+ if len(v) > 1: raise formencode.Invalid('Only one field is allowed.', value, None)
+ return formencode.validators.DictConverter({u'Ja': 1.0, u'Teilweise': 0.5, u'Nein': 0.0}).to_python(v[0][0]), v[0][1]
+
+
+def to_night_light_days(value):
+ """Example: '3 (Montag, Mittwoch, Freitag)'"""
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None: return None, None
+ if len(v) > 1: raise formencode.Invalid('Only one field is allowed.', value, None)
+ return to_unsigned(v[0][0]), v[0][1]
+
+
+def to_sled_rental(value):
+ """Example: 'Talstation Serlesbahnan; [[Pinnisalm]]'"""
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None: return None, None
+ sled_rental = True
+ if len(v) == 1:
+ if v[0][0] == 'Nein': return False, None
+ return True, value
+
+
+def to_cachet(value):
+ if value == '': return None
+ elif value == 'Nein': return value
+ elif value.startswith(u'Tiroler Naturrodelbahn-Gütesiegel '):
+ p = value.split(" ")
+ to_unsigned(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("Unbekanntes Gütesiegel", value, None)
+
+
+def to_information_web(value):
+ if value == 'Nein': return value
+ to_url(value) # is the URL valid?
+ return value
+
+
+def to_information_phone(value):
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None: return None
+ c = formencode.national.InternationalPhoneNumber(default_cc=lambda: 43)
+ for t in v:
+ if t[0] == 'Nein':
+ if len(v) != 1: raise formencode.Invalid("Bei 'Nein' ist nur ein Eintrag erlaubt", value, None)
+ else:
+ if c.to_python(t[0]) != t[0]: raise formencode.Invalid(u"Telefonnummer hat nicht das gewünschte Format.", value, None)
+ if (t[1] is None): raise formencode.Invalid(u"Bei Telefonnummern muss ein Kommentar angegeben werden.", value, None)
+ return value
+
+
+def to_overnight(value):
+ """'Nein' -> False, None
+ <unparsed text> -> True, <unparsed text>"""
+ if value == '': return None, None
+ if value == 'Nein': return False, None
+ return True, value
+
+
+def from_overnight(is_possible, comment):
+ """is_possible can be None, True or False
+ comment has to be a unicode string."""
+ if is_possible is None:
+ assert comment is None
+ return ''
+ if not is_possible:
+ assert comment is None
+ return u'Nein'
+ return comment
+
+
+def to_sled_rental_inn(value):
+ """'Nein' -> False, None
+ <unparsed text> -> True, <unparsed text>"""
+ return to_overnight(value) # same implementation as to_overnight
+
+
+def from_sled_rental_inn(is_possible, comment):
+ return from_overnight(is_possible, comment) # same implementation
+
+
+def to_forum_id(value):
+ if value == u'Nein': return 0
+ return to_unsigned(value)
+
+
+def to_mobile_provider(value):
+ """A1; T-Mobile A (gut)"""
+ model.validators.ValueCommentList().to_python(value) # no exception should be raised.
+ return value
+
+
+def to_email_list(value):
+ """Converts a list of email addresses with optional comments to the primary (first) adress,
+ comment for the primary address and the complete list.
+ >>> email_primary, email_primary_comment, email_list = to_email_list('first@example.com (Nur Winter); second@example.com')
+ >>> print email_primary
+ 'first@example.com'
+ >>> print email_primary_comment
+ 'Nur Winter'
+ >>> print email_list
+ 'first@example.com (Nur Winter); second@example.com'
+ """
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None or len(v) == 0: return None, None, None
+ for e, c in v: to_email(e) # check for valid addresses
+ return v[0][0], v[0][1], v
+
+
+def to_phone_list(value):
+ """Converts a list of internationa telephone number with optional comments to the primary (first) number,
+ comment for the primary number and the complete list.
+ >>> phone_primary, phone_primary_comment, phone_list = to_phone_list('+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456')
+ >>> print phone_primary
+ '+43-699-1234567'
+ >>> print phone_primary_comment
+ 'nicht nach 20:00 Uhr'
+ >>> print phone_list
+ '+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456'
+ """
+ v = model.validators.ValueCommentList().to_python(value)
+ if v is None or len(v) == 0: return None, None, None
+ ip = formencode.national.InternationalPhoneNumber(default_cc=lambda: 43)
+ for p, c in v: ip.to_python(p) # check for valid phone numbers
+ return v[0][0], v[0][1], v
+
+
+def to_wiki_list(value):
+ """Validates a list of wiki pages like "[[Birgitzer Alm]]; [[Kemater Alm]]" """
+ v = value.split(";")
+ for w in v:
+ if not w.startswith('[[') or not w.endswith(']]'): raise formencode.Invalid('No valid wiki page name', value, None)
+
+
def conv(fnct, value, fieldname):
"Like one of the to_xxx functions (e.g. to_bool), but adds the field name to the error message"
try: return fnct(value)
return unicode(exception)
+def wikipage_to_wrsleddingcache1_2(page_id, page_title, page_text):
+ """Converts a wiki page about a sledding route to a wradmin.model.WrSleddingCache1_2 class
+ that can be inserted to the wradmin.model.wrsleddingcache1_2_table.
+ It needs the wiki page id, the wiki page title and the page text ("old_text") as they come from the database."""
+ sl = model.WrSleddingCache1_2()
+ sl.page_id = page_id
+ sl.page_title = to_title(page_title)
+
+ # Match Rodelbahnbox
+ wikitext = page_text
+ regexp = re.compile(u"\{\{(Rodelbahnbox[^\}]*)\}\}", re.DOTALL)
+ match = regexp.search(wikitext)
+ if not match:
+ raise Exception(u"No 'Rodelbahnbox' found")
+ box = match.group(1)
+
+ # Process Rodelbahnbox
+ for property in box.split('|'):
+ property = property.strip()
+ if property == u'Rodelbahnbox': continue
+ key_value = property.split('=')
+ if len(key_value) != 2:
+ raise Exception(u"Property '%s' has unexpected format" % key_value)
+ key = key_value[0].strip()
+ value = key_value[1].strip()
+ if key == u'Rodelbahnnummer': pass
+ elif key == u'Länge': sl.length = conv(to_unsigned, value, u'Länge')
+ elif key == u'Gehzeit': sl.walktime = conv(to_unsigned, value, u'Gehzeit')
+ elif key == u'Höhe oben': sl.height_top = conv(to_unsigned, value, u'Höhe oben')
+ elif key == u'Höhe unten': sl.height_bottom = conv(to_unsigned, value, u'Höhe unten')
+ elif key == u'Aufstieg getrennt': sl.walkup_separate = conv(to_bool, value, u'Aufstieg getrennt')
+ elif key == u'Lift': sl.lift = conv(to_bool, value, u'Lift')
+ elif key == u'Beleuchtung': sl.night_light = conv(to_bool, value, u'Beleuchtung')
+ elif key == u'Rodelverleih': sl.sledge_rental = conv(to_bool, value, u'Rodelverleih')
+ elif key == u'Öffentliche Anreise': sl.public_transport = conv(to_bool, value, u'Öffentliche Anreise')
+ elif key == u'Bild': sl.image = conv(to_string, value, key)
+ elif key == u'Position': (sl.position_latitude, sl.position_longitude) = conv(to_geo, value, u'Position') # '47.583333 N 15.75 E'
+ elif key == u'Auskunft': sl.information = conv(to_phone_info, value, u'Auskunft')
+ elif key == u'In Übersichtskarte': sl.show_in_overview = conv(to_bool, value, u'In Übersichtskarte')
+ elif key == u'Aufnahmedatum': sl.creation_date = conv(to_date, value, u'Aufnahmedatum') # '2006-03-15'
+ elif key == u'Lawinengefahr':
+ # sl.avalanches is not part of the 1.2 sleddingcache table. We store it in the WrSleddingCache1_2 anyway.
+ sl.avalanches = conv(model.validators.GermanAvalanches().to_python, value, key)
+ else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Rodelbahnbox: '%s' (mit Wert '%s')" % (key, value), value, None)
+ sl.under_construction = None
+
+ # Match Forumlink (e.g. {{Forumlink|68}})
+ match = re.search(u"\{\{Forumlink\|(\d+)\}\}", wikitext)
+ if match: sl.forum_id = match.group(1)
+
+ return sl
+
+
+def wikipage_to_wrsleddingcache(wiki_page):
+ """Converts a sled-route wiki page (wradmin.model.page_table)
+ to a sledding route wrsleddingcache database record (wradmin.model.wrsleddingcache_table).
+ Raises a RuntimeError if the format is not OK
+ sledding_wiki is a column of tabe "page".
+ Returns the WrSleddingCache class"""
+ sl = model.WrSleddingCache()
+ sl.page_id = wiki_page.page_id
+ sl.page_title = to_title(wiki_page.page_title)
+ errors = [] # List of errors with localized messages
+
+ # Match Rodelbahnbox
+ wikitext = wiki_page.old_text
+ regexp = re.compile(u"\{\{(Rodelbahnbox[^\}]*)\}\}", re.DOTALL)
+ match = regexp.search(wikitext)
+ if not match:
+ raise RuntimeError(_(u"No 'Rodelbahnbox' found"))
+ box = match.group(1)
+
+ # Process Rodelbahnbox
+ for property in box.split('|'):
+ property = property.strip()
+ if property == u'Rodelbahnbox': continue
+ key_value = property.split('=')
+ if len(key_value) != 2:
+ raise RuntimeError(_(u"Property '%s' has unexpected format") % key_value)
+ key = key_value[0].strip()
+ value = key_value[1].strip()
+ if key in [u'Rodelbahnnummer', u'Lift']:
+ errors.append(_("Property '%s' is not supported anymore, see %s.") % (key, 'http://www.winterrodeln.org/wiki/Vorlage:Rodelbahnbox'))
+ elif key == u'Position': (sl.position_latitude, sl.position_longitude) = conv(to_geo, value, key) # '47.583333 N 15.75 E'
+ elif key == u'Position oben': (sl.top_latitude, sl.top_longitude) = conv(to_geo, value, key) # '47.583333 N 15.75 E'
+ elif key == u'Höhe oben': sl.top_elevation = conv(to_unsigned, value, key) # '2000'
+ elif key == u'Position unten': (sl.bottom_latitude, sl.bottom_longitude) = conv(to_geo, value, key) # '47.583333 N 15.75 E'
+ elif key == u'Höhe unten': sl.bottom_elevation = conv(to_unsigned, value, key) # '1200'
+ elif key == u'Länge': sl.length = conv(to_unsigned, value, key) # 3500
+ elif key == u'Schwierigkeit': sl.difficulty = conv(model.validators.GermanDifficulty().to_python, value, key) # 'mittel'
+ elif key == u'Lawinen': sl.avalanches = conv(model.validators.GermanAvalanches().to_python, value, key) # 'kaum'
+ elif key == u'Betreiber': sl.operator = value # 'Max Mustermann'
+ elif key == u'Öffentliche Anreise': sl.public_transport = conv(model.validators.GermanPublicTransport().to_python, value, key) # 'Mittelmäßig'
+ elif key == u'Gehzeit': sl.walkup_time = conv(to_unsigned, value, key) # 90
+ elif key == u'Aufstieg getrennt': sl.walkup_separate, sl.walkup_separate_comment = conv(to_walkup_separate, value, key) # 'Ja'
+ elif key == u'Aufstiegshilfe': sl.lift, sl.lift_comment = conv(to_lift, value, key) # 'Gondel (unterer Teil)'
+ elif key == u'Beleuchtungsanlage': sl.night_light, sl.night_light_comment = conv(to_night_light, value, key)
+ elif key == u'Beleuchtungstage': sl.night_light_days, sl.night_light_days_comment = conv(to_night_light_days, value, key) # '3 (Montag, Mittwoch, Freitag)'
+ elif key == u'Rodelverleih': sl.sled_rental, sl.sled_rental_comment = conv(to_sled_rental, value, key) # 'Talstation Serlesbahnan'
+ elif key == u'Gütesiegel': sl.cachet = conv(to_cachet, value, key) # 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'
+ elif key == u'Webauskunft': sl.information_web = conv(to_information_web, value, key) # 'http://www.nösslachhütte.at/page9.php'
+ elif key == u'Telefonauskunft': sl.information_phone = conv(to_information_phone, value, key) # '+43-664-5487520 (Mitterer Alm)'
+ elif key == u'Bild': sl.image = conv(to_string, value, key)
+ elif key == u'In Übersichtskarte': sl.show_in_overview = conv(to_bool, value, key)
+ elif key == u'Forumid': sl.forum_id = conv(to_forum_id, value, key)
+ else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Rodelbahnbox: '%s' (mit Wert '%s')" % (key, value), value, None)
+ sl.under_construction = None
+ return sl
+
+
+def wrSleddingCache1_2_to_WrSleddingCache(wrSleddingCache1_2):
+ """Converts the old WrSleddingCache format (1.2) WrSleddingCache1_2
+ to the new format (1.3) WrSleddingCache."""
+ wrSleddingCache = model.WrSleddingCache() # Create an object in the new format
+ wrSleddingCache.page_id = wrSleddingCache1_2.page_id
+ wrSleddingCache.page_title = wrSleddingCache1_2.page_id
+ wrSleddingCache.position_latitude = wrSleddingCache1_2.position_latitude
+ wrSleddingCache.position_longitude = wrSleddingCache1_2.position_longitude
+ wrSleddingCache.top_latitude = None
+ wrSleddingCache.top_longitude = None
+ wrSleddingCache.top_elevation = wrSleddingCache1_2.height_top
+ wrSleddingCache.bottom_latitude = None
+ wrSleddingCache.bottom_longitude = None
+ wrSleddingCache.bottom_elevation = wrSleddingCache1_2.height_bottom
+ wrSleddingCache.length = wrSleddingCache1_2.length
+ wrSleddingCache.difficulty = None
+ if 'avalanches' in dir(wrSleddingCache1_2): wrSleddingCache.avalanches = wrSleddingCache1_2.avalanches
+ else: wrSleddingCache.avalanches = None
+ wrSleddingCache.operator = None
+ if wrSleddingCache1_2.public_transport is None: wrSleddingCache.public_transport = None
+ else: wrSleddingCache.public_transport = 6 if wrSleddingCache1_2.public_transport else 5
+ wrSleddingCache.walkup_time = wrSleddingCache1_2.walktime
+ if wrSleddingCache1_2.walkup_separate is None: wrSleddingCache.walkup_separate = None
+ wrSleddingCache.walkup_separate = 1.0 if wrSleddingCache1_2.walkup_separate else 0.0
+ wrSleddingCache.walkup_separate_comment = None
+ wrSleddingCache.lift = wrSleddingCache1_2.lift
+ if wrSleddingCache1_2.lift: wrSleddingCache.lift_details = "Ja"
+ else: wrSleddingCache.lift_details = None
+ if wrSleddingCache1_2.night_light is None: wrSleddingCache.night_light = None
+ else: wrSleddingCache.night_light = 1.0 if wrSleddingCache1_2.night_light else 0.0
+ wrSleddingCache.night_light_comment = None
+ wrSleddingCache.night_light_days = None
+ wrSleddingCache.night_light_days_comment = None
+ wrSleddingCache.sled_rental = wrSleddingCache1_2.sledge_rental
+ wrSleddingCache.sled_rental_comment = None
+ wrSleddingCache.cachet = None
+ wrSleddingCache.information_web = None
+ if wrSleddingCache1_2.information is None: wrSleddingCache.information_phone = None
+ else:
+ m = re.match('^([-\d/\+]{5,}) \((.+)\)', wrSleddingCache1_2.information)
+ if m is None: raise formencode.Invalid('PhoneInfo is invalid', value, None)
+ (phone, info) = m.groups()
+ # check phone
+ phone = wradmin.model.validators.AustrianPhoneNumber().to_python(phone)
+ # convert phone
+ c = formencode.national.InternationalPhoneNumber(default_cc=lambda: 43)
+ phone = c.to_python(phone)
+ wrSleddingCache.information_phone = '%s (%s)' % (phone, info)
+ wrSleddingCache.image = wrSleddingCache1_2.image
+ wrSleddingCache.show_in_overview = wrSleddingCache1_2.show_in_overview
+ wrSleddingCache.forum_id = wrSleddingCache1_2.forum_id
+ wrSleddingCache.under_construction = wrSleddingCache1_2.under_construction
+ return wrSleddingCache
+
+
+def wrSleddingCache_to_Rodelbahnbox(wrSleddingCache):
+ """Converts the WrSleddingCache class to the {{Rodelbahnbox}} representation.
+wrSleddingCache.cachet
+wrSleddingCache.forum_id
+wrSleddingCache.image
+wrSleddingCache.information_phone
+wrSleddingCache.information_web
+wrSleddingCache.lift
+wrSleddingCache.lift_details
+wrSleddingCache.night_light
+wrSleddingCache.night_light_comment
+wrSleddingCache.night_light_days
+wrSleddingCache.night_light_days_comment
+wrSleddingCache.show_in_overview
+wrSleddingCache.sled_rental
+wrSleddingCache.sled_rental_comment
+wrSleddingCache.under_construction
+ """
+ keys = []
+ values = []
+ keys.append(u'Position')
+ values.append(wradmin.model.validators.Geo().from_python((wrSleddingCache.position_latitude, wrSleddingCache.position_longitude)))
+ keys.append(u'Position oben')
+ values.append(wradmin.model.validators.Geo().from_python((wrSleddingCache.top_latitude, wrSleddingCache.top_longitude)))
+ keys.append(u'Höhe oben')
+ values.append(from_unsigned(wrSleddingCache.top_elevation))
+ keys.append(u'Position unten')
+ values.append(wradmin.model.validators.Geo().from_python((wrSleddingCache.bottom_latitude, wrSleddingCache.bottom_longitude)))
+ keys.append(u'Höhe unten')
+ values.append(from_unsigned(wrSleddingCache.bottom_elevation))
+ keys.append(u'Länge')
+ values.append(from_unsigned(wrSleddingCache.length))
+ keys.append(u'Schwierigkeit')
+ values.append(model.validators.GermanDifficulty().from_python(wrSleddingCache.difficulty))
+ keys.append(u'Lawinen')
+ values.append(model.validators.GermanAvalanches().from_python(wrSleddingCache.avalanches))
+ keys.append(u'Betreiber')
+ values.append(formencode.validators.String().from_python(wrSleddingCache.operator))
+ keys.append(u'Öffentliche Anreise')
+ values.append(model.validators.GermanPublicTransport().from_python(wrSleddingCache.public_transport))
+ keys.append(u'Gehzeit')
+ values.append(from_unsigned(wrSleddingCache.walkup_time))
+ keys.append(u'Aufstieg getrennt')
+ v = model.validators.GermanTristateFloat().from_python(wrSleddingCache.walkup_separate)
+ if not wrSleddingCache.walkup_separate_comment is None: v += ' (%s)' % wrSleddingCache.walkup_separate_comment
+ values.append(v)
+
+ b = u"""
+| Aufstiegshilfe = Gondel (unterer Teil)
+| Beleuchtungsanlage = Ja
+| Beleuchtungstage = 3 (Montag, Mittwoch, Freitag)
+| Rodelverleih = Ja (Talstation Serlesbahnan)
+| Gütesiegel = Tiroler Naturrodelbahn-Gütesiegel 2009 mittel
+| Webauskunft = http://www.nösslachhütte.at/page9.php
+| Telefonauskunft = +43-664-5487520 (Mitterer Alm)
+| Bild = Rodelbahn_Mitterer_Alm_04.jpg
+| In Übersichtskarte = Ja
+| Forumid = 33
+}}
+ """
+
+ c = u"""
+ keys.append(u'Betreiber')
+ values.append(formencode.validators.String().from_python(wrInnCache.operator))
+ keys.append(u'Sitzplätze')
+ values.append(from_unsigned(wrInnCache.seats))
+ keys.append(u'Übernachtung')
+ values.append(from_overnight(wrInnCache.overnight, wrInnCache.overnight_comment))
+ keys.append(u'Rauchfrei')
+ values.append(wradmin.model.validators.GermanTristate.from_python((wrInnCache.nonsmoker_area, wrInnCache.smoker_area)))
+ keys.append(u'Rodelverleih')
+ values.append(from_sled_rental_inn(wrInnCache.sled_rental, wrInnCache.sled_rental_comment))
+ keys.append(u'Handyempfang')
+ values.append(formencode.validators.String().from_python(wrInnCache.mobile_provider))
+ keys.append(u'Homepage')
+ values.append(formencode.validators.String().from_python(wrInnCache.homepage))
+ keys.append(u'E-Mail')
+ values.append(formencode.validators.String().from_python(wrInnCache.email_list))
+ keys.append(u'Telefon')
+ values.append(formencode.validators.String().from_python(wrInnCache.phone_list))
+ keys.append(u'Bild')
+ values.append(formencode.validators.String().from_python(wrInnCache.image))
+ keys.append(u'Rodelbahnen')
+ values.append(formencode.validators.String().from_python(wrInnCache.sledding_list))
+ """
+ result = [u'{{Rodelbahnbox']
+ for i in xrange(len(keys)): result.append(u'| %-20s = %s' % (keys[i], values[i]))
+ result.append('}}\n')
+ return '\n'.join(result)
+
+
+def wikipage_to_wrinncache1_2(page_id, page_title, page_text):
+ """Converts a wiki page about an inn to an wradmin.model.WrInnCache1_2 class
+ that can be inserted to the wradmin.model.wrinncache1_2_table.
+ It uses only text operations and does not query or update the database.
+ It needs the wiki page id, the wiki page title and the page text ("old_text") as they come from the database."""
+ inn = model.WrInnCache1_2()
+ inn.page_id = page_id
+ inn.page_title = to_title(page_title)
+
+ # Match Gasthausbox
+ wikitext = page_text
+ regexp = re.compile(u"\{\{(Gasthausbox[^\}]*)\}\}", re.DOTALL)
+ match = regexp.search(wikitext)
+ if not match:
+ raise Exception(u"No 'Gasthausbox' found")
+ box = match.group(1)
+
+ # Process Gashausbox
+ for property in box.split('|'):
+ property = property.strip()
+ if property == u'Gasthausbox': continue
+ key_value = property.split('=')
+ if len(key_value) != 2:
+ raise Exception(u"Property '%s' has unexpected format" % key_value)
+ key = key_value[0].strip()
+ value = key_value[1].strip()
+ if key == u'Gasthausnummer': pass
+ elif key == u'E-Mail': inn.email = conv(to_email, value, u'E-Mail')
+ elif key == u'Homepage': inn.homepage = conv(to_url, value, u'Homepage')
+ elif key == u'Höhe': inn.height = conv(to_unsigned, value, u'Höhe')
+ elif key == u'Bild': inn.image = conv(to_string, value, key)
+ elif key == u'Position': inn.position_latitude, inn.position_longitude = conv(to_geo, value, u'Position') # '47.583333 N 15.75 E'
+ elif key == u'Telefon (Festnetz)': inn.phone = conv(to_phone, value, u'Telefon (Festnetz)')
+ elif key == u'Telefon (Mobil)': inn.mobile_phone = conv(to_phone, value, u'Telefon (Mobil)')
+ elif key == u'Rauchfrei': inn.nonsmoker_area, inn.smoker_area = conv(to_tristate, value, u'Rauchfrei')
+ elif key == u'Aufnahmedatum': inn.creation_date = conv(to_date, value, u'Aufnahmedatum') # '2006-03-15'
+ else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Gasthausbox: '%s' (mit Wert '%s')" % (key, value), value, None)
+ inn.under_construction = None
+ return inn
+
+
+def wikipage_to_wrinncache(wiki_page):
+ """Converts a inn wiki page (wradmin.model.page_table) to a wrinncache database record
+ (wradmin.model.wrinncache_table)."""
+ inn = model.WrInnCache()
+ inn.page_id = inn_wiki.page_id
+ inn.page_title = to_title(inn_wiki.page_title)
+
+ # Match Gasthausbox
+ wikitext = inn_wiki.old_text
+ regexp = re.compile(u"\{\{(Gasthausbox[^\}]*)\}\}", re.DOTALL)
+ match = regexp.search(wikitext)
+ if not match:
+ raise Exception(u"No 'Gasthausbox' found")
+ box = match.group(1)
+
+ # Process Gashausbox
+ for property in box.split('|'):
+ property = property.strip()
+ if property == u'Gasthausbox': continue
+ key_value = property.split('=')
+ if len(key_value) != 2:
+ raise Exception(u"Property '%s' has unexpected format" % key_value)
+ key = key_value[0].strip()
+ value = key_value[1].strip()
+ if key == u'Position': (inn.position_latitude, inn.position_longitude) = conv(to_geo, value, key) # '47.583333 N 15.75 E'
+ elif key == u'Höhe': inn.position_elevation = conv(to_unsigned, value, key)
+ elif key == u'Operator': inn.operator = conv(to_string, value, key)
+ elif key == u'Sitzplätze': inn.seats = conv(to_unsigned, value, key)
+ elif key == u'Übernachtung': inn.overnight, inn.overnight_comment = conv(to_overnight, value, key)
+ elif key == u'Rauchfrei': inn.nonsmoker_area, inn.smoker_area = conv(to_tristate, value, key)
+ elif key == u'Rodelverleih': inn.sled_rental, inn.sled_rental_comment = conv(to_sled_rental_inn, value, key)
+ elif key == u'Handyempfang': inn.mobile_provider = conv(to_mobile_provider, value, key)
+ elif key == u'Homepage': inn.homepage = conv(to_information_web, value, key)
+ elif key == u'E-Mail': inn.email, inn.email_comment, inn.email_list = conv(to_email_list, value, key)
+ elif key == u'Telefon': inn.phone, inn.phone_comment, inn.phone_list = conv(to_phone_list, value, key)
+ elif key == u'Bild': inn.image = conv(to_string, value, key)
+ elif key == u'Rodelbahnen': inn.sledding_list = conv(to_sledding_list, value, key)
+ else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Gasthausbox: '%s' (mit Wert '%s')" % (key, value), value, None)
+ inn.under_construction = None
+ return inn
+
+
+def wrInnCache1_2_to_WrInnCache(wrInnCache1_2):
+ """Converts the old WrInnCache format to the new format."""
+ wrInnCache = model.WrInnCache() # Create an object in the new format
+ wrInnCache.page_id = wrInnCache1_2.page_id
+ wrInnCache.page_title = wrInnCache1_2.page_id
+ wrInnCache.position_latitude = wrInnCache1_2.position_latitude
+ wrInnCache.position_longitude = wrInnCache1_2.position_longitude
+ wrInnCache.position_elevation = wrInnCache1_2.height
+ wrInnCache.operator = None
+ wrInnCache.seats = None
+ wrInnCache.overnight = None
+ wrInnCache.overnight_comment = None
+ wrInnCache.smoker_area = wrInnCache1_2.smoker_area
+ wrInnCache.nonsmoker_area = wrInnCache1_2.nonsmoker_area
+ wrInnCache.sled_rental = None
+ wrInnCache.sled_rental_comment = None
+ wrInnCache.mobile_provider = None
+ wrInnCache.homepage = wrInnCache1_2.homepage
+ wrInnCache.email = wrInnCache1_2.email
+ wrInnCache.email_comment = None
+ wrInnCache.email_list = wrInnCache1_2.email
+ phone_list = []
+ c = formencode.national.InternationalPhoneNumber(default_cc=lambda: 43)
+ if not wrInnCache1_2.phone is None: phone_list.append(c.to_python(wrInnCache1_2.phone))
+ if not wrInnCache1_2.mobile_phone is None: phone_list.append(c.to_python(wrInnCache1_2.mobile_phone))
+ if len(phone_list) >= 1: wrInnCache.phone = phone_list[0]
+ else: wrInnCache.phone = None
+ wrInnCache.phone_comment = None
+ wrInnCache.phone_list = "; ".join(phone_list)
+ wrInnCache.image = wrInnCache1_2.image
+ wrInnCache.sledding_list = None
+ wrInnCache.under_construction = wrInnCache1_2.under_construction
+ return wrInnCache
+
+
+def wrInnCache_to_Gasthausbox(wrInnCache):
+ """Converts the WrInnCache class to the {{Gasthausbox}} representation."""
+ keys = []
+ values = []
+ keys.append(u'Position')
+ values.append(wradmin.model.validators.Geo().from_python((wrInnCache.position_latitude, wrInnCache.position_longitude)))
+ keys.append(u'Höhe')
+ values.append(from_unsigned(wrInnCache.position_elevation))
+ keys.append(u'Betreiber')
+ values.append(formencode.validators.String().from_python(wrInnCache.operator))
+ keys.append(u'Sitzplätze')
+ values.append(from_unsigned(wrInnCache.seats))
+ keys.append(u'Übernachtung')
+ values.append(from_overnight(wrInnCache.overnight, wrInnCache.overnight_comment))
+ keys.append(u'Rauchfrei')
+ values.append(wradmin.model.validators.GermanTristate.from_python((wrInnCache.nonsmoker_area, wrInnCache.smoker_area)))
+ keys.append(u'Rodelverleih')
+ values.append(from_sled_rental_inn(wrInnCache.sled_rental, wrInnCache.sled_rental_comment))
+ keys.append(u'Handyempfang')
+ values.append(formencode.validators.String().from_python(wrInnCache.mobile_provider))
+ keys.append(u'Homepage')
+ values.append(formencode.validators.String().from_python(wrInnCache.homepage))
+ keys.append(u'E-Mail')
+ values.append(formencode.validators.String().from_python(wrInnCache.email_list))
+ keys.append(u'Telefon')
+ values.append(formencode.validators.String().from_python(wrInnCache.phone_list))
+ keys.append(u'Bild')
+ values.append(formencode.validators.String().from_python(wrInnCache.image))
+ keys.append(u'Rodelbahnen')
+ values.append(formencode.validators.String().from_python(wrInnCache.sledding_list))
+ result = [u'{{Gasthausbox']
+ for i in xrange(len(keys)): result.append(u'| %-17s = %s' % (keys[i], values[i]))
+ result.append('}}\n')
+ return '\n'.join(result)
+
+
+
# User management
# ---------------
meta.engine = engine
+# Current table definition
+# - version 1.2
+# - version 1.3 (no changes)
wrreport_table = sa.Table("wrreport", meta.metadata,
sa.Column("id", types.Integer, primary_key=True),
sa.Column("page_id", types.Integer, schema.ForeignKey('wrsleddingcache.page_id')),
)
-wrsleddingcache_table = sa.Table("wrsleddingcache", meta.metadata,
+# Old table definition
+# - version 1.2
+wrsleddingcache_table1_2 = sa.Table("wrsleddingcache1_2", meta.metadata,
sa.Column("page_id", types.Integer, primary_key=True),
sa.Column("page_title", types.Unicode(255)),
sa.Column("length", types.Integer),
)
-wrinncache_table = sa.Table("wrinncache", meta.metadata,
+# Current table definition
+# - version 1.3 (changes made from version 1.2)
+wrsleddingcache_table = sa.Table("wrsleddingcache", meta.metadata,
+ sa.Column("page_id", types.Integer, primary_key=True),
+ sa.Column("page_title", types.Unicode(255)),
+ sa.Column("position_latitude", types.Float),
+ sa.Column("position_longitude", types.Float),
+ sa.Column("top_latitude", types.Float),
+ sa.Column("top_longitude", types.Float),
+ sa.Column("top_elevation", types.Integer),
+ sa.Column("bottom_latitude", types.Float),
+ sa.Column("bottom_longitude", types.Float),
+ sa.Column("bottom_elevation", types.Integer),
+ sa.Column("length", types.Integer),
+ sa.Column("difficulty", types.Integer),
+ sa.Column("avalanches", types.Integer),
+ sa.Column("operator", types.Unicode(255)),
+ sa.Column("public_transport", types.Integer),
+ sa.Column("walkup_time", types.Integer),
+ sa.Column("walkup_separate", types.Float),
+ sa.Column("walkup_separate_comment", types.Unicode(255)),
+ sa.Column("lift", types.Boolean),
+ sa.Column("lift_details", types.Unicode(255)),
+ sa.Column("night_light", types.Float),
+ sa.Column("night_light_comment", types.Unicode(255)),
+ sa.Column("night_light_days", types.Integer),
+ sa.Column("night_light_days_comment", types.Unicode(255)),
+ sa.Column("sled_rental", types.Boolean),
+ sa.Column("sled_rental_comment", types.Unicode(255)),
+ sa.Column("cachet", types.Unicode(255)),
+ sa.Column("information_web", types.Unicode(255)),
+ sa.Column("information_phone", types.Unicode(255)),
+ sa.Column("image", types.Unicode(255)),
+ sa.Column("show_in_overview", types.Boolean),
+ sa.Column("forum_id", types.Integer),
+ sa.Column("under_construction", types.Boolean),
+ )
+
+
+# Old table definition
+# - version 1.2
+wrinncache_table1_2 = sa.Table("wrinncache1_2", meta.metadata,
sa.Column("page_id", types.Integer, primary_key=True),
sa.Column("page_title", types.Unicode(255)),
sa.Column("height", types.Integer),
)
+# Current table definition
+# - version 1.3 (changes made from version 1.2)
+wrinncache_table = sa.Table("wrinncache", meta.metadata,
+ sa.Column("page_id", types.Integer, primary_key=True),
+ sa.Column("page_title", types.Unicode(255)),
+ sa.Column("position_latitude", types.Float),
+ sa.Column("position_longitude", types.Float),
+ sa.Column("position_elevation", types.Integer),
+ sa.Column("seats", types.Integer),
+ sa.Column("overnight", types.Boolean),
+ sa.Column("overnight_comment", types.Unicode(255)),
+ sa.Column("smoker_area", types.Boolean),
+ sa.Column("nonsmoker_area", types.Boolean),
+ sa.Column("sled_rental", types.Boolean),
+ sa.Column("sled_rental_comment", types.Unicode(255)),
+ sa.Column("mobile_provider", types.Unicode),
+ sa.Column("homepage", types.Unicode(255)),
+ sa.Column("email", types.Unicode(255)),
+ sa.Column("email_comment", types.Unicode(255)),
+ sa.Column("email_list", types.Unicode),
+ sa.Column("phone", types.Unicode(30)),
+ sa.Column("phone_comment", types.Unicode(255)),
+ sa.Column("phone_list", types.Unicode),
+ sa.Column("image", types.Unicode(255)),
+ sa.Column("sledding_list", types.Unicode),
+ sa.Column("under_construction", types.Boolean),
+ )
+
+
page_table = sa.Table("page", meta.metadata,
sa.Column("page_id", types.Integer, primary_key=True),
sa.Column("page_namespace", types.Integer, nullable=False),
pass
+# Old version (not mapped)
+class WrSleddingCache1_2(object):
+ pass
+
+
class WrSleddingCache(object):
pass
+# Old version (not mapped)
+class WrInnCache1_2(object):
+ pass
+
+
class WrInnCache(object):
pass
+# Page (not mapped)
+class Page(object):
+ pass
+
+
orm.mapper(WrReport, wrreport_table)
# We could add a relation but we don't need it yet:
# orm.mapper(WrSleddingCache, wrsleddingcache_table, properties = {'reports': orm.relation(WrReport, backref='sledding')})
orm.mapper(WrSleddingCache, wrsleddingcache_table)
orm.mapper(WrInnCache, wrinncache_table)
+
+# Old tables
+orm.mapper(WrSleddingCache1_2, wrsleddingcache_table1_2)
+orm.mapper(WrInnCache1_2, wrinncache_table1_2)
GenericDateTimeConverter.__init__(self, '%Y-%m-%d', **keywords)
def _to_python(self, value, state):
- GenericDateTimeConverter._to_python(self, value, state).date()
+ return GenericDateTimeConverter._to_python(self, value, state).date()
class GermanTristate(formencode.FancyValidator):
return "\n".join(result)
+# deprecated
class AustrianPhoneNumber(formencode.FancyValidator):
"""
Validates and converts phone numbers to +##/###/####### or +##/###/#######-### (having an extension)
return '+%s/%s-%s' % (country, phone, extension)
+# Deprecated
class PhoneInfo(formencode.FancyValidator):
"Validates a info of the form '0644/1234567 (Schnee Alm)'"
messages = {'infoFormat': "'%%(value)s' is no valid format, please use a form like '0644/1234567 (Schnee Alm)'"}
phone = AustrianPhoneNumber().to_python(phone)
return "%s (%s)" % (phone, info)
+
+
+class ValueComment(formencode.FancyValidator):
+ """Converts value with a potentially optional comment to a python tuple:
+ u'' <=> (None, None)
+ u'value' <=> (u'value', None)
+ u'value (comment)' <=> (u'value', u'comment')"""
+
+ def __init__(self, comment_is_optional=True):
+ self.comment_is_optional = comment_is_optional
+
+ def _to_python(self, value, state):
+ self.assert_string(value, state)
+ if value == u'': return None
+ left = value.find('(')
+ right = value.rfind(')')
+ if left < 0 and right < 0:
+ if not comment_is_optional: raise formencode.Invalid(u'Mandatory comment not present', value, state)
+ return value, None
+ elif left >= 0 and right >= 0 and left < right:
+ return value[:left].strip(), value[left+1:right].strip()
+ else: raise formencode.Invalid(u'Invalid format', value, state)
+
+ def from_python(self, value):
+ assert len(value) == 2
+ if value[0] is None:
+ assert value[1] == None
+ return u''
+ if value[1] is None:
+ return value[0]
+ return u'%s (%s)' % (value[0], value[1])
+
+
+class ValueCommentList(formencode.FancyValidator):
+ """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, None is returned."""
+ messages = {'infoFormat': "'%%(value)s' is no valid format, please use a form like 'value1 (optional comment1); value2 (optional comment2)'"}
+
+ def _to_python(self, value, state):
+ self.assert_string(value, state)
+ value_options = [s.strip() for s in value.split(';')]
+ result = []
+ for value_option in value_options:
+ left = value_option.find('(')
+ right = value_option.rfind(')')
+ if left < 0 and right < 0:
+ result.append((value_option, None))
+ elif left >= 0 and right >= 0 and left < right:
+ result.append((value_option[:left].strip(), value_option[left+1:right].strip()))
+ else: raise formencode.Invalid(self.message('infoFormat', state) % {'value': value}, value, state)
+ return result
+
+ def _from_python(self, value, state):
+ result = []
+ for v, c in value:
+ if c is None: result.append(v)
+ else: result.append('%s (%s)' % (v, c))
+ return "; ".join(result)
+
+
+class GermanDifficulty(formencode.FancyValidator):
+ """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"""
+ dc = formencode.validators.DictConverter({u'leicht': 1, u'mittel': 2, u'schwer': 3})
+
+ def _to_python(self, value, state):
+ """Value has to be one of u'', u'leicht', u'mittel' or u'schwer':
+ u'' => None
+ u'leicht' => 1
+ u'mittel' => 2
+ u'schwer' => 3"""
+ self.assert_string(value, state)
+ return self.dc.to_python(value)
+
+ def from_python(self, value):
+ """value has to be one of None, 1, 2, 3:
+ None => u''
+ 1 => u'leicht'
+ 2 => u'mittel'
+ 3 => u'schwer'"""
+ if value is None: return u''
+ return self.dc.from_python(value)
+
+
+class GermanAvalanches(formencode.FancyValidator):
+ """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"""
+ dc = formencode.validators.DictConverter({u'kaum': 1, u'selten': 2, u'gelegentlich': 3, u'häufig': 4})
+
+ def _to_python(self, value, state):
+ """value has to be one of u'', u'kaum', u'selten', u'gelegentlich', u'häufig':
+ u'' => None
+ u'kaum' => 1
+ u'selten' => 2
+ u'gelegentlich' => 3
+ u'häufig' => 4"""
+ self.assert_string(value, state)
+ return self.dc.to_python(value)
+
+ def from_python(self, value):
+ """value has to be one of None, 1, 2, 3, 4:
+ None => u''
+ 1 => u'kaum'
+ 2 => u'selten'
+ 3 => u'gelegentlich'
+ 4 => u'häufig'"""
+ if value is None: return u''
+ return self.dc.from_python(value)
+
+
+class GermanPublicTransport(formencode.FancyValidator):
+ """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"""
+ dc = formencode.validators.DictConverter({u'Sehr gut': 1, u'Gut': 2, u'Mittelmäßig': 3, u'Schlecht': 4, u'Nein': 5, u'Ja': 6})
+
+ def _to_python(self, value, state):
+ """value has to be one of u'', u'Sehr gut', u'Gut', u'Mittelmäßig', u'Schlecht', u'Nein', u'Ja':
+ u'' => None
+ u'Sehr gut' => 1
+ u'Gut' => 2
+ u'Mittelmäßig' => 3
+ u'Schlecht' => 4
+ u'Nein' => 5
+ u'Ja' => 6"""
+ self.assert_string(value, state)
+ return self.dc.to_python(value)
+
+ def from_python(self, value):
+ """value has to be one of None, 1, 2, 3, 4, 5, 6:
+ None => u''
+ 1 => u'Sehr gut'
+ 2 => u'Gut'
+ 3 => u'Mittelmäßig'
+ 4 => u'Schlecht'
+ 5 => u'Nein'
+ 6 => u'Ja'"""
+ if value is None: return u''
+ return self.dc.from_python(value)
+
+
+class GermanTristateFloat(formencode.FancyValidator):
+ """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"""
+ dc = formencode.validators.DictConverter({u'Ja': 1.0, u'Teilweise': 0.5, u'Nein': 0.0})
+
+ def _to_python(self, value, state):
+ """value has to be one of u'', u'Ja', u'Teilweise', u'Nein':
+ u'' => None
+ u'Ja' => 1.0
+ u'Teilweise' => 0.5
+ u'Nein' => 0.0"""
+ self.assert_string(value, state)
+ return self.dc.to_python(value)
+
+ def from_python(self, value):
+ """value has to be one of None, 0.0, 0.5, 1.0:
+ None => u''
+ 0.0 => u'Nein'
+ 0.5 => u'Teilweise'
+ 1.0 => u'Ja'"""
+ if value is None: return u''
+ return self.dc.from_python(value)
+
+
+class GermanTristateFloatComment(formencode.FancyValidator):
+ """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')
+ """
+ tf = GermanTristateFloat()
+
+ def _to_python(self, value, state):
+ self.assert_string(value, state)
+ return self.dc.to_python(value)
+
+ def from_python(self, value):
+ """value has to be one of None, 0.0, 0.5, 1.0:
+ None => u''
+ 0.0 => u'Nein'
+ 0.5 => u'Teilweise'
+ 1.0 => u'Ja'"""
+ if value is None: return u''
+ return self.dc.from_python(value)
+