]> ToastFreeware Gitweb - philipp/winterrodeln/wrpylib.git/blob - scripts/update_sledrun_wikitext.py
Implement own input function.
[philipp/winterrodeln/wrpylib.git] / scripts / update_sledrun_wikitext.py
1 #!/usr/bin/python
2 import argparse
3 import json
4 import sys
5 from difflib import SequenceMatcher
6 from enum import Enum, auto
7 from typing import List, Optional
8
9 from termcolor import cprint  # python3-termcolor
10 import jsonschema
11
12 from wrpylib.mwapi import WikiSite
13 from wrpylib.wrmwmarkup import create_sledrun_wiki
14
15
16 def _format_range_unified(start: int, stop: int) -> str:
17     """Copied from difflib._format_range_unified"""
18     beginning = start + 1  # lines start numbering with one
19     length = stop - start
20     if length == 1:
21         return str(beginning)
22     if not length:
23         beginning -= 1  # empty ranges begin at line just before the range
24     return f'{beginning},{length}'
25
26
27 def unified_diff(a: str, b: str, context: int = 3):
28     a_lines = a.splitlines()
29     b_lines = b.splitlines()
30     for group in SequenceMatcher(None, a_lines, b_lines).get_grouped_opcodes(context):
31         first, last = group[0], group[-1]
32         file1_range = _format_range_unified(first[1], last[2])
33         file2_range = _format_range_unified(first[3], last[4])
34         cprint(f'@@ -{file1_range} +{file2_range} @@', 'magenta')
35
36         for tag, i1, i2, j1, j2 in group:
37             if tag == 'equal':
38                 for line in a_lines[i1:i2]:
39                     print(f'  {line}')
40                 continue
41             if tag in {'replace', 'delete'}:
42                 for line in a_lines[i1:i2]:
43                     cprint(f'- {line}', 'red')
44             if tag in {'replace', 'insert'}:
45                 for line in b_lines[j1:j2]:
46                     cprint(f'+ {line}', 'green')
47
48
49 class Choice(Enum):
50     yes = auto()
51     no = auto()
52     quit = auto()
53
54
55 def input_yes_no_quit(text: str, default: Optional[Choice]) -> Choice:
56     while True:
57         result = input(text)
58         if result in ['Y', 'y', 'yes']:
59             return Choice.yes
60         elif result in ['N', 'n', 'no']:
61             return Choice.no
62         elif result in ['Q', 'q', 'quit']:
63             return Choice.quit
64         elif result == '' and default is not None:
65             return default
66         cprint(f'Unrecognized input: "{result}"', 'red')
67
68
69 def update_sledrun(site: WikiSite, wiki_page: dict, json_page: dict, map_page: dict, impression_page: dict):
70     json_page_main_slot = json_page['revisions'][0]['slots']['main']
71     assert json_page_main_slot['contentmodel'] == 'json'
72     sledrun_json = json.loads(json_page_main_slot['content'])
73     jsonschema.validate(instance=sledrun_json, schema=site.sledrun_schema())
74
75     if 'missing' in map_page:
76         map_json = None
77     else:
78         map_page_main_slot = map_page['revisions'][0]['slots']['main']
79         assert map_page_main_slot['contentmodel'] == 'json'
80         map_json = json.loads(map_page_main_slot['content'])
81
82     if 'missing' in impression_page:
83         impression_title = None
84     else:
85         impression_title = impression_page['title']
86
87     new_text = create_sledrun_wiki(sledrun_json, map_json, impression_title).strip()
88
89     previous_text = wiki_page['revisions'][0]['slots']['main']['content'].strip()
90     if new_text == previous_text:
91         return
92
93     cprint(wiki_page['title'], 'green')
94     unified_diff(previous_text, new_text)
95     choice = input_yes_no_quit('Do you accept the changes [yes, no, quit]? ', None)
96     if choice == Choice.no:
97         return
98
99     if choice == Choice.quit:
100         sys.exit(0)
101
102     site(
103         'edit',
104         pageid=wiki_page['pageid'],
105         text=new_text,
106         summary='Rodelbahnbeschreibung aus JSON Daten aktualisiert.',
107         minor=1,
108         bot=1,
109         baserevid=wiki_page['revisions'][0]['revid'],
110         nocreate=1,
111         token=site.token(),
112     )
113
114
115 def update_sledrun_wikitext(ini_files: List[str], update_all: bool):
116     site = WikiSite(ini_files)
117     cm_limit = 5  # 'max'
118     for wikitext_result in site.query(list='categorymembers', cmtitle='Kategorie:Rodelbahn', cmlimit=cm_limit):
119         wikitext_title_list = [page["title"] for page in wikitext_result['categorymembers']]
120         json_title_list = [f'{title}/Rodelbahn.json' for title in wikitext_title_list]
121
122         wikitext_result = query_revisions(site, wikitext_title_list, [])
123         json_result = query_revisions(site, json_title_list, [])
124
125         update_wikitext_title_list = []
126         update_json_title_list = []
127         for wikitext_page, json_page in zip(wikitext_result, json_result):
128             assert wikitext_page['title'] + '/Rodelbahn.json' == json_page['title']
129             if 'missing' in json_page:
130                 continue
131             if wikitext_page['revisions'][0]['timestamp'] < json_page['revisions'][0]['timestamp']:
132                 update_wikitext_title_list.append(wikitext_page['title'])
133                 update_json_title_list.append(json_page['title'])
134
135         if len(update_wikitext_title_list) == 0:
136             continue
137
138         update_map_title_list = [f'{title}/Landkarte.json' for title in update_wikitext_title_list]
139         update_impression_title_list = [f'{title}/Impressionen' for title in update_wikitext_title_list]
140
141         update_wikitext_result = query_revisions(site, update_wikitext_title_list, ['content'])
142         update_json_result = query_revisions(site, update_json_title_list, ['content'])
143         update_map_result = query_revisions(site, update_map_title_list, ['content'])
144         update_impression_result = query_revisions(site, update_impression_title_list, ['content'])
145
146         for wikitext_page, json_page, map_page, impression_page in \
147                 zip(update_wikitext_result, update_json_result, update_map_result, update_impression_result):
148             assert wikitext_page['title'] + '/Rodelbahn.json' == json_page['title']
149             update_sledrun(site, wikitext_page, json_page, map_page, impression_page)
150
151
152 def query_revisions(site: WikiSite, title_list: List[str], extra_rv_prop: List[str]) -> List[dict]:
153     rv_prop = ['timestamp', 'ids'] + extra_rv_prop
154     pages = next(site.query(prop='revisions', titles=title_list, rvslots='*', rvprop=rv_prop))['pages']
155     pages = sorted(pages, key=lambda p: title_list.index(p['title']))
156     assert len(title_list) == len(pages)
157     return pages
158
159
160 def main():
161     parser = argparse.ArgumentParser(description='Update sledrun wikitext from JSON')
162     parser.add_argument('--all', action='store_true',
163                         help='update all sledruns regardless of modification date differences')
164     parser.add_argument('inifile', nargs='+', help='inifile.ini, see: https://www.winterrodeln.org/trac/wiki/ConfigIni')
165     args = parser.parse_args()
166     update_sledrun_wikitext(args.inifile, args.all)
167
168
169 if __name__ == '__main__':
170     main()