Package: dm.historical
Utilities to access the historical state of objects stored in
a (history aware) ZODB (Zope Object DataBase).
They can be useful to find out what happened to objects in the past
and to restore accidentally deleted or modified objects.I have used them recently to analyse a case where colleagues reported
about mysteriously lost objects. It turned out that the objects
have not been lost at all but only became unindexed.This version is tested against ZODB 3.4 and ZODB 3.6. It will not work
with ZODB 3.2. It may (or may not) work with more recent ZODB versions.
Beside ZODB, this version requires Zope’sDateTimepackage.APICurrently, there are the utilities:getHistory(obj, first=0, last=20),getObjectAt(obj, time),generateHistory(obj)andgenerateBTreeHistory(btree).timecan be a ZopeDateTimeinstance, a float in seconds since epoch
or a serial/transaction id.generateHistoryis a generator version ofgetHistory.WhilegenerateHistory(btree)reports only changes to the top level
btree object,generateBTreeHistorycombines the history for all
(current) subtrees and subbuckets, i.e. it treats the btree as the
application sees it and not on the persistence level only.
Note that the history records generated bygenerateBTreeHistorymay not be complete: some deletion operations may be missing. However,
there will be at least one entry in the history refering to such deletions.Usage exampleIn the example below, the ZODB contains a folderf. Let’s have a look
at its history.>>> from dm.historical import getHistory, getObjectAt
>>> from pprint import pprint as pp
>>> h = getHistory(f)
>>> pp(h)
[{'description': '',
  'obj': <Folder at /f>,
  'size': 157L,
  'tid': '\x03r\xc8\x1b\xf3hhU',
  'time': DateTime('2008/01/01 09:59:57.049 GMT+1'),
  'user_name': '',
  'version': ''},
 {'description': '',
  'obj': <Folder at /f>,
  'size': 124L,
  'tid': '\x03r\xc8\x1bX\x0e\xfb\xcc',
  'time': DateTime('2008/01/01 09:59:20.639 GMT+1'),
  'user_name': '',
  'version': ''},
 {'description': '',
  'obj': <Folder at /f>,
  'size': 48L,
  'tid': '\x03r\xc8\x15\x1c\x9f\xb03',
  'time': DateTime('2008/01/01 09:53:06.709 GMT+1'),
  'user_name': '',
  'version': ''}]This tells us that the ZODB knows about 3 transactions affectingf. We can access its state after the first transaction.>>> f1 = h[-1]['obj']
>>> f1.objectIds()
[]fwas empty at that time. Let’s see howfwas changed by the
other transactions:>>> for hr in h:
...   print (hr['time'].strftime('%H:%M:%S'), hr['obj'].objectIds())
...
09:59:57 ['x', 'y']
09:59:20 ['x']
09:53:06 []This tells us that the second transaction addedxand the third
transactiony.We can control which history records are retrieved with the
optional parametersfirstandlast.>>> pp(getHistory(f,last=1))
[{'description': '',
  'obj': <Folder at /f>,
  'size': 157L,
  'tid': '\x03r\xc8\x1b\xf3hhU',
  'time': DateTime('2008/01/01 09:59:57.049 GMT+1'),
  'user_name': '',
  'version': ''}]
>>> pp(getHistory(f,first=2))
[{'description': '',
  'obj': <Folder at /f>,
  'size': 48L,
  'tid': '\x03r\xc8\x15\x1c\x9f\xb03',
  'time': DateTime('2008/01/01 09:53:06.709 GMT+1'),
  'user_name': '',
  'version': ''}]getObjectAtcan be used to retrieve the historical state
at a given time. Say, we want to learn howfwas at 9:55.
The most easy way to specify a time is via aDateTime.>>> from DateTime import DateTime
>>> dt=DateTime(2008,1,1,9,55)
>>> dt
DateTime('2008/01/01 09:55:00 GMT+1')
>>> getObjectAt(f, dt).objectIds()
[]When we request the state beyond the ZODB’s maintained history,
we get aPOSKeyError.>>> getObjectAt(f, DateTime(2008,1,1,9,50))
Traceback (most recent call last):
  ...
ZODB.POSException.POSKeyError: ('\x00\x00\x00\x00\x00j\x82\xd3', '\x03r\xc8\x12\x00\x00\x00\x00')The time can also be specified via a float in the unit seconds since epoch
or via a serial/tid. However, these will be used rarely.>>> ts=dt.timeTime()
>>> ts
1199177700.0
>>> getObjectAt(f, ts).objectIds()
[]
>>> getObjectAt(f, b'\x03r\xc8\x15\x1c\x9f\xb03').objectIds()
[]DependenciesThis package depends on a ZODB variant (ZODBorZODB3, maybe others)
andDateTime.HistoryVersion 2.0Made compatible with Python 3Version 1.2Made compatible with ZODB 3.10Version 1.1addedgenerateHistoryandgenerateBTreeHistory.