4 from ...types import Timestamp
5 from ...util import none_or
6 from .collection import Collection
8 logger = logging.getLogger("mw.database.collections.pages")
11 class RecentChanges(Collection):
12 # (https://www.mediawiki.org/wiki/Manual:Recentchanges_table)
14 'edit': 0, # edit of existing page
16 'move': 2, # Marked as obsolete
17 'log': 3, # log action (introduced in MediaWiki 1.2)
18 'move_over_redirect': 4, # Marked as obsolete
19 'external': 5 # An external recent change. Primarily used by Wikidata
22 def listen(self, last=None, types=None, max_wait=5):
24 Listens to the recent changes table. Given no parameters, this function
25 will return an iterator over the entire recentchanges table and then
26 continue to "listen" for new changes to come in every 5 seconds.
30 a recentchanges row to pick up after
32 a set of recentchanges types to filter for
34 the maximum number of seconds to wait between repeated queries
37 A never-ending iterator over change rows.
41 after = last['rc_timestamp']
42 after_id = last['rc_id']
48 rcs = self.query(after=after, after_id=after_id, direction="newer")
55 time.sleep(max_wait - (time.time() - start))
57 def query(self, before=None, after=None, before_id=None, after_id=None,
58 types=None, direction=None, limit=None):
60 Queries the ``recentchanges`` table. See
61 `<https://www.mediawiki.org/wiki/Manual:Recentchanges_table>`_
64 before : :class:`mw.Timestamp`
66 after : :class:`mw.Timestamp`
73 Which types of changes to return?
75 * ``edit`` -- Edits to existing pages
76 * ``new`` -- Edits that create new pages
77 * ``move`` -- (obsolete)
78 * ``log`` -- Log actions (introduced in MediaWiki 1.2)
79 * ``move_over_redirect`` -- (obsolete)
80 * ``external`` -- An external recent change. Primarily used by Wikidata
85 limit the number of records returned
87 before = none_or(before, Timestamp)
88 after = none_or(after, Timestamp)
89 before_id = none_or(before_id, int)
90 after_id = none_or(after_id, int)
91 types = none_or(types, levels=self.TYPES)
92 direction = none_or(direction, levels=self.DIRECTIONS)
93 limit = none_or(limit, int)
96 SELECT * FROM recentchanges
101 if before is not None:
102 query += " AND rc_timestamp < %s "
103 values.append(before.short_format())
104 if after is not None:
105 query += " AND rc_timestamp < %s "
106 values.append(after.short_format())
107 if before_id is not None:
108 query += " AND rc_id < %s "
109 values.append(before_id)
110 if after_id is not None:
111 query += " AND rc_id < %s "
112 values.append(after_id)
113 if types is not None:
114 query += " AND rc_type IN ({0}) ".format(
115 ",".join(self.TYPES[t] for t in types)
118 if direction is not None:
119 direction = ("ASC " if direction == "newer" else "DESC ")
120 query += " ORDER BY rc_timestamp {0}, rc_id {0}".format(dir)
122 if limit is not None:
123 query += " LIMIT %s "
126 cursor.execute(query, values)