]> ToastFreeware Gitweb - philipp/winterrodeln/wrpylib.git/blob - bots/update_sledrun_json_from_wikitext_gastronomy.py
Fine-tune parsing of gastronomy.
[philipp/winterrodeln/wrpylib.git] / bots / update_sledrun_json_from_wikitext_gastronomy.py
1 #!/usr/bin/python
2 """
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.
5
6 Update a sledrun JSON page from a detail in a sledrun wikitext page.
7
8 The following generators and filters are supported:
9
10 &params;
11 """
12 import io
13 import json
14 import re
15 from itertools import takewhile, dropwhile
16 from typing import Optional
17
18 import jsonschema
19 import mwparserfromhell
20 from mwparserfromhell.nodes.extras import Parameter
21
22 import pywikibot
23 from mwparserfromhell.nodes import Tag, Text, ExternalLink, Template, Wikilink, Heading
24 from mwparserfromhell.wikicode import Wikicode
25 from pywikibot import pagegenerators, Page
26 from pywikibot.bot import (
27     AutomaticTWSummaryBot,
28     ConfigParserBot,
29     ExistingPageBot,
30     NoRedirectPageBot,
31     SingleSiteBot,
32 )
33 from pywikibot.logging import warning
34 from pywikibot.site._namespace import BuiltinNamespace
35 from wrpylib.json_tools import order_json_keys
36
37 from wrpylib.wrmwmarkup import create_sledrun_wiki, lonlat_to_json, lonlat_ele_to_json, parse_wrmap
38 from wrpylib.wrvalidators import rodelbahnbox_from_template, tristate_german_to_str, difficulty_german_to_str, \
39     avalanches_german_to_str, public_transport_german_to_str, opt_lonlat_from_str, \
40     opt_uint_from_str
41 from wrpylib.lib_sledrun_wikitext_to_json import optional_set, get_sledrun_description, wikilink_to_json, \
42     external_link_to_json
43
44 docuReplacements = {'&params;': pagegenerators.parameterHelp}
45
46
47 class UpdateSledrunJsonFromWikiText(
48     SingleSiteBot,
49     ConfigParserBot,
50     ExistingPageBot,
51     NoRedirectPageBot,
52     AutomaticTWSummaryBot,
53 ):
54     def setup(self) -> None:
55         schema = Page(self.site, 'Winterrodeln:Datenschema/Rodelbahn/V1.json')
56         assert schema.content_model == 'json'
57         self.sledrun_schema = json.loads(schema.text)
58
59     def treat_page(self) -> None:
60         """Load the given page, do some changes, and save it."""
61         wikitext_content_model = 'wikitext'
62         if self.current_page.content_model != wikitext_content_model:
63             warning(f"The content model of {self.current_page.title()} is {self.current_page.content_model} "
64                     f"instead of {wikitext_content_model}.")
65             return
66
67         wikicode = mwparserfromhell.parse(self.current_page.text)
68
69         sledrun_json_page = Page(self.site, self.current_page.title() + '/Rodelbahn.json')
70         if not sledrun_json_page.exists():
71             return
72         sledrun_json = json.loads(sledrun_json_page.text)
73         sledrun_json_orig = json.loads(sledrun_json_page.text)
74         sledrun_json_orig_text = json.dumps(sledrun_json_orig, ensure_ascii=False, indent=4)
75
76         for v in wikicode.get_sections(levels=[2], matches='Allgemeines'):
77             def _gastronomy(value: str):
78                 gastronomy = []
79                 line_iter = io.StringIO(value)
80                 line = next(line_iter, None)
81                 while line is not None and not line.startswith("* '''Hütten''':"):
82                     line = next(line_iter, None)
83                 if line is None:
84                     return gastronomy
85                 line = re.match(r"^\* '''Hütten''':\s*(.*)\s*", line).group(1)
86                 if len(line) == 0:
87                     line = next(line_iter, '')
88                     if not line.startswith('** '):
89                         return gastronomy
90                     line = re.match(r"^\*\*\s*(.*)\s*", line).group(1)
91                 while True:
92                     g = {}
93                     wiki = mwparserfromhell.parse(line)
94                     wiki_link = next(wiki.ifilter_wikilinks(), None)
95                     if isinstance(wiki_link, Wikilink):
96                         g['wr_page'] = wikilink_to_json(wiki_link)
97                     ext_link = next(wiki.ifilter_external_links(), None)
98                     if isinstance(ext_link, ExternalLink):
99                         g['weblink'] = external_link_to_json(ext_link)
100                     remaining = str(Wikicode(n for n in wiki.nodes
101                                              if isinstance(n, (Text, Tag)) and str(n).strip() != '*')).strip()
102                     match = re.match(r'(.*)\((.+)\)', remaining)
103                     if match:
104                         name, note = match.groups()
105                         name = name.strip()
106                         note = note.strip()
107                         if len(name) > 0:
108                             g['name'] = name
109                         if len(note) > 0:
110                             g['note'] = note
111                     elif len(remaining) > 0 and remaining != '...':
112                         g['name'] = remaining
113                     if len(g) != 0:
114                         gastronomy.append(g)
115                     line = next(line_iter, '')
116                     if not line.startswith('** '):
117                         break
118                     line = re.match(r"^\*\*\s*(.*)\s*", line).group(1)
119                 return gastronomy
120
121             w = _gastronomy(str(v))
122             if len(w) > 0:
123                 sledrun_json['gastronomy'] = w
124
125         jsonschema.validate(instance=sledrun_json, schema=self.sledrun_schema)
126         sledrun_json_ordered = order_json_keys(sledrun_json, self.sledrun_schema)
127         assert sledrun_json_ordered == sledrun_json
128         if sledrun_json == sledrun_json_orig:
129             return
130         sledrun_json_text = json.dumps(sledrun_json_ordered, ensure_ascii=False, indent=4)
131         summary = 'Gastronomie Information im Rodelbahn JSON aktualisiert vom Wikitext.'
132         self.userPut(sledrun_json_page, sledrun_json_orig_text, sledrun_json_text, summary=summary, contentmodel='json')
133
134
135 def main(*args: str) -> None:
136     local_args = pywikibot.handle_args(args)
137     gen_factory = pagegenerators.GeneratorFactory()
138     gen_factory.handle_args(local_args)
139     gen = gen_factory.getCombinedGenerator(preload=True)
140     if gen:
141         bot = UpdateSledrunJsonFromWikiText(generator=gen)
142         bot.run()
143     else:
144         pywikibot.bot.suggest_help(missing_generator=True)
145
146
147 if __name__ == '__main__':
148     main()