Changed comments to that the file system path in the examples correponds to the real...
[philipp/winterrodeln/wrpylib.git] / wrpylib / wrmwcache.py
1 #!/usr/bin/python2.6
2 # -*- coding: iso-8859-15 -*-
3 # $Id$
4 # $HeadURL$
5 """Contains functions that maintain/update the cache tables."""
6 from xml.etree import ElementTree
7 from sqlalchemy import schema
8 from sqlalchemy.sql import select
9 import formencode
10 from wrpylib import mwdb, wrmwdb, mwmarkup, wrmwmarkup
11
12
13 class UpdateCacheError(RuntimeError):
14     pass
15
16
17 def update_wrsledruncache(connection):
18     """Updates the wrsledruncache table from the wiki. If convert errors occur, an UpdateCacheError exception
19     is raised. No other exception type should be raised under normal circumstances.
20     
21     >>> from sqlalchemy.engine import create_engine
22     >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8&use_unicode=0')
23     >>> update_wrsledruncache(engine.connect())
24     """
25     metadata = schema.MetaData()
26     wrsledruncache = wrmwdb.wrsledruncache_table(metadata)
27     page = mwdb.page_table(metadata)
28     categorylinks = mwdb.categorylinks_table(metadata)
29     revision = mwdb.revision_table(metadata)
30     text = mwdb.text_table(metadata)
31
32     transaction = connection.begin()
33
34     # Query all sled runs
35     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==u'Rodelbahn'))
36     sledrun_pages = connection.execute(q)
37     # Original SQL:
38     # 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     
40     # Delete all existing entries in wrsledruncache
41     # We rely on transactions MySQL InnoDB
42     connection.execute(wrsledruncache.delete())
43     
44     # Refill wrsledruncache table
45     for sledrun_page in sledrun_pages:
46         try: 
47             start, end, sledrun = wrmwmarkup.rodelbahnbox_to_sledrun(sledrun_page.old_text)
48             sledrun.page_id = sledrun_page.page_id
49             sledrun.page_title = sledrun_page.page_title
50             sledrun.under_construction = connection.execute(select([categorylinks], (categorylinks.c.cl_from==sledrun_page.page_id) & (categorylinks.c.cl_to == u'In_Arbeit')).alias('x').count()).fetchone()[0] > 0 # It would be better to do this in the query above
51             connection.execute(wrsledruncache.insert(sledrun.__dict__))
52         except (RuntimeError, formencode.Invalid) as e:
53             transaction.rollback()
54             error_msg = u"Error at sled run '{0}': {1}".format(sledrun_page.page_title, unicode(e))
55             raise UpdateCacheError(error_msg, sledrun_page.page_title, e)
56     transaction.commit()
57
58
59 def update_wrinncache(connection):
60     """Updates the wrinncache table from the wiki. If convert errors occur, an UpdateCacheError exception
61     is raised. No other exception type should be raised under normal circumstances.
62     
63     >>> from sqlalchemy.engine import create_engine
64     >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8&use_unicode=0')
65     >>> update_wrinncache(engine.connect())
66     """
67     metadata = schema.MetaData()
68     wrinncache = wrmwdb.wrinncache_table(metadata)
69     page = mwdb.page_table(metadata)
70     categorylinks = mwdb.categorylinks_table(metadata)
71     revision = mwdb.revision_table(metadata)
72     text = mwdb.text_table(metadata)
73
74     transaction = connection.begin()
75
76     # Query all inns
77     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==u'Gasthaus'))
78     inn_pages = connection.execute(q)
79         
80     # Delete all existing entries in wrinncache
81     # We rely on transactions MySQL InnoDB
82     connection.execute(wrinncache.delete())
83         
84     # Refill wrinncache table
85     for inn_page in inn_pages:
86         try: 
87             start, end, inn = wrmwmarkup.gasthausbox_to_inn(inn_page.old_text)
88             inn.page_id = inn_page.page_id
89             inn.page_title = inn_page.page_title
90             inn.under_construction = connection.execute(select([categorylinks], (categorylinks.c.cl_from==inn_page.page_id) & (categorylinks.c.cl_to == u'In_Arbeit')).alias('x').count()).fetchone()[0] > 0 # It would be better to do this in the query above
91             connection.execute(wrinncache.insert(inn.__dict__))
92         except (RuntimeError, formencode.Invalid) as e:
93             transaction.rollback()
94             error_msg = u"Error as inn '{0}': {1}".format(inn_page.page_title, unicode(e))
95             raise UpdateCacheError(error_msg, inn_page.page_title, e)
96     transaction.commit()
97
98
99 def update_wrreportcache(connection, page_id=None):
100     """Updates the wrreportcache table.
101     :param connection: sqlalchemy connection
102     :param page_id: Updates only the reportcache table for the sledrun described on the Winterrodeln wiki page
103         with the specified page_id. Use None for this parameter to update the whole table.
104
105     >>> from sqlalchemy.engine import create_engine
106     >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8&use_unicode=1')
107     >>> update_wrreportcache(engine.connect())
108     """
109     metadata = schema.MetaData()
110     wrreport = wrmwdb.wrreport_table(metadata)
111     wrreportcache = wrmwdb.wrreportcache_table(metadata)
112     transaction = connection.begin()
113
114     # Delte the datasets we are going to update
115     sql_del = wrreportcache.delete()
116     if not page_id is None: sql_del = sql_del.where(wrreportcache.c.page_id == page_id)
117     connection.execute(sql_del)
118
119     def insert_row(connection, rowlist):
120         if len(rowlist) == 0: return
121         # Build XML
122         reports_xml = ElementTree.Element('reports')
123         for row in rowlist:
124             report_xml = ElementTree.SubElement(reports_xml, 'report')
125             report_xml.set('report_id', unicode(row.report_id))
126             report_xml.set('date_report', unicode(row.report_date_report))
127             report_xml.set('condition', unicode(row.report_condition))
128             report_xml.set('author_name', unicode(row.report_author_name))
129             report_xml.set('author_username', unicode(row.report_author_username))
130             report_xml.text = unicode(row.report_description)
131         reports_xml.set('page_id', unicode(row.page_id))
132         reports_xml.set('page_title', row.page_title)
133         reports_xml = unicode(ElementTree.tostring(reports_xml, 'utf8'), 'utf8') # there is not ElementTree.tounicode())
134         # Insert the report(s)
135         row = dict(rowlist[0])
136         row['reports_xml'] = reports_xml
137         connection.execute(wrreportcache.insert(values=row))
138
139     # Select the rows to update
140     sql = 'select page_id, page_title, wrreport.id as report_id, date_report as report_date_report, `condition` as report_condition, description as report_description, author_name as report_author_name, if(author_userid is null, null, author_username) as report_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))
141     cursor = connection.execute(sql)
142     page_id = None
143     rowlist = []
144     for row in cursor:
145         if row.page_id != page_id:
146             insert_row(connection, rowlist)
147             page_id = row.page_id
148             rowlist = []
149         rowlist.append(row)
150     insert_row(connection, rowlist)
151     transaction.commit()
152
153
154 def update_wrmapcache(connection):
155     """Updates the wrmappointcache and wrmappathcache tables from the wiki. If convert errors occur, an UpdateCacheError exception
156     is raised. No other exception type should be raised under normal circumstances.
157     
158     >>> from sqlalchemy.engine import create_engine
159     >>> engine = create_engine('mysql://philipp@localhost:3306/philipp_winterrodeln_wiki?charset=utf8&use_unicode=0')
160     >>> update_wrmapcache(engine.connect())
161     """
162     metadata = schema.MetaData()
163     page = mwdb.page_table(metadata)
164     categorylinks = mwdb.categorylinks_table(metadata)
165     revision = mwdb.revision_table(metadata)
166     text = mwdb.text_table(metadata)
167
168     transaction = connection.begin()
169
170     # Query all sledruns
171     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==u'Rodelbahn'))
172     sledrun_pages = connection.execute(q)
173     # Original SQL:
174     # 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"
175     
176     # Delete all existing entries in wrmappointcache
177     # We rely on transactions MySQL InnoDB
178     connection.execute('delete from wrmappointcache')
179     connection.execute('delete from wrmappathcache')
180     
181     # Refill wrmappointcache and wrmappathcache tables
182     for sledrun_page in sledrun_pages:
183         try: 
184             result = mwmarkup.parse_googlemap(sledrun_page.old_text)
185             if not result is None:
186                 center, zoom, coords, paths = result
187                 # Points
188                 for coord in coords:
189                     lon, lat, point_type, label = coord
190                     point_types = {u'Gasthaus': u'hut', u'Haltestelle': u'busstop', u'Parkplatz': u'carpark', u'Achtung': u'warning'}
191                     if not point_type is None:
192                         if not point_types.has_key(point_type): raise RuntimeError(u'Unknown point type {0}'.format(point_type))
193                         point_type = point_types[point_type]
194                         sql = u'insert into wrmappointcache (page_id, type, point, label) values (%s, %s, POINT(%s, %s), %s)'
195                         connection.execute(sql, (sledrun_page.page_id, point_type, lon, lat, None))
196                 # Paths
197                 for path_type, coords in paths:
198                     path_type = path_type.lower()
199                     path_types = {u'6#ff014e9a': u'sledrun', u'6#ffe98401': u'walkup', u'6#ff7f7fff': u'alternative', u'3#ff000000': u'lift'}
200                     if not path_types.has_key(path_type): raise RuntimeError(u'Unknown path type {0}'.format(path_type))
201                     path_type = path_types[path_type]
202                     path = u", ".join(["{0} {1}".format(lon, lat) for lon, lat, symbol, title in coords])
203                     path = u'LineString({0})'.format(path)
204                     sql = u'insert into wrmappathcache (path, page_id, type) values (GeomFromText(%s), %s, %s)'
205                     connection.execute(sql, (path, sledrun_page.page_id, path_type))
206         except RuntimeError as e:
207             error_msg = u"Error at sledrun '{0}': {1}".format(sledrun_page.page_title, unicode(e))
208             transaction.rollback()
209             raise UpdateCacheError(error_msg, sledrun_page.page_title, e)
210     transaction.commit()