3 Update sledrun JSON files based on OpenData published at
4 https://www.data.gv.at/katalog/dataset/land-tirol_rodelbahnenintirol
6 At the time of writing, the OpenData were be outdated - there is another official list:
7 https://www.tirol.gv.at/sport/richtlinien-und-initiativen/sport-guetesiegel/naturrodelbahnguetesiegel/rodelbahnen/
10 Get list of existing Gütesiegel entries:
14 json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.name') AS name,
15 json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.stätte_id') AS staette_id,
17 point(json_extract(sledrun_json, '$.position.longitude'),
18 json_extract(sledrun_json, '$.position.latitude'))) AS position,
19 json_extract(sledrun_json, '$.entry_under_construction') AS in_arbeit
20 FROM wrsledrunjsoncache JOIN page ON sledrun_page_id=page_id
21 WHERE json_exists(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel')
25 Get existing Gütesiegel entry with matching stätte_id:
28 FROM wrsledrunjsoncache
29 WHERE json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.stätte_id') = 3344;
32 Get list of sledruns closer to a given point (longitude, latitude) - distance is in meter:
34 SELECT page_title, name, aswkt(position) as position, in_arbeit, distance
36 SELECT *, st_distance_sphere(point(11.4, 47.3), position) as distance
40 json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.name') AS name,
41 point(json_extract(sledrun_json, '$.position.longitude'),
42 json_extract(sledrun_json, '$.position.latitude')) AS position,
43 json_extract(sledrun_json, '$.entry_under_construction') AS in_arbeit
44 FROM wrsledrunjsoncache JOIN page ON sledrun_page_id=page_id
45 WHERE json_exists(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel')
48 WHERE distance < 20000
55 from typing import List, Union
60 from fiona.transform import transform_geom
61 from requests import Session
62 from shapely.geometry import shape
63 from sqlalchemy import create_engine, text
65 from wrpylib.json_tools import format_json
68 def update_sledrun(connection: sqlalchemy.engine.Connection, api_url: str, session: Session,
69 csrf_token: str, shp_crs: Union[dict, str], lon_lat_crs: Union[dict, str], feature: dict):
70 # Is a sledrun already existing with the given stätte_id?
71 properties = feature['properties']
73 sledrun_type = properties['ATTR_TYP']
74 assert sledrun_type in ['Naturrodelbahn', 'Sommerrodelbahn']
75 if sledrun_type != 'Naturrodelbahn':
78 count, = connection.execute(text("""
80 FROM wrsledrunjsoncache
81 WHERE json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.stätte_id') = :staette_id;"""),
82 [{'staette_id': properties['STAETTE_ID']}]).fetchone()
85 # Is a sledrun already existing with the given name?
86 rows = list(connection.execute(text("""
87 SELECT sledrun_page_id, sledrun_json_page_id, sledrun_json_rev_id, sledrun_json
88 FROM wrsledrunjsoncache
89 WHERE json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.name') = :name;"""),
90 [{'name': properties['STAETTE_NA']}]))
92 print(f"{properties['STAETTE_NA']} ({properties['STAETTE_ID']}) not found")
94 # Is there a sledrun that's close?
95 line_string_lon_lat = transform_geom(shp_crs, lon_lat_crs, feature['geometry'])
96 point = shape(line_string_lon_lat).representative_point()
97 lon, lat = point.x, point.y
98 candidates = connection.execute(text("""
99 SELECT page_title, name, aswkt(position) as position, in_arbeit, distance
101 SELECT *, st_distance_sphere(point(:lon, :lat), position) as distance
105 json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.name') AS name,
106 point(json_extract(sledrun_json, '$.position.longitude'),
107 json_extract(sledrun_json, '$.position.latitude')) AS position,
108 json_extract(sledrun_json, '$.entry_under_construction') AS in_arbeit
109 FROM wrsledrunjsoncache JOIN page ON sledrun_page_id=page_id
112 WHERE distance < 5000
114 """), [{'lon': lon, 'lat': lat}])
115 candidates = list(candidates)
116 if len(candidates) == 0:
117 print(' No candidates.')
118 print(' Candidates:')
119 for candidate in candidates:
120 print(f' {candidate}')
122 print(f"{properties['STAETTE_NA']} -> {properties['STAETTE_ID']} (match by name)")
124 # add 'stätte_id' to JSON
126 sledrun_json = json.loads(row.sledrun_json)
127 gs = sledrun_json['tiroler_naturrodelbahn_gütesiegel']
128 if gs.get('stätte_id') is not None:
129 print(f"{properties['STAETTE_NA']} has a different ID")
131 gs['stätte_id'] = properties['STAETTE_ID']
132 new_sledrun_json = format_json(sledrun_json)
135 'pageid': row.sledrun_json_page_id,
136 'text': new_sledrun_json,
137 'summary': '"Stätte-ID" von Tiroler Naturrodelbahn-Gütesiegel eingefügt.',
140 'baserevid': row.sledrun_json_rev_id,
142 'contentformat': 'application/json',
143 'contentmodel': 'json',
147 response = session.post(url=api_url, data=post_data)
148 response = response.json()['edit']
149 if response['result'] != 'Success':
150 raise RuntimeError(response['reason'])
151 assert row.sledrun_json_page_id == response['pageid']
152 if 'nochange' in response:
153 print("Change already done previously - table wrsledrunjsoncache doesn't seem to be up-to-date.")
155 assert row.sledrun_json_rev_id == response['oldrevid']
156 print(f"Successfully add stätte_id {properties['STAETTE_ID']} to {properties['STAETTE_NA']}")
158 # update wrsledrunjsoncache (not strictly necessary but why not as we have the data)
159 connection.execute(text("""
160 UPDATE wrsledrunjsoncache
162 sledrun_json=:sledrun_json,
163 sledrun_json_rev_id=:sledrun_json_rev_id
165 sledrun_page_id=:sledrun_page_id AND sledrun_json_page_id=:sledrun_json_page_id;
167 'sledrun_json': new_sledrun_json,
168 'sledrun_json_rev_id': response['newrevid'],
169 'sledrun_page_id': row.sledrun_page_id,
170 'sledrun_json_page_id': row.sledrun_json_page_id,
174 print(f'{len(rows)} rows match the name {properties["STAETTE_NA"]}.')
176 print(f"{properties['STAETTE_NA']} -> {properties['STAETTE_ID']} (match by stätte_id)")
179 def update_gütesiegel(inifile: List[str], shapefile: str):
180 config = configparser.ConfigParser()
183 api_url = config.get('robot', 'wikiurl')
184 api_user = config.get('robot', 'botpassword_bot')
185 api_password = config.get('robot', 'botpassword_password')
187 session = requests.Session()
196 response = session.get(url=api_url, params=get_data).json()
197 login_token = response['query']['tokens']['logintoken']
203 'lgpassword': api_password,
204 'lgtoken': login_token,
207 response = session.post(url=api_url, data=post_data).json()['login']
208 if response['result'] != 'Success':
209 raise RuntimeError(response['reason'])
218 response = session.get(url=api_url, params=get_data).json()
219 csrf_token = response['query']['tokens']['csrftoken']
221 host = config.get('mysql', 'host')
222 dbname = config.get('mysql', 'dbname')
223 user = config.get('mysql', 'user_name')
224 passwd = config.get('mysql', 'user_pass')
226 engine = create_engine(f'mysql://{user}@{host}:3306/{dbname}?passwd={passwd}&charset=utf8mb4')
227 connection = engine.connect()
228 with fiona.open(shapefile) as shp:
230 update_sledrun(connection, api_url, session, csrf_token, shp.crs, 'EPSG:4326', feature)
234 parser = argparse.ArgumentParser(description='Update Tiroler Gütesiegel in sledrun JSON '
235 'based on official information.')
236 parser.add_argument('inifile', nargs='+', help='inifile.ini, see: https://www.winterrodeln.org/trac/wiki/ConfigIni')
237 parser.add_argument('shapefile', help='Shape file as provided by '
238 'https://www.data.gv.at/katalog/dataset/land-tirol_rodelbahnenintirol')
239 args = parser.parse_args()
240 update_gütesiegel(args.inifile, args.shapefile)
243 if __name__ == '__main__':