{ "info": { "author": "Pythonian", "author_email": "aurelien.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 FORMULA\n=================\n\n# Purpose\n\nThis [tshistory][tshistory] component provides a formula language to\nbuild computed series.\n\nFormulas are defined using a simple lisp-like syntax, using a\npre-defined function library.\n\nFormulas are read-only series (you can't `update` or `replace`\nvalues).\n\nThey also have an history, which is built, time stamps wise, using the\nunion of all constituent time stamps, and value wise, by applying the\nformula.\n\nBecause of this the `staircase` operator is available on formulae.\nSome `staircase` operations can have a very fast implementation if the\nformula obeys commutativity rules.\n\n[tshistory]: https://hg.sr.ht/~pythonian/tshistory\n\n\n# Formula\n\n## General Syntax\n\nFormulas are expressed in a lisp-like syntax using `operators`,\npositional (mandatory) parameters and keyword (optional) parameters.\n\nThe general form is:\n\n `( ... #: ... #: )`\n\nHere are a couple examples:\n\n* `(add (series \"wallonie\") (series \"bruxelles\") (series \"flandres\"))`\n\nHere we see the two fundamental `add` and `series` operators at work.\n\nThis would form a new synthetic series out of three base series (which\ncan be either raw series or formulas themselves).\n\nSome notes:\n\n* operator names can contain dashes or arbitrary caracters\n\n* literal values can be: `3` (integer), `5.2` (float), `\"hello\"`\n (string) and `#t` or `#f` (true ot false)\n\n\n## Pre-defined operators\n\n### *\n\nPerforms a scalar product on a series.\n\nExample: `(* -1 (series \"positive-things\"))`\n\n### +\n\nAdd a constant quantity to a series.\n\nExample: `(+ 42 (series \"i-feel-undervalued\"))`\n\n### /\n\nPerform a scalar division between numbers or a series and a scalar.\n\nExample: `(/ (series \"div-me\") (/ 3 2))`\n\n### add\n\nLinear combination of two or more series. Takes a variable number\nof series as input.\n\nExample: `(add (series \"wallonie\") (series \"bruxelles\") (series \"flandres\"))`\n\nTo specify the behaviour of the `add` operation in the face of missing\ndata, the series can be built with the `fill` keyword. This option is\nonly really applied when several series are combined. By default, if\nan input series has missing values for a given time stamp, the\nresulting series has no value for this timestamp (unless a fill rule\nis provided).\n\n### clip\n\nSet an upper/lower threashold for a series. Takes a series as\npositional parameter and accepts two optional keywords `min` and `max`\nwhich must be numbers (integers or floats).\n\nExample: `(clip (series \"must-be-positive\") #:min 0)`\n\n### date\n\nProduces an utc timestamp from its input string date in iso format.\n\nThe `tz` keyword allows to specify an alternate time zone.\nThe `naive` keyword forces production of a naive timestamp.\nBoth `tz` and `naive` keywords are mutually exlcusive.\n\n### div\n\nElement wise division of two series.\n\nExample: `(div (series \"$-to-\u20ac\") (series \"\u20ac-to-\u00a3\"))`\n\n### min\n\nComputes the row-wise minimum of its input series.\n\nExample: `(min (series \"station0\") (series \"station1\") (series \"station2\"))`\n\n### max\n\nComputes the row-wise maximum of its input series.\n\nExample: `(max (series \"station0\") (series \"station1\") (series \"station2\"))`\n\n### mul\n\nElement wise multiplication of series. Takes a variable number of series\nas input.\n\nExample: `(mul (series \"banana-spot-price ($)\") (series \"$-to-\u20ac\" #:fill 'ffill'))`\n\nThis might convert a series priced in dollars to a series priced in\neuros, using a currency exchange rate series with a forward-fill\noption.\n\n### naive\n\nAllow demoting a series from a tz-aware index (strongly recommended)\nto a tz-naive index (unfortunately sometimes unavoidable for interop\nwith other tz-naive series).\n\nOne must provide a country code and a target timezone.\n\nExample: `(naive (series \"tz-aware-series-from-poland\") \"PL\" \"Europe/Warsaw\")`\n\n### priority\n\nThe priority operator combines its input series as layers. For each\ntimestamp in the union of all series time stamps, the value comes from\nthe first series that provides a value.\n\nExample: `(priority (series \"realized\") (series \"nominated\") (series \"forecasted\"))`\n\nHere `realized` values show up first, and any missing values come from\n`nominated` first and then only from `forecasted`.\n\n### resample\n\nResamples its input series using `freq` and the aggregation method\n`method` (as described in the pandas documentation).\n\nExample: `(resample (series \"hourly\") \"D\")`\n\n### row-mean\n\nThis operator computes the row-wise mean of its input series using the\nseries `weight` option if present. The missing points are handled as\nif the whole series were absent.\n\nExample: `(row-mean (series \"station0\") (series \"station1\" #:weight 2) (series \"station2\"))`\n\nWeights are provided as a keyword to `series`. No weight is\ninterpreted as 1.\n\n### series\n\nThe `series` operator accepts several keywords:\n\n* `fill` to specify a filling policy to avoid `nans` when the series\n will be `add`ed with others; accepted values are `\"ffill\"`\n (forward-fill), `\"bfill\"` (backward-fill) or any floating value.\n\n* `prune` to indicate how many points must be truncated from the tail\n end (useful for priorities).\n\nFor instance in `(add (series \"a\" #:fill 0) (series \"b\")` will make\nsure that series `a`, if shorter than series `b` will get zeroes\ninstead of nans where `b` provides values.\n\nIn `(series \"realized\" #:prune 3)` we would drop the last three points.\n\n### slice\n\nThis allows cutting a series at date points. It takes one positional\nparameter (the series) and two optional keywords `fromdate` and\n`todate` which must be strings in the [iso8601][iso8601] format.\n\nExample: `(slice (series \"cut-me\") #:fromdate \"2018-01-01\")`\n\n[iso8601]: https://en.wikipedia.org/wiki/ISO_8601\n\n### std\n\nComputes the standard deviation over its input series.\n\nExample: `(std (series \"station0\") (series \"station1\") (series \"station2\"))`\n\n### timedelta\n\nTakes a timestamp and a number of years, months, weekds, days,\nhours, minutes (int) and computes a new date according to the asked\ndelta elements.\n\nExample: `(timedelta (date \"2020-1-1\") #:weeks 1 #:hours 2)`\n\n### today\n\nProduces a timezone-aware timestamp as of today\n\nThe `tz` keyword allows to specify an alternate time zone.\nThe `naive` keyword forces production of a naive timestamp.\nBoth `tz` and `naive` keywords are mutually exlcusive.\n\nExample: `(today)`\n\n\n# Registering new operators\n\nThis is a fundamental need. Operators are fixed python functions\nexposed through a lispy syntax. Applications need a variety of fancy\noperators.\n\n## declaring a new operator\n\nOne just needs to decorate a python with the `func` decorator:\n\n```python\n from tshistory_formula.registry import func\n\n @func('identity')\n def identity(series):\n return series\n```\n\nThe operator will be known to the outer world by the name given to\n`@func`, not the python function name (which can be arbitrary).\n\nThis is enough to get a working transformation operator. However\noperators built to construct series rather than just transform\npre-existing series are more complicated.\n\n## custom series operator\n\nWe start with an example, a `shifted` operator that gets a series with shifted\nfrom_value_date/to_value_date boundaries by a constant `delta` amount.\n\nWe would use it like this: `(shifted \"shiftme\" #:days -1)`\n\nAs we can see the standard `series` operator won't work there, that is\napplying a shift operator (`(shift (series \"shiftme\"))`) *after* the\ncall to series is too late. The from/to implicit parameters have\nalready been handled by `series` itself and there is nothing left to\n*shift*.\n\nHence `shifted` must be understood as an alternative to `series` itself.\nHere is a possible implementation:\n\n```python\n from tshistory_formula.registry import func, finder\n\n @func('shifted')\n def shifted(__interpreter__, name, days=0):\n args = __interpreter__.getargs.copy()\n fromdate = args.get('from_value_date')\n todate = args.get('to_value_date')\n if fromdate:\n args['from_value_date'] = fromdate + timedelta(days=days)\n if todate:\n args['to_value_date'] = todate + timedelta(days=days)\n\n return __interpreter__.get(name, args)\n\n @finder('shifted')\n def find_series(cn, tsh, tree):\n return {\n tree[1]: tsh.metadata(cn, tree[1])\n }\n```\n\nAs we can see, we use a new `finder` protocol. But first let's examine\nhow the `shiftme` operator is implemented.\n\nFirst it takes a special `__interpreter__` parameter, which will\nreceive the formula interpreter object, providing access to an\nimportant internal API of the evaluation process.\n\nIndeed from the interpreter we can read the `getargs` attribute, which\ncontains a dictionary of the actual query mapping. We are specially\ninterested in the `from_value_date` and `to_value_date` items in our\nexample, but all the parameters of `tshistory.get` are available\nthere.\n\nOnce we have shifted the from/to value date parameter we again use the\ninterpreter to make a call to `get` which will in turn perform a call\nto the underlying `tshistory.get` (which, we don't know in advance,\nmay yield a primary series or another formula computed series).\n\nImplementing the operator this way, we actually miss two important\npieces of information:\n\n* the system cannot determine a series is _produced_ by the `shifted`\n operator like it can with `series`\n\n* and because of this it cannot know the technical metadata of the\n produced series (e.g. the `tzaware` attribute)\n\nThis is where the `finder` protocol and its decorator function comes\ninto play. For `shifted` we define a finder. It is a function that\ntakes the db connection (`cn`), time series protocol handler (`tsh`)\nand formula syntax tree (`tree`), and must return a mapping from\nseries name to its metadata.\n\nThe tree is an obvious Python data structure representing a use of the\noperator in a formula.\n\nFor instance because of the `shifted` python signature, any use will\nbe like that:\n\n* in lisp `... (shifted \"shift-me\" #:hours +1) ... ` (the dots\n indicate that it can be part of a larger formula)\n\n* tree in python: `['shifted', \"shift-me\", 'hours', 1]`\n\nThe name is always in position 1 in the list. Hence the implementation\nof the shifted *finder*:\n\n```python\n return {\n tree[1]: tsh.metadata(cn, tree[1])\n }\n```\n\nFor the metadata we delegate the computation to the underlying series metadata.\n\nWe might want to provide an ad-hoc metadata dictionary if we had a\nproxy operator that would forward the series from an external source:\n\n```python\n @func('proxy')\n def proxy(\n __interpreter__,\n series_uid: str,\n default_start: date,\n default_end : date) -> pd.Series:\n i = __interpreter__\n args = i.getargs.copy()\n from_value_date = args.get('from_value_date') or default_start\n to_value_date = args.get('to_value_date') or default_end\n\n proxy = ProxyClient()\n return proxy.get(\n series_uid,\n from_value_date,\n to_value_date,\n )\n\n @finder('proxy')\n def proxy(cn, tsh, tree):\n return {\n tree[1]: {\n 'index_type': 'datetime64[ns]',\n 'tzaware': False,\n 'value_type': 'float64'\n }\n }\n```\n\nHere, because we have no other means to know (and the proxy provides\nsome useful documentation), we write the metadata ourselves\nexplicitly.\n\nAlso note how accessing the `__interpreter__` again is used to forward\nthe query arguments.\n\n\n## Editor Infos\n\nThe `tshistory_formula` package provides a custom callback for the\n`editor` capabilities of [tshistory_editor][tshistory_editor].\n\nA dedicated protocol is available to inform the editor on the way\nto decompose/display a formula.\n\nExample of such a function:\n\n```python\n from tshistory_formula.registry import editor_info\n\n @editor_info\n def operator_with_series(builder, expr):\n for subexpr in expr[1:]:\n with builder.series_scope(subexpr):\n builder.buildinfo_expr(subexpr)\n\n```\n\nThe exact ways to use the builder will be provided soon.\n\n[tshistory_editor]: https://hg.sr.ht/~pythonian/tshistory_editor\n\n\n# Series API\n\nA few api calls are added to the `tshistory` base:\n\n* `.register_formula` to define a formula\n\n* `.eval_formula` to evaluate on-the-fly a formula (useful to check\n that it computes before registering it)\n\n## register_formula\n\nExemple:\n\n```python\n tsh.register_formula(\n cn,\n 'my-sweet-formula',\n '(* 3.14 (series \"going-round\"))',\n reject_unkown=True,\n update=True\n )\n```\n\nFirst comes the db connection object, second the formula name, last\nthe actual expression.\n\nThe `reject_unknown` parameter, which is True by default, makes the\nmethod fail if one constituent of the formula does not exist\n(e.g. \"going-round\" is neither a primary series or a formula).\n\nThe `update` parameter tells wether an existing formula can be\noverwritten (False by default).\n\n# eval_formula\n\nExample:\n\n```python\n >>> tsh.eval_formula(cn, '(* 3.14 (series \"going-round\"))')\n ...\n 2020-01-01 3.14\n 2020-01-02 6.28\n 2020-01-03 9.42\n dtype: float64\n```\n\n# Command line\n\nThe `tsh` command carries formula specific subcommands. The output\nbelow shows only the specific formula subcommands:\n\n```shell\n$ tsh\nUsage: tsh [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n ingest-formulas ingest a csv file of formulas Must be a...\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_formula", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "tshistory-formula", "package_url": "https://pypi.org/project/tshistory-formula/", "platform": "", "project_url": "https://pypi.org/project/tshistory-formula/", "project_urls": { "Homepage": "https://hg.sr.ht/~pythonian/tshistory_formula" }, "release_url": "https://pypi.org/project/tshistory-formula/0.10.0/", "requires_dist": [ "tshistory", "psyl", "dash (==1.13.4)", "dash-html-components (==1.0.3)", "decorator" ], "requires_python": "", "summary": "Computed timeseries plugin for `tshistory`", "version": "0.10.0", "yanked": false, "yanked_reason": null }, "last_serial": 12561978, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "ceb778816d56b40d446e21d68bc2229c", "sha256": "ebb6da42627a3b90dcd1ea5c4ce68287a911db6d846c19f875dcf397008d19f0" }, "downloads": -1, "filename": "tshistory_formula-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "ceb778816d56b40d446e21d68bc2229c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12055, "upload_time": "2019-07-16T14:55:41", "upload_time_iso_8601": "2019-07-16T14:55:41.514194Z", "url": "https://files.pythonhosted.org/packages/6b/d4/e748b3e27c3828e03251e0608d2b5cc41872eb6a3b865c4c2add552460db/tshistory_formula-0.1.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.10.0": [ { "comment_text": "", "digests": { "md5": "023c0110d6c0dd5ea7089e51eb74b186", "sha256": "3040bf28edcfd8c793aff89913c21a2d9fe9981bf227b542ae5ee9aa55ce8f0b" }, "downloads": -1, "filename": "tshistory_formula-0.10.0-py3-none-any.whl", "has_sig": false, "md5_digest": "023c0110d6c0dd5ea7089e51eb74b186", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 39109, "upload_time": "2022-01-13T14:30:50", "upload_time_iso_8601": "2022-01-13T14:30:50.070420Z", "url": "https://files.pythonhosted.org/packages/28/2e/3baaf29eb6d79fc8101d1b11ff55d71b008db13b88837c5bffa081817df8/tshistory_formula-0.10.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "53451a2c0a62f01bbed8e03857ddc9e7", "sha256": "ce3e3c90dc559d0667b90fee991385a6315c2f9260ae0539c906ceaf6fd744ef" }, "downloads": -1, "filename": "tshistory_formula-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "53451a2c0a62f01bbed8e03857ddc9e7", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17124, "upload_time": "2019-10-28T09:47:02", "upload_time_iso_8601": "2019-10-28T09:47:02.781750Z", "url": "https://files.pythonhosted.org/packages/01/81/26167b2e80f70897850a7a13570459a57a538d49555a5dbe637e353cf6aa/tshistory_formula-0.4.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "5ff2414360e23a1c555d091bab12d834", "sha256": "999d250429e820b2419d0b55033f2b95db4dbfb54a9f2f7173a6e83028ab6ba5" }, "downloads": -1, "filename": "tshistory_formula-0.5.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5ff2414360e23a1c555d091bab12d834", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 23423, "upload_time": "2019-11-20T15:05:47", "upload_time_iso_8601": "2019-11-20T15:05:47.950931Z", "url": "https://files.pythonhosted.org/packages/a8/84/83941c74b85233edbf0befd96e37001b79a5300bcc6f3b08e7f9e43981fe/tshistory_formula-0.5.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "85a7b441773cfc26d9aeed29658c8a43", "sha256": "c1dddfe8ae5172e40673299602626ac4963646897c1e267046be78460d8b7f98" }, "downloads": -1, "filename": "tshistory_formula-0.6.0-py3-none-any.whl", "has_sig": false, "md5_digest": "85a7b441773cfc26d9aeed29658c8a43", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24155, "upload_time": "2020-02-04T14:15:21", "upload_time_iso_8601": "2020-02-04T14:15:21.494782Z", "url": "https://files.pythonhosted.org/packages/28/2b/043ee5e9f0bc1df022af99b87d8341d227513d9ce862ef523bce4a8453b7/tshistory_formula-0.6.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.7.0": [ { "comment_text": "", "digests": { "md5": "cb142efc5a9e75afbc70cba4a04f9cb9", "sha256": "9c474e3b0d67dfb47c0b6595662a19d883f9153c3e0fe9930ff1623b933bb978" }, "downloads": -1, "filename": "tshistory_formula-0.7.0-py3-none-any.whl", "has_sig": false, "md5_digest": "cb142efc5a9e75afbc70cba4a04f9cb9", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26184, "upload_time": "2020-07-03T13:16:25", "upload_time_iso_8601": "2020-07-03T13:16:25.557284Z", "url": "https://files.pythonhosted.org/packages/03/85/20188cf348067253fbc721ee21d84f0407d4a415e48b5d1fe0226c0a3e5e/tshistory_formula-0.7.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.7.1": [ { "comment_text": "", "digests": { "md5": "4703595e364c06338ef125d1054638ff", "sha256": "de93e288771845c9533ed15f42952fb3b44befce185ec93e0b2271d0ea7cc54f" }, "downloads": -1, "filename": "tshistory_formula-0.7.1-py3-none-any.whl", "has_sig": false, "md5_digest": "4703595e364c06338ef125d1054638ff", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26320, "upload_time": "2020-07-20T09:32:37", "upload_time_iso_8601": "2020-07-20T09:32:37.466233Z", "url": "https://files.pythonhosted.org/packages/a1/21/c14c73b059eb2f4a51843dd63894e0ec66b1c822970a3b62b5aa53290b5d/tshistory_formula-0.7.1-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.7.2": [ { "comment_text": "", "digests": { "md5": "211e1fa0c95681270c1daa48e8142533", "sha256": "a58f2d28af9e62fa01d89af3e044b961a44d4e641140d8f406b90195b1f392fc" }, "downloads": -1, "filename": "tshistory_formula-0.7.2-py3-none-any.whl", "has_sig": false, "md5_digest": "211e1fa0c95681270c1daa48e8142533", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26450, "upload_time": "2020-07-30T14:49:30", "upload_time_iso_8601": "2020-07-30T14:49:30.233889Z", "url": "https://files.pythonhosted.org/packages/df/f2/7cbcefc36e80b08c3ac5b1d8186be1f478a74574352a0b17fa9337b76987/tshistory_formula-0.7.2-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.7.3": [ { "comment_text": "", "digests": { "md5": "bb0d9a82043589fad5154877fb908553", "sha256": "0af0bbb414bb2ee791cbdd8cbd9b87c729d34753f183bb6eec4492a19ff88af4" }, "downloads": -1, "filename": "tshistory_formula-0.7.3-py3-none-any.whl", "has_sig": false, "md5_digest": "bb0d9a82043589fad5154877fb908553", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 27277, "upload_time": "2020-08-17T16:35:13", "upload_time_iso_8601": "2020-08-17T16:35:13.076528Z", "url": "https://files.pythonhosted.org/packages/f6/7d/df86e2a91040964716f6f306a9c84629264377d4b0fded774837e54542d6/tshistory_formula-0.7.3-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.8.0": [ { "comment_text": "", "digests": { "md5": "050846de83196e2a57f78e776648fdd8", "sha256": "2fe5af21b037205a29c225a7124ee8a1025b0131973085da4df001e51f7a2314" }, "downloads": -1, "filename": "tshistory_formula-0.8.0-py3-none-any.whl", "has_sig": false, "md5_digest": "050846de83196e2a57f78e776648fdd8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32274, "upload_time": "2020-12-11T16:04:36", "upload_time_iso_8601": "2020-12-11T16:04:36.382720Z", "url": "https://files.pythonhosted.org/packages/20/50/59b9af4167d0ffbe1fb8905d9632d406bb68527813795b63e2e065585edd/tshistory_formula-0.8.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.8.1": [ { "comment_text": "", "digests": { "md5": "6e65be95aa78c5302965e2c403ee40a9", "sha256": "48104177eecc4a79c0320fae48adff2840db50f65517fe4037666315d2f15e26" }, "downloads": -1, "filename": "tshistory_formula-0.8.1-py3-none-any.whl", "has_sig": false, "md5_digest": "6e65be95aa78c5302965e2c403ee40a9", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32982, "upload_time": "2021-01-19T14:03:20", "upload_time_iso_8601": "2021-01-19T14:03:20.825392Z", "url": "https://files.pythonhosted.org/packages/48/8f/141e672c1b060bd98ed963324b5027fa0cd642dd3f2cfd60efb6d647251a/tshistory_formula-0.8.1-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "0.9.0": [ { "comment_text": "", "digests": { "md5": "694df37946acaa18d2ed5392cb16a162", "sha256": "6edae642bf49345150c15fea4b0de5b3b544cc092dd384672a6635137954116e" }, "downloads": -1, "filename": "tshistory_formula-0.9.0-py3-none-any.whl", "has_sig": false, "md5_digest": "694df37946acaa18d2ed5392cb16a162", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 38472, "upload_time": "2021-08-09T16:15:34", "upload_time_iso_8601": "2021-08-09T16:15:34.198614Z", "url": "https://files.pythonhosted.org/packages/cb/e3/ba094844d0a8901afa64d9ca629846e483650406e5744131a1aa0a23be0d/tshistory_formula-0.9.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "023c0110d6c0dd5ea7089e51eb74b186", "sha256": "3040bf28edcfd8c793aff89913c21a2d9fe9981bf227b542ae5ee9aa55ce8f0b" }, "downloads": -1, "filename": "tshistory_formula-0.10.0-py3-none-any.whl", "has_sig": false, "md5_digest": "023c0110d6c0dd5ea7089e51eb74b186", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 39109, "upload_time": "2022-01-13T14:30:50", "upload_time_iso_8601": "2022-01-13T14:30:50.070420Z", "url": "https://files.pythonhosted.org/packages/28/2e/3baaf29eb6d79fc8101d1b11ff55d71b008db13b88837c5bffa081817df8/tshistory_formula-0.10.0-py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }