{ "info": { "author": "Pythonian", "author_email": "aurelien.campeas@pythonian.fr, arnaud.campeas@pythonian.fr", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Topic :: Database", "Topic :: Scientific/Engineering", "Topic :: Software Development :: Version Control" ], "description": "TSHISTORY\n=========\n\nThis is a library to store/retrieve pandas timeseries to/from a\npostgres database, tracking their successive versions.\n\n# Introduction\n\n## Purpose\n\n`tshistory` is targetted at applications using time series where\n[backtesting][backtesting] and [cross-validation][cross-validation]\nare an essential feature.\n\nIt provides exhaustivity and efficiency of the storage, with a simple\nPython api.\n\nIt can be used as a building block for machine learning, model\noptimization and validation, both for inputs and outputs.\n\n\n## Principles\n\nThere are many ways to represent timeseries in a relational database,\nand `tshistory` provides two things:\n\n* a base python API which abstracts away the underlying storage\n\n* a postgres model, which emphasizes the compact storage of successive\n states of series\n\nThe core idea of tshistory is to handle successive versions of\ntimeseries as they grow in time, allowing to get older states of any\nseries.\n\n\n# Basic usage\n\n## Starting with a fresh database\n\nYou need a postgresql database. You can create one like this:\n\n```shell\n createdb mydb\n```\n\nThen, initialize the `tshistory` tables, like this:\n\n```python\n tsh init-db postgresql://me:password@localhost/mydb\n```\n\nFrom this you're ready to go !\n\n\n## Creating a series\n\nHowever here's a simple example:\n\n```python\n >>> import pandas as pd\n >>> from tshistory.api import timeseries\n >>>\n >>> tsa = timeseries('postgres://me:password@localhost/mydb')\n >>>\n >>> series = pd.Series([1, 2, 3],\n ... pd.date_range(start=pd.Timestamp(2017, 1, 1),\n ... freq='D', periods=3))\n # db insertion\n >>> tsa.update('my_series', series, 'babar@pythonian.fr')\n ...\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 3.0\n Freq: D, Name: my_series, dtype: float64\n\n # note how our integers got turned into floats\n # (there are no provisions to handle integer series as of today)\n\n # retrieval\n >>> tsa.get('my_series')\n ...\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 3.0\n Name: my_series, dtype: float64\n```\n\nNote that we generally adopt the convention to name the time series\napi object `tsa`.\n\n\n## Updating a series\n\nThis is good. Now, let's insert more:\n\n```python\n >>> series = pd.Series([2, 7, 8, 9],\n ... pd.date_range(start=pd.Timestamp(2017, 1, 2),\n ... freq='D', periods=4))\n # db insertion\n >>> tsa.update('my_series', series, 'babar@pythonian.fr')\n ...\n 2017-01-03 7.0\n 2017-01-04 8.0\n 2017-01-05 9.0\n Name: my_series, dtype: float64\n\n # you get back the *new information* you put inside\n # and this is why the `2` doesn't appear (it was already put\n # there in the first step)\n\n # db retrieval\n >>> tsa.get('my_series')\n ...\n2017-01-01 1.0\n2017-01-02 2.0\n2017-01-03 7.0\n2017-01-04 8.0\n2017-01-05 9.0\nName: my_series, dtype: float64\n```\n\nIt is important to note that the third value was *replaced*, and the two\nlast values were just *appended*. As noted the point at `2017-1-2` wasn't a new information so it was\njust ignored.\n\n## Retrieving history\n\nWe can access the whole history (or parts of it) in one call:\n\n```python\n >>> history = tsa.history('my_series')\n ...\n >>>\n >>> for idate, series in history.items(): # it's a dict\n ... print('insertion date:', idate)\n ... print(series)\n ...\n insertion date: 2018-09-26 17:10:36.988920+02:00\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 3.0\n Name: my_series, dtype: float64\n insertion date: 2018-09-26 17:12:54.508252+02:00\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 7.0\n 2017-01-04 8.0\n 2017-01-05 9.0\n Name: my_series, dtype: float64\n```\n\nNote how this shows the full serie state for each insertion date. Also the insertion date is timzeone aware.\n\nSpecific versions of a series can be retrieved individually using the `get` method as follows:\n```python\n >>> tsa.get('my_series', revision_date=pd.Timestamp('2018-09-26 17:11+02:00'))\n ...\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 3.0\n Name: my_series, dtype: float64\n >>>\n >>> tsa.get('my_series', revision_date=pd.Timestamp('2018-09-26 17:14+02:00'))\n ...\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 7.0\n 2017-01-04 8.0\n 2017-01-05 9.0\n Name: my_series, dtype: float64\n```\n\n\nIt is possible to retrieve only the differences between successive insertions:\n\n```python\n >>> diffs = tsa.history('my_series', diffmode=True)\n ...\n >>> for idate, series in diffs.items():\n ... print('insertion date:', idate)\n ... print(series)\n ...\n insertion date: 2018-09-26 17:10:36.988920+02:00\n 2017-01-01 1.0\n 2017-01-02 2.0\n 2017-01-03 3.0\n Name: my_series, dtype: float64\n insertion date: 2018-09-26 17:12:54.508252+02:00\n 2017-01-03 7.0\n 2017-01-04 8.0\n 2017-01-05 9.0\n Name: my_series, dtype: float64\n```\n\nYou can see a series metadata:\n\n```python\n >>> tsa.update_metadata('series', {'foo': 42})\n >>> tsa.metadata('series')\n {foo: 42}\n```\n\n\n## Staircase series\n\nA staircase series can be defined as a series of which values originate from successive\nrevisions with a fixed time span between revision date and value date. This is\nespecially useful for backtesting.\n\n### Basic staircase\n\nLet us take an example assuming a series called `daily_series` has been created with\ninsertions given by the following table (considering row indices are value dates, and\ncolumns indices are insertion dates):\n\n| | 2020-01-01
00:00+00 | 2020-01-02
00:00+00 | 2020-01-03
00:00+00 |\n|-----------:|:----------------------:|:----------------------:|:----------------------:|\n| 2020-01-01 | 1.1 | | |\n| 2020-01-02 | 2.1 | 2.2 | |\n| 2020-01-03 | 3.1 | 3.2 | 3.3 |\n| 2020-01-04 | | 4.2 | 4.3 |\n| 2020-01-05 | | | 5.3 |\n\nSupposing this series is a forecast published on a daily basis, we can for example\nreconstruct the day-ahead forecast series, i.e. the values such that the time span\nbetween revision date and value date is 1 day (or more) as follows:\n```python\n >>> tsa.staircase('daily_series',\n from_value_date=pd.Timestamp('2020-01-01'),\n to_value_date=pd.Timestamp('2020-01-07'),\n delta=pd.Timedelta(days=1))\n ...\n 2020-01-02 2.1\n 2020-01-03 3.2\n 2020-01-04 4.3\n 2020-01-05 5.3\n Name: daily_series, dtype: float64\n```\n\nThe name \"staircase\" refers to the way in which these values are picked from the history:\n\n| | 2020-01-01
00:00+00 | 2020-01-02
00:00+00 | 2020-01-03
00:00+00 |\n|-----------:|:----------------------:|:----------------------:|:----------------------:|\n| 2020-01-01 | | | |\n| 2020-01-02 | **2.1** | | |\n| 2020-01-03 | | **3.2** | |\n| 2020-01-04 | | | **4.3** |\n| 2020-01-05 | | | **5.3** |\n\n\nNow if instead we consider an hourly forecast series, we may want to define day-ahead\nforecast as a staircase series with a daily revision occurring at 9am, and link each\nrevision to the 24 hours of the next day. More generally we may want to reconstruct a\nstaircase series where successive revisions each relate to several value dates. Such\ncases should instead be handled using the `block_staircase` method described below.\n\n### Block staircase\n\nLet us take another example considering the series `hourly_series` with following insertions:\n\n| | 2020-01-01
06:00+00 | 2020-01-01
14:00+00 | 2020-01-02
06:00+00 | 2020-01-02
14:00+00 |\n|--------------------:|:----------------------:|:----------------------:|:----------------------:|:----------------------:|\n| 2020-01-01 00:00+00 | 1.1 | 1.2 | | |\n| 2020-01-01 08:00+00 | 2.1 | 2.2 | | |\n| 2020-01-01 16:00+00 | 3.1 | 3.2 | | |\n| 2020-01-02 00:00+00 | 4.1 | 4.2 | 4.3 | 4.4 |\n| 2020-01-02 08:00+00 | 5.1 | 5.2 | 5.3 | 5.4 |\n| 2020-01-02 16:00+00 | 6.1 | 6.2 | 6.3 | 6.4 |\n| 2020-01-03 00:00+00 | 7.1 | 7.2 | 7.3 | 7.4 |\n| 2020-01-03 08:00+00 | 8.1 | 8.2 | 8.3 | 8.4 |\n| 2020-01-03 16:00+00 | 9.1 | 9.2 | 9.3 | 9.4 |\n| 2020-01-04 00:00+00 | | | 10.3 | 10.4 |\n| 2020-01-04 08:00+00 | | | 11.3 | 11.4 |\n| 2020-01-04 16:00+00 | | | 12.3 | 12.4 |\n\nThen the day-ahead forecast with revisions at 9am can be computed as follows:\n```python\n >>> tsa.block_staircase('hourly_series',\n from_value_date=pd.Timestamp('2020-01-01', tz=\"utc\"),\n to_value_date=pd.Timestamp('2020-01-05', tz=\"utc\"),\n revision_freq={'days': 1},\n revision_time={'hour': 9},\n revision_tz='utc',\n maturity_offset={'days': 1},\n maturity_time={'hour': 0})\n ...\n 2020-01-02 00:00:00+00:00 4.1\n 2020-01-02 08:00:00+00:00 5.1\n 2020-01-02 16:00:00+00:00 6.1\n 2020-01-03 00:00:00+00:00 7.3 \n 2020-01-03 08:00:00+00:00 8.3 \n 2020-01-03 16:00:00+00:00 9.3 \n 2020-01-04 00:00:00+00:00 10.3\n 2020-01-04 08:00:00+00:00 11.3\n 2020-01-04 16:00:00+00:00 12.3\n Name: hourly_series, dtype: float64\n```\n\nNote that with `revision_time={'hour': 9}`, the method ends up picking values from the\ntwo 6am insertions. Taking revision time after 14:00, say `revision_time={'hour': 20}`,\nwould instead select values from the 2pm insertions.\n\nIn general, the arguments of `block_staircase` should be used as follows:\n* `from_value_date` and `to_value_date`: time range on which values are retrieved\n* `revision_freq`: revision frequency, as a dictionary of integers of which keys must be taken from\n`[\"years\", \"months\", \"weeks\", \"bdays\", \"days\", \"hours\", \"minutes\", \"seconds\"]`\n* `revision_time`: revision time, as a dictionary of integers of which keys should be\ntaken from `[\"year\", \"month\", \"day\", \"weekday\", \"hour\", \"minute\", \"second\"]`. It is used\nfor revision date initialisation. The next revision dates are then obtained by\nsuccessively adding `revision_freq`.\n* `revision_tz`: time zone in which revision date and time are expressed\n* `maturity_offset`: time span between each revision date and start time\nof related block of values, as dictionary of integers. Its keys must be taken from\n`[\"years\", \"months\", \"weeks\", \"bdays\", \"days\", \"hours\", \"minutes\", \"seconds\"]`. No lag\nis considered if it is not specified, i.e. the revision date is the block start date\n* `maturity_time`: start time of each block, as a dictionary of integers of which keys\nshould be taken from `[\"year\", \"month\", \"day\", \"hour\", \"minute\", \"second\"]`. The start\ndate of each block is thus obtained by adding `maturity_offset` to revision date and\nthen applying `maturity_time`. If not specified block start date is just the revision\ndate shifted by `maturity_offset`\n\n### Other use cases\n\nThe `block_staircase` method covers multiple use cases, such as week-ahead revisions or\nrevision by business day, as described in the following examples.\n\n#### Week-ahead staircase\n\nConsider a series named `weekly_series` with following insertions:\n\n| | 2021-01-05
(Tue) | 2021-01-07
(Thu) | 2021-01-12
(Tue) | 2021-01-14
(Thu) |\n|:-----------------|:-------------------:|:-------------------:|:-------------------:|:-------------------:|\n| 2021-01-11 (Mon) | 1.1 | 1.2 | | |\n| 2021-01-12 (Tue) | 2.1 | 2.2 | | |\n| 2021-01-13 (Wed) | 3.1 | 3.2 | | |\n| 2021-01-14 (Thu) | 4.1 | 4.2 | | |\n| 2021-01-15 (Fri) | 5.1 | 5.2 | | |\n| 2021-01-16 (Sat) | 6.1 | 6.2 | | |\n| 2021-01-17 (Sun) | 7.1 | 7.2 | | |\n| 2021-01-18 (Mon) | 8.1 | 8.2 | 8.3 | 8.4 |\n| 2021-01-19 (Tue) | 9.1 | 9.2 | 9.3 | 9.4 |\n| 2021-01-20 (Wed) | 10.1 | 10.2 | 10.3 | 10.4 |\n| 2021-01-21 (Thu) | | | 11.3 | 11.4 |\n| 2021-01-22 (Fri) | | | 12.3 | 12.4 |\n| 2021-01-23 (Sat) | | | 13.3 | 13.4 |\n| 2021-01-24 (Sun) | | | 14.3 | 14.4 |\n| 2021-01-25 (Mon) | | | 15.3 | 15.4 |\n| 2021-01-26 (Tue) | | | 16.3 | 16.4 |\n| 2021-01-27 (Wed) | | | 17.3 | 17.4 |\n\nThen the week-ahead staircase with weekly revision on Friday can be retrieved as\nfollows:\n```python\n >>> tsa.block_staircase('weekly_series',\n from_value_date=pd.Timestamp('2021-01-10'),\n to_value_date=pd.Timestamp('2021-01-30'),\n revision_freq={'days': 7},\n revision_time={'weekday': 4},\n revision_tz='utc',\n maturity_offset={'days': 3},\n maturity_time={'hour': 0})\n ...\n 2021-01-11 1.2\n 2021-01-12 2.2\n 2021-01-13 3.2\n 2021-01-14 4.2\n 2021-01-15 5.2\n 2021-01-16 6.2\n 2021-01-17 7.2\n 2021-01-18 8.4\n 2021-01-19 9.4\n 2021-01-20 10.4\n 2021-01-21 11.4\n 2021-01-22 12.4\n 2021-01-23 13.4\n 2021-01-24 14.4\n 2021-01-25 15.4\n 2021-01-26 16.4\n 2021-01-27 17.4\n Name: weekly_series, dtype: float64\n```\n\nIt is also possible to retrieve a month-ahead staircase series taking instead\n`revision_freq={'months': 1}` and, for example, `revision_time={'day': 15}` to perform\nmonthly revision every 15th day of the month.\n\n#### Revision by business day\n\nThe block_staircase method allows to express revision frequency and/or maturity time\nspan in business days. Consider a series named `business_day_series` with these\ninsertions:\n\n| | 2021-01-13
(Wed) | 2021-01-14
(Thu) | 2021-01-15
(Fri) | 2021-01-16
(Sat) | 2021-01-17
(Sun) | 2021-01-18
(Mon) |\n|:-----------------|:-------------------:|:-------------------:|:-------------------:|:-------------------:|:-------------------:|:-------------------:|\n| 2021-01-13 (Wed) | 3.1 | | | | | |\n| 2021-01-14 (Thu) | 4.1 | 4.2 | | | | |\n| 2021-01-15 (Fri) | 5.1 | 5.2 | 5.3 | | | |\n| 2021-01-16 (Sat) | 6.1 | 6.2 | 6.3 | 6.4 | | |\n| 2021-01-17 (Sun) | | 7.2 | 7.3 | 7.4 | 7.5 | |\n| 2021-01-18 (Mon) | | | 8.3 | 8.4 | 8.5 | 9.6 |\n| 2021-01-19 (Tue) | | | | 9.4 | 9.5 | 11.6 |\n| 2021-01-20 (Wed) | | | | | 10.5 | 12.6 |\n| 2021-01-21 (Thu) | | | | | | 13.6 |\n\nThen we can retrieve a business-day-ahead staircase series with revision every business\nday as follows:\n```python\n >>> tsa.block_staircase('business_day_series',\n from_value_date=pd.Timestamp('2021-01-13'),\n to_value_date=pd.Timestamp('2021-01-21'),\n revision_freq={'bdays': 1},\n revision_tz='utc',\n maturity_offset={'bdays': 1})\n ...\n 2021-01-14 4.1\n 2021-01-15 5.2\n 2021-01-16 6.2\n 2021-01-17 7.2\n 2021-01-18 8.3\n 2021-01-19 11.6\n 2021-01-20 12.6\n 2021-01-21 13.6\n Name: weekly_series, dtype: float64\n```\n\n# The API object\n\nIn the few examples above we manipulate the time series through an\nobject that talks directly to the postgresql back end.\n\nIt is possible to also talk to a rest api using the same api, like\nshown below and proceed exactly like in the above code examples:\n\n```python\n >>> from tshistory.api import timeseries\n >>>\n >>> tsa = timeseries('http://my.timeseries.info/api')\n```\n\n## Using an HTTP/REST end point\n\nFor the rest api, you need to build a small [flask][flask] app like\nthis (in an `app.py` module):\n\n```python\nfrom flask import Flask\n\nfrom tshistory.api import timeseries\nfrom tshistory.http.server import blueprint as blueprint\n\n\ndef make_app(dburi):\n app = Flask('my-timeseries-app')\n app.register_blueprint(\n blueprint(timeseries(dburi)),\n url_prefix='/api'\n )\n return app\n```\n\nThen, you can start it in development mode like this:\n\n```python\napp = make_app('postgresql://me:password@localhost/mydb')\napp.run('192.168.1.1', 8080)\n```\n\nor just leave it to a wsgi container in e.g. a `wsgi.py` module:\n\n```python\nfrom my_series_app.app import make_app\n\napp = make_app('postgresql://me:password@localhost/mydb')\n```\n\n## API surface\n\nFor now we only provide a list of supported methods.\n\n\n### Information access (read methods)\n\n* catalog\n\n* exists\n\n* get\n\n* history\n\n* interval\n\n* metadata\n\n* staircase\n\n* block_staircase\n\n* type\n\n\n### Information update (write methods)\n\n* update\n\n* update_metadata\n\n* replace\n\n* rename\n\n* delete\n\n\n# Command line\n\n## Basic operations\n\nA command line tool is provided, called `tsh`. It provides its usage\nguidelines:\n\n```shell\n $ tsh\n Usage: tsh [OPTIONS] COMMAND [ARGS]...\n\n Options:\n --help Show this message and exit.\n\nCommands:\n check coherence checks of the db\n get show a serie in its current state\n history show a serie full history\n info show global statistics of the repository\n init-db initialize an new db.\n log show revision history of entire repository or...\n view visualize time series through the web\n```\n\n`Info` provides an overview of the time series repository (number of\ncommitted changes, number and series and their names).\n\n```shell\n $ tsh info postgres://babar:babarpassword@dataserver:5432/banana_studies\n changeset count: 209\n series count: 144\n series names: banana_spot_price, banana_trades, banana_turnover\n```\n\n`Log` provides the full history of editions to time series in the\nrepository.\n\n```shell\n $ tsh log postgres://babar:babar@dataserver:5432/banana_studies --limit 3\n revision: 206\n author: BABAR\n date: 2017-06-06 15:32:51.502507\n series: banana_spot_price\n\n revision: 207\n author: BABAR\n date: 2017-06-06 15:32:51.676507\n series: banana_trades\n\n revision: 209\n author: CELESTE\n date: 2017-06-06 15:32:51.977507\n series: banana_turnover\n```\n\nAll options of all commands can be obtained by using the `--help`\nswitch:\n\n```shell\n $ tsh log --help\n Usage: tsh log [OPTIONS] DB_URI\n\n Options:\n -l, --limit TEXT\n --show-diff\n -s, --serie TEXT\n --from-rev TEXT\n --to-rev TEXT\n --help Show this message and exit.\n```\n\n\n## Extensions\n\nIt is possible to augment the `tsh` command with new subcommands (or\naugment, modify existing commands).\n\nAny program doing so must define a new command and declare a setup\ntools entry point named `tshistory:subcommand` as in e.g.:\n\n```python\n\n entry_points={'tshistory.subcommands': [\n 'view=tsview.command:view'\n ]}\n```\n\nFor instance, the [tsview][tsview] python package provides such a\n`view` subcommand for generic time series visualisation.\n\n[tsview]: https://hg.sr.ht/~pythonian/tsview\n[backtesting]: https://en.wikipedia.org/wiki/Backtesting\n[cross-validation]: https://en.wikipedia.org/wiki/Cross-validation_(statistics)\n[flask]: https://www.palletsprojects.com/p/flask/\n\n\n# Status\n\nIt is currently considered `beta` software even though it has been in\nproduction for two years. It is still evolving. Schema/Database\nchanges come with migration procedure using the `tsh` utility.\n\nWhen it is good: if you do mostly appends (and occasional edits in the\npast) it will store data in a very compact way.\n\nReading any version of the series will always be the fastest (io-bound)\noperation.\n\nAlternative backend storage and storage strategies will be considered\nin the future.\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://hg.sr.ht/~pythonian/tshistory", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "tshistory", "package_url": "https://pypi.org/project/tshistory/", "platform": "", "project_url": "https://pypi.org/project/tshistory/", "project_urls": { "Homepage": "https://hg.sr.ht/~pythonian/tshistory" }, "release_url": "https://pypi.org/project/tshistory/0.14.1/", "requires_dist": [ "pandas (<1.2,>=1.0.5)", "dateutils", "sqlalchemy", "sqlhelp", "click", "mock", "inireader", "pytz", "colorama", "tqdm", "flask (<2.0)", "flask-restx", "jinja2 (<3.0)", "requests" ], "requires_python": "", "summary": "Timeseries store with version control", "version": "0.14.1", "yanked": false, "yanked_reason": null }, "last_serial": 12597537, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "f0553bee423f1b83bc542350e7300fa8", "sha256": "bf84232a53a9f005ae16b1e47a79c421ea7fcc4c5b050fc67bce77e7a86b5d3d" }, "downloads": -1, "filename": "tshistory-0.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "f0553bee423f1b83bc542350e7300fa8", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13527, "upload_time": "2017-11-09T14:29:31", "upload_time_iso_8601": "2017-11-09T14:29:31.766267Z", "url": "https://files.pythonhosted.org/packages/08/d8/46bfc71719129b445259e2b49573749b1a7af74abf3cf4a3bd34f8e2ba26/tshistory-0.1.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "0a14878b83b8ff3e771ba372b541066d", "sha256": "1cb6a7a9aaf7b810e61ccf705fba7859c98764d46538e6470ab774fece4f46df" }, "downloads": -1, "filename": "tshistory-0.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "0a14878b83b8ff3e771ba372b541066d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13598, "upload_time": "2017-11-17T16:39:16", "upload_time_iso_8601": "2017-11-17T16:39:16.023197Z", "url": "https://files.pythonhosted.org/packages/7d/85/e5107801d9b27801e5e841f9dda0a0b049c44e953d4e4b95f1160a755bde/tshistory-0.1.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.10.0": [ { "comment_text": "", "digests": { "md5": "46698e29bc2e02f3d4e6893a0dfa7165", "sha256": "6dbeb770d4ed6ac998fc30c730e053a2dbb630a8f58ef27b0b9394b89f5ad738" }, "downloads": -1, "filename": "tshistory-0.10.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "46698e29bc2e02f3d4e6893a0dfa7165", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 26072, "upload_time": "2019-11-20T14:59:21", "upload_time_iso_8601": "2019-11-20T14:59:21.215376Z", "url": "https://files.pythonhosted.org/packages/94/53/f5a66e5cb24b85fabf730734e3fec5ec3b2ddfca5e63f543eac632231815/tshistory-0.10.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.11.0": [ { "comment_text": "", "digests": { "md5": "248be4b0e1da30d1041701accf3c1d92", "sha256": "863541f18c0a79dae84d88fffde0eb2f4503c3957ced390480aacba26045b522" }, "downloads": -1, "filename": "tshistory-0.11.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "248be4b0e1da30d1041701accf3c1d92", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 26715, "upload_time": "2020-02-04T14:09:02", "upload_time_iso_8601": "2020-02-04T14:09:02.926781Z", "url": "https://files.pythonhosted.org/packages/49/75/e09cbda5f1025ceaf61ab690b5ba929de5a7375e409f1c8badc5062b5bd9/tshistory-0.11.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.12.0": [ { "comment_text": "", "digests": { "md5": "e872500c18cfedda9e5734cbe7f67042", "sha256": "e6fa1056ef1e85adc83235cdba334f0add9b0efaffdd0e122554bd548234c714" }, "downloads": -1, "filename": "tshistory-0.12.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e872500c18cfedda9e5734cbe7f67042", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 29685, "upload_time": "2020-07-03T13:08:56", "upload_time_iso_8601": "2020-07-03T13:08:56.397003Z", "url": "https://files.pythonhosted.org/packages/d1/6c/44da96bbd7c252b09341c47d527aa40c08fccce41514d74d771e9b30d617/tshistory-0.12.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.12.1": [ { "comment_text": "", "digests": { "md5": "ff98e5cc89af66c1f1cc03443a699baf", "sha256": "ccf5005d5c51ae919a052257244dd56c9956cf4fc497ee9864ae032ded24b709" }, "downloads": -1, "filename": "tshistory-0.12.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ff98e5cc89af66c1f1cc03443a699baf", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 29856, "upload_time": "2020-07-30T14:45:59", "upload_time_iso_8601": "2020-07-30T14:45:59.399237Z", "url": "https://files.pythonhosted.org/packages/03/87/74de7b246469ad896d7bc363e4a063a48b449c5a084a9ac67c0694738f5d/tshistory-0.12.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.12.2": [ { "comment_text": "", "digests": { "md5": "b281c721c54eb16d2921f6a3cd2d74f0", "sha256": "f484d61f372e37949bbc7d77571eaba8746afa482dc94f54ab508773bddb78e9" }, "downloads": -1, "filename": "tshistory-0.12.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "b281c721c54eb16d2921f6a3cd2d74f0", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 29587, "upload_time": "2020-09-03T16:58:30", "upload_time_iso_8601": "2020-09-03T16:58:30.107190Z", "url": "https://files.pythonhosted.org/packages/07/9d/51f975377968c98ab26565e8912a8e0ea8dc72c03f53080b83e2d1babdb9/tshistory-0.12.2-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.12.3": [ { "comment_text": "", "digests": { "md5": "5c7fa1d5de716b4d10cfaf7925c57372", "sha256": "2e8ddb17f7ac7198d2a99225e644c01773f0f6e3f5c467ed75e926c9782b0d97" }, "downloads": -1, "filename": "tshistory-0.12.3-py3-none-any.whl", "has_sig": false, "md5_digest": "5c7fa1d5de716b4d10cfaf7925c57372", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 29938, "upload_time": "2020-12-11T15:36:09", "upload_time_iso_8601": "2020-12-11T15:36:09.758340Z", "url": "https://files.pythonhosted.org/packages/90/75/e42233914298309e96c1c5fe4fb9015fc6a922edb35eccc589bc125118b7/tshistory-0.12.3-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.13.0": [ { "comment_text": "", "digests": { "md5": "24c7a45556fbef412fcab56ca5679bb6", "sha256": "bf1135fee1c558ad7f580d0833ac49d3a4745c4248c6825b80cd40dbd4231418" }, "downloads": -1, "filename": "tshistory-0.13.0-py3-none-any.whl", "has_sig": false, "md5_digest": "24c7a45556fbef412fcab56ca5679bb6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32842, "upload_time": "2021-08-09T16:16:03", "upload_time_iso_8601": "2021-08-09T16:16:03.145379Z", "url": "https://files.pythonhosted.org/packages/4c/ac/303720e8aa46854cc5f08630ef573edc23687a65b3cd075123da236f2a02/tshistory-0.13.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.14.0": [ { "comment_text": "", "digests": { "md5": "07ed20f2f32644f7c976dced9f3b715e", "sha256": "14888703e563142991ff699d163d6ccf3d7155a322163109888b752395b8d51c" }, "downloads": -1, "filename": "tshistory-0.14.0-py3-none-any.whl", "has_sig": false, "md5_digest": "07ed20f2f32644f7c976dced9f3b715e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 49414, "upload_time": "2022-01-13T14:05:58", "upload_time_iso_8601": "2022-01-13T14:05:58.716253Z", "url": "https://files.pythonhosted.org/packages/cb/79/ca6ba57747279bd585ae5c208324abca09c1ccbc28d431ff2a3916275a97/tshistory-0.14.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.14.1": [ { "comment_text": "", "digests": { "md5": "fba0032cd0b64ff8240e355e2c8ace7b", "sha256": "4135b27f9c49f685c0963cc0770b83bbbe3e3db00bae93f67c35c3eed9c0c1e6" }, "downloads": -1, "filename": "tshistory-0.14.1-py3-none-any.whl", "has_sig": false, "md5_digest": "fba0032cd0b64ff8240e355e2c8ace7b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 56815, "upload_time": "2022-01-17T14:51:21", "upload_time_iso_8601": "2022-01-17T14:51:21.817938Z", "url": "https://files.pythonhosted.org/packages/18/3e/03b12b1d4eed452ed827a658c8fee9bcb08cfd4afece515a6bad3c919899/tshistory-0.14.1-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "15347710a2ffc6825f71c2cfaec514da", "sha256": "c80b2b7c0be6b4f8f77aacac5536836aca2a163dfa139d0464abbb3659e7b95b" }, "downloads": -1, "filename": "tshistory-0.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "15347710a2ffc6825f71c2cfaec514da", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 14484, "upload_time": "2018-03-09T11:34:50", "upload_time_iso_8601": "2018-03-09T11:34:50.897595Z", "url": "https://files.pythonhosted.org/packages/eb/c2/e8f2d3147e5e49e20d5621f45225a52499830338440ab7572afdd40c38d6/tshistory-0.2.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "e2e2915f19ee9c07b342e45afc44d843", "sha256": "6e2a146a5a665c56fb20cc88d873f059be6ae94d5eedecd425260b0b107ac920" }, "downloads": -1, "filename": "tshistory-0.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e2e2915f19ee9c07b342e45afc44d843", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21641, "upload_time": "2018-10-05T10:36:27", "upload_time_iso_8601": "2018-10-05T10:36:27.622050Z", "url": "https://files.pythonhosted.org/packages/bf/57/1def61a8cd595e030393b2e1b94dd3dae0e505c87d5a358099e330699808/tshistory-0.4.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "5316ebe23c4d75241c84326377b04de5", "sha256": "9d02ebc8d3d9168b341ba4238144bd51191f519e9a2fe0a72cc32657decf2312" }, "downloads": -1, "filename": "tshistory-0.5.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5316ebe23c4d75241c84326377b04de5", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 22655, "upload_time": "2018-11-16T16:52:44", "upload_time_iso_8601": "2018-11-16T16:52:44.380755Z", "url": "https://files.pythonhosted.org/packages/64/20/7b71a04a084c9dce1e422f48179a53b3f2f0ba0ce109333ad054bc4cdccf/tshistory-0.5.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "7772398abaa6f51b79845701ba8e3c2e", "sha256": "333e5a899af5c91014662581c95879724efc4700907968c8b2a4abbe4b4f74f4" }, "downloads": -1, "filename": "tshistory-0.6.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "7772398abaa6f51b79845701ba8e3c2e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 22782, "upload_time": "2019-01-11T16:54:00", "upload_time_iso_8601": "2019-01-11T16:54:00.755849Z", "url": "https://files.pythonhosted.org/packages/87/cc/125fb94f04cc62e0955e852b56eaa3895aac6386b90ee273d74698255402/tshistory-0.6.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.7.0": [ { "comment_text": "", "digests": { "md5": "9fbca660ed4ab792c4e6b9260a6973aa", "sha256": "d63dd2dbd98be781dba92cb03fbca56002a5b2ff2eb507c7b1fd29a5d270a3b9" }, "downloads": -1, "filename": "tshistory-0.7.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9fbca660ed4ab792c4e6b9260a6973aa", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 20722, "upload_time": "2019-07-19T11:48:59", "upload_time_iso_8601": "2019-07-19T11:48:59.690845Z", "url": "https://files.pythonhosted.org/packages/e8/24/a8665f2049982c6be5ec2bfd67271db15ed90ef2e68d6e00511c3eddef67/tshistory-0.7.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.8.0": [ { "comment_text": "", "digests": { "md5": "de139422cb0b459d4a26e2e7efb59958", "sha256": "f0c66e2f8b865ae76ea151d7ca1d29b83b65d88235a4bd2aaadd3fbb6893d219" }, "downloads": -1, "filename": "tshistory-0.8.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "de139422cb0b459d4a26e2e7efb59958", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 24056, "upload_time": "2019-09-04T16:54:05", "upload_time_iso_8601": "2019-09-04T16:54:05.446338Z", "url": "https://files.pythonhosted.org/packages/d2/81/17709ce596d3a450638bb5990e1bf277e566524773d82a3f5d7e18eb1671/tshistory-0.8.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.9.0": [ { "comment_text": "", "digests": { "md5": "ce3dde17c46e4b43b75edc28bb54d234", "sha256": "a9130f16991b1f50a36a4a97856a12032d0d7d3fdc5955154b0b079ea5e625a7" }, "downloads": -1, "filename": "tshistory-0.9.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ce3dde17c46e4b43b75edc28bb54d234", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 25815, "upload_time": "2019-10-28T09:28:20", "upload_time_iso_8601": "2019-10-28T09:28:20.565908Z", "url": "https://files.pythonhosted.org/packages/f6/52/5dbdba25a38b165fae2597d7dc10722e754539685881c3af8e605fee0c2c/tshistory-0.9.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "fba0032cd0b64ff8240e355e2c8ace7b", "sha256": "4135b27f9c49f685c0963cc0770b83bbbe3e3db00bae93f67c35c3eed9c0c1e6" }, "downloads": -1, "filename": "tshistory-0.14.1-py3-none-any.whl", "has_sig": false, "md5_digest": "fba0032cd0b64ff8240e355e2c8ace7b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 56815, "upload_time": "2022-01-17T14:51:21", "upload_time_iso_8601": "2022-01-17T14:51:21.817938Z", "url": "https://files.pythonhosted.org/packages/18/3e/03b12b1d4eed452ed827a658c8fee9bcb08cfd4afece515a6bad3c919899/tshistory-0.14.1-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }