]> code.communitydata.science - rises_declines_wikia_code.git/blob - mediawiki_dump_tools/Mediawiki-Utilities/mw/database/collections/revisions.py
add copy of the GPL
[rises_declines_wikia_code.git] / mediawiki_dump_tools / Mediawiki-Utilities / mw / database / collections / revisions.py
1 import logging
2 import time
3 from itertools import chain
4
5 from ...types import Timestamp
6 from ...util import iteration, none_or
7 from .collection import Collection
8
9 logger = logging.getLogger("mw.database.collections.revisions")
10
11
12 class AllRevisions(Collection):
13     def get(self, rev_id, include_page=False):
14         """
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.
18
19         :Parameters:
20             rev_id : int
21                 Revision ID
22             include_page : bool
23                 Join revision returned against ``page``
24
25         :Returns:
26             A revision row
27         """
28         rev_id = int(rev_id)
29         try:
30             rev_row = self.db.revisions.get(rev_id, include_page=include_page)
31         except KeyError as e:
32             rev_row = self.db.archives.get(rev_id)
33
34         return rev_row
35
36     def query(self, *args, **kwargs):
37         """
38         Queries revisions (excludes revisions to deleted pages)
39
40         :Parameters:
41             page_id : int
42                 Page identifier.  Filter revisions to this page.
43             user_id : int
44                 User identifier.  Filter revisions to those made by this user.
45             user_text : str
46                 User text (user_name or IP address).  Filter revisions to those
47                 made by this user.
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.
52             before_id : int
53                 Filter revisions to those with an ID before this ID
54             after_id : int
55                 Filter revisions to those with an ID after this ID
56             direction : str
57                 "newer" or "older"
58             limit : int
59                 Limit the number of results
60             include_page : bool
61                 Join revisions returned against ``page``
62
63         :Returns:
64             An iterator over revision rows.
65         """
66
67         revisions = self.db.revisions.query(*args, **kwargs)
68         archives = self.db.archives.query(*args, **kwargs)
69
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))
74
75             if direction == "newer":
76                 collated_revisions = iteration.sequence(
77                     revisions,
78                     archives,
79                     compare=lambda r1, r2:\
80                             (r1['rev_timestamp'], r1['rev_id']) <=
81                             (r2['rev_timestamp'], r2['rev_id'])
82                 )
83             else:  # direction == "older"
84                 collated_revisions = iteration.sequence(
85                     revisions,
86                     archives,
87                     compare=lambda r1, r2:\
88                             (r1['rev_timestamp'], r1['rev_id']) >=
89                             (r2['rev_timestamp'], r2['rev_id'])
90                 )
91         else:
92             collated_revisions = chain(revisions, archives)
93
94         if 'limit' in kwargs:
95             limit = kwargs['limit']
96
97             for i, rev in enumerate(collated_revisions):
98                 yield rev
99                 if i >= limit:
100                     break
101
102         else:
103             for rev in collated_revisions:
104                 yield rev
105
106
107 class Revisions(Collection):
108     
109     def get(self, rev_id, include_page=False):
110         """
111         Gets a single revisions by ID.  Checks the ``revision`` table.   This
112         method throws a :class:`KeyError` if a revision cannot be found.
113
114         :Parameters:
115             rev_id : int
116                 Revision ID
117             include_page : bool
118                 Join revision returned against ``page``
119
120         :Returns:
121             A revision row
122         """
123         rev_id = int(rev_id)
124
125         query = """
126             SELECT *, FALSE AS archived FROM revision
127         """
128         if include_page:
129             query += """
130                 INNER JOIN page ON page_id = rev_page
131             """
132
133         query += " WHERE rev_id = %s"
134
135         cursor.execute(query, [rev_id])
136
137         for row in cursor:
138             return row
139
140         raise KeyError(rev_id)
141
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):
145         """
146         Queries revisions (excludes revisions to deleted pages)
147
148         :Parameters:
149             page_id : int
150                 Page identifier.  Filter revisions to this page.
151             user_id : int
152                 User identifier.  Filter revisions to those made by this user.
153             user_text : str
154                 User text (user_name or IP address).  Filter revisions to those
155                 made by this user.
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.
160             before_id : int
161                 Filter revisions to those with an ID before this ID
162             after_id : int
163                 Filter revisions to those with an ID after this ID
164             direction : str
165                 "newer" or "older"
166             limit : int
167                 Limit the number of results
168             include_page : bool
169                 Join revisions returned against ``page``
170
171         :Returns:
172             An iterator over revision rows.
173         """
174         start_time = time.time()
175
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)
185
186         query = """
187             SELECT *, FALSE AS archived FROM revision
188         """
189
190         if include_page:
191             query += """
192                 INNER JOIN page ON page_id = rev_page
193             """
194
195         query += """
196             WHERE 1
197         """
198         values = []
199
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)
221
222         if direction is not None:
223             
224             direction = ("ASC " if direction == "newer" else "DESC ")
225             
226             if before_id != None or after_id != None:
227                 query += " ORDER BY rev_id {0}, rev_timestamp {0}".format(direction)
228             else:
229                 query += " ORDER BY rev_timestamp {0}, rev_id {0}".format(direction)
230
231         if limit is not None:
232             query += " LIMIT %s "
233             values.append(limit)
234
235         cursor = self.db.shared_connection.cursor()
236         cursor.execute(query, values)
237         count = 0
238         for row in cursor:
239             yield row
240             count += 1
241
242         logger.debug("%s revisions read in %s seconds" % (count, time.time() - start_time))
243
244
245 class Archives(Collection):
246     def get(self, rev_id):
247         """
248         Gets a single revisions by ID.  Checks the ``archive`` table. This
249         method throws a :class:`KeyError` if a revision cannot be found.
250
251         :Parameters:
252             rev_id : int
253                 Revision ID
254
255         :Returns:
256             A revision row
257         """
258         rev_id = int(rev_id)
259
260         query = """
261             SELECT
262                 ar_id,
263                 ar_rev_id AS rev_id,
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,
270                 ar_user AS rev_user,
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,
275                 ar_len AS rev_len,
276                 ar_parent_id AS rev_parent_id,
277                 ar_sha1 AS rev_sha1,
278                 TRUE AS archived
279             FROM archive
280             WHERE ar_rev_id = %s
281         """
282
283         cursor.execute(query, [rev_id])
284         for row in cursor:
285             return row
286
287         raise KeyError(rev_id)
288
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):
293         """
294         Queries archived revisions (revisions of deleted pages)
295
296         :Parameters:
297             page_id : int
298                 Page identifier.  Filter revisions to this page.
299             user_id : int
300                 User identifier.  Filter revisions to those made by this user.
301             user_text : str
302                 User text (user_name or IP address).  Filter revisions to those
303                 made by this user.
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.
308             before_id : int
309                 Filter revisions to those with an ID before this ID
310             after_id : int
311                 Filter revisions to those with an ID after this ID
312             direction : str
313                 "newer" or "older"
314             limit : int
315                 Limit the number of results
316             include_page : bool
317                 This field is ignored.  It's only here for compatibility with
318                 :class:`mw.database.Revision`.
319
320         :Returns:
321             An iterator over revision rows.
322         """
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)
331
332         start_time = time.time()
333         cursor = self.db.shared_connection.cursor()
334
335         query = """
336             SELECT
337                 ar_id,
338                 ar_rev_id AS rev_id,
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,
345                 ar_user AS rev_user,
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,
350                 ar_len AS rev_len,
351                 ar_parent_id AS rev_parent_id,
352                 ar_sha1 AS rev_sha1,
353                 TRUE AS archived
354             FROM archive
355         """
356
357         query += """
358             WHERE 1
359         """
360         values = []
361
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)
389
390         if direction is not None:
391             dir = ("ASC " if direction == "newer" else "DESC ")
392             
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)
397             else:
398                 query += " ORDER BY ar_id {0}".format(dir)
399         
400         if limit is not None:
401             query += " LIMIT %s "
402             values.append(limit)
403
404         cursor.execute(query, values)
405         count = 0
406         for row in cursor:
407             yield row
408             count += 1
409
410         logger.debug("%s revisions read in %s seconds" % (count, time.time() - start_time))

Community Data Science Collective || Want to submit a patch?