#!/usr/bin/python3.4
-# -*- coding: iso-8859-15 -*-
-import wrpylib.mwmarkup
+# coding=utf-8
import unittest
+import mwparserfromhell
+import wrpylib.mwmarkup
class TestMwMarkup(unittest.TestCase):
{{Rodelbahnbox
| Position = 47.309820 N 9.986508 E
| Position oben =
- | Höhe oben = 1244
+ | Höhe oben = 1244
| Position unten =
- | Höhe unten = 806
- | Länge = 5045
+ | Höhe unten = 806
+ | Länge = 5045
| Schwierigkeit =
| Lawinen = gelegentlich
| Betreiber =
- | Öffentliche Anreise = Ja
+ | Öffentliche Anreise = Ja
| Gehzeit = 105
| Aufstieg getrennt = Nein
| Aufstiegshilfe = Nein
| Beleuchtungsanlage = Nein
| Beleuchtungstage =
| Rodelverleih = Ja
- | Gütesiegel =
+ | Gütesiegel =
| Webauskunft =
- | Telefonauskunft = +43-664-1808482 (Bergkristallhütte)
- | Bild = Rodelbahn Bergkristallhütte 2009-03-03.jpg
- | In Übersichtskarte = Ja
+ | Telefonauskunft = +43-664-1808482 (Bergkristallhütte)
+ | Bild = Rodelbahn Bergkristallhütte 2009-03-03.jpg
+ | In Übersichtskarte = Ja
| Forumid = 72
}}
- Die Rodelbahn zur Bergkristallhütte ist durchaus abwechslungsreich.'''
+ Die Rodelbahn zur Bergkristallhütte ist durchaus abwechslungsreich.'''
start, end = wrpylib.mwmarkup.find_template(wikitext, 'Rodelbahnbox')
- assert start == wikitext.find('{{')
- assert end == wikitext.find('}}')+2
+ self.assertEqual(start, wikitext.find('{{'))
+ self.assertEqual(end, wikitext.find('}}')+2)
def test_TemplateValidator(self):
v = wrpylib.mwmarkup.TemplateValidator()
- value = '{{Rodelbahnbox | Unbenannt | Position = 47.309820 N 9.986508 E | Aufstieg möglich = Ja }}'
+ value = '{{Rodelbahnbox | Unbenannt | Position = 47.309820 N 9.986508 E | Aufstieg möglich = Ja }}'
title, anonym_params, named_params = v.to_python(value)
- assert title == 'Rodelbahnbox'
- assert anonym_params == ['Unbenannt']
- assert list(named_params.keys()) == ['Position', 'Aufstieg möglich']
- assert list(named_params.values()) == ['47.309820 N 9.986508 E', 'Ja']
+ self.assertEqual(title, 'Rodelbahnbox')
+ self.assertEqual(anonym_params, ['Unbenannt'])
+ self.assertEqual(list(named_params.keys()), ['Position', 'Aufstieg möglich'])
+ self.assertEqual(list(named_params.values()), ['47.309820 N 9.986508 E', 'Ja'])
value2 = v.from_python((title, anonym_params, named_params))
- assert value2 == '{{Rodelbahnbox|Unbenannt|Position=47.309820 N 9.986508 E|Aufstieg möglich=Ja}}'
+ self.assertEqual(value2, '{{Rodelbahnbox|Unbenannt|Position=47.309820 N 9.986508 E|Aufstieg möglich=Ja}}')
v = wrpylib.mwmarkup.TemplateValidator(as_table=True)
value3 = v.from_python((title, anonym_params, named_params))
- assert value3 == \
- '{{Rodelbahnbox\n' + \
- '| Unbenannt\n' + \
- '| Position = 47.309820 N 9.986508 E\n' + \
- '| Aufstieg möglich = Ja\n' + \
- '}}'
+ self.assertEqual(value3,
+ '{{Rodelbahnbox\n' +
+ '| Unbenannt\n' +
+ '| Position = 47.309820 N 9.986508 E\n' +
+ '| Aufstieg möglich = Ja\n' +
+ '}}')
v = wrpylib.mwmarkup.TemplateValidator(strip=False)
title, anonym_params, named_params = v.to_python(value)
- assert title == 'Rodelbahnbox '
- assert anonym_params == [' Unbenannt ']
- assert list(named_params.keys()) == [' Position ', ' Aufstieg möglich ']
- assert list(named_params.values()) == [' 47.309820 N 9.986508 E ', ' Ja ']
+ self.assertEqual(title, 'Rodelbahnbox ')
+ self.assertEqual(anonym_params, [' Unbenannt '])
+ self.assertEqual(list(named_params.keys()), [' Position ', ' Aufstieg möglich '])
+ self.assertEqual(list(named_params.values()), [' 47.309820 N 9.986508 E ', ' Ja '])
def test_split_template(self):
{{Rodelbahnbox
| Position = 47.309820 N 9.986508 E
| Position oben =
- | Höhe oben = 1244
+ | Höhe oben = 1244
| Position unten =
- | Höhe unten = 806
- | Länge = 5045
+ | Höhe unten = 806
+ | Länge = 5045
| Schwierigkeit =
| Lawinen = gelegentlich
| Betreiber =
- | Öffentliche Anreise = Ja
+ | Öffentliche Anreise = Ja
| Gehzeit = 105
| Aufstieg getrennt = Nein
| Aufstiegshilfe = Nein
| Beleuchtungsanlage = Nein
| Beleuchtungstage =
| Rodelverleih = Ja
- | Gütesiegel =
+ | Gütesiegel =
| Webauskunft =
- | Telefonauskunft = +43-664-1808482 (Bergkristallhütte)
- | Bild = Rodelbahn Bergkristallhütte 2009-03-03.jpg
- | In Übersichtskarte = Ja
+ | Telefonauskunft = +43-664-1808482 (Bergkristallhütte)
+ | Bild = Rodelbahn Bergkristallhütte 2009-03-03.jpg
+ | In Übersichtskarte = Ja
| Forumid = 72
}}
- Die Rodelbahn zur Bergkristallhütte ist durchaus abwechslungsreich.'''
+ Die Rodelbahn zur Bergkristallhütte ist durchaus abwechslungsreich.'''
start, end = wrpylib.mwmarkup.find_template(wikitext, 'Rodelbahnbox')
template_title, parameters = wrpylib.mwmarkup.split_template(wikitext[start:end])
assert template_title == 'Rodelbahnbox'
assert len(parameters) == 22
assert parameters['Position'] == '47.309820 N 9.986508 E'
- assert parameters['Telefonauskunft'] == '+43-664-1808482 (Bergkristallhütte)'
+ assert parameters['Telefonauskunft'] == '+43-664-1808482 (Bergkristallhütte)'
assert parameters['Schwierigkeit'] == ''
(Parkplatz)47.114958,11.266026
Erster Parkplatz
- (Gasthaus) 47.114715, 11.266262, Alt Bärnbad (Gasthaus)
+ (Gasthaus) 47.114715, 11.266262, Alt Bärnbad (Gasthaus)
6#FF014E9A
47.114715,11.266262
47.114135,11.268381
assert attributes['zoom'] == 15
assert coords == [
(11.266026, 47.114958, 'Parkplatz', 'Erster Parkplatz'),
- (11.266262, 47.114715, 'Gasthaus', 'Alt Bärnbad (Gasthaus)')]
+ (11.266262, 47.114715, 'Gasthaus', 'Alt Bärnbad (Gasthaus)')]
assert paths == [
('6#FF014E9A', [
(11.266262, 47.114715, None, None),
except wrpylib.mwmarkup.ParseError:
pass
+
+class TestMwParserFromHell(unittest.TestCase):
+ def test_find_template(self):
+ wikitext = '''== Allgemeines ==
+ {{Rodelbahnbox
+ | Position = 47.309820 N 9.986508 E
+ | Position oben =
+ | Höhe oben = 1244
+ | Position unten =
+ | Höhe unten = 806
+ | Länge = 5045
+ | Schwierigkeit =
+ | Lawinen = gelegentlich
+ | Betreiber =
+ | Öffentliche Anreise = Ja
+ | Gehzeit = 105
+ | Aufstieg getrennt = Nein
+ | Aufstiegshilfe = Nein
+ | Beleuchtungsanlage = Nein
+ | Beleuchtungstage =
+ | Rodelverleih = Ja
+ | Gütesiegel =
+ | Webauskunft =
+ | Telefonauskunft = +43-664-1808482 (Bergkristallhütte)
+ | Bild = Rodelbahn Bergkristallhütte 2009-03-03.jpg
+ | In Übersichtskarte = Ja
+ | Forumid = 72
+ }}
+ Die Rodelbahn zur Bergkristallhütte ist durchaus abwechslungsreich.'''
+ wikicode = mwparserfromhell.parse(wikitext)
+ rb = list(wikicode.filter_templates())[0]
+ self.assertEqual(rb.name.strip(), 'Rodelbahnbox')
+ self.assertEqual(rb.get('Aufstiegshilfe').value.strip(), 'Nein')
+ self.assertEqual(rb[:2], '{{')
+ self.assertEqual(rb[-2:], '}}')
+
+ def test_template_to_table(self):
+ wikitext = '{{Rodelbahnbox | Unbenannt | Position = 47.309820 N 9.986508 E | Aufstieg möglich = Ja }}'
+ wikicode = mwparserfromhell.parse(wikitext)
+ template = list(wikicode.filter_templates())[0]
+ self.assertEqual(template.name.strip(), 'Rodelbahnbox')
+ self.assertEqual(template.params[0].strip(), 'Unbenannt')
+ self.assertEqual(template.params[1].name.strip(), 'Position')
+ self.assertEqual(template.params[1].value.strip(), '47.309820 N 9.986508 E')
+ self.assertEqual(template.params[2].name.strip(), 'Aufstieg möglich')
+ self.assertEqual(template.params[2].value.strip(), 'Ja')
+
+ template = mwparserfromhell.nodes.template.Template('Rodelbahnbox')
+ template.add(1, 'Unbenannt')
+ template.add('Position', '47.309820 N 9.986508 E')
+ template.add('Aufstieg möglich', 'Ja')
+ self.assertEqual(template, '{{Rodelbahnbox|Unbenannt|Position=47.309820 N 9.986508 E|Aufstieg möglich=Ja}}')
+
+ wrpylib.mwmarkup.template_to_table(template)
+ self.assertEqual(template,
+ '{{Rodelbahnbox\n' +
+ '| Unbenannt\n' +
+ '| Position = 47.309820 N 9.986508 E\n' +
+ '| Aufstieg möglich = Ja\n' +
+ '}}')
+
+ wrpylib.mwmarkup.template_to_table(template, 18)
+ self.assertEqual(template,
+ '{{Rodelbahnbox\n' +
+ '| Unbenannt\n' +
+ '| Position = 47.309820 N 9.986508 E\n' +
+ '| Aufstieg möglich = Ja\n' +
+ '}}')
+
+ def test_split_template(self):
+ wikitext = '''== Allgemeines ==
+ {{Rodelbahnbox
+ | Position = 47.309820 N 9.986508 E
+ | Position oben =
+ | Höhe oben = 1244
+ | Position unten =
+ | Höhe unten = 806
+ | Länge = 5045
+ | Schwierigkeit =
+ | Lawinen = gelegentlich
+ | Betreiber =
+ | Öffentliche Anreise = Ja
+ | Gehzeit = 105
+ | Aufstieg getrennt = Nein
+ | Aufstiegshilfe = Nein
+ | Beleuchtungsanlage = Nein
+ | Beleuchtungstage =
+ | Rodelverleih = Ja
+ | Gütesiegel =
+ | Webauskunft =
+ | Telefonauskunft = +43-664-1808482 (Bergkristallhütte)
+ | Bild = Rodelbahn Bergkristallhütte 2009-03-03.jpg
+ | In Übersichtskarte = Ja
+ | Forumid = 72
+ }}
+ Die Rodelbahn zur Bergkristallhütte ist durchaus abwechslungsreich.'''
+ wikicode = mwparserfromhell.parse(wikitext)
+ template = wikicode.filter_templates(matches='Rodelbahnbox')[0]
+ self.assertEqual(template.name.strip(), 'Rodelbahnbox')
+ self.assertEqual(len(template.params), 22)
+ self.assertEqual(template.get('Position').value.strip(), '47.309820 N 9.986508 E')
+ self.assertEqual(template.get('Telefonauskunft').value.strip(), '+43-664-1808482 (Bergkristallhütte)')
+ self.assertEqual(template.get('Schwierigkeit').value.strip(), '')
+
+ def test_create_template(self):
+ template = mwparserfromhell.nodes.template.Template('Rodelbahnbox')
+ template.add(1, 'Unbenannt')
+ template.add('Position', '47.309820 N 9.986508 E')
+ template.add('Aufstieg möglich', 'Ja')
+ self.assertEqual(template, '{{Rodelbahnbox|Unbenannt|Position=47.309820 N 9.986508 E|Aufstieg möglich=Ja}}')
+
+ wrpylib.mwmarkup.template_to_table(template)
+ self.assertEqual(template,
+ '{{Rodelbahnbox\n' +
+ '| Unbenannt\n' +
+ '| Position = 47.309820 N 9.986508 E\n' +
+ '| Aufstieg möglich = Ja\n' +
+ '}}')
+
+ def test_find_tag(self):
+ wikitext = 'This is <tag>my first tag</tag> and <tag>my second tag</tag>.'
+ wikicode = mwparserfromhell.parse(wikitext)
+ tag_iter = wikicode.ifilter_tags()
+ tag = next(tag_iter)
+ self.assertEqual(tag.tag.strip(), 'tag')
+ self.assertEqual(tag.contents.strip(), 'my first tag')
+ tag = next(tag_iter)
+ self.assertEqual(tag.tag.strip(), 'tag')
+ self.assertEqual(tag.contents.strip(), 'my second tag')
+
+ wikitext = 'This is <tag myattrib="4"/>.'
+ wikicode = mwparserfromhell.parse(wikitext)
+ tag = next(wikicode.ifilter_tags())
+ self.assertEqual('tag', tag.tag)
+
+ def test_parse_googlemap(self):
+ wikitext = '''
+ <googlemap version="0.9" lat="47.113291" lon="11.272337" zoom="15">
+ (Parkplatz)47.114958,11.266026
+ Erster Parkplatz
+
+ (Gasthaus) 47.114715, 11.266262, Alt Bärnbad (Gasthaus)
+ 6#FF014E9A
+ 47.114715,11.266262
+ 47.114135,11.268381
+ 47.113421,11.269322
+ 47.11277,11.269979
+ 47.112408,11.271119
+ </googlemap>
+ '''
+ attributes, coords, paths = wrpylib.mwmarkup.parse_googlemap(wikitext)
+ self.assertEqual(attributes['lon'], 11.272337)
+ self.assertEqual(attributes['lat'], 47.113291)
+ self.assertEqual(attributes['zoom'], 15)
+ self.assertEqual(coords, [
+ (11.266026, 47.114958, 'Parkplatz', 'Erster Parkplatz'),
+ (11.266262, 47.114715, 'Gasthaus', 'Alt Bärnbad (Gasthaus)')])
+ self.assertEqual(paths, [
+ ('6#FF014E9A', [
+ (11.266262, 47.114715, None, None),
+ (11.268381, 47.114135, None, None),
+ (11.269322, 47.113421, None, None),
+ (11.269979, 47.11277, None, None),
+ (11.271119, 47.112408, None, None)])])
+ with self.assertRaises(wrpylib.mwmarkup.ParseError):
+ wrpylib.mwmarkup.parse_googlemap(wikitext.replace('<googlemap', '|googlemap'))
#!/usr/bin/python3.4
# -*- coding: iso-8859-15 -*-
import collections
-import wrpylib.wrmwmarkup
-import wrpylib.mwmarkup
import textwrap
import unittest
+import wrpylib.mwmarkup
+import wrpylib.wrmwmarkup
class TestWrMwMarkup(unittest.TestCase):
[11.269979, 47.11277],
[11.271119, 47.112408]]
-
def test_parse_wrmap(self):
wikitext = '''
<wrmap lat="47.2417134" lon="11.21408895" zoom="14" width="700" height="400">
[11.230868, 47.244951],
[11.237853, 47.245470]]
-
def test_create_wrmap(self):
geojson = {
'type': 'FeatureCollection',
import wrpylib.wrvalidators
import formencode
import unittest
+from wrpylib.wrvalidators import *
+
+
+class TestConverter(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(Converter.from_str('abc'), 'abc')
+ self.assertEqual(Converter.from_str(''), '')
+
+ def test_to_str(self):
+ self.assertEqual(Converter.to_str('abc'), 'abc')
+ self.assertEqual(Converter.to_str(''), '')
+ self.assertEqual(Converter.to_str(42), '42')
+
+
+class TestInt(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(int_from_str('42'), 42)
+ self.assertEqual(int_from_str('+42'), 42)
+ self.assertEqual(int_from_str('-20'), -20)
+ self.assertEqual(int_from_str('0', min=0), 0)
+ self.assertEqual(int_from_str('10', max=10), 10)
+ with self.assertRaises(ValueError):
+ int_from_str('abc')
+ with self.assertRaises(ValueError):
+ int_from_str('')
+ with self.assertRaises(ValueError):
+ int_from_str('-1', min=0)
+ with self.assertRaises(ValueError):
+ int_from_str('11', max=10)
+ with self.assertRaises(ValueError):
+ int_from_str('10.0')
+ with self.assertRaises(ValueError):
+ int_from_str('0d')
+
+ def test_to_str(self):
+ self.assertEqual(int_to_str(20), '20')
+ self.assertEqual(int_to_str(-20), '-20')
+ self.assertEqual(int_to_str(0), '0')
+
+
+class TestOptInt(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(opt_int_from_str('42'), 42)
+ self.assertEqual(opt_int_from_str('+42'), 42)
+ self.assertEqual(opt_int_from_str('-20'), -20)
+ self.assertEqual(opt_int_from_str(''), None)
+ with self.assertRaises(ValueError):
+ opt_int_from_str('abc')
+ with self.assertRaises(ValueError):
+ opt_int_from_str('10.0')
+ with self.assertRaises(ValueError):
+ opt_int_from_str('0d')
+
+ def test_to_str(self):
+ self.assertEqual(opt_int_to_str(20), '20')
+ self.assertEqual(opt_int_to_str(-20), '-20')
+ self.assertEqual(opt_int_to_str(0), '0')
+ self.assertEqual(opt_int_to_str(None), '')
+
+
+class TestEnumConverter(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(enum_from_str(''), [])
+ self.assertEqual(enum_from_str('abc'), ['abc'])
+ self.assertEqual(enum_from_str('abc; def'), ['abc', 'def'])
+ self.assertEqual(enum_from_str('abc; def;ghi'), ['abc', 'def', 'ghi'])
+
+ def test_to_str(self):
+ self.assertEqual(enum_to_str(['abc', 'def', 'ghi']), 'abc; def; ghi')
+ self.assertEqual(enum_to_str(['abc']), 'abc')
+ self.assertEqual(enum_to_str(['']), '')
+ self.assertEqual(enum_to_str([]), '')
+
+
+class TestDateTime(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(DateTime.from_str('2015-12-31 23:07:42'), datetime.datetime(2015, 12, 31, 23, 7, 42))
+
+ def test_to_str(self):
+ self.assertEqual(DateTime.to_str(datetime.datetime(2015, 12, 31, 23, 7, 42)), '2015-12-31 23:07:42')
+
+
+class TestDifficultyGerman(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(difficulty_german_from_str('leicht'), 1)
+ self.assertEqual(difficulty_german_from_str('mittel'), 2)
+ with self.assertRaises(ValueError):
+ difficulty_german_from_str('dontknow')
+ with self.assertRaises(ValueError):
+ difficulty_german_from_str('')
+
+ def test_to_str(self):
+ self.assertEqual(difficulty_german_to_str(1), 'leicht')
+
+
+class TestTristateGerman(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(tristate_german_from_str('Ja'), 1.0)
+ self.assertEqual(tristate_german_from_str('Teilweise'), 0.5)
+ self.assertEqual(tristate_german_from_str('Nein'), 0)
+ with self.assertRaises(ValueError):
+ tristate_german_from_str('')
+ with self.assertRaises(ValueError):
+ tristate_german_from_str('Vielleicht')
+
+ def test_to_str(self):
+ self.assertEqual(tristate_german_to_str(1.0), 'Ja')
+ self.assertEqual(tristate_german_to_str(0.5), 'Teilweise')
+ self.assertEqual(tristate_german_to_str(0.0), 'Nein')
+
+
+class TestOptTristateGerman(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(opt_tristate_german_from_str('Ja'), 1.0)
+ self.assertEqual(opt_tristate_german_from_str('Teilweise'), 0.5)
+ self.assertEqual(opt_tristate_german_from_str('Nein'), 0)
+ self.assertEqual(opt_tristate_german_from_str(''), None)
+ with self.assertRaises(ValueError):
+ opt_tristate_german_from_str('Vielleicht')
+
+ def test_to_str(self):
+ self.assertEqual(opt_tristate_german_to_str(1.0), 'Ja')
+ self.assertEqual(opt_tristate_german_to_str(0.5), 'Teilweise')
+ self.assertEqual(opt_tristate_german_to_str(0.0), 'Nein')
+ self.assertEqual(opt_tristate_german_to_str(None), '')
+
+
+class TestOptTristateGermanComment(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(opt_tristate_german_comment_from_str('Ja'), (1.0, None))
+ self.assertEqual(opt_tristate_german_comment_from_str('Teilweise'), (0.5, None))
+ self.assertEqual(opt_tristate_german_comment_from_str('Nein'), (0, None))
+ self.assertEqual(opt_tristate_german_comment_from_str('Teilweise (nur ganz oben nicht)'), (0.5, 'nur ganz oben nicht'))
+ self.assertEqual(opt_tristate_german_comment_from_str(''), (None, None))
+ with self.assertRaises(ValueError):
+ opt_tristate_german_from_str('Vielleicht')
+ with self.assertRaises(ValueError):
+ opt_tristate_german_from_str('(Ja)')
+
+ def test_to_str(self):
+ self.assertEqual(opt_tristate_german_comment_to_str((1.0, None)), 'Ja')
+ self.assertEqual(opt_tristate_german_comment_to_str((0.5, None)), 'Teilweise')
+ self.assertEqual(opt_tristate_german_comment_to_str((0.0, None)), 'Nein')
+ self.assertEqual(opt_tristate_german_comment_to_str((None, None)), '')
+
+
+class TestLonLat(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(lonlat_from_str('47.076207 N 11.453553 E'), LonLat(11.453553, 47.076207))
+ with self.assertRaises(ValueError):
+ lonlat_from_str('47.076207 N 11.453553')
+
+ def test_to_str(self):
+ self.assertEqual(lonlat_to_str(LonLat(11.453553, 47.076207)), '47.076207 N 11.453553 E')
+
+
+class TestValueCommentConverter(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(value_comment_from_str('abc (defg)'), ('abc', 'defg'))
+ self.assertEqual(value_comment_from_str('abc ()'), ('abc', ''))
+ self.assertEqual(value_comment_from_str('(def)'), ('', 'def'))
+ self.assertEqual(value_comment_from_str('ab((cd))'), ('ab', '(cd)'))
+ self.assertEqual(value_comment_from_str('ab((c(d)[(])))'), ('ab', '(c(d)[(]))'))
+ self.assertEqual(value_comment_from_str('ab((cd)'), ('ab(', 'cd'))
+ self.assertEqual(value_comment_from_str('abcd (ef) '), ('abcd', 'ef'))
+ self.assertEqual(value_comment_from_str('abc', comment_optional=True), ('abc', ''))
+ with self.assertRaises(ValueError):
+ value_comment_from_str('abc (')
+ with self.assertRaises(ValueError):
+ value_comment_from_str('abc )')
+ with self.assertRaises(ValueError):
+ value_comment_from_str('abc (def)g')
+ with self.assertRaises(ValueError):
+ value_comment_from_str('abc (b))')
+ with self.assertRaises(ValueError):
+ value_comment_from_str('abc')
+
+ def test_to_str(self):
+ self.assertEqual(value_comment_to_str(('abc', 'defg')), 'abc (defg)')
+ self.assertEqual(value_comment_to_str(('abc', '')), 'abc ()')
+ self.assertEqual(value_comment_to_str(('', 'def')), '(def)')
+ self.assertEqual(value_comment_to_str(('ab', '(cd)')), 'ab ((cd))')
+ self.assertEqual(value_comment_to_str(('ab', '(c(d)[(]))')), 'ab ((c(d)[(])))')
+ self.assertEqual(value_comment_to_str(('ab(', 'cd')), 'ab( (cd)')
+ self.assertEqual(value_comment_to_str(('abcd', 'ef')), 'abcd (ef)')
+ self.assertEqual(value_comment_to_str(('abc', ''), comment_optional=True), 'abc')
+
+
+class TestNoGermanConverter(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(no_german_from_str('abc'), (True, 'abc'))
+ self.assertEqual(no_german_from_str('Nein'), (False, None))
+ with self.assertRaises(ValueError):
+ no_german_from_str('')
+
+ def test_to_str(self):
+ self.assertEqual(no_german_to_str((True, 'abc')), 'abc')
+ self.assertEqual(no_german_to_str((False, None)), 'Nein')
+
+
+class TestOptNoGerman(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(opt_no_german_from_str('abc'), (True, 'abc'))
+ self.assertEqual(opt_no_german_from_str('Nein'), (False, None))
+ self.assertEqual(opt_no_german_from_str(''), (None, None))
+
+ def test_to_str(self):
+ self.assertEqual(opt_no_german_to_str((True, 'abc')), 'abc')
+ self.assertEqual(opt_no_german_to_str((False, None)), 'Nein')
+ self.assertEqual(opt_no_german_to_str((None, None)), '')
+
+
+class TestLiftGermanValidator(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(lift_german_from_str(''), None)
+ self.assertEqual(lift_german_from_str('Nein'), [])
+ self.assertEqual(lift_german_from_str('Sessellift'), [('Sessellift', None)])
+ self.assertEqual(lift_german_from_str('Gondel (nur bis zur Hälfte)'), [('Gondel', 'nur bis zur Hälfte')])
+ self.assertEqual(lift_german_from_str('Sessellift; Taxi'), [('Sessellift', None), ('Taxi', None)])
+ self.assertEqual(lift_german_from_str('Sessellift (Wochenende); Taxi (6 Euro)'), [('Sessellift', 'Wochenende'), ('Taxi', '6 Euro')])
+
+ def test_to_str(self):
+ self.assertEqual(lift_german_to_str(None), '')
+ self.assertEqual(lift_german_to_str([]), 'Nein')
+ self.assertEqual(lift_german_to_str([('Sessellift', None)]), 'Sessellift')
+ self.assertEqual(lift_german_to_str([('Gondel', 'nur bis zur Hälfte')]), 'Gondel (nur bis zur Hälfte)')
+ self.assertEqual(lift_german_to_str([('Sessellift', None), ('Taxi', None)]), 'Sessellift; Taxi')
+ self.assertEqual(lift_german_to_str([('Sessellift', 'Wochenende'), ('Taxi', '6 Euro')]), 'Sessellift (Wochenende); Taxi (6 Euro)')
+
+
+class TestNightLightDays(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(nightlightdays_from_str(''), (None, None))
+ self.assertEqual(nightlightdays_from_str('2 (Mo, Di)'), (2, 'Mo, Di'))
+ self.assertEqual(nightlightdays_from_str('7'), (7, None))
+ self.assertEqual(nightlightdays_from_str('0'), (0, None))
+ self.assertEqual(nightlightdays_from_str('(keine Ahnung)'), (None, 'keine Ahnung'))
+ with self.assertRaises(ValueError):
+ nightlightdays_from_str('8')
+ with self.assertRaises(ValueError):
+ nightlightdays_from_str('5 (Montag')
+ with self.assertRaises(ValueError):
+ nightlightdays_from_str('5.2')
+
+ def test_to_str(self):
+ self.assertEqual(nightlightdays_to_str((None, None)), '')
+ self.assertEqual(nightlightdays_to_str((2, 'Mo, Di')), '2 (Mo, Di)')
+ self.assertEqual(nightlightdays_to_str((7, None)), '7')
+ self.assertEqual(nightlightdays_to_str((0, None)), '0')
+
+
+class TestSledRental(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(sledrental_from_str(''), None)
+ self.assertEqual(sledrental_from_str('Nein'), [])
+ self.assertEqual(sledrental_from_str('Talstation'), [('Talstation', None)])
+ self.assertEqual(sledrental_from_str('Talstation (unten)'), [('Talstation', 'unten')])
+ self.assertEqual(sledrental_from_str('Talstation (unten); Mittelstation'), [('Talstation', 'unten'), ('Mittelstation', None)])
+ with self.assertRaises(ValueError):
+ sledrental_from_str('(unten)')
+ with self.assertRaises(ValueError):
+ sledrental_from_str('Talstation (unten); ; Mittelstation')
+
+ def test_to_str(self):
+ self.assertEqual(sledrental_to_str(None), '')
+ self.assertEqual(sledrental_to_str([]), 'Nein')
+ self.assertEqual(sledrental_to_str([('Talstation', None)]), 'Talstation')
+ self.assertEqual(sledrental_to_str([('Talstation', 'unten')]), 'Talstation (unten)')
+ self.assertEqual(sledrental_to_str([('Talstation', 'unten'), ('Mittelstation', None)]), 'Talstation (unten); Mittelstation')
+
+
+class TestSingleCachet(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'), ('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel'))
+ self.assertEqual(single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwer'), ('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer'))
+ with self.assertRaises(ValueError):
+ single_cachet_german_from_str('')
+ with self.assertRaises(ValueError):
+ single_cachet_german_from_str('Salzburger Naturrodelbahn-Gütesiegel 2013 schwer')
+ with self.assertRaises(ValueError):
+ single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 4013 schwer')
+ with self.assertRaises(ValueError):
+ single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 13 schwer')
+ with self.assertRaises(ValueError):
+ single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwerer')
+
+ def test_to_str(self):
+ self.assertEqual(single_cachet_german_to_str(('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')), 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+ self.assertEqual(single_cachet_german_to_str(('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer')), 'Tiroler Naturrodelbahn-Gütesiegel 2013 schwer')
+
+
+class TestCachetGerman(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(cachet_german_from_str(''), None)
+ self.assertEqual(cachet_german_from_str('Nein'), [])
+ self.assertEqual(cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'), [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')])
+ self.assertEqual(cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwer; Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'),
+ [('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer'), ('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')])
+ with self.assertRaises(ValueError):
+ cachet_german_from_str('Ja')
+ with self.assertRaises(ValueError):
+ cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwer Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+
+ def test_to_str(self):
+ self.assertEqual(cachet_german_to_str(None), '')
+ self.assertEqual(cachet_german_to_str([]), 'Nein')
+ self.assertEqual(cachet_german_to_str([('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]), 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+ self.assertEqual(cachet_german_to_str([('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer'), ('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]),
+ 'Tiroler Naturrodelbahn-Gütesiegel 2013 schwer; Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+
+
+class TestUrl(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(url_from_str('http://www.winterrodeln.org/wiki/Arzler_Alm/'), 'http://www.winterrodeln.org/wiki/Arzler_Alm/')
+ self.assertEqual(url_from_str('http://www.winterrodeln.org/wiki/Nösslachhütte/'), 'http://www.winterrodeln.org/wiki/Nösslachhütte/')
+ self.assertEqual(url_from_str('https://www.winterrodeln.org/wiki/Nösslachhütte/'), 'https://www.winterrodeln.org/wiki/Nösslachhütte/')
+ with self.assertRaises(ValueError):
+ url_from_str('mailto:office@example.com')
+ with self.assertRaises(ValueError):
+ url_from_str('/wiki/Arzler_Alm/')
+
+ def test_to_str(self):
+ self.assertEqual(url_to_str('http://www.winterrodeln.org/wiki/Arzler_Alm/'), 'http://www.winterrodeln.org/wiki/Arzler_Alm/')
+ self.assertEqual(url_to_str('http://www.winterrodeln.org/wiki/Nösslachhütte/'), 'http://www.winterrodeln.org/wiki/Nösslachhütte/')
+ self.assertEqual(url_to_str('https://www.winterrodeln.org/wiki/Nösslachhütte/'), 'https://www.winterrodeln.org/wiki/Nösslachhütte/')
+
+
+class TestWebauskunft(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(webauskunft_from_str('http://www.example.com/current'), (True, 'http://www.example.com/current'))
+ self.assertEqual(webauskunft_from_str(''), (None, None))
+ self.assertEqual(webauskunft_from_str('Nein'), (False, None))
+
+ def test_to_str(self):
+ self.assertEqual(webauskunft_to_str((True, 'http://www.example.com/current')), 'http://www.example.com/current')
+ self.assertEqual(webauskunft_to_str((None, None)), '')
+ self.assertEqual(webauskunft_to_str((False, None)), 'Nein')
+
+
+class TestPhoneNumber(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(phone_number_from_str('+43-699-123456789'), '+43-699-123456789')
+ self.assertEqual(phone_number_from_str('+43-69945'), '+43-69945')
+ self.assertEqual(phone_number_from_str('+43-512-507-6418'), '+43-512-507-6418')
+ with self.assertRaises(ValueError):
+ phone_number_from_str('+43-')
+ with self.assertRaises(ValueError):
+ phone_number_from_str('0512123456789')
+
+ def test_to_str(self):
+ self.assertEqual(phone_number_to_str('+43-699-123456789'), '+43-699-123456789')
+ self.assertEqual(phone_number_to_str('+43-69945'), '+43-69945')
+ self.assertEqual(phone_number_to_str('+43-512-507-6418'), '+43-512-507-6418')
+
+
+class TestTelefonauskunft(unittest.TestCase):
+ def test_from_str(self):
+ self.assertEqual(telefonauskunft_from_str(''), None)
+ self.assertEqual(telefonauskunft_from_str('Nein'), [])
+ self.assertEqual(telefonauskunft_from_str('+43-512-123456 (untertags)'), [('+43-512-123456', 'untertags')])
+ self.assertEqual(telefonauskunft_from_str('+43-512-1234 (untertags); +43-664-123456 (Alm)'), [('+43-512-1234', 'untertags'), ('+43-664-123456', 'Alm')])
+ with self.assertRaises(ValueError):
+ telefonauskunft_from_str('+43-512-123456+ (untertags)')
+ with self.assertRaises(ValueError):
+ telefonauskunft_from_str('+43-512-123456')
+
+ def test_to_str(self):
+ self.assertEqual(telefonauskunft_to_str(None), '')
+ self.assertEqual(telefonauskunft_to_str([]), 'Nein')
+ self.assertEqual(telefonauskunft_to_str([('+43-512-123456', 'untertags')]), '+43-512-123456 (untertags)')
+ self.assertEqual(telefonauskunft_to_str([('+43-512-1234', 'untertags'), ('+43-664-123456', 'Alm')]), '+43-512-1234 (untertags); +43-664-123456 (Alm)')
+
+
+class TestBox(unittest.TestCase):
+ def test_from_str(self):
+ value = '{{MyTemplate|apple=2|banana=5}}'
+ converter_dict = OrderedDict([('apple', opt_int_converter), ('banana', opt_int_converter)])
+ result = box_from_str(value, 'MyTemplate', converter_dict)
+ self.assertEqual(result['apple'], 2)
+ self.assertEqual(result['banana'], 5)
+
+ value = '{{MyTemplate\n | apple = 2 \n| banana = 5 }}'
+ result = box_from_str(value, 'MyTemplate', converter_dict)
+ self.assertEqual(result['apple'], 2)
+ self.assertEqual(result['banana'], 5)
+
+ with self.assertRaises(ValueError):
+ box_from_str(value, 'myTemplate', converter_dict)
+ with self.assertRaises(ValueError):
+ value = '{{MyTemplate|apple=2|banana=five}}'
+ box_from_str(value, 'MyTemplate', converter_dict)
+ with self.assertRaises(ValueError):
+ value = '{{MyTemplate|apple=2}}'
+ box_from_str(value, 'MyTemplate', converter_dict)
+ with self.assertRaises(ValueError):
+ value = '{{MyTemplate|apple=2|banana=5|cherry=6}}'
+ box_from_str(value, 'MyTemplate', converter_dict)
+
+ def test_to_str(self):
+ value = OrderedDict([('apple', 2), ('banana', 5)])
+ converter_dict = OrderedDict([('apple', opt_int_converter), ('banana', opt_int_converter)])
+ result = box_to_str(value, 'MyTemplate', converter_dict)
+ self.assertEqual(result, '{{MyTemplate|apple=2|banana=5}}')
+
+
+class TestRodelbahnbox(unittest.TestCase):
+ def setUp(self):
+ self.maxDiff = None
+ self.value = \
+'''{{Rodelbahnbox
+| Position = 46.807218 N 12.806522 E
+| Position oben = 46.799014 N 12.818658 E
+| Höhe oben = 1046
+| Position unten =
+| Höhe unten =
+| Länge = 3500
+| Schwierigkeit = mittel
+| Lawinen = kaum
+| Betreiber = Bringungsgemeinschaft Kreithof-Dolomitenhütte
+| Öffentliche Anreise = Schlecht
+| Aufstieg möglich = Ja
+| Aufstieg getrennt = Teilweise
+| Gehzeit = 75
+| Aufstiegshilfe = Taxi; Sonstige (PKW bis Kreithof)
+| Beleuchtungsanlage = Ja
+| Beleuchtungstage = 7
+| Rodelverleih = Nein
+| Gütesiegel = Tiroler Naturrodelbahn-Gütesiegel 2009 mittel
+| Webauskunft = http://www.lienzerdolomiten.info/at/tobogorpt.html
+| Telefonauskunft = +43-664-2253782 (Dolomitenhütte)
+| Bild = Dolomitenrodelbahn Tristach 2011-12-22 oberer Bereich.jpg
+| In Übersichtskarte = Ja
+| Forumid = 139
+}}'''
+
+ def test_from_str(self):
+ value = rodelbahnbox_from_str(self.value)
+ self.assertEqual(value['Position'], LonLat(12.806522, 46.807218))
+ self.assertEqual(value['Position oben'], LonLat(12.818658, 46.799014))
+ self.assertEqual(value['Höhe oben'], 1046)
+ self.assertEqual(value['Position unten'], LonLat(None, None))
+ self.assertEqual(value['Höhe unten'], None)
+ self.assertEqual(value['Länge'], 3500)
+ self.assertEqual(value['Schwierigkeit'], 2)
+ self.assertEqual(value['Lawinen'], 1)
+ self.assertEqual(value['Betreiber'], 'Bringungsgemeinschaft Kreithof-Dolomitenhütte')
+ self.assertEqual(value['Öffentliche Anreise'], 4)
+ self.assertEqual(value['Aufstieg möglich'], True)
+ self.assertEqual(value['Aufstieg getrennt'], (0.5, None))
+ self.assertEqual(value['Gehzeit'], 75)
+ self.assertEqual(value['Aufstiegshilfe'], [('Taxi', None), ('Sonstige', 'PKW bis Kreithof')])
+ self.assertEqual(value['Beleuchtungsanlage'], (1.0, None))
+ self.assertEqual(value['Beleuchtungstage'], (7, None))
+ self.assertEqual(value['Rodelverleih'], [])
+ self.assertEqual(value['Gütesiegel'], [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')])
+ self.assertEqual(value['Webauskunft'], (True, 'http://www.lienzerdolomiten.info/at/tobogorpt.html'))
+ self.assertEqual(value['Telefonauskunft'], [('+43-664-2253782', 'Dolomitenhütte')])
+ self.assertEqual(value['Bild'], 'Dolomitenrodelbahn Tristach 2011-12-22 oberer Bereich.jpg')
+ self.assertEqual(value['In Übersichtskarte'], True)
+ self.assertEqual(value['Forumid'], 139)
+
+ def test_to_str(self):
+ value = OrderedDict([
+ ('Position', LonLat(12.806522, 46.807218)),
+ ('Position oben', LonLat(12.818658, 46.799014)),
+ ('Höhe oben', 1046),
+ ('Position unten', LonLat(None, None)),
+ ('Höhe unten', None),
+ ('Länge', 3500),
+ ('Schwierigkeit', 2),
+ ('Lawinen', 1),
+ ('Betreiber', 'Bringungsgemeinschaft Kreithof-Dolomitenhütte'),
+ ('Öffentliche Anreise', 4),
+ ('Aufstieg möglich', True),
+ ('Aufstieg getrennt', (0.5, None)),
+ ('Gehzeit', 75),
+ ('Aufstiegshilfe', [('Taxi', None), ('Sonstige', 'PKW bis Kreithof')]),
+ ('Beleuchtungsanlage', (1.0, None)),
+ ('Beleuchtungstage', (7, None)),
+ ('Rodelverleih', []),
+ ('Gütesiegel', [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]),
+ ('Webauskunft', (True, 'http://www.lienzerdolomiten.info/at/tobogorpt.html')),
+ ('Telefonauskunft', [('+43-664-2253782', 'Dolomitenhütte')]),
+ ('Bild', 'Dolomitenrodelbahn Tristach 2011-12-22 oberer Bereich.jpg'),
+ ('In Übersichtskarte', True),
+ ('Forumid', 139)])
+ self.assertEqual(rodelbahnbox_to_str(value), self.value)
class TestWrValidators(unittest.TestCase):
- def test_OrderedSchema(self):
- v = wrpylib.wrvalidators.OrderedSchema()
- v.pre_validators = [formencode.Validator()]
- v.chained_validators = [formencode.Validator()]
- v.add_field('c', formencode.Validator())
- v.add_field('b', formencode.Validator())
- v.add_field('a', formencode.Validator())
- v.add_field('d', formencode.Validator())
- other = collections.OrderedDict([('d', 'd'), ('b', 'b'), ('a', 'a'), ('c', 'c')])
- python = v.to_python(other)
- assert list(python.keys()) == list(other.keys())
- assert list(python.values()) == list(other.values())
- other2 = v.from_python(python)
- assert list(other.keys()) == list(other2.keys())
- assert list(other.values()) == list(other2.values())
-
-
- def test_NoneValidator(self):
- v = wrpylib.wrvalidators.NoneValidator(wrpylib.wrvalidators.Unicode())
- assert v.to_python('') == None
- assert v.from_python(None) == ''
-
-
- # test_NeinValidator
-
-
- # test_Unicode
-
-
- # test_UnicodeNone
-
-
- # test_Unsigned
-
-
- def test_UnsignedNone(self):
- v = wrpylib.wrvalidators.UnsignedNone()
- assert v.to_python('42') == 42
- assert v.to_python('') == None
- assert v.from_python(42) == '42'
- assert v.from_python(None) == ''
-
-
- # test_UnsignedNeinNone
-
-
- # test_Loop
-
-
- # test_DictValidator
-
-
- # test_GermanBoolNone
-
-
- def test_GermanTristateTuple(self):
- v = wrpylib.wrvalidators.GermanTristateTuple()
- assert v.to_python('') == (None, None)
- assert v.to_python('Ja') == (True, False)
- assert v.to_python('Nein') == (False, True)
- assert v.to_python('Teilweise') == (True, True)
- assert v.from_python((None, None)) == ''
- assert v.from_python((False, True)) == 'Nein'
- assert v.from_python((True, False)) == 'Ja'
- assert v.from_python((True, True)) == 'Teilweise'
-
-
- def test_GermanTristateFloat(self):
- v = wrpylib.wrvalidators.GermanTristateFloat()
- assert v.to_python('') == None
- assert v.to_python('Ja') == 1.0
- assert v.to_python('Nein') == 0.0
- assert v.to_python('Teilweise') == 0.5
- assert v.from_python(None) == ''
- assert v.from_python(0.0) == 'Nein'
- assert v.from_python(1.0) == 'Ja'
- assert v.from_python(0.5) == 'Teilweise'
-
-
- # test_ValueComment
-
-
- # test_SemicolonList
-
-
- def test_ValueCommentList(self):
- v = wrpylib.wrvalidators.ValueCommentList()
- assert v.to_python('abc') == [('abc', None)]
- assert v.to_python('abc def') == [('abc def', None)]
- assert v.to_python('value (comment)') == [('value', 'comment')]
- assert v.to_python('value (comment)') == [('value', 'comment')]
- assert v.to_python('value1 (comment); value2') == [('value1', 'comment'), ('value2', None)]
- assert v.to_python('value1 (comment1); value2; value3 (comment3)') == [('value1', 'comment1'), ('value2', None), ('value3', 'comment3')]
- assert v.to_python('value1 (comment1); [[link (linkcomment)]] (not easy)') == [('value1', 'comment1'), ('[[link (linkcomment)]]', 'not easy')]
-
-
- # test_GenericDateTime
-
-
- # test_DateTimeNoSec
-
-
- # test_DateNone
-
-
- # test_Geo
-
-
- def test_GeoNone(self):
- coord = '47.076207 N 11.453553 E'
- v = wrpylib.wrvalidators.GeoNone()
- (lat, lon) = v.to_python(coord)
- assert lat == 47.076207
- assert lon == 11.453553
- assert v.to_python('') == (None, None)
-
- assert v.from_python((lat, lon)) == coord
- assert v.from_python((None, None)) == ''
-
-
- # test_MultiGeo
-
-
- # test_AustrianPhoneNumber
-
-
- # test_AustrianPhoneNumberNone
-
-
- # test_AustrianPhoneNumberCommentLoop
-
-
- # test_GermanDifficulty
-
-
- # test_GermanAvalanches
-
-
- def test_GermanPublicTransport(self):
- v = wrpylib.wrvalidators.GermanPublicTransport()
- assert v.to_python('') is None
- assert v.to_python('Sehr gut') == 1
- assert v.to_python('Gut') == 2
- assert v.to_python('Mittelmäßig') == 3
- assert v.to_python('Schlecht') == 4
- assert v.to_python('Nein') == 5
- assert v.to_python('Ja') == 6
-
- assert v.from_python(None) == ''
- assert v.from_python(1) == 'Sehr gut'
- assert v.from_python(2) == 'Gut'
- assert v.from_python(3) == 'Mittelmäßig'
- assert v.from_python(4) == 'Schlecht'
- assert v.from_python(5) == 'Nein'
- assert v.from_python(6) == 'Ja'
- assert v.from_python(1) == 'Sehr gut'
-
-
- # test_GermanTristateFloatComment
-
-
- # test_UnsignedCommentNone
-
-
- # test_GermanCachet
-
-
- # test_url
-
-
- def test_UrlNeinNone(self):
- v = wrpylib.wrvalidators.UrlNeinNone()
- assert v.to_python('') == None
- assert v.to_python('Nein') == 'Nein'
- assert v.to_python('http://www.höttingeralm.at') == 'http://www.höttingeralm.at'
- assert v.from_python(None) == ''
- assert v.from_python('Nein') == 'Nein'
- assert v.from_python('http://www.höttingeralm.at') == 'http://www.höttingeralm.at'
-
-
def test_ValueCommentListNeinLoopNone(self):
v = wrpylib.wrvalidators.ValueCommentListNeinLoopNone()
assert v.to_python('') == None
assert v.from_python('T-Mobile (gut); A1') == 'T-Mobile (gut); A1'
- # test_PhoneNumber
-
-
def test_PhoneCommentListNeinLoopNone(self):
v = wrpylib.wrvalidators.PhoneCommentListNeinLoopNone(comments_are_optional=True)
assert v.to_python('') == None
assert v.from_python(testvalue) == testvalue
- # test_WikiPage
-
-
- # test_WikiPageList
-
-
def test_WikiPageListLoopNone(self):
v = wrpylib.wrvalidators.WikiPageListLoopNone()
assert v.to_python('') == None
assert v.from_python('[[Birgitzer Alm]]; [[Kemater Alm]]') == '[[Birgitzer Alm]]; [[Kemater Alm]]'
- # test_TupleSecondValidator
-
-
- def test_BoolUnicodeTupleValidator(self):
- v = wrpylib.wrvalidators.BoolUnicodeTupleValidator()
- assert v.to_python('') == (None, None)
- assert v.to_python('Nein') == (False, None)
- assert v.to_python('any text') == (True, 'any text')
- assert v.from_python((None, None)) == ''
- assert v.from_python((False, None)) == 'Nein'
- assert v.from_python((True, 'any text')) == 'any text'
-
-
-
-
- def test_GermanLift(self):
- v = wrpylib.wrvalidators.GermanLift()
- assert v.to_python('') == (None, None)
- assert v.to_python('Nein') == (False, None)
- assert v.to_python('Sessellift (4 Euro)') == (True, 'Sessellift (4 Euro)')
- assert v.from_python((None, None)) == ''
- assert v.from_python((False, None)) == 'Nein'
- assert v.from_python((True, 'Sessellift (4 Euro)')) == 'Sessellift (4 Euro)'
-
-
- def test_SledRental(self):
- v = wrpylib.wrvalidators.SledRental()
- assert v.to_python('') == (None, None)
- assert v.to_python('Nein') == (False, None)
- assert v.to_python('Ja') == (True, 'Ja')
- assert v.to_python('Talstation (nur mit Ticket); Schneealm') == (True, 'Talstation (nur mit Ticket); Schneealm')
- assert v.from_python((None, None)) == ''
- assert v.from_python((False, None)) == 'Nein'
- assert v.from_python((True, 'Talstation (nur mit Ticket); Schneealm')) == 'Talstation (nur mit Ticket); Schneealm'
- assert v.from_python((True, 'Ja')) == 'Ja'
-
-
- def test_RodelbahnboxDictValidator(self):
- v = wrpylib.wrvalidators.RodelbahnboxDictValidator()
- other = collections.OrderedDict([
- ('Position', '47.309820 N 9.986508 E'),
- ('Position oben', ''),
- ('Höhe oben', '1244'),
- ('Position unten', ''),
- ('Höhe unten', '806'),
- ('Länge', '5045'),
- ('Schwierigkeit', ''),
- ('Lawinen', 'gelegentlich'),
- ('Betreiber', ''),
- ('Öffentliche Anreise', 'Ja'),
- ('Aufstieg möglich', 'Ja'),
- ('Aufstieg getrennt', 'Nein'),
- ('Gehzeit', '105'),
- ('Aufstiegshilfe', 'Nein'),
- ('Beleuchtungsanlage', 'Nein'),
- ('Beleuchtungstage', ''),
- ('Rodelverleih', 'Ja'),
- ('Gütesiegel', ''),
- ('Webauskunft', ''),
- ('Telefonauskunft', '+43-664-1808482 (Bergkristallhütte)'),
- ('Bild', 'Rodelbahn Bergkristallhütte 2009-03-03.jpg'),
- ('In Übersichtskarte', 'Ja'),
- ('Forumid', '72')])
- python = v.to_python(other, None)
- other2 = v.from_python(python, None)
- assert other == other2
-
-
def test_GasthausboxDictValidator(self):
v = wrpylib.wrvalidators.GasthausboxDictValidator()
other = collections.OrderedDict([
#!/usr/bin/python3.4
-# -*- coding: iso-8859-15 -*-
+# coding=utf-8
# $Id$
# $HeadURL$
-"""This module contains general functions that help parsing the mediawiki markup.
-I looked for an already existing MediaWiki parser in Python but I didn't find anything
-that convinced me. However, here are the links:
+"""For parsing MediaWiki text, we rely on the package mwparserfromhell (https://github.com/earwig/mwparserfromhell).
+This module just contains a few additional useful functions.
+Other Python MediaWiki parsers:
* py-wikimarkup https://github.com/dcramer/py-wikimarkup
* mwlib http://code.pediapress.com/wiki/wiki
+* https://www.mediawiki.org/wiki/Alternative_parsers
"""
-import re
-import xml.etree.ElementTree
-import collections
-import formencode
-
class ParseError(RuntimeError):
"""Exception used by some of the functions"""
pass
-def find_template(wikitext, template_title):
- """Returns the tuple (start, end) of the first occurence of the template '{{template ...}} within wikitext'.
- (None, None) is returned if the template is not found.
- If you are sure that the wikitext contains the template, the template could be extracted like follows:
-
- >>> wikitext = u'This is a {{Color|red|red text}} template.'
- >>> start, end = find_template(wikitext, u'Color')
- >>> print wikitext[start:end]
- {{Color|red|red text}}
-
- or just:
-
- >>> print wikitext.__getslice__(*find_template(wikitext, u'Color'))
- {{Color|red|red text}}
-
- The search is done with regular expression. It gives wrong results when parsing a template
- containing the characters "}}"
-
- :param wikitext: The text (preferalbe unicode) that has the template in it.
- :param template_title: The page title of the template with or without namespace (but as in the wikitext).
- :return:
- (start, end) of the first occurence with start >= 0 and end > start.
- (None, None) if the template is not found.
- """
- match = re.search("\{\{" + template_title + "\s*(\|[^\}]*)?\}\}", wikitext, re.DOTALL)
- if match is None: return None, None
- return match.start(), match.end()
-
-
-class TemplateValidator(formencode.FancyValidator):
- def __init__(self, strip=True, as_table=False, as_table_keylen=None):
- """Validates a MediaWiki template, e.g. {{Color|red}}
- :param stip: If strip is True, the title, and the parameter keys and values are stripped in to_python.
- :param as_table: formats the returned template in one row for each parameter
- :param as_table_keylen: length of the key field for from_python. None for "automatic"."""
- self.strip = (lambda s: s.strip()) if strip else (lambda s: s)
- self.as_table = as_table
- self.as_table_keylen = as_table_keylen
-
- def to_python(self, value, state=None):
- """Takes a template, like u'{{Color|red|text=Any text}}' and translates it to a Python tuple
- (title, anonym_params, named_params) where title is the template title,
- anonym_params is a list of anonymous parameters and named_params is a OrderedDict
- of named parameters. Whitespace of the parameters is stripped."""
- if not value.startswith('{{'):
- raise formencode.Invalid('Template does not start with "{{"', value, state)
- if not value.endswith('}}'):
- raise formencode.Invalid('Template does not end with "}}"', value, state)
- parts = value[2:-2].split('|')
-
- # template name
- title = self.strip(parts[0])
- if len(title) == 0:
- raise formencode.Invalid('Empty template tilte.', value, state)
- del parts[0]
-
- # anonymous parameters
- anonym_params = []
- while len(parts) > 0:
- equalsign_pos = parts[0].find('=')
- if equalsign_pos >= 0: break # named parameter
- anonym_params.append(self.strip(parts[0]))
- del parts[0]
-
- # named or numbered parameters
- named_params = collections.OrderedDict()
- while len(parts) > 0:
- equalsign_pos = parts[0].find('=')
- if equalsign_pos < 0:
- raise formencode.Invalid('Anonymous parameter after named parameter.', value, state)
- key, sep, value = parts[0].partition('=')
- key = self.strip(key)
- if len(key) == 0:
- raise formencode.Invalid('Empty key.', value, state)
- if key in named_params:
- raise formencode.Invalid('Duplicate key: "{0}"'.format(key), value, state)
- named_params[key] = self.strip(value)
- del parts[0]
-
- return title, anonym_params, named_params
-
- def from_python(self, value, state=None):
- """Formats a MediaWiki template.
- value is a tuple: (title, anonym_params, named_params)
- where title is the template title, anonym_params is a list of anonymous parameters and
- named_params is a dict or OrderedDict of named parameters."""
- title, anonym_params, named_params = value
- pipe_char, equal_char, end_char = ('\n| ', ' = ', '\n}}') if self.as_table else ('|', '=', '}}')
- parts = ["{{" + title]
- parts += anonym_params
- as_table_keylen = self.as_table_keylen
- if self.as_table and as_table_keylen is None:
- as_table_keylen = max(list(map(len, iter(named_params.keys()))))
- for k, v in named_params.items():
- if self.as_table:
- k = k.ljust(as_table_keylen)
- parts.append((k + equal_char + v).rstrip())
- else:
- parts.append(k + equal_char + v)
- return pipe_char.join(parts) + end_char
-
-
-def split_template(template):
- """Deprecated legacy function.
-
- Takes a template, like u'{{Color|red|text=Any text}}' and translates it to a Python tuple
- (template_title, parameters) where parameters is a Python dictionary {u'1': u'red', u'text'=u'Any text'}.
- Anonymous parameters get integer keys (converted to unicode) starting with 1
- like in MediaWiki, named parameters are unicode strings.
- Whitespace is stripped.
- If an unexpected format is encountered, a ValueError is raised."""
- try:
- title, anonym_params, named_params = TemplateValidator().to_python(template)
- parameters = dict(named_params)
- for i in range(len(anonym_params)):
- parameters[str(i+1)] = anonym_params[i]
- except formencode.Invalid as e:
- raise ValueError(e[0])
- return title, parameters
-
-
-def create_template(template_title, anonym_params=[], named_param_keys=[], named_param_values=[], as_table=False, as_table_keylen=None):
- """Deprecated legacy function.
-
- Formats a MediaWiki template.
- :param template_title: Unicode string with the template name
- :param anonym_params: list with parameters without keys
- :param named_param_keys: list with keys of named parameters
- :param named_param_values: list with values of named parameters, corresponding to named_param_keys.
- :param as_table: formats the returned template in one row for each parameter
- :param as_table_keylen: length of the key field. None for "automatic".
- :return: unicode template"""
- named_params = collections.OrderedDict(list(zip(named_param_keys, named_param_values)))
- return TemplateValidator(as_table=as_table, as_table_keylen=as_table_keylen).from_python((template_title, anonym_params, named_params))
+def template_to_table(template, keylen=None):
+ """Reformat the given template to be tabular.
+ >>> template
+ {{foo|bar|bazz=7}}
+ >>> template_to_table(template)
+ {{foo
+ | bar
+ | bazz = 7
+ }}
-def find_tag(wikitext, tagname, pos=0):
- """Returns position information of the first occurence of the tag '<tag ...>...</tag>'
- or '<tag ... />'.
- If you are sure that the wikitext contains the tag, the tag could be extracted like follows:
-
- >>> wikitext = u'This is a <tag>mytag</tag> tag.'
- >>> start, content, endtag, end = find_template(wikitext, u'tag')
- >>> print wikitext[start:end]
- <tag>mytag</tag>
-
- :param wikitext: The text (preferalbe unicode) that has the template in it.
- :param tagname: Name of the tag, e.g. u'tag' for <tag>.
- :param pos: position within wikitext to start searching the tag.
- :return:
- (start, content, endtag, end). start is the position of '<' of the tag,
- content is the beginning of the content (after '>'), enttag is the
- beginning of the end tag ('</') and end is one position after the end tag.
- For single tags, (start, None, None, end) is returned.
- If the tag is not found (or only the start tag is present,
- (None, None, None, None) is returned.
+ :param keylen: length of the keys or None for automatic determination
"""
- # Find start tag
- regexp_starttag = re.compile("<{0}.*?(/?)>".format(tagname), re.DOTALL)
- match_starttag = regexp_starttag.search(wikitext, pos)
- if match_starttag is None:
- return None, None, None, None
-
- # does the tag have content?
- if len(match_starttag.group(1)) == 1: # group(1) is either '' or '/'.
- # single tag
- return match_starttag.start(), None, None, match_starttag.end()
-
- # tag with content
- regexp_endtag = re.compile('</{0}>'.format(tagname), re.DOTALL)
- match_endtag = regexp_endtag.search(wikitext, match_starttag.end())
- if match_endtag is None:
- # No closing tag - error in wikitext
- return None, None, None, None
- return match_starttag.start(), match_starttag.end(), match_endtag.start(), match_endtag.end()
-
-
-def parse_googlemap(wikitext):
- """Parses the (unicode) u'<googlemap ...>content</googlemap>' of the googlemap extension.
- If wikitext does not contain the <googlemap> tag or if the <googlemap> tag contains
- invalid formatted lines, a ParseError is raised.
- Use find_tag(wikitext, 'googlemap') to find the googlemap tag within an arbitrary
- wikitext before using this function.
-
- :param wikitext: wikitext containing the template. Example:
-
- wikitext = '''
- <googlemap version="0.9" lat="47.113291" lon="11.272337" zoom="15">
- (Parkplatz)47.114958,11.266026
- Parkplatz
-
- (Gasthaus) 47.114715, 11.266262, Alt Bärnbad (Gasthaus)
- 6#FF014E9A
- 47.114715,11.266262
- 47.114135,11.268381
- 47.113421,11.269322
- 47.11277,11.269979
- 47.112408,11.271119
- </googlemap>
- '''
- :returns: The tuple (attributes, coords, paths) is returned.
- attributes is a dict that contains the attribues that are present
- (e.g. lon, lat, zoom, width, height) converted to float (lon, lat) or int.
- coords is a list of (lon, lat, symbol, title) tuples.
- paths is a list of (style, coords) tuples.
- coords is again a list of (lon, lat, symbol, title) tuples."""
-
- def is_coord(line):
- """Returns True if the line contains a coordinate."""
- match = re.search('[0-9]{1,2}\.[0-9]+, ?[0-9]{1,2}\.[0-9]+', line)
- return not match is None
-
- def is_path(line):
- """Returns True if the line contains a path style definition."""
- match = re.match('[0-9]#[0-9a-fA-F]{8}', line)
- return not match is None
-
- def parse_coord(line):
- """Returns (lon, lat, symbol, title). If symbol or text is not present, None is returned."""
- match = re.match('\(([^)]+)\) ?([0-9]{1,2}\.[0-9]+), ?([0-9]{1,2}\.[0-9]+), ?(.*)', line)
- if not match is None: return (float(match.group(3)), float(match.group(2)), match.group(1), match.group(4))
- match = re.match('\(([^)]+)\) ?([0-9]{1,2}\.[0-9]+), ?([0-9]{1,2}\.[0-9]+)', line)
- if not match is None: return (float(match.group(3)), float(match.group(2)), match.group(1), None)
- match = re.match('([0-9]{1,2}\.[0-9]+), ?([0-9]{1,2}\.[0-9]+), ?(.*)', line)
- if not match is None: return (float(match.group(2)), float(match.group(1)), None, match.group(3))
- match = re.match('([0-9]{1,2}\.[0-9]+), ?([0-9]{1,2}\.[0-9]+)', line)
- if not match is None: return (float(match.group(2)), float(match.group(1)), None, None)
- return ParseError('Could not parse line ' + line)
-
- start, content, endtag, end = find_tag(wikitext, 'googlemap')
- if start is None:
- raise ParseError('<googlemap> tag not found.')
- if content is None:
- xml_only = wikitext[start:endtag]
- else:
- xml_only = wikitext[start:content]+wikitext[endtag:end]
-
- try:
- gm = xml.etree.ElementTree.XML(xml_only.encode('UTF8'))
- except xml.etree.ElementTree.ParseError as e:
- row, column = e.position
- raise ParseError("XML parse error in <googlemap ...>.")
-
- # parse attributes
- attributes = {}
- try:
- for key in ['lon', 'lat']:
- if gm.get(key) is not None:
- attributes[key] = float(gm.get(key))
- for key in ['zoom', 'width', 'height']:
- if gm.get(key) is not None:
- attributes[key] = int(gm.get(key))
- except ValueError as error:
- raise ParseError('Error at parsing attribute {0} of <googlemap>: {1}'.format(key, str(error)))
-
- # parse points and lines
- coords = []
- paths = []
- lines = wikitext[content:endtag].split("\n")
- i = 0
- while i < len(lines):
- line = lines[i].strip()
- i += 1
-
- # Skip whitespace
- if len(line) == 0: continue
-
- # Handle a path
- if is_path(line):
- match = re.match('([0-9]#[0-9a-fA-F]{8})', line)
- style = match.group(1)
- local_coords = []
- while i < len(lines):
- line = lines[i].strip()
- i += 1
- if is_path(line):
- i -= 1
- break
- if is_coord(line):
- lon, lat, symbol, title = parse_coord(line)
- local_coords.append((lon, lat, symbol, title))
- paths.append((style, local_coords))
- continue
-
- # Handle a coordinate
- if is_coord(line):
- lon, lat, symbol, title = parse_coord(line)
- while i < len(lines):
- line = lines[i].strip()
- i += 1
- if is_path(line) or is_coord(line):
- i -= 1
- break
- if len(line) > 0 and title is None: title = line
- coords.append((lon, lat, symbol, title))
- continue
-
- raise ParseError('Unknown line syntax: ' + line)
-
- return (attributes, coords, paths)
-
+ if keylen is None:
+ shown_keys = [len(param.name.strip()) for param in template.params if param.showkey]
+ keylen = max(shown_keys) if shown_keys else 0
+ template.name = '{}\n'.format(template.name.strip())
+ for param in template.params:
+ if param.showkey:
+ param.name = ' {{:{}}} '.format(keylen).format(param.name.strip())
+ value = param.value.strip()
+ if len(value) > 0:
+ param.value = ' {}\n'.format(value)
+ else:
+ param.value = '\n'
return GasthausboxValidator().from_python(inn)
-def find_template_latlon_ele(wikitext, template_title):
- """Finds the first occurance of the '{{template_title|47.076207 N 11.453553 E|1890}}' template
- and returns the tuple (start, end, lat, lon, ele) or (None, None, None, None, None) if the
- template was not found. If the template has no valid format, an exception is thrown."""
- start, end = wrpylib.mwmarkup.find_template(wikitext, template_title)
- if start is None: return (None,) * 5
- title, params = wrpylib.mwmarkup.split_template(wikitext[start:end])
- lat, lon = wrpylib.wrvalidators.GeoNone().to_python(params['1'].strip())
- ele = wrpylib.wrvalidators.UnsignedNone().to_python(params['2'].strip())
- return start, end, lat, lon, ele
+def split_template_latlon_ele(template):
+ """Template is a mwparserfromhell.nodes.template.Template instance. Returns (latlon, ele)."""
+ latlon = opt_geostr_to_lat_lon(template.params[1].strip())
+ ele = opt_intstr_to_int(template.params[2].strip())
+ return latlon, ele
-def create_template_latlon_ele(template_title, lat, lon, ele):
- geo = wrpylib.wrvalidators.GeoNone().from_python((lat, lon))
+def create_template_latlon_ele(template_name, latlon, ele):
+ geo = wrpylib.wrvalidators.GeoNone().from_python((latlon))
if len(geo) == 0: geo = ' '
ele = wrpylib.wrvalidators.UnsignedNone().from_python(ele)
if len(ele) == 0: ele = ' '
- return wrpylib.mwmarkup.create_template(template_title, [geo, ele])
+ return wrpylib.mwmarkup.create_template(template_name, [geo, ele])
def find_template_PositionOben(wikitext):
return start, end, city, stop, lat, lon, ele
-def find_all_templates(wikitext, find_func):
- """Returns a list of return values of find_func that searches for a template.
- Example:
- >>> find_all_templates(wikitext, find_template_Haltestelle)
- Returns an empty list if the template was not found at all.
- """
- results = []
- result = find_func(wikitext)
- start, end = result[:2]
- while start is not None:
- results.append(result)
- result = find_func(wikitext[end:])
- if result[0] is None:
- start = None
- else:
- start = result[0] + end
- end += result[1]
- result = (start, end) + result[2:]
- return results
-
-
-def googlemap_to_wrmap(attributes, coords, paths):
- """Converts the output of parse_googlemap to the GeoJSON format wrmap uses.
- :returns: (GeoJSON as nested Python datatypes)
- """
- json_features = []
-
- # point
- for point in coords:
- lon, lat, symbol, title = point
- properties = {'type': 'punkt' if symbol is None else symbol.lower()}
- if title is not None: properties['name'] = title
- json_features.append({
- 'type': 'Feature',
- 'geometry': {'type': 'Point', 'coordinates': [lon, lat]},
- 'properties': properties})
-
- # path
- for path in paths:
- style, entries = path
- style = style.lower()
- PATH_TYPES = {'6#ff014e9a': 'rodelbahn', '6#ffe98401': 'gehweg', '6#ff7f7fff': 'alternative', '3#ff000000': 'lift', '3#ffe1e100': 'anfahrt'}
- if style in PATH_TYPES:
- properties = {'type': PATH_TYPES[style]}
- else:
- properties = {'type': 'line'}
- properties['dicke'] = style[0]
- properties['farbe'] = style[4:]
- json_features.append({
- 'type': 'Feature',
- 'geometry': {
- 'type': 'LineString',
- 'coordinates': [[lon, lat] for lon, lat, symbol, title in entries]},
- 'properties': properties})
-
- geojson = {
- 'type': 'FeatureCollection',
- 'features': json_features,
- 'properties': attributes}
- return geojson
-
-
def parse_wrmap_coordinates(coords):
'''gets a string coordinates and returns an array of lon/lat coordinate pairs, e.g.
47.12 N 11.87 E
# -*- 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.
+"""
+A converter is a Python variable (may be a class, class instance or anything else) that has the member
+functions from_str and to_str. From string takes a string "from the outside", checks it and returns a Python variable
+representing that value in Python. It reports error by raising ValueError. to_str does the opposite, however, it
+can assume that the value it has to convert to a string is valid. If it gets an invalid value, the behavior is
+undefined.
"""
import datetime
+import urllib.parse
import re
import xml.dom.minidom as minidom
from xml.parsers.expat import ExpatError
-import collections
+from collections import OrderedDict, namedtuple
+
+import mwparserfromhell
import formencode
import formencode.national
+from wrpylib.mwmarkup import template_to_table
class OrderedSchema(formencode.Schema):
self.chained_validators = []
try:
result = formencode.Schema._convert_to_python(self, value, state)
- ordered_result = collections.OrderedDict()
+ ordered_result = OrderedDict()
for key in value.keys():
ordered_result[key] = result[key]
for validator in chained_validators:
# apply original _convert_from_python method
try:
result = formencode.Schema._convert_from_python(self, value, state)
- ordered_result = collections.OrderedDict()
+ ordered_result = OrderedDict()
for key in value.keys():
ordered_result[key] = result[key]
# apply pre_validators
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."""
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, state=None):
- self.assert_string(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 '%.6f N %.6f E' % (latitude, longitude)
+# Meta converter types and functions
+# ----------------------------------
+
+class Converter:
+ @classmethod
+ def from_str(cls, value):
+ return value
+
+ @classmethod
+ def to_str(cls, value):
+ return str(value)
+
+
+FromToConverter = namedtuple('FromToConverter', ['from_str', 'to_str'])
+
+
+def opt_from_str(value, from_str, none=None):
+ return none if value == '' else from_str(value)
+
+
+def opt_to_str(value, to_str, none=None):
+ return '' if value == none else to_str(value)
+
+
+class OptionalConverter(Converter):
+ converter = Converter
+ none = None
+
+ @classmethod
+ def from_str(cls, value):
+ return opt_from_str(value, cls.converter, cls.none)
+
+ @classmethod
+ def to_str(cls, value):
+ return opt_to_str(value, cls.converter, cls.none)
+
+
+def choice_from_str(value, choices):
+ if value not in choices:
+ raise ValueError('{} is an invalid value')
+ return value
+
+
+def dictkey_from_str(value, key_str_dict):
+ try:
+ return dict(list(zip(key_str_dict.values(), key_str_dict.keys())))[value]
+ except KeyError:
+ raise ValueError("Invalid value '{}'".format(value))
+
+
+def dictkey_to_str(value, key_str_dict):
+ try:
+ return key_str_dict[value]
+ except KeyError:
+ raise ValueError("Invalid value '{}'".format(value))
+
+
+class DictKeyConverter(Converter):
+ key_str_dict = OrderedDict()
+
+ @classmethod
+ def from_str(cls, value):
+ return dictkey_from_str(value, cls.key_str_dict)
+
+ @classmethod
+ def to_str(cls, value):
+ return dictkey_to_str(value, cls.key_str_dict)
+
+
+
+# Basic type converter functions
+# ------------------------------
+
+
+def str_from_str(value):
+ return value
+
+
+def str_to_str(value):
+ return value
+
+
+def opt_str_from_str(value):
+ return opt_from_str(value, str_from_str)
+
+
+def opt_str_to_str(value):
+ return opt_to_str(value, str_to_str)
+
+
+opt_str_converter = FromToConverter(opt_str_from_str, opt_str_to_str)
+
+
+def req_str_from_str(value):
+ if value == '':
+ raise ValueError('missing required value')
+ return str_from_str(value)
+
+
+class Str(Converter):
+ pass
+
+
+class OptStr(OptionalConverter):
+ converter = Str
+
+
+def int_from_str(value, min=None, max=None):
+ value = int(value)
+ if min is not None and value < min:
+ raise ValueError('{} must be >= than {}'.format(value, min))
+ if max is not None and value > max:
+ raise ValueError('{} must be <= than {}'.format(value, max))
+ return value
+
+
+def int_to_str(value):
+ return str(value)
+
+
+def opt_int_from_str(value, min=None, max=None):
+ return opt_from_str(value, lambda val: int_from_str(val, min, max))
+
+
+def opt_int_to_str(value):
+ return opt_to_str(value, int_to_str)
+
+
+opt_int_converter = FromToConverter(opt_int_from_str, opt_int_to_str)
+
+
+class Int(Converter):
+ min = None
+ max = None
+
+ @classmethod
+ def from_str(cls, value):
+ return int_from_str(value, cls.min, cls.max)
+
+
+IntConverter = FromToConverter(int_from_str, int_to_str)
+
+
+class OptInt(OptionalConverter):
+ converter = Int
+
+
+class DateTime(Converter):
+ format='%Y-%m-%d %H:%M:%S'
+
+ @classmethod
+ def from_str(cls, value):
+ return datetime.datetime.strptime(value, cls.format)
+
+ @classmethod
+ def to_str(cls, value):
+ return value.strftime(cls.format)
+
+
+# Complex types
+# -------------
+
+def enum_from_str(value, from_str=req_str_from_str, separator=';', min_len=0):
+ """Semicolon separated list of entries with the same "type"."""
+ values = value.split(separator)
+ if len(values) == 1 and values[0] == '':
+ values = []
+ if len(values) < min_len:
+ raise ValueError('at least {} entry/entries have to be in the enumeration'.format(min_len))
+ return list(map(from_str, map(str.strip, values)))
+
+
+def enum_to_str(value, to_str=opt_str_to_str, separator='; '):
+ return separator.join(map(to_str, value))
+
+
+# Specific converter functions
+# ----------------------------
+
+BOOL_GERMAN = OrderedDict([(False, 'Nein'), (True, 'Ja')])
+
+
+def bool_german_from_str(value):
+ return dictkey_from_str(value, BOOL_GERMAN)
+
+
+def bool_german_to_str(value):
+ return dictkey_to_str(value, BOOL_GERMAN)
+
+
+def opt_bool_german_from_str(value):
+ return opt_from_str(value, bool_german_from_str)
+
+
+def opt_bool_german_to_str(value):
+ return opt_to_str(value, bool_german_to_str)
+
+
+opt_bool_german_converter = FromToConverter(opt_bool_german_from_str, opt_bool_german_to_str)
+
+
+TRISTATE_GERMAN = OrderedDict([(0.0, 'Nein'), (0.5, 'Teilweise'), (1.0, 'Ja')])
+
+
+def tristate_german_from_str(value):
+ return dictkey_from_str(value, TRISTATE_GERMAN)
+
+
+def tristate_german_to_str(value):
+ return dictkey_to_str(value, TRISTATE_GERMAN)
+
+
+def opt_tristate_german_from_str(value):
+ return opt_from_str(value, tristate_german_from_str)
+
+
+def opt_tristate_german_to_str(value):
+ return opt_to_str(value, tristate_german_to_str)
+
+
+def meter_from_str(value):
+ return int_from_str(value, min=0)
+
+
+def meter_to_str(value):
+ return int_to_str(value)
+
+
+def opt_meter_from_str(value):
+ return opt_from_str(value, meter_from_str)
+
+
+def opt_meter_to_str(value):
+ return opt_to_str(value, meter_to_str)
+
+
+opt_meter_converter = FromToConverter(opt_meter_from_str, opt_meter_to_str)
+
+
+def minutes_from_str(value):
+ return int_from_str(value, min=0)
+
+
+def minutes_to_str(value):
+ return int_to_str(value)
+
+
+def opt_minutes_from_str(value):
+ return opt_from_str(value, minutes_from_str)
+
+
+def opt_minutes_to_str(value):
+ return opt_to_str(value, minutes_to_str)
+
+
+opt_minutes_converter = FromToConverter(opt_minutes_from_str, opt_minutes_to_str)
+
+
+LonLat = namedtuple('LonLat', ['lon', 'lat'])
+
+
+lonlat_none = LonLat(None, None)
+
+
+def lonlat_from_str(value):
+ """Converts a winterrodeln geo string like '47.076207 N 11.453553 E' (being '<latitude> N <longitude> E'
+ to the LonLat(lon, lat) named tupel."""
+ r = re.match('(\d+\.\d+) N (\d+\.\d+) E', value)
+ if r is None: raise ValueError("Coordinates '{}' have not a format like '47.076207 N 11.453553 E'".format(value))
+ return LonLat(float(r.groups()[1]), float(r.groups()[0]))
+
+
+def lonlat_to_str(value):
+ return '{:.6f} N {:.6f} E'.format(value.lat, value.lon)
+
+
+def opt_lonlat_from_str(value):
+ return opt_from_str(value, lonlat_from_str, lonlat_none)
+
+
+def opt_lonlat_to_str(value):
+ return opt_to_str(value, lonlat_to_str, lonlat_none)
+
+
+opt_lonlat_converter = FromToConverter(opt_lonlat_from_str, opt_lonlat_to_str)
-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):
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
+DIFFICULTY_GERMAN = OrderedDict([(1, 'leicht'), (2, 'mittel'), (3, 'schwer')])
- 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, state=None):
- self.assert_string(value, state)
- 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')
- # 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, state)
- (country, phone, extension) = m.groups()
-
- # Phone
- 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 = str(self.default_cc)
-
- if extension is None: return '+%s/%s' % (country, phone)
- return '+%s/%s-%s' % (country, phone, extension)
+def difficulty_german_from_str(value):
+ return dictkey_from_str(value, DIFFICULTY_GERMAN)
-# Deprecated
-class AustrianPhoneNumberNone(NoneValidator):
- def __init__(self):
- NoneValidator.__init__(self, AustrianPhoneNumber())
+def difficulty_german_to_str(value):
+ return dictkey_to_str(value, DIFFICULTY_GERMAN)
-# Deprecated
-class AustrianPhoneNumberCommentLoop(NoneValidator):
- def __init__(self):
- NoneValidator.__init__(self, Loop(ValueComment(AustrianPhoneNumber())))
+def opt_difficulty_german_from_str(value):
+ return opt_from_str(value, difficulty_german_from_str)
-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, {'': None, 'leicht': 1, 'mittel': 2, 'schwer': 3})
+def opt_difficulty_german_to_str(value):
+ return opt_to_str(value, difficulty_german_to_str)
-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, {'': None, 'kaum': 1, 'selten': 2, 'gelegentlich': 3, '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, {'': None, 'Sehr gut': 1, 'Gut': 2, 'Mittelmäßig': 3, 'Schlecht': 4, 'Nein': 5, 'Ja': 6})
+opt_difficulty_german_converter = FromToConverter(opt_difficulty_german_from_str, opt_difficulty_german_to_str)
+
+
+AVALANCHES_GERMAN = OrderedDict([(1, 'kaum'), (2, 'selten'), (3, 'gelegentlich'), (4, 'häufig')])
+
+
+def avalanches_german_from_str(value):
+ return dictkey_from_str(value, AVALANCHES_GERMAN)
+
+
+def avalanches_german_to_str(value):
+ return dictkey_to_str(value, AVALANCHES_GERMAN)
+
+
+def opt_avalanches_german_from_str(value):
+ return opt_from_str(value, avalanches_german_from_str)
+
+
+def opt_avalanches_german_to_str(value):
+ return opt_to_str(value, avalanches_german_to_str)
+
+
+opt_avalanches_german_converter = FromToConverter(opt_avalanches_german_from_str, opt_avalanches_german_to_str)
+
+
+PUBLIC_TRANSPORT_GERMAN = OrderedDict([(1, 'Sehr gut'), (2, 'Gut'), (3, 'Mittelmäßig'), (4, 'Schlecht'), (5, 'Nein'), (6, 'Ja')])
+
+
+def public_transport_german_from_str(value):
+ return dictkey_from_str(value, PUBLIC_TRANSPORT_GERMAN)
+
+
+def public_transport_german_to_str(value):
+ return dictkey_to_str(value, PUBLIC_TRANSPORT_GERMAN)
+
+
+def opt_public_transport_german_from_str(value):
+ return opt_from_str(value, public_transport_german_from_str)
+
+
+def opt_public_transport_german_to_str(value):
+ return opt_to_str(value, public_transport_german_to_str)
+
+
+opt_public_transport_german_converter = FromToConverter(opt_public_transport_german_from_str, opt_public_transport_german_to_str)
+
+
+def value_comment_from_str(value, value_from_str=str_from_str, comment_from_str=str_from_str, comment_optional=False):
+ """Makes it possible to have a mandatory comment in parenthesis at the end of the string."""
+ open_brackets = 0
+ comment = ''
+ comment_end_pos = None
+ for i, char in enumerate(value[::-1]):
+ if char == ')':
+ open_brackets += 1
+ if open_brackets == 1:
+ comment_end_pos = i
+ if len(value[-1-comment_end_pos:].rstrip()) > 1:
+ raise ValueError('invalid characters after comment')
+ elif char == '(':
+ open_brackets -= 1
+ if open_brackets == 0:
+ comment = value[-i:-1-comment_end_pos]
+ value = value[:-i-1].rstrip()
+ break
+ else:
+ if open_brackets > 0:
+ raise ValueError('bracket mismatch')
+ if not comment_optional:
+ raise ValueError('mandatory comment not found')
+ return value_from_str(value), comment_from_str(comment)
+
+
+def value_comment_to_str(value, value_to_str=str_to_str, comment_to_str=str_to_str, comment_optional=False):
+ left = value_to_str(value[0])
+ comment = comment_to_str(value[1])
+ if len(comment) > 0 or not comment_optional:
+ comment = '({})'.format(comment)
+ if len(left) == 0:
+ return comment
+ if len(comment) == 0:
+ return left
+ return '{} {}'.format(left, comment)
+
+
+def opt_tristate_german_comment_from_str(value):
+ """Ja, Nein or Vielleicht, optionally with comment in parenthesis."""
+ return value_comment_from_str(value, opt_tristate_german_from_str, opt_str_from_str, True)
+
+
+def opt_tristate_german_comment_to_str(value):
+ return value_comment_to_str(value, opt_tristate_german_to_str, opt_str_to_str, True)
+
+
+opt_tristate_german_comment_converter = FromToConverter(opt_tristate_german_comment_from_str, opt_tristate_german_comment_to_str)
+
+
+def no_german_from_str(value, from_str=req_str_from_str, use_tuple=True, no_value=None):
+ if value == 'Nein':
+ return (False, no_value) if use_tuple else no_value
+ return (True, from_str(value)) if use_tuple else from_str(value)
+
+
+def no_german_to_str(value, to_str=str_to_str, use_tuple=True, no_value=None):
+ if use_tuple:
+ if not value[0]:
+ return 'Nein'
+ return to_str(value[1])
+ else:
+ if value == no_value:
+ return 'Nein'
+ return to_str(value)
+
+
+def opt_no_german_from_str(value, from_str=str_from_str, use_tuple=True, no_value=None, none=(None, None)):
+ return opt_from_str(value, lambda v: no_german_from_str(v, from_str, use_tuple, no_value), none)
+
+
+def opt_no_german_to_str(value, to_str=str_to_str, use_tuple=True, no_value=None, none=(None, None)):
+ return opt_to_str(value, lambda v: no_german_to_str(v, to_str, use_tuple, no_value), none)
class GermanTristateFloatComment(ValueComment):
ValueComment.__init__(self, GermanTristateFloat())
+def night_light_from_str(value):
+ """'Beleuchtungsanlage' Tristate with optional comment:
+ '' <=> (None, None)
+ 'Ja' <=> (1.0, None)
+ 'Teilweise' <=> (0.5, None)
+ 'Nein' <=> (0.0, None)
+ 'Ja (aber schmal)' <=> (1.0, 'aber schmal')
+ 'Teilweise (oben)' <=> (0.5, 'oben')
+ 'Nein (aber breit)' <=> (0.0, 'aber breit')
+ """
+ return
+
+
+class NightLightDays(Int):
+ min = 0
+ max = 7
+
+
+class OptNightLightDays(OptionalConverter):
+ converter = NightLightDays
+
+
+def nightlightdays_from_str(value):
+ return value_comment_from_str(value, lambda val: opt_from_str(val, lambda v: int_from_str(v, min=0, max=7)), opt_str_from_str, comment_optional=True)
+
+
+def nightlightdays_to_str(value):
+ return value_comment_to_str(value, lambda val: opt_to_str(val, int_to_str), opt_str_to_str, comment_optional=True)
+
+
+nightlightdays_converter = FromToConverter(nightlightdays_from_str, nightlightdays_to_str)
+
+
class UnsignedCommentNone(NoneValidator):
"""Converts the a property with unsigned values an optional comment
in parenthesis to a text:
NoneValidator.__init__(self, ValueComment(Unsigned(max=max)), (None, None))
-class GermanCachet(formencode.FancyValidator):
+CACHET_REGEXP = [r'(Tiroler Naturrodelbahn-Gütesiegel) ([12]\d{3}) (leicht|mittel|schwer)$']
+
+
+def single_cachet_german_from_str(value):
+ for pattern in CACHET_REGEXP:
+ match = re.match(pattern, value)
+ if match:
+ return match.groups()
+ raise ValueError("'{}' is no valid cachet".format(value))
+
+
+def single_cachet_german_to_str(value):
+ return ' '.join(value)
+
+
+def cachet_german_from_str(value):
"""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, state=None):
- self.assert_string(value, state)
- 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("Unbekanntes Gütesiegel", value, state)
+ '' => None
+ 'Nein' => []
+ 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel' => [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]"""
+ return opt_no_german_from_str(value, lambda val: enum_from_str(val, single_cachet_german_from_str), False, [], None)
+
- def from_python(self, value, state=None):
- if value is None: return ''
- assert value != ''
- return self.to_python(value, state)
+def cachet_german_to_str(value):
+ return opt_no_german_to_str(value, lambda val: enum_to_str(val, single_cachet_german_to_str), False, [], None)
+
+
+cachet_german_converter = FromToConverter(cachet_german_from_str, cachet_german_to_str)
+
+
+def url_from_str(value):
+ result = urllib.parse.urlparse(value)
+ if result.scheme not in ['http', 'https']:
+ raise ValueError('scheme has to be http or https')
+ if not result.netloc:
+ raise ValueError('url does not contain netloc')
+ return value
+
+
+def url_to_str(value):
+ return value
+
+
+def webauskunft_from_str(value):
+ return opt_no_german_from_str(value, url_from_str)
+
+
+def webauskunft_to_str(value):
+ return opt_no_german_to_str(value, url_to_str)
+
+
+webauskunft_converter = FromToConverter(webauskunft_from_str, webauskunft_to_str)
class Url(formencode.FancyValidator):
NoneValidator.__init__(self, NeinValidator(Loop(ValueCommentList())))
+def phone_number_from_str(value):
+ match = re.match(r'\+\d+(-\d+)*$', value)
+ if match is None:
+ raise ValueError('invalid format of phone number - use something like +43-699-1234567')
+ return value
+
+
+def phone_number_to_str(value):
+ return value
+
+
+def telefonauskunft_from_str(value):
+ return opt_no_german_from_str(value, lambda val: enum_from_str(val, lambda v: value_comment_from_str(v, phone_number_from_str, req_str_from_str, False)), False, [], None)
+
+
+def telefonauskunft_to_str(value):
+ return opt_no_german_to_str(value, lambda val: enum_to_str(val, lambda v: value_comment_to_str(v, phone_number_to_str, str_to_str)), False, [], None)
+
+
+telefonauskunft_converter = FromToConverter(telefonauskunft_from_str, telefonauskunft_to_str)
+
+
class PhoneNumber(formencode.FancyValidator):
"""Telefonnumber in international format, e.g. u'+43-699-1234567'"""
def __init__(self, default_cc=43):
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')
+ '' <=> (None, None)
+ 'Nein' <=> (False, None)
+ 'any text' <=> (True, 'any text')
"""
def __init__(self, validator=UnicodeNone()):
NoneValidator.__init__(self, NeinValidator(TupleSecondValidator(True, validator), (False, None)), (None, None))
+LIFT_GERMAN = ['Sessellift', 'Gondel', 'Linienbus', 'Taxi', 'Sonstige']
+
+
+def lift_german_from_str(value):
+ """Checks a lift_details property. It is a value comment property with the following
+ values allowed:
+ 'Sessellift'
+ 'Gondel'
+ 'Linienbus'
+ 'Taxi'
+ 'Sonstige'
+ Alternatively, the value u'Nein' is allowed.
+ An empty string maps to (None, None).
+
+ Examples:
+ '' <=> None
+ 'Nein' <=> []
+ 'Sessellift <=> [('Sessellift', None)]
+ 'Gondel (nur bis zur Hälfte)' <=> [('Gondel', 'nur bis zur Hälfte')]
+ 'Sessellift; Taxi' <=> [('Sessellift', None), ('Taxi', None)]
+ 'Sessellift (Wochenende); Taxi (6 Euro)' <=> [('Sessellift', 'Wochenende'), ('Taxi', '6 Euro')]
+ """
+ return opt_no_german_from_str(value, lambda value_enum: enum_from_str(value_enum, lambda value_comment: value_comment_from_str(value_comment, lambda v: choice_from_str(v, LIFT_GERMAN), opt_str_from_str, comment_optional=True)), use_tuple=False, no_value=[], none=None)
+
+
+def lift_german_to_str(value):
+ return opt_no_german_to_str(value, lambda value_enum: enum_to_str(value_enum, lambda value_comment: value_comment_to_str(value_comment, str_to_str, opt_str_to_str, comment_optional=True)), use_tuple=False, no_value=[], none=None)
+
+
+lift_german_converter = FromToConverter(lift_german_from_str, lift_german_to_str)
+
+
class GermanLift(BoolUnicodeTupleValidator):
"""Checks a lift_details property. It is a value comment property with the following
values allowed:
BoolUnicodeTupleValidator.__init__(self, Loop(ValueCommentList(DictValidator({'Sessellift': 'Sessellift', 'Gondel': 'Gondel', 'Linienbus': 'Linienbus', 'Taxi': 'Taxi', 'Sonstige': '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()))
+def sledrental_from_str(value):
+ """The value can be an empty string, 'Nein' or a semicolon-separated list of strings with optional comments.
+ '' => None
+ 'Nein' => []
+ 'Talstation (nur mit Ticket); Schneealm' => [('Talstation', 'nur mit Ticket'), ('Schneealm', None)]"""
+ return opt_no_german_from_str(value, lambda val: enum_from_str(val, lambda v: value_comment_from_str(v, req_str_from_str, opt_str_from_str, True)), False, [], None)
-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('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 sledrental_to_str(value):
+ return opt_no_german_to_str(value, lambda val: enum_to_str(val, lambda v: value_comment_to_str(v, str_to_str, opt_str_to_str, True)), False, [], None)
+
+
+sledrental_converter = FromToConverter(sledrental_from_str, sledrental_to_str)
+
+
+class ValueErrorList(ValueError):
+ pass
+
+
+def box_from_template(template, name, converter_dict):
+ if template.name.strip() != name:
+ raise ValueError('Box name has to be "{}"'.format(name))
+ result = OrderedDict()
+ exceptions_dict = OrderedDict()
+ # check values
+ for key, converter in converter_dict.items():
+ try:
+ if not template.has(key):
+ raise ValueError('Missing parameter "{}"'.format(key))
+ result[key] = converter.from_str(str(template.get(key).value.strip()))
+ except ValueError as e:
+ exceptions_dict[key] = e
+ # check if keys are superfluous
+ superfluous_keys = {str(p.name.strip()) for p in template.params} - set(converter_dict.keys())
+ for key in superfluous_keys:
+ exceptions_dict[key] = ValueError('Superfluous parameter: "{}"'.format(key))
+ if len(exceptions_dict) > 0:
+ raise ValueErrorList('{} error(s) occurred when parsing template parameters.'.format(len(exceptions_dict)), exceptions_dict)
+ return result
+
+
+def box_to_template(value, name, converter_dict):
+ template = mwparserfromhell.nodes.template.Template(name)
+ for key, converter in converter_dict.items():
+ template.add(key, converter.to_str(value[key]))
+ return template
+
+
+def template_from_str(value, name):
+ wikicode = mwparserfromhell.parse(value)
+ template_list = wikicode.filter_templates(name)
+ if len(name) == 0:
+ raise ValueError('No "{}" template was found'.format(name))
+ if len(template_list) > 1:
+ raise ValueError('{} "{}" templates were found'.format(len(template_list), name))
+ return template_list[0]
+
+
+def box_from_str(value, name, converter_dict):
+ template = template_from_str(value, name)
+ return box_from_template(template, name, converter_dict)
+
+
+def box_to_str(value, name, converter_dict):
+ return str(box_to_template(value, name, converter_dict))
+
+
+RODELBAHNBOX_TEMPLATE_NAME = 'Rodelbahnbox'
+
+
+RODELBAHNBOX_DICT = OrderedDict([
+ ('Position', opt_lonlat_converter), # '47.583333 N 15.75 E'
+ ('Position oben', opt_lonlat_converter), # '47.583333 N 15.75 E'
+ ('Höhe oben', opt_meter_converter), # '2000'
+ ('Position unten', opt_lonlat_converter), # '47.583333 N 15.75 E'
+ ('Höhe unten', opt_meter_converter), # '1200'
+ ('Länge', opt_meter_converter), # 3500
+ ('Schwierigkeit', opt_difficulty_german_converter), # 'mittel'
+ ('Lawinen', opt_avalanches_german_converter), # 'kaum'
+ ('Betreiber', opt_str_converter), # 'Max Mustermann'
+ ('Öffentliche Anreise', opt_public_transport_german_converter), # 'Mittelmäßig'
+ ('Aufstieg möglich', opt_bool_german_converter), # 'Ja'
+ ('Aufstieg getrennt', opt_tristate_german_comment_converter), # 'Ja'
+ ('Gehzeit', opt_minutes_converter), # 90
+ ('Aufstiegshilfe', lift_german_converter), # 'Gondel (unterer Teil)'
+ ('Beleuchtungsanlage', opt_tristate_german_comment_converter),
+ ('Beleuchtungstage', nightlightdays_converter), # '3 (Montag, Mittwoch, Freitag)'
+ ('Rodelverleih', sledrental_converter), # 'Talstation Serlesbahnan'
+ ('Gütesiegel', cachet_german_converter), # 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'
+ ('Webauskunft', webauskunft_converter), # 'http://www.nösslachhütte.at/page9.php'
+ ('Telefonauskunft', telefonauskunft_converter), # '+43-664-5487520 (Mitterer Alm)'
+ ('Bild', opt_str_converter),
+ ('In Übersichtskarte', opt_bool_german_converter),
+ ('Forumid', opt_int_converter)
+])
+
+
+def rodelbahnbox_from_template(template):
+ return box_from_template(template, RODELBAHNBOX_TEMPLATE_NAME, RODELBAHNBOX_DICT)
+
+
+def rodelbahnbox_to_template(value):
+ return box_to_template(value, RODELBAHNBOX_TEMPLATE_NAME, RODELBAHNBOX_DICT)
+
+
+def rodelbahnbox_from_str(value):
+ return box_from_str(value, RODELBAHNBOX_TEMPLATE_NAME, RODELBAHNBOX_DICT)
+
+
+def rodelbahnbox_to_str(value):
+ template = rodelbahnbox_to_template(value)
+ template_to_table(template, 20)
+ return str(template)
+
+
+GASTHAUSBOX_TEMPLATE_NAME = 'Gasthausbox'
+
+
+GASTHAUSBOX_DICT = OrderedDict([
+ ('Position', opt_lonlat_converter), # '47.583333 N 15.75 E'
+ ('Höhe', opt_meter_converter),
+ ('Betreiber', opt_str_converter),
+ ('Sitzplätze', opt_int_converter),
+ ('Übernachtung', BoolUnicodeTupleValidator()),
+ ('Rauchfrei', opt_tristate_german_validator),
+ ('Rodelverleih', BoolUnicodeTupleValidator()),
+ ('Handyempfang', ValueCommentListNeinLoopNone()),
+ ('Homepage', webauskunft_converter),
+ ('E-Mail', EmailCommentListNeinLoopNone(allow_masked_email=True)),
+ ('Telefon', PhoneCommentListNeinLoopNone(comments_are_optional=True)),
+ ('Bild', opt_str_converter),
+ ('Rodelbahnen', WikiPageListLoopNone())])
+
def sledrun_page_title_to_pretty_url(page_title):