3 from itertools import chain
5 from ...types import Timestamp
6 from ...util import iteration, none_or
7 from .collection import Collection
9 logger = logging.getLogger("mw.database.collections.revisions")
12 class AllRevisions(Collection):
13 def get(self, rev_id, include_page=False):
15 Gets a single revisions by ID. Checks both the ``revision`` and
16 ``archive`` tables. This method throws a :class:`KeyError` if a
17 revision cannot be found.
23 Join revision returned against ``page``
30 rev_row = self.db.revisions.get(rev_id, include_page=include_page)
32 rev_row = self.db.archives.get(rev_id)
36 def query(self, *args, **kwargs):
38 Queries revisions (excludes revisions to deleted pages)
42 Page identifier. Filter revisions to this page.
44 User identifier. Filter revisions to those made by this user.
46 User text (user_name or IP address). Filter revisions to those
48 before : :class:`mw.Timestamp`
49 Filter revisions to those made before this timestamp.
50 after : :class:`mw.Timestamp`
51 Filter revisions to those made after this timestamp.
53 Filter revisions to those with an ID before this ID
55 Filter revisions to those with an ID after this ID
59 Limit the number of results
61 Join revisions returned against ``page``
64 An iterator over revision rows.
67 revisions = self.db.revisions.query(*args, **kwargs)
68 archives = self.db.archives.query(*args, **kwargs)
70 if 'direction' in kwargs:
71 direction = kwargs['direction']
72 if direction not in self.DIRECTIONS:
73 raise TypeError("direction must be in {0}".format(self.DIRECTIONS))
75 if direction == "newer":
76 collated_revisions = iteration.sequence(
79 compare=lambda r1, r2:\
80 (r1['rev_timestamp'], r1['rev_id']) <=
81 (r2['rev_timestamp'], r2['rev_id'])
83 else: # direction == "older"
84 collated_revisions = iteration.sequence(
87 compare=lambda r1, r2:\
88 (r1['rev_timestamp'], r1['rev_id']) >=
89 (r2['rev_timestamp'], r2['rev_id'])
92 collated_revisions = chain(revisions, archives)
95 limit = kwargs['limit']
97 for i, rev in enumerate(collated_revisions):
103 for rev in collated_revisions:
107 class Revisions(Collection):
109 def get(self, rev_id, include_page=False):
111 Gets a single revisions by ID. Checks the ``revision`` table. This
112 method throws a :class:`KeyError` if a revision cannot be found.
118 Join revision returned against ``page``
126 SELECT *, FALSE AS archived FROM revision
130 INNER JOIN page ON page_id = rev_page
133 query += " WHERE rev_id = %s"
135 cursor.execute(query, [rev_id])
140 raise KeyError(rev_id)
142 def query(self, page_id=None, user_id=None, user_text=None,
143 before=None, after=None, before_id=None, after_id=None,
144 direction=None, limit=None, include_page=False):
146 Queries revisions (excludes revisions to deleted pages)
150 Page identifier. Filter revisions to this page.
152 User identifier. Filter revisions to those made by this user.
154 User text (user_name or IP address). Filter revisions to those
156 before : :class:`mw.Timestamp`
157 Filter revisions to those made before this timestamp.
158 after : :class:`mw.Timestamp`
159 Filter revisions to those made after this timestamp.
161 Filter revisions to those with an ID before this ID
163 Filter revisions to those with an ID after this ID
167 Limit the number of results
169 Join revisions returned against ``page``
172 An iterator over revision rows.
174 start_time = time.time()
176 page_id = none_or(page_id, int)
177 user_id = none_or(user_id, int)
178 user_text = none_or(user_text, str)
179 before = none_or(before, Timestamp)
180 after = none_or(after, Timestamp)
181 before_id = none_or(before_id, int)
182 after_id = none_or(after_id, int)
183 direction = none_or(direction, levels=self.DIRECTIONS)
184 include_page = bool(include_page)
187 SELECT *, FALSE AS archived FROM revision
192 INNER JOIN page ON page_id = rev_page
200 if page_id is not None:
201 query += " AND rev_page = %s "
202 values.append(page_id)
203 if user_id is not None:
204 query += " AND rev_user = %s "
205 values.append(user_id)
206 if user_text is not None:
207 query += " AND rev_user_text = %s "
208 values.append(user_text)
209 if before is not None:
210 query += " AND rev_timestamp < %s "
211 values.append(before.short_format())
212 if after is not None:
213 query += " AND rev_timestamp > %s "
214 values.append(after.short_format())
215 if before_id is not None:
216 query += " AND rev_id < %s "
217 values.append(before_id)
218 if after_id is not None:
219 query += " AND rev_id > %s "
220 values.append(after_id)
222 if direction is not None:
224 direction = ("ASC " if direction == "newer" else "DESC ")
226 if before_id != None or after_id != None:
227 query += " ORDER BY rev_id {0}, rev_timestamp {0}".format(direction)
229 query += " ORDER BY rev_timestamp {0}, rev_id {0}".format(direction)
231 if limit is not None:
232 query += " LIMIT %s "
235 cursor = self.db.shared_connection.cursor()
236 cursor.execute(query, values)
242 logger.debug("%s revisions read in %s seconds" % (count, time.time() - start_time))
245 class Archives(Collection):
246 def get(self, rev_id):
248 Gets a single revisions by ID. Checks the ``archive`` table. This
249 method throws a :class:`KeyError` if a revision cannot be found.
264 ar_page_id AS rev_page,
265 ar_page_id AS page_id,
266 ar_title AS page_title,
267 ar_namespace AS page_namespace,
268 ar_text_id AS rev_text_id,
269 ar_comment AS rev_comment,
271 ar_user_text AS rev_user_text,
272 ar_timestamp AS rev_timestamp,
273 ar_minor_edit AS rev_minor_edit,
274 ar_deleted AS rev_deleted,
276 ar_parent_id AS rev_parent_id,
283 cursor.execute(query, [rev_id])
287 raise KeyError(rev_id)
289 def query(self, page_id=None, user_id=None, user_text=None,
290 before=None, after=None, before_id=None, after_id=None,
291 before_ar_id=None, after_ar_id=None,
292 direction=None, limit=None, include_page=True):
294 Queries archived revisions (revisions of deleted pages)
298 Page identifier. Filter revisions to this page.
300 User identifier. Filter revisions to those made by this user.
302 User text (user_name or IP address). Filter revisions to those
304 before : :class:`mw.Timestamp`
305 Filter revisions to those made before this timestamp.
306 after : :class:`mw.Timestamp`
307 Filter revisions to those made after this timestamp.
309 Filter revisions to those with an ID before this ID
311 Filter revisions to those with an ID after this ID
315 Limit the number of results
317 This field is ignored. It's only here for compatibility with
318 :class:`mw.database.Revision`.
321 An iterator over revision rows.
323 page_id = none_or(page_id, int)
324 user_id = none_or(user_id, int)
325 before = none_or(before, Timestamp)
326 after = none_or(after, Timestamp)
327 before_id = none_or(before_id, int)
328 after_id = none_or(after_id, int)
329 direction = none_or(direction, levels=self.DIRECTIONS)
330 limit = none_or(limit, int)
332 start_time = time.time()
333 cursor = self.db.shared_connection.cursor()
339 ar_page_id AS rev_page,
340 ar_page_id AS page_id,
341 ar_title AS page_title,
342 ar_namespace AS page_namespace,
343 ar_text_id AS rev_text_id,
344 ar_comment AS rev_comment,
346 ar_user_text AS rev_user_text,
347 ar_timestamp AS rev_timestamp,
348 ar_minor_edit AS rev_minor_edit,
349 ar_deleted AS rev_deleted,
351 ar_parent_id AS rev_parent_id,
362 if page_id is not None:
363 query += " AND ar_page_id = %s "
364 values.append(page_id)
365 if user_id is not None:
366 query += " AND ar_user = %s "
367 values.append(user_id)
368 if user_text is not None:
369 query += " AND ar_user_text = %s "
370 values.append(user_text)
371 if before is not None:
372 query += " AND ar_timestamp < %s "
373 values.append(before.short_format())
374 if after is not None:
375 query += " AND ar_timestamp > %s "
376 values.append(after.short_format())
377 if before_id is not None:
378 query += " AND ar_rev_id < %s "
379 values.append(before_id)
380 if after_id is not None:
381 query += " AND ar_rev_id > %s "
382 values.append(after_id)
383 if before_ar_id is not None:
384 query += " AND ar_id < ? "
385 values.append(before_ar_id)
386 if after_ar_id is not None:
387 query += " AND ar_id > ? "
388 values.append(after_ar_id)
390 if direction is not None:
391 dir = ("ASC " if direction == "newer" else "DESC ")
393 if before is not None or after is not None:
394 query += " ORDER BY ar_timestamp {0}, ar_rev_id {0}".format(dir)
395 elif before_id is not None or after_id is not None:
396 query += " ORDER BY ar_rev_id {0}, ar_timestamp {0}".format(dir)
398 query += " ORDER BY ar_id {0}".format(dir)
400 if limit is not None:
401 query += " LIMIT %s "
404 cursor.execute(query, values)
410 logger.debug("%s revisions read in %s seconds" % (count, time.time() - start_time))