{ "info": { "author": "Dieter Maurer", "author_email": "dieter@handshake.de", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Framework :: ZODB", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Database", "Topic :: Utilities" ], "description": "dm.historical\n=============\n\nUtilities to access the historical state of objects stored in\na (history aware) ZODB (Zope Object DataBase).\nThey can be useful to find out what happened to objects in the past\nand to restore accidentally deleted or modified objects.\n\nI have used them recently to analyse a case where colleagues reported\nabout mysteriously lost objects. It turned out that the objects\nhave not been lost at all but only became unindexed.\n\nThis version is tested against ZODB 3.4 and ZODB 3.6. It will not work\nwith ZODB 3.2. It may (or may not) work with more recent ZODB versions.\nBeside ZODB, this version requires Zope's ``DateTime`` package.\n\n\nAPI\n---\n\nCurrently, there are the utilities:\n``getHistory(obj, first=0, last=20)``,\n``getObjectAt(obj, time)``,\n``generateHistory(obj)`` and ``generateBTreeHistory(btree)``.\n\n*time* can be a Zope ``DateTime`` instance, a float in seconds since epoch\nor a serial/transaction id.\n\n``generateHistory`` is a generator version of ``getHistory``.\n\nWhile ``generateHistory(btree)`` reports only changes to the top level\nbtree object, ``generateBTreeHistory`` combines the history for all\n(current) subtrees and subbuckets, i.e. it treats the btree as the\napplication sees it and not on the persistence level only.\nNote that the history records generated by ``generateBTreeHistory``\nmay not be complete: some deletion operations may be missing. However,\nthere will be at least one entry in the history refering to such deletions.\n\n\nUsage example\n-------------\n\nIn the example below, the ZODB contains a folder ``f``. Let's have a look\nat its history.\n\n>>> from dm.historical import getHistory, getObjectAt\n>>> from pprint import pprint as pp\n>>> h = getHistory(f)\n>>> pp(h)\n[{'description': '',\n 'obj': ,\n 'size': 157L,\n 'tid': '\\x03r\\xc8\\x1b\\xf3hhU',\n 'time': DateTime('2008/01/01 09:59:57.049 GMT+1'),\n 'user_name': '',\n 'version': ''},\n {'description': '',\n 'obj': ,\n 'size': 124L,\n 'tid': '\\x03r\\xc8\\x1bX\\x0e\\xfb\\xcc',\n 'time': DateTime('2008/01/01 09:59:20.639 GMT+1'),\n 'user_name': '',\n 'version': ''},\n {'description': '',\n 'obj': ,\n 'size': 48L,\n 'tid': '\\x03r\\xc8\\x15\\x1c\\x9f\\xb03',\n 'time': DateTime('2008/01/01 09:53:06.709 GMT+1'),\n 'user_name': '',\n 'version': ''}]\n\nThis tells us that the ZODB knows about 3 transactions affecting\n``f``. We can access its state after the first transaction.\n\n>>> f1 = h[-1]['obj']\n>>> f1.objectIds()\n[]\n\n``f`` was empty at that time. Let's see how ``f`` was changed by the\nother transactions:\n\n>>> for hr in h:\n... print (hr['time'].strftime('%H:%M:%S'), hr['obj'].objectIds())\n...\n09:59:57 ['x', 'y']\n09:59:20 ['x']\n09:53:06 []\n\nThis tells us that the second transaction added ``x`` and the third\ntransaction ``y``.\n\nWe can control which history records are retrieved with the\noptional parameters *first* and *last*.\n\n>>> pp(getHistory(f,last=1))\n[{'description': '',\n 'obj': ,\n 'size': 157L,\n 'tid': '\\x03r\\xc8\\x1b\\xf3hhU',\n 'time': DateTime('2008/01/01 09:59:57.049 GMT+1'),\n 'user_name': '',\n 'version': ''}]\n>>> pp(getHistory(f,first=2))\n[{'description': '',\n 'obj': ,\n 'size': 48L,\n 'tid': '\\x03r\\xc8\\x15\\x1c\\x9f\\xb03',\n 'time': DateTime('2008/01/01 09:53:06.709 GMT+1'),\n 'user_name': '',\n 'version': ''}]\n\n``getObjectAt`` can be used to retrieve the historical state\nat a given time. Say, we want to learn how ``f`` was at 9:55.\nThe most easy way to specify a time is via a ``DateTime``.\n\n>>> from DateTime import DateTime\n>>> dt=DateTime(2008,1,1,9,55)\n>>> dt\nDateTime('2008/01/01 09:55:00 GMT+1')\n>>> getObjectAt(f, dt).objectIds()\n[]\n\nWhen we request the state beyond the ZODB's maintained history,\nwe get a ``POSKeyError``.\n\n>>> getObjectAt(f, DateTime(2008,1,1,9,50))\nTraceback (most recent call last):\n ...\nZODB.POSException.POSKeyError: ('\\x00\\x00\\x00\\x00\\x00j\\x82\\xd3', '\\x03r\\xc8\\x12\\x00\\x00\\x00\\x00')\n\nThe time can also be specified via a float in the unit seconds since epoch\nor via a serial/tid. However, these will be used rarely.\n\n>>> ts=dt.timeTime()\n>>> ts\n1199177700.0\n>>> getObjectAt(f, ts).objectIds()\n[]\n>>> getObjectAt(f, b'\\x03r\\xc8\\x15\\x1c\\x9f\\xb03').objectIds()\n[]\n\n\nDependencies\n------------\n\nThis package depends on a ZODB variant (`ZODB` or `ZODB3`, maybe others)\nand `DateTime`.\n\n\nHistory\n-------\n\nVersion 2.0\n...........\n\nMade compatible with Python 3\n\nVersion 1.2\n...........\n\nMade compatible with ZODB 3.10\n\n\n\nVersion 1.1\n...........\n\nadded ``generateHistory`` and ``generateBTreeHistory``.", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "", "keywords": "historical state ZODB analysis recovery", "license": "BSD (see \"dm/historical/LICENSE.txt\", for details)", "maintainer": "", "maintainer_email": "", "name": "dm.historical", "package_url": "https://pypi.org/project/dm.historical/", "platform": "", "project_url": "https://pypi.org/project/dm.historical/", "project_urls": null, "release_url": "https://pypi.org/project/dm.historical/2.0.1/", "requires_dist": null, "requires_python": "", "summary": "Historical state of objects stored in the ZODB", "version": "2.0.1" }, "last_serial": 4545899, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "7ebb8d3fa75174d63d883cfa967abe1c", "sha256": "f0446ad050dc373f3574c01cbe5eb89d794f9a6b755f65955e9ffc32fe5e41d3" }, "downloads": -1, "filename": "dm.historical-1.0.tar.gz", "has_sig": false, "md5_digest": "7ebb8d3fa75174d63d883cfa967abe1c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6837, "upload_time": "2008-01-01T10:18:08", "url": "https://files.pythonhosted.org/packages/7f/ec/cff6da2312977dbb63f2a858f0b372888fab56a45b10edb281a4a4c3aa79/dm.historical-1.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "44f06658c2b73275f2f54535b14d12e0", "sha256": "6dc124a17f9c289a8ace614714c0ddd149e02f18a817096ec31e9a4691f7d8f8" }, "downloads": -1, "filename": "dm.historical-1.0.1.tar.gz", "has_sig": false, "md5_digest": "44f06658c2b73275f2f54535b14d12e0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6999, "upload_time": "2008-10-03T07:50:10", "url": "https://files.pythonhosted.org/packages/dc/a7/9aefbddde8b40ead484ccab5a8f7983098e75574646acba14c2f5da2b969/dm.historical-1.0.1.tar.gz" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "b15f0bd9041f22bbcb3204c9c576ce95", "sha256": "16858f1c624507e0021f221a43d6f41616573c7acb945a4e665979281e684dc6" }, "downloads": -1, "filename": "dm.historical-1.1.tar.gz", "has_sig": false, "md5_digest": "b15f0bd9041f22bbcb3204c9c576ce95", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8013, "upload_time": "2009-02-01T13:49:41", "url": "https://files.pythonhosted.org/packages/90/e8/f8c24ea27c7ac4ce6a32caab19028555994af4cda9249fd2aa6b427edaaf/dm.historical-1.1.tar.gz" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "70d9ec928c4b8e3c205eb5279b899641", "sha256": "ed039461a79383818e9e9da395c46d911f3f33f759f99c382708bba501d72733" }, "downloads": -1, "filename": "dm.historical-1.2.tar.gz", "has_sig": false, "md5_digest": "70d9ec928c4b8e3c205eb5279b899641", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8458, "upload_time": "2013-07-03T11:28:24", "url": "https://files.pythonhosted.org/packages/23/66/0adaa521c4bd615a529361242e4cbe98687d25bda9e93cffeab44fc32f18/dm.historical-1.2.tar.gz" } ], "2.0": [ { "comment_text": "", "digests": { "md5": "60d9321b802a5bf21ab6995c4cf396d0", "sha256": "90cbc385a3c5203c584fddaf5b35314f70bbd54586bbfdb853e63641976f9b77" }, "downloads": -1, "filename": "dm.historical-2.0.tar.gz", "has_sig": false, "md5_digest": "60d9321b802a5bf21ab6995c4cf396d0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8577, "upload_time": "2018-01-24T11:02:34", "url": "https://files.pythonhosted.org/packages/8b/e8/1262bdc0282728713107495acf5b623aa405700f2d222dd6730bf52a82f9/dm.historical-2.0.tar.gz" } ], "2.0.1": [ { "comment_text": "", "digests": { "md5": "556465530547919bf2cc1351f322235c", "sha256": "77d898a10733feb155660e49e9215e000d824da82d3c8219a7f784c66041a1ba" }, "downloads": -1, "filename": "dm.historical-2.0.1.tar.gz", "has_sig": false, "md5_digest": "556465530547919bf2cc1351f322235c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8578, "upload_time": "2018-11-30T06:36:36", "url": "https://files.pythonhosted.org/packages/e4/a9/13a1581adabe54a835b3999d8b74f0a5499c43639f049ab984201e10aa81/dm.historical-2.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "556465530547919bf2cc1351f322235c", "sha256": "77d898a10733feb155660e49e9215e000d824da82d3c8219a7f784c66041a1ba" }, "downloads": -1, "filename": "dm.historical-2.0.1.tar.gz", "has_sig": false, "md5_digest": "556465530547919bf2cc1351f322235c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8578, "upload_time": "2018-11-30T06:36:36", "url": "https://files.pythonhosted.org/packages/e4/a9/13a1581adabe54a835b3999d8b74f0a5499c43639f049ab984201e10aa81/dm.historical-2.0.1.tar.gz" } ] }