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