5 from difflib import SequenceMatcher
6 from enum import Enum, auto
7 from typing import List, Optional
9 from termcolor import cprint # python3-termcolor
12 from wrpylib.mwapi import WikiSite
13 from wrpylib.wrmwmarkup import create_sledrun_wiki
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
23 beginning -= 1 # empty ranges begin at line just before the range
24 return f'{beginning},{length}'
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')
36 for tag, i1, i2, j1, j2 in group:
38 for line in a_lines[i1:i2]:
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')
55 def input_yes_no_quit(text: str, default: Optional[Choice]) -> Choice:
58 if result in ['Y', 'y', 'yes']:
60 elif result in ['N', 'n', 'no']:
62 elif result in ['Q', 'q', 'quit']:
64 elif result == '' and default is not None:
66 cprint(f'Unrecognized input: "{result}"', 'red')
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())
75 if 'missing' in map_page:
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'])
82 if 'missing' in impression_page:
83 impression_title = None
85 impression_title = impression_page['title']
87 new_text = create_sledrun_wiki(sledrun_json, map_json, impression_title).strip()
89 previous_text = wiki_page['revisions'][0]['slots']['main']['content'].strip()
90 if new_text == previous_text:
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:
99 if choice == Choice.quit:
104 pageid=wiki_page['pageid'],
106 summary='Rodelbahnbeschreibung aus JSON Daten aktualisiert.',
109 baserevid=wiki_page['revisions'][0]['revid'],
115 def update_sledrun_wikitext(ini_files: List[str], update_all: bool):
116 site = WikiSite(ini_files)
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]
122 wikitext_result = query_revisions(site, wikitext_title_list, [])
123 json_result = query_revisions(site, json_title_list, [])
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:
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'])
135 if len(update_wikitext_title_list) == 0:
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]
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'])
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)
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)
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)
169 if __name__ == '__main__':