3 User script for pywikibot (https://gerrit.wikimedia.org/r/pywikibot/core.git), tested with version 6.6.1.
4 Put it in directory scripts/userscripts.
6 Create a sledrun JSON page from a sledrun wikitext page (including map).
8 The following generators and filters are supported:
15 from itertools import takewhile, dropwhile
16 from typing import Any, Optional
18 import mwparserfromhell
19 from mwparserfromhell.nodes.extras import Parameter
22 from mwparserfromhell.nodes import Tag, Text, ExternalLink, Template, Wikilink, Heading
23 from mwparserfromhell.wikicode import Wikicode
24 from pywikibot import pagegenerators, Page
25 from pywikibot.bot import (
26 AutomaticTWSummaryBot,
32 from pywikibot.logging import warning
33 from pywikibot.site._namespace import BuiltinNamespace
35 from wrpylib.wrmwmarkup import create_sledrun_wiki, lonlat_to_json, lonlat_ele_to_json, parse_wrmap
36 from wrpylib.wrvalidators import rodelbahnbox_from_template, tristate_german_to_str, difficulty_german_to_str, \
37 avalanches_german_to_str, public_transport_german_to_str, opt_str_opt_comment_enum_to_str, opt_lonlat_from_str, \
40 from pywikibot.site import Namespace
42 docuReplacements = {'¶ms;': pagegenerators.parameterHelp}
45 def template_to_json(value: Template) -> dict:
47 for p in value.params:
48 parameter.append({'value': str(p)})
50 'name': str(value.name),
51 'parameter': parameter
55 def wikilink_to_json(value: Wikilink) -> dict:
56 wl = {'title': str(value.title)}
57 if value.text is not None:
58 wl['text'] = str(value.text)
62 def external_link_to_json(value: ExternalLink) -> dict:
63 link = {'url': str(value.url)}
64 if value.title is not None:
65 link['text'] = str(value.title)
69 class SledrunWikiTextToJsonBot(
74 AutomaticTWSummaryBot,
76 def treat_page(self) -> None:
77 """Load the given page, do some changes, and save it."""
78 wikitext_content_model = 'wikitext'
79 if self.current_page.content_model != wikitext_content_model:
80 warning(f"The content model of {self.current_page.title()} is {self.current_page.content_model} "
81 f"instead of {wikitext_content_model}.")
84 wikicode = mwparserfromhell.parse(self.current_page.text)
85 wikilink_list = wikicode.filter_wikilinks()
86 category_sledrun = 'Kategorie:Rodelbahn'
87 if sum(1 for c in wikilink_list if c.title == category_sledrun) == 0:
88 warning(f'The page {self.current_page.title()} does not have category {category_sledrun}.')
91 sledrun_json_page = Page(self.site, self.current_page.title() + '/Rodelbahn.json')
92 if sledrun_json_page.exists():
93 warning(f"{sledrun_json_page.title()} already exists, skipping {self.current_page.title()}.")
96 map_json_page = Page(self.site, self.current_page.title() + '/Landkarte.json')
97 if map_json_page.exists():
98 warning(f"{map_json_page.title()} already exists, skipping {self.current_page.title()}.")
102 v = wikicode.filter_tags(matches='wrmap')
104 map_json = parse_wrmap(str(v[0]))
107 "name": self.current_page.title(),
109 "entry_under_construction": sum(1 for c in wikilink_list if c.text == 'Kategorie:In Arbeit') > 0,
112 for v in wikicode.get_sections(levels=[2], matches='Allgemeines'):
113 for w in v.ifilter_text(recursive=False):
116 sledrun_json["description"] = str(x)
120 rbb_list = wikicode.filter_templates(recursive=False, matches=lambda t: t.name.strip() == 'Rodelbahnbox')
121 if len(rbb_list) == 1:
122 rbb = rodelbahnbox_from_template(rbb_list[0])
125 image_page = Page(self.site, v, ns=BuiltinNamespace.FILE)
126 if not image_page.exists():
127 warning(f"{image_page.title()} does not exist.")
128 sledrun_json['image'] = v
132 sledrun_json['length'] = v
134 v = rbb['Schwierigkeit']
136 sledrun_json['difficulty'] = difficulty_german_to_str(v)
140 sledrun_json['avalanches'] = avalanches_german_to_str(v)
142 v, w = rbb['Betreiber']
144 sledrun_json['has_operator'] = v
146 sledrun_json['operator'] = w
148 v = rbb['Aufstieg möglich']
150 sledrun_json['walkup_possible'] = v
152 v, w = rbb['Aufstieg getrennt']
154 sledrun_json['walkup_separate'] = tristate_german_to_str(v)
156 sledrun_json['walkup_comment'] = w # TODO
160 sledrun_json['walkup_time'] = v
162 def _walkup_support():
163 walkup_support_rbb = rbb['Aufstiegshilfe']
164 if walkup_support_rbb is not None:
166 for walkup_support_type, comment in walkup_support_rbb:
167 walkup_support = {'type': walkup_support_type}
168 if comment is not None:
169 walkup_support['comment']: comment
170 walkup_supports.append(walkup_support)
171 sledrun_json['walkup_supports'] = walkup_supports
174 v, w = rbb['Beleuchtungsanlage']
176 sledrun_json['nightlight_possible'] = tristate_german_to_str(v)
178 sledrun_json['nightlight_description'] = w
181 v = rbb['Rodelverleih']
183 sledrun_json['sled_rental_direct'] = v != []
185 for name, comment in v:
187 name_code = mwparserfromhell.parse(name)
188 wiki_link = next(name_code.ifilter_wikilinks(), None)
189 if isinstance(wiki_link, Wikilink):
190 x['wr_page'] = wikilink_to_json(wiki_link)
193 if comment is not None:
194 x['comment'] = comment
196 sledrun_json['sled_rental'] = w
200 v = rbb['Gütesiegel']
202 sledrun_json['cachet'] = len(v) > 0
205 v = rbb['In Übersichtskarte']
207 sledrun_json['show_in_overview'] = v
211 sledrun_json['forum_id'] = v
215 sledrun_json['position'] = lonlat_to_json(v)
217 v = lonlat_ele_to_json(rbb['Position oben'], rbb['Höhe oben'])
219 sledrun_json['top'] = v
221 v = lonlat_ele_to_json(rbb['Position unten'], rbb['Höhe unten'])
223 sledrun_json['bottom'] = v
225 v = rbb['Telefonauskunft']
227 sledrun_json['info_phone'] = [{'phone': p, 'name': n} for p, n in v]
229 v = rbb['Öffentliche Anreise']
231 sledrun_json['public_transport'] = public_transport_german_to_str(v)
234 bb_iter = wikicode.ifilter_templates(recursive=False, matches=lambda t: t.name.strip() == 'Buttonleiste')
235 bb = next(bb_iter, None)
237 video = bb.get('video', None)
238 if isinstance(video, Parameter):
239 sledrun_json['videos'] = [{'url': video.value}]
242 def _public_transport():
243 pt_sections = wikicode.get_sections(levels=[2], matches='Anreise mit öffentlichen Verkehrsmitteln',
244 include_headings=False)
245 if len(pt_sections) < 1:
248 node = next((node for node in pt.nodes if isinstance(node, Tag) and node.wiki_markup == '*'), None)
250 description = str(Wikicode(pt.nodes[:pt.nodes.index(node)])).strip()
252 sledrun_json["public_transport_description"] = str(description)
254 public_transport_stops = []
255 public_transport_lines = []
256 public_transport_links = []
258 for node in pt.nodes:
259 if isinstance(node, Template):
260 if node.name == 'Haltestelle':
262 public_transport_stops.append(ya)
264 z = node.get(1, None)
266 ya['municipality'] = str(z)
267 z = node.get(2, None)
269 ya['name_local'] = str(z)
270 za = str(node.get(3, '')).strip()
271 zb = str(node.get(4, '')).strip()
272 z = lonlat_ele_to_json(opt_lonlat_from_str(za), opt_uint_from_str(zb))
275 elif node.name in ["Fahrplan Abfahrtsmonitor VVT"]:
276 ya['monitor_template'] = template_to_json(node)
277 elif node.name in ["Fahrplan Hinfahrt VVT"]:
278 ya['route_arrival_template'] = template_to_json(node)
279 elif node.name in ["Fahrplan Rückfahrt VVT"]:
280 ya['route_departure_template'] = template_to_json(node)
281 elif node.name in ["Fahrplan Linie VVT"]:
283 public_transport_stops.append(ya)
286 'timetable_template': template_to_json(node),
288 public_transport_lines.append(y)
289 elif isinstance(node, ExternalLink):
290 public_transport_links.append(external_link_to_json(node))
292 public_transport_stops.append(ya)
293 if len(public_transport_stops) > 0:
294 sledrun_json['public_transport_stops'] = public_transport_stops
295 if len(public_transport_lines) > 0:
296 sledrun_json['public_transport_lines'] = public_transport_lines
297 if len(public_transport_links) > 0:
298 sledrun_json['public_transport_links'] = public_transport_links
302 car_section_list = wikicode.get_sections(levels=[2], matches='Anreise mit dem Auto')
303 if not car_section_list:
305 v = car_section_list[0]
307 description_nodes = dropwhile(lambda w: isinstance(w, Heading), v.nodes)
308 description_nodes = takewhile(lambda w: not (isinstance(w, Tag) and w.wiki_markup == '*'),
310 if description := str(Wikicode(list(description_nodes))).strip():
311 sledrun_json["car_description"] = description
314 for w in v.ifilter_templates(matches='Parkplatz'):
315 za = str(w.get(1, '')).strip()
316 zb = str(w.get(2, '')).strip()
317 z = lonlat_ele_to_json(opt_lonlat_from_str(za), opt_uint_from_str(zb))
319 x.append({'position': z})
321 sledrun_json['car_parking'] = x
324 for w in io.StringIO(str(v)):
325 match = re.match(r"\*\* von \'\'\'(.+)\'\'\'(.*): ([\d.,]+) km", w.rstrip())
327 ya, yb, yc = match.groups()
328 yc = float(yc.replace(',', '.'))
331 'route': (ya.strip() + ' ' + yb.strip()).strip(),
334 sledrun_json['car_distances'] = x
338 for v in wikicode.get_sections(levels=[2], matches='Allgemeines'):
339 def _gastronomy(value: str):
341 line_iter = io.StringIO(value)
342 line = next(line_iter, None)
343 while line is not None and line.rstrip() != "* '''Hütten''':":
344 line = next(line_iter, None)
347 while line is not None:
348 line = next(line_iter, None)
350 if line.startswith('** '):
352 wiki = mwparserfromhell.parse(line)
353 wiki_link = next(wiki.ifilter_wikilinks(), None)
354 if isinstance(wiki_link, Wikilink):
355 g['wr_page'] = wikilink_to_json(wiki_link)
356 ext_link = next(wiki.ifilter_external_links(), None)
357 if isinstance(ext_link, ExternalLink):
358 g['weblink'] = external_link_to_json(ext_link)
359 remaining = str(Wikicode(n for n in wiki.nodes
360 if isinstance(n, (Text, Tag)) and str(n).strip() != '*')).strip()
361 match = re.match(r'\((.+)\)', remaining)
363 remaining = match.group(1)
364 if len(remaining) > 0:
365 g['note'] = remaining
370 w = _gastronomy(str(v))
372 sledrun_json['gastronomy'] = w
374 def _sled_rental_description():
375 line_iter = io.StringIO(str(v))
376 line = next(line_iter, None)
378 while line is not None and (match := re.match(r"\* '''Rodelverleih''':(.*)", line)) is None:
379 line = next(line_iter, None)
382 result = [match.group(1)]
383 line = next(line_iter, None)
384 while line is not None and re.match(r"\* ", line) is None:
386 line = next(line_iter, None)
387 sledrun_json['sled_rental_description'] = ''.join(result).strip()
388 _sled_rental_description()
393 if isinstance(w, Tag) and str(w) == "'''Siehe auch'''":
398 if isinstance(w, ExternalLink):
399 x.append(external_link_to_json(w))
400 elif isinstance(w, (Text, Tag)) and str(w).strip() in ['', '*', ':']:
406 sledrun_json['see_also'] = x
408 sledrun_json['allow_reports'] = True
411 sledrun_impressions_page = Page(self.site, self.current_page.title() + '/Impressionen')
412 if sledrun_impressions_page.exists():
413 impressions = sledrun_impressions_page.title()
415 text = create_sledrun_wiki(sledrun_json, map_json, impressions)
416 summary = 'Rodelbahnbeschreibung nach Konvertierung nach und von JSON.'
417 self.put_current(text, summary=summary)
420 def main(*args: str) -> None:
421 local_args = pywikibot.handle_args(args)
422 gen_factory = pagegenerators.GeneratorFactory()
423 gen_factory.handle_args(local_args)
424 gen = gen_factory.getCombinedGenerator(preload=True)
426 bot = SledrunWikiTextToJsonBot(generator=gen)
429 pywikibot.bot.suggest_help(missing_generator=True)
432 if __name__ == '__main__':