1 """Contains functions that maintain/update the cache tables."""
2 from sqlalchemy import schema
3 from sqlalchemy.sql import select
4 from sqlalchemy.sql.expression import func as sqlfunc
6 from wrpylib import mwdb, wrmwdb, wrmwmarkup, wrvalidators
9 class UpdateCacheError(RuntimeError):
13 def update_wrsledruncache(connection):
14 """Updates the wrsledruncache table from the wiki. If convert errors occur, an UpdateCacheError exception
15 is raised. No other exception type should be raised under normal circumstances.
17 >>> from sqlalchemy.engine import create_engine
18 >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4')
19 >>> update_wrsledruncache(engine.connect())
21 metadata = schema.MetaData()
22 wrsledruncache = wrmwdb.wrsledruncache_table(metadata)
23 page = mwdb.page_table(metadata)
24 categorylinks = mwdb.categorylinks_table(metadata)
25 revision = mwdb.revision_table(metadata)
26 text = mwdb.text_table(metadata)
31 transaction = connection.begin()
34 q = select([page, categorylinks, revision, text], (page.c.page_latest==revision.c.rev_id) & (text.c.old_id==revision.c.rev_text_id) & (categorylinks.c.cl_from==page.c.page_id) & (categorylinks.c.cl_to=='Rodelbahn'))
35 sledrun_pages = connection.execute(q)
37 # sql = u"select page_id, rev_id, old_id, page_title, old_text, 'In_Arbeit' in (select cl_to from categorylinks where cl_from=page_id) as under_construction from page, revision, text, categorylinks where page_latest=rev_id and old_id=rev_text_id and cl_from=page_id and cl_to='Rodelbahn' order by page_title"
39 # Delete all existing entries in wrsledruncache
40 # We rely on transactions MySQL InnoDB
41 connection.execute(wrsledruncache.delete())
43 # Refill wrsledruncache table
44 for sledrun_page in sledrun_pages:
46 rodelbahnbox = wrvalidators.rodelbahnbox_from_str(sledrun_page.old_text)
47 sledrun = wrmwmarkup.sledrun_from_rodelbahnbox(rodelbahnbox, Sledrun())
48 sledrun.page_id = sledrun_page.page_id
49 sledrun.page_title = sledrun_page.page_title
50 sledrun.name_url = wrvalidators.sledrun_page_title_to_pretty_url(sledrun_page.page_title)
51 sledrun.under_construction = connection.execute(select([sqlfunc.count()], (categorylinks.c.cl_from==sledrun_page.page_id) & (categorylinks.c.cl_to == 'In_Arbeit')).alias('x')).fetchone()[0] > 0
52 connection.execute(wrsledruncache.insert(sledrun.__dict__))
53 except ValueError as e:
54 transaction.rollback()
55 error_msg = "Error at sled run '{0}': {1}".format(sledrun_page.page_title, str(e))
56 raise UpdateCacheError(error_msg, sledrun_page.page_title, e)
60 def update_wrinncache(connection):
61 """Updates the wrinncache table from the wiki. If convert errors occur, an UpdateCacheError exception
62 is raised. No other exception type should be raised under normal circumstances.
64 >>> from sqlalchemy.engine import create_engine
65 >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4')
66 >>> update_wrinncache(engine.connect())
68 metadata = schema.MetaData()
69 wrinncache = wrmwdb.wrinncache_table(metadata)
70 page = mwdb.page_table(metadata)
71 categorylinks = mwdb.categorylinks_table(metadata)
72 revision = mwdb.revision_table(metadata)
73 text = mwdb.text_table(metadata)
78 transaction = connection.begin()
81 q = select([page, categorylinks, revision, text], (page.c.page_latest==revision.c.rev_id) & (text.c.old_id==revision.c.rev_text_id) & (categorylinks.c.cl_from==page.c.page_id) & (categorylinks.c.cl_to=='Gasthaus'))
82 inn_pages = connection.execute(q)
84 # Delete all existing entries in wrinncache
85 # We rely on transactions MySQL InnoDB
86 connection.execute(wrinncache.delete())
88 # Refill wrinncache table
89 for inn_page in inn_pages:
91 gasthausbox = wrvalidators.gasthausbox_from_str(inn_page.old_text)
92 inn = wrmwmarkup.inn_from_gasthausbox(gasthausbox, Inn())
93 inn.page_id = inn_page.page_id
94 inn.page_title = inn_page.page_title
95 inn.under_construction = connection.execute(select([sqlfunc.count()], (categorylinks.c.cl_from==inn_page.page_id) & (categorylinks.c.cl_to == 'In_Arbeit')).alias('x')).fetchone()[0] > 0 # It would be better to do this in the query above
96 connection.execute(wrinncache.insert(inn.__dict__))
97 except ValueError as e:
98 transaction.rollback()
99 error_msg = "Error as inn '{0}': {1}".format(inn_page.page_title, str(e))
100 raise UpdateCacheError(error_msg, inn_page.page_title, e)
104 def update_wrreportcache(connection, page_id=None):
105 """Updates the wrreportcache table.
106 :param connection: sqlalchemy connection
107 :param page_id: Updates only the reportcache table for the sledrun described on the Winterrodeln wiki page
108 with the specified page_id. Use None for this parameter to update the whole table.
110 >>> from sqlalchemy.engine import create_engine
111 >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4')
112 >>> update_wrreportcache(engine.connect())
114 metadata = schema.MetaData()
115 wrreportcache = wrmwdb.wrreportcache_table(metadata)
116 transaction = connection.begin()
118 # Delete the datasets we are going to update
119 sql_del = wrreportcache.delete()
120 if not page_id is None: sql_del = sql_del.where(wrreportcache.c.page_id == page_id)
121 connection.execute(sql_del)
123 def insert_row(connection, rowlist):
124 if len(rowlist) == 0: return
126 row = dict(rowlist[0])
127 connection.execute(wrreportcache.insert(values=row))
129 # Select the rows to update
130 sql = 'select page_id, page_title, wrreport.id as report_id, date_report, `condition`, description, author_name, if(author_userid is null, null, author_username) as author_username from wrreport where {0}`condition` is not null and date_invalid > now() and delete_date is null order by page_id, date_report desc, date_entry desc'.format('' if page_id is None else 'page_id={0} and '.format(page_id))
131 cursor = connection.execute(sql)
135 if row.page_id != page_id:
136 insert_row(connection, rowlist)
137 page_id = row.page_id
140 insert_row(connection, rowlist)
144 def update_wrmapcache(connection):
145 """Updates the wrmappointcache and wrmappathcache tables from the wiki. If convert errors occur, an UpdateCacheError exception
146 is raised. No other exception type should be raised under normal circumstances.
148 >>> from sqlalchemy.engine import create_engine
149 >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4')
150 >>> # or: engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4&passwd=XXXXX')
151 >>> update_wrmapcache(engine.connect())
153 metadata = schema.MetaData()
154 page = mwdb.page_table(metadata)
155 categorylinks = mwdb.categorylinks_table(metadata)
156 revision = mwdb.revision_table(metadata)
157 text = mwdb.text_table(metadata)
159 transaction = connection.begin()
162 q = select([page, categorylinks, revision, text], (page.c.page_latest==revision.c.rev_id) & (text.c.old_id==revision.c.rev_text_id) & (categorylinks.c.cl_from==page.c.page_id) & (categorylinks.c.cl_to=='Rodelbahn'))
163 sledrun_pages = connection.execute(q)
165 # sql = u"select page_id, rev_id, old_id, page_title, old_text, 'In_Arbeit' in (select cl_to from categorylinks where cl_from=page_id) as under_construction from page, revision, text, categorylinks where page_latest=rev_id and old_id=rev_text_id and cl_from=page_id and cl_to='Rodelbahn' order by page_title"
167 # Delete all existing entries in wrmappointcache
168 # We rely on transactions MySQL InnoDB
169 connection.execute('delete from wrmappointcache')
170 connection.execute('delete from wrmappathcache')
172 # Refill wrmappointcache and wrmappathcache tables
173 for sledrun_page in sledrun_pages:
175 import mwparserfromhell
176 wikicode = mwparserfromhell.parse(sledrun_page.old_text)
177 wrmap_list = wikicode.filter_tags(recursive=False, matches=lambda tag: tag.tag == 'wrmap')
178 if len(wrmap_list) == 0:
179 continue # not wrmap in page
180 if len(wrmap_list) > 1:
181 raise UpdateCacheError('{} <wrmap ...> entries found in article "{}"'.format(len(wrmap_list), sledrun_page.page_title))
182 wrmap = wrmap_list[0]
183 geojson = wrmwmarkup.parse_wrmap(str(wrmap))
185 for feature in geojson['features']:
186 properties = feature['properties']
187 coordinates = feature['geometry']['coordinates']
190 if properties['type'] in wrmwmarkup.WRMAP_POINT_TYPES:
191 lon, lat = coordinates
192 label = properties.get('name')
193 point_types = {'gasthaus': 'hut', 'haltestelle': 'busstop', 'parkplatz': 'carpark', 'achtung': 'warning', 'foto': 'photo', 'verleih': 'rental', 'punkt': 'point'}
194 point_type = point_types[properties['type']]
195 sql = 'insert into wrmappointcache (page_id, type, point, label) values (%s, %s, POINT(%s, %s), %s)'
196 connection.execute(sql, (sledrun_page.page_id, point_type, lon, lat, label))
199 elif properties['type'] in wrmwmarkup.WRMAP_LINE_TYPES:
200 path_types = {'rodelbahn': 'sledrun', 'gehweg': 'walkup', 'alternative': 'alternative', 'lift': 'lift', 'anfahrt': 'recommendedcarroute', 'linie': 'line'}
201 path_type = path_types[properties['type']]
202 path = ", ".join(["{0} {1}".format(lon, lat) for lon, lat in coordinates])
203 path = 'LineString({0})'.format(path)
204 if path_type == 'recommendedcarroute': continue
205 sql = 'insert into wrmappathcache (path, page_id, type) values (GeomFromText(%s), %s, %s)'
206 connection.execute(sql, (path, sledrun_page.page_id, path_type))
209 raise RuntimeError('Unknown feature type {0}'.format(properties['type']))
210 except RuntimeError as e:
211 error_msg = "Error at sledrun '{0}': {1}".format(sledrun_page.page_title, str(e))
212 transaction.rollback()
213 raise UpdateCacheError(error_msg, sledrun_page.page_title, e)
217 def update_wrregioncache(connection):
218 """Updates the wrregioncache table from the wiki.
219 It relays on the table wrsledruncache to be up-to-date.
220 No exceptions should be raised under normal circumstances.
222 >>> from sqlalchemy.engine import create_engine
223 >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4')
224 >>> # or: engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8mb4&passwd=XXXXX')
225 >>> update_wrregioncache(engine.connect())
227 metadata = schema.MetaData()
228 wrregion = wrmwdb.wrregion_table(metadata)
229 wrsledruncache = wrmwdb.wrsledruncache_table(metadata)
230 wrregioncache = wrmwdb.wrregioncache_table(metadata)
232 transaction = connection.begin()
234 # Delete all existing entries in wrregioncache
235 # We rely on transactions MySQL InnoDB
236 connection.execute(wrregioncache.delete())
238 # Query all combinations of sledruns and regions
239 sel = select([wrregion.c.id.label('region_id'), sqlfunc.AsWKB(wrregion.c.border).label('border'), wrsledruncache.c.page_id, wrsledruncache.c.position_longitude, wrsledruncache.c.position_latitude], sqlfunc.contains(wrregion.c.border, sqlfunc.point(wrsledruncache.c.position_longitude, wrsledruncache.c.position_latitude)))
240 ins = wrregioncache.insert()
242 # Refill wrregioncache
243 point = ogr.Geometry(ogr.wkbPoint)
244 result = connection.execute(sel)
246 point.SetPoint(0, row.position_longitude, row.position_latitude)
247 if point.Within(ogr.CreateGeometryFromWkb(row.border)):
248 connection.execute(ins.values(region_id=row.region_id, page_id=row.page_id))