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
66 def update_sledrun(connection: sqlalchemy.engine.Connection, api_url: str, session: Session,
67 csrf_token: str, shp_crs: Union[dict, str], lon_lat_crs: Union[dict, str], feature: dict):
68 # Is a sledrun already existing with the given stätte_id?
69 properties = feature['properties']
71 sledrun_type = properties['ATTR_TYP']
72 assert sledrun_type in ['Naturrodelbahn', 'Sommerrodelbahn']
73 if sledrun_type != 'Naturrodelbahn':
76 count, = connection.execute(text("""
78 FROM wrsledrunjsoncache
79 WHERE json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.stätte_id') = :staette_id;"""),
80 [{'staette_id': properties['STAETTE_ID']}]).fetchone()
83 # Is a sledrun already existing with the given name?
84 rows = list(connection.execute(text("""
85 SELECT sledrun_page_id, sledrun_json_page_id, sledrun_json_rev_id, sledrun_json
86 FROM wrsledrunjsoncache
87 WHERE json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.name') = :name;"""),
88 [{'name': properties['STAETTE_NA']}]))
90 print(f"{properties['STAETTE_NA']} ({properties['STAETTE_ID']}) not found")
92 # Is there a sledrun that's close?
93 line_string_lon_lat = transform_geom(shp_crs, lon_lat_crs, feature['geometry'])
94 point = shape(line_string_lon_lat).representative_point()
95 lon, lat = point.x, point.y
96 candidates = connection.execute(text("""
97 SELECT page_title, name, aswkt(position) as position, in_arbeit, distance
99 SELECT *, st_distance_sphere(point(:lon, :lat), position) as distance
103 json_extract(sledrun_json, '$.tiroler_naturrodelbahn_gütesiegel.name') AS name,
104 point(json_extract(sledrun_json, '$.position.longitude'),
105 json_extract(sledrun_json, '$.position.latitude')) AS position,
106 json_extract(sledrun_json, '$.entry_under_construction') AS in_arbeit
107 FROM wrsledrunjsoncache JOIN page ON sledrun_page_id=page_id
110 WHERE distance < 5000
112 """), [{'lon': lon, 'lat': lat}])
113 candidates = list(candidates)
114 if len(candidates) == 0:
115 print(' No candidates.')
116 print(' Candidates:')
117 for candidate in candidates:
118 print(f' {candidate}')
120 print(f"{properties['STAETTE_NA']} -> {properties['STAETTE_ID']} (match by name)")
122 # add 'stätte_id' to JSON
124 sledrun_json = json.loads(row.sledrun_json)
125 gs = sledrun_json['tiroler_naturrodelbahn_gütesiegel']
126 if gs.get('stätte_id') is not None:
127 print(f"{properties['STAETTE_NA']} has a different ID")
129 gs['stätte_id'] = properties['STAETTE_ID']
130 new_sledrun_json = json.dumps(sledrun_json, ensure_ascii=False, indent=4)
133 'pageid': row.sledrun_json_page_id,
134 'text': new_sledrun_json,
135 'summary': '"Stätte-ID" von Tiroler Naturrodelbahn-Gütesiegel eingefügt.',
138 'baserevid': row.sledrun_json_rev_id,
140 'contentformat': 'application/json',
141 'contentmodel': 'json',
145 response = session.post(url=api_url, data=post_data)
146 response = response.json()['edit']
147 if response['result'] != 'Success':
148 raise RuntimeError(response['reason'])
149 assert row.sledrun_json_page_id == response['pageid']
150 if 'nochange' in response:
151 print("Change already done previously - table wrsledrunjsoncache doesn't seem to be up-to-date.")
153 assert row.sledrun_json_rev_id == response['oldrevid']
154 print(f"Successfully add stätte_id {properties['STAETTE_ID']} to {properties['STAETTE_NA']}")
156 # update wrsledrunjsoncache (not strictly necessary but why not as we have the data)
157 connection.execute(text("""
158 UPDATE wrsledrunjsoncache
160 sledrun_json=:sledrun_json,
161 sledrun_json_rev_id=:sledrun_json_rev_id
163 sledrun_page_id=:sledrun_page_id AND sledrun_json_page_id=:sledrun_json_page_id;
165 'sledrun_json': new_sledrun_json,
166 'sledrun_json_rev_id': response['newrevid'],
167 'sledrun_page_id': row.sledrun_page_id,
168 'sledrun_json_page_id': row.sledrun_json_page_id,
172 print(f'{len(rows)} rows match the name {properties["STAETTE_NA"]}.')
174 print(f"{properties['STAETTE_NA']} -> {properties['STAETTE_ID']} (match by stätte_id)")
177 def update_gütesiegel(inifile: List[str], shapefile: str):
178 config = configparser.ConfigParser()
181 api_url = config.get('robot', 'wikiurl')
182 api_user = config.get('robot', 'botpassword_bot')
183 api_password = config.get('robot', 'botpassword_password')
185 session = requests.Session()
194 response = session.get(url=api_url, params=get_data).json()
195 login_token = response['query']['tokens']['logintoken']
201 'lgpassword': api_password,
202 'lgtoken': login_token,
205 response = session.post(url=api_url, data=post_data).json()['login']
206 if response['result'] != 'Success':
207 raise RuntimeError(response['reason'])
216 response = session.get(url=api_url, params=get_data).json()
217 csrf_token = response['query']['tokens']['csrftoken']
219 host = config.get('mysql', 'host')
220 dbname = config.get('mysql', 'dbname')
221 user = config.get('mysql', 'user_name')
222 passwd = config.get('mysql', 'user_pass')
224 engine = create_engine(f'mysql://{user}@{host}:3306/{dbname}?passwd={passwd}&charset=utf8mb4')
225 connection = engine.connect()
226 with fiona.open(shapefile) as shp:
228 update_sledrun(connection, api_url, session, csrf_token, shp.crs, 'EPSG:4326', feature)
232 parser = argparse.ArgumentParser(description='Update Tiroler Gütesiegel in sledrun JSON '
233 'based on official information.')
234 parser.add_argument('inifile', nargs='+', help='inifile.ini, see: https://www.winterrodeln.org/trac/wiki/ConfigIni')
235 parser.add_argument('shapefile', help='Shape file as provided by '
236 'https://www.data.gv.at/katalog/dataset/land-tirol_rodelbahnenintirol')
237 args = parser.parse_args()
238 update_gütesiegel(args.inifile, args.shapefile)
241 if __name__ == '__main__':