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 typing import Any, Optional
17 import mwparserfromhell
18 from mwparserfromhell.nodes.extras import Parameter
21 from mwparserfromhell.nodes import Tag, Text, ExternalLink, Template, Wikilink
22 from mwparserfromhell.wikicode import Wikicode
23 from pywikibot import pagegenerators, Page
24 from pywikibot.bot import (
25 AutomaticTWSummaryBot,
31 from pywikibot.logging import warning
32 from pywikibot.site._namespace import BuiltinNamespace
34 from wrpylib.wrmwmarkup import create_sledrun_wiki, lonlat_to_json, lonlat_ele_to_json, parse_wrmap
35 from wrpylib.wrvalidators import rodelbahnbox_from_template, tristate_german_to_str, difficulty_german_to_str, \
36 avalanches_german_to_str, public_transport_german_to_str, opt_str_opt_comment_enum_to_str, opt_lonlat_from_str, \
39 from pywikibot.site import Namespace
41 docuReplacements = {'¶ms;': pagegenerators.parameterHelp}
44 def str_or_none(value: Any) -> Optional[str]:
50 def template_to_json(value: Template) -> dict:
52 for p in value.params:
53 parameter.append({'value': str(p)})
55 'name': str(value.name),
56 'parameter': parameter
60 def wikilink_to_json(value: Wikilink) -> dict:
61 wl = {'title': str(value.title)}
62 text = str_or_none(value.text)
68 class SledrunWikiTextToJsonBot(
73 AutomaticTWSummaryBot,
75 def treat_page(self) -> None:
76 """Load the given page, do some changes, and save it."""
77 wikitext_content_model = 'wikitext'
78 if self.current_page.content_model != wikitext_content_model:
79 warning(f"The content model of {self.current_page.title()} is {self.current_page.content_model} "
80 f"instead of {wikitext_content_model}.")
83 wikicode = mwparserfromhell.parse(self.current_page.text)
84 wikilink_list = wikicode.filter_wikilinks()
85 category_sledrun = 'Kategorie:Rodelbahn'
86 if sum(1 for c in wikilink_list if c.title == category_sledrun) == 0:
87 warning(f'The page {self.current_page.title()} does not have category {category_sledrun}.')
90 sledrun_json_page = Page(self.site, self.current_page.title() + '/Rodelbahn.json')
91 if sledrun_json_page.exists():
92 warning(f"{sledrun_json_page.title()} already exists, skipping {self.current_page.title()}.")
95 map_json_page = Page(self.site, self.current_page.title() + '/Landkarte.json')
96 if map_json_page.exists():
97 warning(f"{map_json_page.title()} already exists, skipping {self.current_page.title()}.")
101 v = wikicode.filter_tags(matches='wrmap')
103 map_json = parse_wrmap(str(v[0]))
106 "name": self.current_page.title(),
108 "entry_under_construction": sum(1 for c in wikilink_list if c.text == 'Kategorie:In Arbeit') > 0,
111 for v in wikicode.get_sections(levels=[2], matches='Allgemeines'):
112 for w in v.ifilter_text(recursive=False):
115 sledrun_json["description"] = str(x)
119 rbb_list = wikicode.filter_templates(recursive=False, matches=lambda t: t.name.strip() == 'Rodelbahnbox')
120 if len(rbb_list) == 1:
121 rbb = rodelbahnbox_from_template(rbb_list[0])
124 image_page = Page(self.site, v, ns=BuiltinNamespace.FILE)
125 if not image_page.exists():
126 warning(f"{image_page.title()} does not exist.")
127 sledrun_json['image'] = v
131 sledrun_json['length'] = v
133 v = rbb['Schwierigkeit']
135 sledrun_json['difficulty'] = difficulty_german_to_str(v)
139 sledrun_json['avalanches'] = avalanches_german_to_str(v)
141 v, w = rbb['Betreiber']
143 sledrun_json['has_operator'] = v
145 sledrun_json['operator'] = w
147 v = rbb['Aufstieg möglich']
149 sledrun_json['walkup_possible'] = v
151 v, w = rbb['Aufstieg getrennt']
153 sledrun_json['walkup_separate'] = tristate_german_to_str(v)
155 sledrun_json['walkup_comment'] = w # TODO
159 sledrun_json['walkup_time'] = v
161 v, w = rbb['Beleuchtungsanlage']
163 sledrun_json['nightlight_possible'] = tristate_german_to_str(v)
165 sledrun_json['nightlight_description'] = w
168 v = rbb['Rodelverleih']
170 sledrun_json['sled_rental_direct'] = v != []
172 for name, comment in v:
174 name_code = mwparserfromhell.parse(name)
175 wiki_link = next(name_code.ifilter_wikilinks(), None)
176 if isinstance(wiki_link, Wikilink):
177 x['wr_page'] = wikilink_to_json(wiki_link)
180 if comment is not None:
181 x['comment'] = comment
183 sledrun_json['sled_rental'] = w
186 v = rbb['In Übersichtskarte']
188 sledrun_json['show_in_overview'] = v
192 sledrun_json['forum_id'] = v
196 sledrun_json['position'] = lonlat_to_json(v)
198 v = lonlat_ele_to_json(rbb['Position oben'], rbb['Höhe oben'])
200 sledrun_json['top'] = v
202 v = lonlat_ele_to_json(rbb['Position unten'], rbb['Höhe unten'])
204 sledrun_json['bottom'] = v
206 v = rbb['Telefonauskunft']
208 sledrun_json['info_phone'] = [{'phone': p, 'name': n} for p, n in v]
210 v = rbb['Öffentliche Anreise']
212 sledrun_json['public_transport'] = public_transport_german_to_str(v)
215 bb_iter = wikicode.ifilter_templates(recursive=False, matches=lambda t: t.name.strip() == 'Buttonleiste')
216 bb = next(bb_iter, None)
218 video = bb.get('video', None)
219 if isinstance(video, Parameter):
220 sledrun_json['videos'] = [{'url': video.value}]
223 for v in wikicode.get_sections(levels=[2], matches='Anreise mit öffentlichen Verkehrsmitteln',
224 include_headings=False):
225 w = next((w for w in v.nodes if isinstance(w, Tag) and w.wiki_markup == '*'), None)
227 x = str(Wikicode(v.nodes[:v.nodes.index(w)])).strip()
229 sledrun_json["public_transport_description"] = str(x)
231 public_transport_stops = []
232 public_transport_lines = []
235 if isinstance(w, Template):
236 if w.name == 'Haltestelle':
238 public_transport_stops.append(ya)
242 ya['municipality'] = str(z)
245 ya['name_local'] = str(z)
246 za = str_or_none(w.get(3, None))
247 zb = str_or_none(w.get(4, None))
248 z = lonlat_ele_to_json(opt_lonlat_from_str(za), opt_uint_from_str(zb))
251 elif w.name in ["Fahrplan Abfahrtsmonitor VVT"]:
252 ya['monitor_template'] = template_to_json(w)
253 elif w.name in ["Fahrplan Hinfahrt VVT"]:
254 ya['route_arrival_template'] = template_to_json(w)
255 elif w.name in ["Fahrplan Rückfahrt VVT"]:
256 ya['route_departure_template'] = template_to_json(w)
257 elif w.name in ["Fahrplan Linie VVT"]:
259 public_transport_stops.append(ya)
262 'timetable_template': template_to_json(w),
264 public_transport_lines.append(y)
266 public_transport_stops.append(ya)
267 if len(public_transport_stops) > 0:
268 sledrun_json['public_transport_stops'] = public_transport_stops
269 if len(public_transport_lines) > 0:
270 sledrun_json['public_transport_lines'] = public_transport_lines
273 for v in wikicode.get_sections(levels=[2], matches='Anreise mit dem Auto'):
274 for w in v.ifilter_text(recursive=False):
277 sledrun_json["car_description"] = str(x)
280 for w in v.ifilter_templates(matches='Parkplatz'):
281 za = str_or_none(w.get(1, None))
282 zb = str_or_none(w.get(2, None))
283 z = lonlat_ele_to_json(opt_lonlat_from_str(za), opt_uint_from_str(zb))
285 x.append({'position': z})
287 sledrun_json['car_parking'] = x
290 for w in io.StringIO(str(v)):
291 match = re.match(r"\*\* von \'\'\'(.+)\'\'\'(.*): ([\d.,]+) km", w.rstrip())
293 ya, yb, yc = match.groups()
294 yc = float(yc.replace(',', '.'))
297 'route': (ya.strip() + ' ' + yb.strip()).strip(),
300 sledrun_json['car_distances'] = x
303 for v in wikicode.get_sections(levels=[2], matches='Allgemeines'):
304 def _gastronomy(value: str):
306 line_iter = io.StringIO(value)
307 line = next(line_iter, None)
308 while line is not None and line.rstrip() != "* '''Hütten''':":
309 line = next(line_iter, None)
312 while line is not None:
313 line = next(line_iter, None)
315 if line.startswith('** '):
317 wiki = mwparserfromhell.parse(line)
318 wiki_link = next(wiki.ifilter_wikilinks(), None)
319 if isinstance(wiki_link, Wikilink):
320 g['wr_page'] = wikilink_to_json(wiki_link)
321 ext_link = next(wiki.ifilter_external_links(), None)
322 if isinstance(ext_link, ExternalLink):
324 'url': str(ext_link.url),
325 'text': str(ext_link.title)
328 remaining = str(Wikicode(n for n in wiki.nodes
329 if isinstance(n, (Text, Tag)) and str(n).strip() != '*')).\
331 match = re.match(r'\((.+)\)', remaining)
333 remaining = match.group(1)
334 if len(remaining) > 0:
335 g['note'] = remaining
340 w = _gastronomy(str(v))
342 sledrun_json['gastronomy'] = w
344 def _sled_rental_description():
345 line_iter = io.StringIO(str(v))
346 line = next(line_iter, None)
348 while line is not None and (match := re.match(r"\* '''Rodelverleih''':(.*)", line)) is None:
349 line = next(line_iter, None)
352 result = [match.group(1)]
353 line = next(line_iter, None)
354 while line is not None and re.match(r"\* ", line) is None:
356 line = next(line_iter, None)
357 sledrun_json['sled_rental_description'] = ''.join(result).strip()
358 _sled_rental_description()
363 if isinstance(w, Tag) and str(w) == "'''Siehe auch'''":
368 if isinstance(w, ExternalLink):
369 link = {'url': w.url}
370 if w.title is not None:
371 link['text'] = w.title
373 elif isinstance(w, (Text, Tag)) and str(w).strip() in ['', '*', ':']:
379 sledrun_json['see_also'] = x
381 sledrun_json['allow_reports'] = True
384 sledrun_impressions_page = Page(self.site, self.current_page.title() + '/Impressionen')
385 if sledrun_impressions_page.exists():
386 impressions = sledrun_impressions_page.title()
388 text = create_sledrun_wiki(sledrun_json, map_json, impressions)
389 summary = 'Rodelbahnbeschreibung nach Konvertierung nach und von JSON.'
390 self.put_current(text, summary=summary)
393 def main(*args: str) -> None:
394 local_args = pywikibot.handle_args(args)
395 gen_factory = pagegenerators.GeneratorFactory()
396 gen_factory.handle_args(local_args)
397 gen = gen_factory.getCombinedGenerator(preload=True)
399 bot = SledrunWikiTextToJsonBot(generator=gen)
402 pywikibot.bot.suggest_help(missing_generator=True)
405 if __name__ == '__main__':