{ "info": { "author": "Simon Kaeser", "author_email": "skaeser@gmail.com", "bugtrack_url": null, "classifiers": [ "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "Introduction\n============\n\n``horae.timeaware`` provides basic datetime related tasks used by the Horae\nresource planning system.\n\nDependencies\n============\n\nHorae\n-----\n\n* `horae.core `_\n\nThird party\n-----------\n\n* `grok `_\n\nGuided user story\n=================\n\nLet's first make an object aware of time (holding multiple time entries)\n\n >>> from horae.timeaware.timeaware import TimeAware, TimeEntry\n >>> from datetime import datetime, timedelta\n >>> entries = TimeAware()\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 1, 8)\n >>> entry.date_end = datetime(2011, 1, 1, 8)+timedelta(hours=5)\n >>> entry.hours()\n 5.0\n >>> entries.append(entry)\n >>> entries.hours()\n 5.0\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 1, 14)\n >>> entry.date_end = datetime(2011, 1, 1, 14)+timedelta(hours=4, minutes=30)\n >>> entry.hours()\n 4.5\n >>> entries.append(entry)\n >>> entries.hours()\n 9.5\n\nOverlapping entries\n-------------------\n\nEntries are always flattened before doing any calculations. Overlapping entries get combined.\n\n >>> overlapping = TimeAware()\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 1, 9)\n >>> entry.date_end = datetime(2011, 1, 1, 9)+timedelta(hours=5)\n >>> entry.hours()\n 5.0\n >>> overlapping.append(entry)\n >>> overlapping = entries + overlapping\n >>> len(overlapping.entries())\n 1\n >>> overlapping.hours()\n 10.5\n\nRepeating time entries\n----------------------\n\nNow let's add some repeating time entries\n\nYearly\n''''''\n\n >>> from horae.timeaware import interfaces\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 2, 1, 8)\n >>> entry.date_end = datetime(2011, 2, 1, 8)+timedelta(hours=5)\n >>> entry.repeat = interfaces.REPEAT_YEARLY\n >>> entry.entries()\n Traceback (most recent call last):\n ...\n ValueError: ('daterange', 'daterange required for infinite repeating events')\n >>> entry.hours()\n Traceback (most recent call last):\n ...\n ValueError: ('daterange', 'daterange required for infinite repeating events')\n >>> nonrepeating = entry.entries((datetime(2011, 2, 1), datetime(2012, 3, 1)))\n >>> len(nonrepeating)\n 2\n >>> nonrepeating[0].date_start == entry.date_start\n True\n >>> nonrepeating[0].date_end == entry.date_end\n True\n >>> nonrepeating[1].date_start == entry.date_start+timedelta(days=365)\n True\n >>> nonrepeating[1].date_end == entry.date_end+timedelta(days=365)\n True\n\nWhat about leap years?\n\n >>> nonrepeating = entry.entries((datetime(2012, 3, 1), datetime(2013, 3, 1)))\n >>> len(nonrepeating)\n 1\n >>> nonrepeating[0].date_start == entry.date_start+timedelta(days=365)+timedelta(366)\n True\n >>> nonrepeating[0].date_end == entry.date_end+timedelta(days=365)+timedelta(366)\n True\n\nThe entries returned are intersected with the provided daterange\n\n >>> nonrepeating = entry.entries((datetime(2011, 2, 1, 9), datetime(2012, 2, 1, 11)))\n >>> len(nonrepeating)\n 2\n >>> nonrepeating[0].date_start == datetime(2011, 2, 1, 9)\n True\n >>> nonrepeating[0].hours()\n 4.0\n >>> nonrepeating[1].date_end == datetime(2012, 2, 1, 11)\n True\n >>> nonrepeating[1].hours()\n 3.0\n\nNow let's have a look at the other repeating possibilities\n\nMonthly\n'''''''\n\n >>> entry.repeat = interfaces.REPEAT_MONTHLY\n >>> nonrepeating = entry.entries((datetime(2011, 2, 1), datetime(2012, 3, 1)))\n >>> len(nonrepeating)\n 13\n >>> [(e.date_start.year, e.date_start.month) for e in nonrepeating]\n [(2011, 2), (2011, 3), (2011, 4), (2011, 5), (2011, 6), (2011, 7), (2011, 8), (2011, 9), (2011, 10), (2011, 11), (2011, 12), (2012, 1), (2012, 2)]\n\nWeekly\n''''''\n\n >>> entry.repeat = interfaces.REPEAT_WEEKLY\n >>> nonrepeating = entry.entries((datetime(2011, 2, 1), datetime(2011, 3, 1)))\n >>> len(nonrepeating)\n 4\n >>> [(e.date_start.month, e.date_start.day) for e in nonrepeating]\n [(2, 1), (2, 8), (2, 15), (2, 22)]\n\nOn work days\n''''''''''''\n\n >>> entry.repeat = interfaces.REPEAT_WORKDAYS\n >>> nonrepeating = entry.entries((datetime(2011, 2, 1), datetime(2011, 2, 13)))\n >>> len(nonrepeating)\n 9\n >>> [(e.date_start.month, e.date_start.day) for e in nonrepeating]\n [(2, 1), (2, 2), (2, 3), (2, 4), (2, 7), (2, 8), (2, 9), (2, 10), (2, 11)]\n\nDaily\n'''''\n\n >>> entry.repeat = interfaces.REPEAT_DAILY\n >>> nonrepeating = entry.entries((datetime(2011, 2, 1), datetime(2011, 2, 10)))\n >>> len(nonrepeating)\n 9\n >>> [(e.date_start.month, e.date_start.day) for e in nonrepeating]\n [(2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9)]\n\nAdding and subtracting\n----------------------\n\nCombining time aware objects is possible by adding them\n\n >>> entries2 = TimeAware()\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 2, 7)\n >>> entry.date_end = datetime(2011, 1, 2, 12, 30)\n >>> entries2.append(entry)\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 2, 14)\n >>> entry.date_end = datetime(2011, 1, 2, 17, 30)\n >>> entries2.append(entry)\n >>> len(entries2.entries())\n 2\n >>> entries2.hours()\n 9.0\n >>> len(entries.entries())\n 2\n >>> entries.hours()\n 9.5\n >>> combined = entries + entries2\n >>> len(combined.entries())\n 4\n >>> combined.hours()\n 18.5\n\nSubtracting is done similarly but is only possible for time aware objects containing no infinitely repeating entries\notherwise the subtract method has to be used and passed a daterange and returns a list of time entries\n\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 4, 8)\n >>> entry.date_end = datetime(2011, 1, 4, 18, 30)\n >>> entry2 = TimeEntry()\n >>> entry2.date_start = datetime(2011, 1, 4, 12)\n >>> entry2.date_end = datetime(2011, 1, 4, 18, 30)\n >>> subtracted = entry - entry2\n >>> len(subtracted)\n 1\n >>> subtracted[0].date_start == entry.date_start\n True\n >>> subtracted[0].date_end == entry2.date_start\n True\n >>> entry2 = TimeEntry()\n >>> entry2.date_start = datetime(2011, 1, 4, 8)\n >>> entry2.date_end = datetime(2011, 1, 4, 12, 30)\n >>> subtracted = entry - entry2\n >>> len(subtracted)\n 1\n >>> subtracted[0].date_start == entry2.date_end\n True\n >>> subtracted[0].date_end == entry.date_end\n True\n >>> subtracted = entry2 - entry\n >>> len(subtracted)\n 0\n >>> entry3 = TimeEntry()\n >>> entry3.date_start = datetime(2011, 1, 4, 10, 15)\n >>> entry3.date_end = datetime(2011, 1, 4, 14)\n >>> subtracted = entry - entry3\n >>> len(subtracted)\n 2\n >>> subtracted[0].date_start == entry.date_start\n True\n >>> subtracted[0].date_end == entry3.date_start\n True\n >>> subtracted[1].date_start == entry3.date_end\n True\n >>> subtracted[1].date_end == entry.date_end\n True\n\nNow let's try to subtract from a repeating entry\n\n >>> entry.repeat = interfaces.REPEAT_DAILY\n >>> entry - entry3\n Traceback (most recent call last):\n ...\n ValueError: ('daterange', 'daterange required for infinite repeating events')\n >>> subtracted = entry.subtract(entry3, (datetime(2011, 1, 4), datetime(2011, 1, 6)))\n >>> len(subtracted)\n 3\n >>> subtracted[0].date_start == entry.date_start\n True\n >>> subtracted[0].date_end == entry3.date_start\n True\n >>> subtracted[1].date_start == entry3.date_end\n True\n >>> subtracted[1].date_end == entry.date_end\n True\n >>> subtracted[2].date_start == entry.date_start+timedelta(days=1)\n True\n >>> subtracted[2].date_end == entry.date_end+timedelta(days=1)\n True\n\nWhat about subtracting repeating entries from repeating entries?\n\n >>> entry3.repeat = interfaces.REPEAT_DAILY\n >>> subtracted = entry.subtract(entry3, (datetime(2011, 1, 4), datetime(2011, 1, 6)))\n >>> len(subtracted)\n 4\n >>> subtracted[0].date_start == entry.date_start\n True\n >>> subtracted[0].date_end == entry3.date_start\n True\n >>> subtracted[1].date_start == entry3.date_end\n True\n >>> subtracted[1].date_end == entry.date_end\n True\n >>> subtracted[2].date_start == entry.date_start+timedelta(days=1)\n True\n >>> subtracted[2].date_end == entry3.date_start+timedelta(days=1)\n True\n >>> subtracted[3].date_start == entry3.date_end+timedelta(days=1)\n True\n >>> subtracted[3].date_end == entry.date_end+timedelta(days=1)\n True\n\nSubtracting from time awares containing multiple time entries is also possible\n\n >>> entries = TimeAware()\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 5, 8, 15)\n >>> entry.date_end = datetime(2011, 1, 5, 12, 30)\n >>> entries.append(entry)\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 5, 14)\n >>> entry.date_end = datetime(2011, 1, 5, 18, 30)\n >>> entries.append(entry)\n >>> entries.hours()\n 8.75\n >>> entries2 = TimeAware()\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 5, 12)\n >>> entry.date_end = datetime(2011, 1, 5, 14, 30)\n >>> entries2.append(entry)\n >>> entry = TimeEntry()\n >>> entry.date_start = datetime(2011, 1, 5, 16)\n >>> entry.date_end = datetime(2011, 1, 5, 17)\n >>> entries2.append(entry)\n >>> entries2.hours()\n 3.5\n >>> subtracted = entries - entries2\n >>> subtracted.hours()\n 6.75\n >>> entries = subtracted.entries()\n >>> len(entries)\n 3\n >>> entries[0].date_start == datetime(2011, 1, 5, 8, 15)\n True\n >>> entries[0].date_end == datetime(2011, 1, 5, 12)\n True\n >>> entries[1].date_start == datetime(2011, 1, 5, 14, 30)\n True\n >>> entries[1].date_end == datetime(2011, 1, 5, 16)\n True\n >>> entries[2].date_start == datetime(2011, 1, 5, 17)\n True\n >>> entries[2].date_end == datetime(2011, 1, 5, 18, 30)\n True\n\nNow for a slightly more realistic example. Let's say John is working from 8.00 to 12.00 and from\n13.30 to 18.00 on all work days.\n\n >>> worktime = TimeAware()\n >>> morning = TimeEntry()\n >>> morning.date_start = datetime(2011, 1, 3, 8)\n >>> morning.date_end = datetime(2011, 1, 3, 12)\n >>> morning.repeat = interfaces.REPEAT_WORKDAYS\n >>> worktime.append(morning)\n >>> afternoon = TimeEntry()\n >>> afternoon.date_start = datetime(2011, 1, 3, 13, 30)\n >>> afternoon.date_end = datetime(2011, 1, 3, 18)\n >>> afternoon.repeat = interfaces.REPEAT_WORKDAYS\n >>> worktime.append(afternoon)\n\nLet's see how many hours John is working during one day and through the whole 2011.\n\n >>> worktime.hours((datetime(2011, 1, 3), datetime(2011, 1, 4)))\n 8.5\n >>> worktime.hours((datetime(2011, 1, 1), datetime(2012, 1, 1)))\n 2210.0\n\nNow let's say every 4th Friday he takes one day off. Additionally he is on vacation from \nFebruary 21st 2011 till 13th of March.\n\n >>> absence = TimeAware()\n >>> friday = TimeEntry()\n >>> friday.date_start = datetime(2011, 1, 7)\n >>> friday.date_end = datetime(2011, 1, 8)\n >>> friday.repeat = interfaces.REPEAT_4WEEKS\n >>> absence.append(friday)\n >>> vacation = TimeEntry()\n >>> vacation.date_start = datetime(2011, 2, 21)\n >>> vacation.date_end = datetime(2011, 3, 13)\n >>> absence.append(vacation)\n\nLet's see what happens to the hours John is working in 2011 which should result in a reduction\nof the 13 Fridays and 15 days of the vacation where one of them is one of the Fridays already counted.\nWhich leads us to 27 days multiplied by 8.5 hours resulting in 229.5 hours and thus 1980.5 total work\nhours in 2011.\n\n >>> effective_worktime = worktime.subtract(absence, (datetime(2011, 1, 1), datetime(2012, 1, 1)))\n >>> effective_worktime.hours()\n 1980.5\n\nChangelog\n=========\n\n1.0a1 (2012-01-16)\n------------------\n\n* Initial release", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "UNKNOWN", "keywords": "", "license": "GPL", "maintainer": null, "maintainer_email": null, "name": "horae.timeaware", "package_url": "https://pypi.org/project/horae.timeaware/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/horae.timeaware/", "project_urls": { "Download": "UNKNOWN", "Homepage": "UNKNOWN" }, "release_url": "https://pypi.org/project/horae.timeaware/1.0a1/", "requires_dist": null, "requires_python": null, "summary": "Provides basic datetime related tasks used by the Horae resource planning system", "version": "1.0a1" }, "last_serial": 793004, "releases": { "1.0a1": [ { "comment_text": "", "digests": { "md5": "b111335a4175b8ac1a8e531ac62ccb2e", "sha256": "2f3803fe23cbe2ac725a543c54e632b09a25aa44540e681ab36071f434786877" }, "downloads": -1, "filename": "horae.timeaware-1.0a1.tar.gz", "has_sig": false, "md5_digest": "b111335a4175b8ac1a8e531ac62ccb2e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11864, "upload_time": "2012-01-16T12:04:18", "url": "https://files.pythonhosted.org/packages/78/61/9f45919c95b3f1ce7062fd507aaaf3093f3bf49c514db1839b63d7468c23/horae.timeaware-1.0a1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b111335a4175b8ac1a8e531ac62ccb2e", "sha256": "2f3803fe23cbe2ac725a543c54e632b09a25aa44540e681ab36071f434786877" }, "downloads": -1, "filename": "horae.timeaware-1.0a1.tar.gz", "has_sig": false, "md5_digest": "b111335a4175b8ac1a8e531ac62ccb2e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11864, "upload_time": "2012-01-16T12:04:18", "url": "https://files.pythonhosted.org/packages/78/61/9f45919c95b3f1ce7062fd507aaaf3093f3bf49c514db1839b63d7468c23/horae.timeaware-1.0a1.tar.gz" } ] }