1 from itertools import chain
4 from ...types import Timestamp
5 from ...util import none_or
6 from .dummy_checksum import DummyChecksum
7 from .functions import detect
10 def check_rev(session, rev, **kwargs):
12 Checks whether a revision (database row) was reverted (identity) and returns
13 a named tuple of Revert(reverting, reverteds, reverted_to).
16 session : :class:`mw.api.Session`
17 An API session to make use of
19 a revision dict containing 'revid' and 'page.id'
21 a positive integer indicating the maximum number of revisions that can be reverted
22 before : :class:`mw.Timestamp`
23 if set, limits the search for *reverting* revisions to those which were saved before this timestamp
24 properties : set( str )
25 a set of properties to include in revisions (see :class:`mw.api.Revisions`)
28 # extract rev_id, sha1, page_id
32 raise TypeError("rev must have 'rev_id'")
34 page_id = rev['page']['id']
36 page_id = rev['pageid']
38 raise TypeError("rev must have 'page' or 'pageid'")
40 # run the regular check
41 return check(session, rev_id, page_id=page_id, **kwargs)
44 def check(session, rev_id, page_id=None, radius=defaults.RADIUS,
45 before=None, window=None, properties=None):
47 Checks whether a revision was reverted (identity) and returns a named tuple
48 of Revert(reverting, reverteds, reverted_to).
51 session : :class:`mw.api.Session`
52 An API session to make use of
54 the ID of the revision to check
56 the ID of the page the revision occupies (slower if not provided)
58 a positive integer indicating the maximum number of revisions
60 before : :class:`mw.Timestamp`
61 if set, limits the search for *reverting* revisions to those which
62 were saved before this timestamp
64 if set, limits the search for *reverting* revisions to those which
65 were saved within `window` seconds after the reverted edit
66 properties : set( str )
67 a set of properties to include in revisions (see :class:`mw.api.Revisions`)
70 if not hasattr(session, "revisions"):
71 raise TypeError("session wrong type. Expected a mw.api.Session.")
76 raise TypeError("invalid radius. Expected a positive integer.")
78 page_id = none_or(page_id, int)
79 before = none_or(before, Timestamp)
80 properties = set(properties) if properties is not None else set()
82 # If we don't have the page_id, we're going to need to look them up
84 rev = session.revisions.get(rev_id, properties={'ids'})
85 page_id = rev['page']['pageid']
87 # Load history and current rev
88 current_and_past_revs = list(session.revisions.query(
93 properties={'ids', 'timestamp', 'sha1'} | properties
97 # Extract current rev and reorder history
98 current_rev, past_revs = (
99 current_and_past_revs[0], # Current rev is the first one returned
100 reversed(current_and_past_revs[1:]) # The rest are past revs, but they are in the wrong order
103 # Only way to get here is if there isn't enough history. Couldn't be
104 # reverted. Just return None.
107 if window is not None and before is None:
108 before = Timestamp(current_rev['timestamp']) + window
110 # Load future revisions
111 future_revs = session.revisions.query(
114 start_id=rev_id + 1, # Ensures that we skip the current revision
117 properties={'ids', 'timestamp', 'sha1'} | properties
120 # Convert to an iterable of (checksum, rev) pairs for detect() to consume
121 checksum_revisions = chain(
122 ((rev['sha1'] if 'sha1' in rev else DummyChecksum(), rev)
123 for rev in past_revs),
124 [(current_rev.get('sha1', DummyChecksum()), current_rev)],
125 ((rev['sha1'] if 'sha1' in rev else DummyChecksum(), rev)
126 for rev in future_revs),
129 for revert in detect(checksum_revisions, radius=radius):
130 # Check that this is a relevant revert
131 if rev_id in [rev['revid'] for rev in revert.reverteds]: