{ "info": { "author": "Abhinav Upadhyay", "author_email": "abhinav.updadhyay@agilitix.ai", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# Python client for Apptuit.AI\n\n[![Build Status](https://www.travis-ci.org/ApptuitAI/apptuit-py.svg?branch=master)](https://www.travis-ci.org/ApptuitAI/apptuit-py)\n[![codecov](https://codecov.io/gh/ApptuitAI/apptuit-py/branch/master/graph/badge.svg)](https://codecov.io/gh/ApptuitAI/apptuit-py)\n[![PyPI](https://img.shields.io/pypi/v/apptuit.svg)](https://pypi.org/project/apptuit/)\n[![Pyversions](https://img.shields.io/pypi/pyversions/apptuit.svg?style=flat)](https://pypi.org/project/apptuit/)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\n\n\n## Installation\n\n```\npip install apptuit --upgrade\n```\n\n## Dependencies\n\n**Requirements**\n - `requests`, `pyformance` - installed automatically if you use `pip` to install apptuit\n - `pandas` - not installed by default, you should install it manually if you intend to use the `query` API and\n create dataframes using the `to_df()` method (see [Querying for Data](#querying-for-data))\n\n## Usage\n\n### Contents\n - [Introduction](#introduction)\n * [Working with Apptuit Client](#working-with-apptuit-client)\n * [Working with Apptuit Pyformance Reporter](#working-with-apptuit-pyformance-reporter)\n * [Configuration](#configuration)\n - [Sending Data](#sending-data)\n * [Sending data using Apptuit pyformance reporter](#sending-data-using-apptuit-pyformance-reporter)\n * [Error Handling in ApptuitReporter](#error-handling-in-apptuitreporter)\n * [Sending Tags/Metadata](#tagsmetadata)\n * [About Host tag](#about-host-tag)\n * [Restrictions on Tags](#restrictions-on-tags-and-metric-names)\n * [Meta Metrics](#meta-metrics)\n * [Python Process Metrics](#python-process-metrics)\n - [Sending Data using `send()` API](#sending-data-using-send-api)\n - [Sending Data using `send_timeseries()` API](#sending-data-using-send_timeseries-api)\n - [Querying for Data](#querying-for-data)\n\n### Introduction\nThis package provides functionality to send timeseries data to Apptuit and also to query it.\nThere are two main components \n- The Apptuit client - provides core functionality to query and send data\n- Apptuit pyformance reporter - provides a high level abstraction on top of the client\nto make it easy for you to report metrics from your applications to Apptuit.\nIt is based on Coda Hale's metrics design and provides primitives like\n`meter`, `gauge`, `counter` to accumulate and report data.\nIt uses [Pyformance](https://github.com/omergertel/pyformance/) underneath.\n\n#### Working with Apptuit Client:\nThe Apptuit client object can be created as simply as the following line:\n```python\nfrom apptuit import Apptuit\nclient = Apptuit(token=my_apptuit_token,\n global_tags={\"service\": \"order-service\"},\n sanitize_mode=\"prometheus\")\n```\n- `token`: should be your apptuit token\n- `global_tags`: should be the set of default tags you want to apply on all your data. It is an optional parameter\n- `sanitize_mode`: Is a string value which specifies the sanitization mode to be used\nfor metric names and tag keys. \nYou can set `sanitize_mode` to three values:\n - `None`: disables sanitization.\n - `apptuit`: set the sanitize mode to apptuit, which will replace\n all the invalid characters with `_`. Valid characters in this mode are all\n ASCII letters, digits, `/`, `-`, `.`, `_` and Unicode letters.\n Anyhing else is invalid character.\n - `prometheus`: set the sanitize mode to prometheus, which will replace\n all the invalid characters with `_`. Valid characters in this mode are ASCII letters, digits\n and `_`, anything else is considered invalid.\n\n\nApart from these, the Apptuit constructor takes a couple of more optional parameters explained below:\n\n- `api_endpoint`: This should be the http endpoint for calling Apptuit apis. Normally you don't need to specify this and the default value is set to `https://api.apptuit.ai`.\n- `ignore_environ_tags`: This is False by default. It tells the client whether to look up for\nthe global tags in environment variables or not. Global tags are tags which are applied to all the\ndatapoints sent through the client. We will have more to say on this in the configuration section.\n\nThe client provides two methods, `query` and `send`, which are described in the\n[Querying for Data](#querying-for-data) and\n[Sending data using send()](#sending-data-using-send-api) sections respectively.\n\n#### Working with Apptuit Pyformance Reporter\nThe apptuit pyformance reporter is an abstraction based on Code Hale's metrics. It provides\nhigh level primitives to accumulate data in the form of metrics such as `meter`, `timer`,\n`gauge` etc. and send to Apptuit. These things are described in more\ndetail in the [reporter section](#sending-data-using-apptuit-pyformance-reporter),\nhere we will see how to create a reporter and various parameters it supports.\n```python\nfrom apptuit.pyformance import ApptuitReporter\nfrom pyformance import MetricsRegistry\n\nreporter_tags = {\"service\": \"order-service\"}\nregistry = MetricsRegistry()\nreporter = ApptuitReporter(token=\"my_apptuit_token\",\n registry=registry,\n reporting_interval=60,\n tags=reporter_tags,\n collect_process_metrics=True,\n sanitize_mode=\"prometheus\")\n\n```\nHere:\n- `token`: Is your Apptuit token\n- `registry`: Is an instance of MetricsRegistry (explained more in\n[reporter section](#sending-data-using-apptuit-pyformance-reporter))\n- `reporting_interval`: Number of seconds to wait before reporing again\n- `tags`: A dictionary of tag keys and values.\nThese tags apply to all the metrics reported through this reporter.\n- `collect_process_metrics`: Is a boolean value which will enable or disable collection \nof various metrics related to the Python process (CPU, memory, GC, and threads). By default\nit is disabled, set this parameter to `True` to enable it.\n- `sanitize_mode`: This is same as the `sanitize_mode` parameter for the\nclient (see above in client usage example).\n\n\n#### Configuration\nAs we saw above, we need to pass the token and global tags as parameter to the \nApptuit client when instantiating it. Alternatively we can set these as\nenvironment variables, so that we don't need to hard-code them in our code.\nThese environment variables are described below.\n\n* `APPTUIT_API_TOKEN`: If the Apptuit client and the ApptuitReporter are not passed a token parameter they look for the token in this variable. If this variable is also not set, the client will raise\n`ApptuitException` to indicate about the missing token\n* `APPTUIT_TAGS`: This is an alternative for the `global_tags` parameter for the Apptuit client. If the Apptuit client does not receive a value for `global_tags` parameter it checks this environment variable. Both the `global_tags` parameter\nand `APPTUIT_TAGS` environment variable are strictly optional. If present, the Apptuit client adds those tags to every\npoint it is sending.\n\nThe format of the value of this variable is as follows:\n\n```sh\nexport APPTUIT_TAGS=\"tag_key1: tag_val1, tag_key2: tag_val2, tag_key3: tag_val3\"\n```\nThe spaces after the comma and colon are optional.\n\nThe `APPTUIT_TAGS` variable is also read by the `ApptuitReporter`, which combines them with its reporter tags.\nIn case of a conflict of same tag keys in both sets of tags, the reporter tag take preference.\n\n**Note**: Support for these variable was added in the version `1.0.0` of apptuit-py and is not available\nin any of the earlier released versions.\n\n### Sending data\n\nThere are two ways of sending the data to Apptuit. First is to use the `ApptuitReporter`, and\nthe second options is to use the `send()` method of the Apptuit client.\nWe will show how to use both of the options below.\n\n### Sending data using Apptuit pyformance reporter\n\n```python\nimport socket\nfrom pyformance import MetricsRegistry\nfrom apptuit.pyformance.apptuit_reporter import ApptuitReporter\n\nclass OrderService:\n def __init__(self, apptuit_token):\n self.registry = MetricsRegistry()\n self.init_reporter(apptuit_token, self.registry)\n\n def init_reporter(self, token, registry):\n hostname = socket.gethostname()\n global_tags = {\"host\": hostname, \"env\": \"dev\", \"service\": \"order-service\"}\n self.reporter = ApptuitReporter(registry=registry,\n reporting_interval=60, # data reported every 1 minute\n token=token,\n tags=global_tags,\n retry=2 #this will retry in case of 500 response or connection errors occur.\n )\n # reporter.start() will start reporting the data asynchronously based on the reporting_interval set.\n self.reporter.start()\n\n def handle_order(self, order):\n order_counter = self.registry.counter(\"order_count\")\n # order handling related code\n order_counter.inc()\n\n def shutdown(self):\n # you can stop the reporter when you no longer wish to send data or when shutting down\n self.reporter.stop()\n\n```\n\nOne thing worth pointing out in the above example:\n\n- In `handle_order` we create a new counter `order_counter` with the metric name `order_count`. The first\ntime this method is called a new counter object will be created and registered with the registry. For\nsubsequent calls, that counter will get reused since internally the registry will already have a\ncounter with that name.\n\n####\n\n**MetricsRegistry**\n\nMetricsRegistry is the container for all the metrics in our application. We can use it to register and create\nvarious kinds of metrics (meter, gauge, counter etc.). For example:\n\n```python\nfrom pyformance import MetricsRegistry\n\nregistry = MetricsRegistry()\ncounter = registry.counter(\"order_count\")\nmeter = registry.meter(\"order_requests_rate\")\ntimer = registry.timer(\"order_requests_processing_time\")\n```\n\nNow, let's take a look at the different types of metrics and how to use them.\n\n**Meter**\n\nA meter measures the the rate of events, such as requests per second. Meter maintains the mean rate, and\n1-, 5-, 15- minute moving averages.\n\n```python\nfrom pyformance import MetricsRegistry\n\nregistry = MetricsRegistry()\nmetric_name = \"order_requests_rate\"\nrequests_meter = registry.meter(metric_name)\n\ndef handle_request(request):\n requests_meter.mark()\n # handle request\n\n```\n\n**Gauge**\n\nA gauge is an instantaneous measurement of a value. For example, number of pending jobs in a queue.\n\n```python\nfrom queue import Queue\nfrom pyformance import MetricsRegistry\nfrom pyformance.meters.gauge import CallbackGauge\n\nclass QueueManager:\n\n def __init__(self, registry, name):\n self.q = Queue()\n jobs_metric = registry.add(name, CallbackGauge(self.get_queue_size))\n\n def get_queue_size(self):\n return self.q.size()\n```\n\nThe reporter will call the `get_queue_size` function at its scheduled frequency and report\nthe size of the queue.\n\n**Counter**\n\nA counter can be used to simply count some data. It provides two methods `inc()` to increment\nits value and `dec()` to decrement it.\n\n```python\n\nfrom pyformance import MetricsRegistry\n\nregistry = MetricsRegistry()\njobs_counter = registry.counter('pending_jobs')\n\ndef add_job(self, job):\n jobs_counter.inc(1)\n self.q.put(job)\n\ndef take_job(self):\n jobs_counter.dec(1)\n self.q.get()\n```\n\n**Timer**\n\nA timer aggregates timing durations and provides duration statistics, as well as throughput statistics.\n\n```python\nfrom pyformance import MetricsRegistry\n\nregistry = MetricsRegistry()\ntimer = registry.timer(\"response_time\")\n\ndef handle_request(request):\n with timer.time():\n return \"OK\"\n\n```\nThe above example will use the timer to report the time taken to serve each request.\n\n**Histogram**\n\nA histogram measures the statistical distribution of values in a stream of data. It provides aggregate data\nsuch as the min, max, mean, sum, and count.\n\n```python\n\nfrom pyformance import MetricsRegistry\n\nregistry = MetricsRegistry()\n\nresponse_sizes = registry.histogram('response_size')\n\ndef handle_request(request):\n response = do_query(request) # process the query\n response_sizes.add(response.size())\n\n```\n#### Error Handling in ApptuitReporter\nThe ApptuitReporter sends data asynchronously (unless we are explicitly using it in synchronous mode\nby not calling the `start()` method). In asynchronous\nmode it is very difficult to know if the reporter is working properly or not. To make this easier the\n`ApptuitReporter` takes an `error_handler` argument. `error_handler` is expected to be a function reference\nwhich takes 4 arguments. The signature of the function and the arguments are explained below:\n\n```python\n def error_handler(status_code, successful_points_count, failed_points_count, errors):\n pass\n```\n- `status_code`: The HTTP status code of the POST API call to Apptuit\n- `successful_points_count`: Number of points successfully processed\n- `failed_points_count`: Number of points which could not be processed due to errors\n- `errors`: List of error messages describing the reason of failure of each of the failed points\n\nBy default, the `ApptuitReporter` registers a `default_error_handler`, which writes the errors to `stderr`.\nTo override that you can pass your own error handler implementation, or if you don't wish to do anything for errors\nyou can pass `None` for the `error_handler` argument.\n\n**Reporter with default error handler**\n```python\nimport logging\n#reporter with default error handler (writes to stderr)\nreporter = ApptuitReporter(token=my_apptuit_token,\n registry=registry,\n reporting_interval=60,\n tags=reporter_tags)\n```\n**Reporter with No error handler**\n```python\nreporter_with_no_error_handler = ApptuitReporter(\n token=my_apptuit_token,\n registry=registry,\n reporting_interval=60,\n tags=reporter_tags,\n error_handler=None\n )\n```\n\nThe error handler function by definition takes only four arguments.\nIf you wish to pass extra arguments to the error handler you can use\nclosures or partial functions to get around the limitation.\n\n**Passing extra argument using Partial**\n\n```python\nimport logging\nfrom functools import partial\n\ndef custom_error_handler(logger, status_code, successful, failed, errors):\n logger.error(\"ApptuitReporter failed to send %d points, due to errors: %s\" % (failed, str(errors)))\n\nlogger = logging.getLogger(\"logger key\")\napptuit_custom_error_handler = partial(custom_error_handler, logger)\nreporter = ApptuitReporter(\n token=my_apptuit_token,\n registry=registry,\n reporting_interval=60,\n tags=reporter_tags,\n error_handler=apptuit_custom_error_handler\n )\n```\n**Passing extra argument using closure**\n```python\n...\nimport logging\nfrom apptuit import ApptuitSendException\nfrom apptuit.pyformance.apptuit_reporter import ApptuitReporter\n...\n\nclass OrderService:\n def __init__(self, apptuit_token):\n ...\n self.logger = logging.getLogger(\"OrderService\")\n ...\n\n def init_reporter(self, token, registry):\n ...\n def apptuit_error_handler(status_code, successful, failed, errors):\n logger = self.logger\n logger.error(str(ApptuitSendException(\n status_code, successful, failed, errors\n )))\n\n self.reporter = ApptuitReporter(...,\n error_handler=apptuit_error_handler)\n ...\n\n```\n\n#### Tags/Metadata\n\nWhen creating the ApptuitReporter, you can provide a set of tags (referred as reporter tags from now on)\nwhich will be part of all the metrics reported by that reporter. However, in order to provide tags\nspecific to each metric you need to provide them when registering the metric with the registry. For example:\n\n```python\nfrom apptuit import timeseries\nfrom pyformance import MetricsRegistry\n\nregistry = MetricsRegistry()\nmetric_name = \"node_cpu\"\ntags = {\"type\": \"idle\", \"host\": \"node-foo\", \"service\": \"order-service\"}\nmetric = timeseries.encode_metric(metric_name, tags)\nmeter = registry.meter(metric)\n```\nHere we provided the metric specific tags by calling `timeseries.encode_metric` and\nproviding the metric name and the tags as parameters. When registering the metric we provide this\nencoded name to the registry instead of the plain metric name.\n\nTo decode an encoded metric name use the `decode_metric()` function from `timeseries` module.\n```python\n\nfrom apptuit import timeseries\n\nencoded_metric = timeseries.encode_metric(\"node.cpu\", {\"type\": \"idle\"})\nmetric_name, tags = timeseries.decode_metric(encoded_metric)\n```\n\nA *recommended practise* is to maintain a local cache of the created metrics and reuse them, rather than\ncreating them every time:\n\n```python\nimport socket\nimport time\nfrom apptuit import timeseries\nfrom apptuit.pyformance import ApptuitReporter\nfrom pyformance import MetricsRegistry\n\nclass OrderService:\n\n def __init__(self, apptuit_token):\n self.registry = MetricsRegistry()\n self.init_reporter(apptuit_token, self.registry)\n self.order_counters = {}\n\n def init_reporter(self, token, registry):\n hostname = socket.gethostname()\n global_tags = {\"host\": hostname, \"env\": \"dev\", \"service\": \"order-service\"}\n self.reporter = ApptuitReporter(registry=registry,\n reporting_interval=60, # data reported every 1 minute\n token=token,\n tags=global_tags)\n # reporter.start() will start reporting the data asynchronously based on the reporting_interval set.\n self.reporter.start()\n\n def get_order_counter(self, city_code):\n # We have counters for every city code\n if city_code not in self.order_counters:\n tags = {\"city-code\": city_code}\n metric = timeseries.encode_metric(\"order_count\", tags=tags)\n self.order_counters[city_code] = self.registry.counter(metric)\n return self.order_counters[city_code]\n\n def handle_order(self, order):\n order_counter = self.get_order_counter(order.city_code)\n order_counter.inc()\n self.process_order(order)\n\n def shutdown(self):\n # you can stop the reporter when you no longer wish to send data or when shutting down\n self.reporter.stop()\n\n def process_order(self, order):\n time.sleep(5)\n\n```\n\nHere we have a method `get_order_counter` which takes the `city_code` as a parameter. There\nis a local cache of counters keyed by the encoded metric names. This avoids the unnecessary overhead\nof encoding the metric name and tags every time, if we already have created a counter for that city.\nIt also ensures that we will report separate time-series for order-counts of different city codes.\n\n#### About Host Tag\nThe reporter will add a `host` tag key with host name as its value (obtained by calling `socket.gethostname()`).\nThis is helpful in order to group the metrics by host if the reporter is being run on multiple servers. The value\nof the `host` tag key can be overridden by passing our own `host` tag in the `tags` parameter to the reporter or\nby setting a `host` tag in the global environment variable for tags\n\nIf we don't wish for the `host` tag to be set by default we can disable it by setting the\n`disable_host_tag` parameter of the reporter to `True`. Alternatively we can set the environment\nvariable `APPTUIT_DISABLE_HOST_TAG` to `True` to disable it.\n\n#### Restrictions on Tags and Metric names\n- **Allowed characters in tag keys and metric names** - Tag keys and metric names can have any unicode \netters (as defined by unicode specification) and the following special characters: `.`, `-`, `_`, `/`.\nHowever, if we are looking to follow Prometheus compliant naming\n([see specification])(https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels)\nwe should restrict them to ASCII letters, digits and underscores only and it must match the\nregex `[a-zA-Z_][a-zA-Z0-9_]*`. No such restriction is applicable on tag values.\n- **Maximum number of tags** - Apptuit currently allows upto 25 tag key-value pairs per datapoint\n\n#### Meta Metrics\nThe `ApptuitReporter` also reports a set of meta metrics which can be a useful indicator if the reporter is \nworking as expected or not, as well as to get a sense of how many points are being sent and the latency of\nthe Apptuit API. These meta metrics are described below.\n\n- `apptuit_reporter_send_total` - Total number of points sent\n- `apptuit_reporter_send_successful` - Number of points which were succssfully processed\n- `apptuit_reporter_send_failed` - Number of points which failed\n- `apptuit_reporter_send_time` - Timing stats of of the send API\n\n#### Python Process Metrics\nThe `ApptutiReporter` can also be configured to report various metrics of\nthe Python process it is running in. By default it is disabled but we can enable it by\npassing setting the parameter `collect_process_metrics` to `True` when creating the\nreporter object. The reporter will collect metrics related to the system resource usage\nby the process (cpu, memory, IPC etc.) as well as metrics related to garbage collection\nand threads. The complete list of all the metrics collected is provided below:\n- `python_cpu_time_used_seconds` - Total time spent by the process in user mode and system mode.\n- `python_memory_usage_bytes` - Total amount of memory used by the process.\n- `python_page_faults` - Total number of page faults received by the process.\n- *`python_process_swaps` - Total number of times the process was swapped-out of the main memory.\n- `python_block_operations` - Total number of block input and output operations.\n- `python_ipc_messages` - Total number of inter-process messages sent and received by the process. \n- *`python_system_signals` - Total number of signals received by the process.\n- `python_context_switches` - Total number of context switches of the process.\n- `python_thread` - Count of active, demon and dummy threads.\n- `python_gc_collection` - Count of objects collected in gc for each generation. \n- `python_gc_threshold` - Value of garbage collector threshold for each generation.\n\n**Note** - Metrics marked with `*` are zero on Linux because it does not support them\n\n#### Global tags, reporter tags and metric tags\n\nWhen using the reporter we have three sets of tags, it's better to clarify a few things about them.\n\n- `ApptuitReporter` takes a set of tags as parameter. It adds these tags to all the metrics it is reporting.\n- If the environment variable `APPTUIT_TAGS` is set, the reporter takes those into account as well, however\nthe tags passed to it take preference in case of a conflict because of common tag keys.\n- Each metric being reported by the reporter might also have some tags attached, in case of a conflict\nbecause of common tag keys, the metric tags take preference over reporter or global tags.\n\n#### Sending data using send() API\n\nApart from using the Pyformance reporter, you can also use the low level `send()` API from the apptuit\nclient to directly send the data. If you want tags while sending you can use the global_tags\nparameter of Apptuit class. If global_tags are set then environmental tags will not be used.\n\n```python\nfrom apptuit import Apptuit, DataPoint\nimport time\nimport random\nimport socket\n\ntoken = \"mytoken\"\nclient = Apptuit(token=token)\nmetrics = [\"proc.cpu.percent\", \"node.memory.bytes\", \"network.send.bytes\", \"network.receive.bytes\", \"node.load.avg\"]\ntags = {\"host\": socket.gethostname()}\ncurtime = int(time.time())\ndps = []\nwhile True:\n curtime = int(time.time())\n for metric in metrics:\n dps.append(DataPoint(metric, tags, curtime, random.random()))\n if len(dps) == 100:\n client.send(dps, \n retry_count=3 #this will retry in case of 500 response or connection errors occur.\n )\n dps = []\n time.sleep(60)\n```\n\n#### Sending data using send_timeseries() API\nThe `send` API works with a list of DataPoint objects. Creating each DataPoint object involves validating the metric name and\nthe tags. If we are creating thousands of DataPoint objects with the metric name and tags, it can quickly get very expensive.\nIn order to avoid that overhead, there is an alternative `send_timeseries` API as well, which accepts a list of `TimeSeries`\nobjects. This is much more convenient as we need to create a `TimeSeries` object with a metric name and tags. Thereafter\nwe can add points to that timeseries object by calling its `add_point()` method. This avoids creation of DataPoint objects\nand the overhead of the tag validation.\n\nFollowing is an example from a scraper we run internally. We call an HTTP API, get a JSON response and send it to us in the\nform of timeseries. In order to avoid too many API calls to Apptuit we call `send_timeseries` whenever we have accumulated\n50000 or more points. Once we make a `send_timeseries` call we reset the `series_list` object to contain just the latest\n`TimeSeries` object (all the earlier series would have been sent to Apptuit).\n\n```python\nfrom apptuit import Apptuit, TimeSeries\n\nseries_list = []\npoints_count = 0\ntoken = \"mytoken\"\nclient = Apptuit(token=token)\nresponse_data = make_request()\nfor result in response_data[\"results\"]:\n metric_name = result[\"metric\"]\n tags = result[\"tags\"]\n series = TimeSeries(metric_name, tags)\n series_list.append(series)\n for timestamp, value in result[\"values\"]:\n series.add_point(timestamp, value)\n points_count += 1\n if points_count >= 50000:\n client.send_timeseries(series_list)\n points_count = 0\n series_list = [TimeSeries(metric_name, tags)]\nif points_count > 0:\n client.send_timeseries(series_list)\n```\n\n### Querying for data\n\n```python\n\nfrom apptuit import Apptuit\nimport time\ntoken = 'my_token' # replace with your Apptuit token\napptuit = Apptuit(token=token)\nstart_time = int(time.time()) - 3600 # let's query for data going back 1 hour from now\nquery_res = apptuit.query(\"fetch('proc.cpu.percent').downsample('1m', 'avg')\", start=start_time\n retry_count=3 #this will retry in case of 500 response or connection errors occur.\n )\n# we can create a Pandas dataframe from the result object by calling to_df()\ndf = query_res[0].to_df()\n# Another way of creating the DF is accessing by the metric name in the query\nanother_df = query_res['proc.cpu.percent'].to_df()\n```\nIt should be noted that using the `to_df()` method requires that you have `pandas` installed.\nWe don't install `pandas` by default as part of the requirements because not every user of the library\nwould want to query or create dataframes (many users just use the `send` API or the reporter functionality)\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://github.com/ApptuitAI/apptuit-py", "keywords": "", "license": "Apache License 2.0", "maintainer": "", "maintainer_email": "", "name": "apptuit", "package_url": "https://pypi.org/project/apptuit/", "platform": "", "project_url": "https://pypi.org/project/apptuit/", "project_urls": { "Homepage": "https://github.com/ApptuitAI/apptuit-py" }, "release_url": "https://pypi.org/project/apptuit/2.4.0/", "requires_dist": [ "requests", "pyformance" ], "requires_python": "", "summary": "Apptuit Python Client", "version": "2.4.0" }, "last_serial": 5994128, "releases": { "0.2.0": [ { "comment_text": "", "digests": { "md5": "c0c47c9395c2830339830c669773b317", "sha256": "32cb8716039833489a2bd2e5d6efb29a89e144274225fda2afe4ddd161a49a04" }, "downloads": -1, "filename": "apptuit-0.2.0-py2-none-any.whl", "has_sig": false, "md5_digest": "c0c47c9395c2830339830c669773b317", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 5420, "upload_time": "2018-09-17T12:31:17", "url": "https://files.pythonhosted.org/packages/26/3b/d62fba909b2bc20cfbb7f33e336329b9e18165c76fd2042e8aac18304598/apptuit-0.2.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "130f607dec64c802941ed82b061dda4f", "sha256": "60cc4412bbcf98ac7cf470f9b4eb9ecb37803699cd48d880ea99f251a29d83fc" }, "downloads": -1, "filename": "apptuit-0.2.0.tar.gz", "has_sig": false, "md5_digest": "130f607dec64c802941ed82b061dda4f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5085, "upload_time": "2018-09-17T12:31:18", "url": "https://files.pythonhosted.org/packages/ab/5b/df9efa562082ebaae41df751660b5d933c20fc4ce38716d4013060506d8e/apptuit-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "c0bb92bccf8f898706537d22e396c226", "sha256": "6eedf7dec15234eba2e2bcc56fc299bc279156df0025f0a3f05b3e1298e49acb" }, "downloads": -1, "filename": "apptuit-0.2.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c0bb92bccf8f898706537d22e396c226", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 9544, "upload_time": "2018-09-17T13:04:52", "url": "https://files.pythonhosted.org/packages/20/f6/d0f39a4c622f2044e44bb932b0ce596f5aa3ee54a6b97b9406bacdf75935/apptuit-0.2.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "588006033021740114451935e41a8524", "sha256": "e321a29d74f3a58b8b4a9d87c3e7db99a3e7eaa63f1afc3c2ed0fb21ca8e7621" }, "downloads": -1, "filename": "apptuit-0.2.1.tar.gz", "has_sig": false, "md5_digest": "588006033021740114451935e41a8524", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5085, "upload_time": "2018-09-17T13:04:54", "url": "https://files.pythonhosted.org/packages/2f/0a/313d9fcc3c64bacd3eff10fee9e7dba85131d2f17143ed0f983c019e930f/apptuit-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "16d98979fb9bf1b489b375d4dd0359bb", "sha256": "3a660f3774d2dd49a9a147e0ce7c2f33aea29e68b8f628eac526f189efebd1fc" }, "downloads": -1, "filename": "apptuit-0.2.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "16d98979fb9bf1b489b375d4dd0359bb", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 9538, "upload_time": "2018-09-18T04:11:46", "url": "https://files.pythonhosted.org/packages/98/55/c6420f2ba61b1bb5c1997bba4970f6cb0b1c324ef10d500449052ffc72d2/apptuit-0.2.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2b83e6584660fdc61da5d0315bdd58f6", "sha256": "d0e92fef321154a8fcaa5ed019b720fe48ae787fda8ecf8b7274b0dee6628b98" }, "downloads": -1, "filename": "apptuit-0.2.2.tar.gz", "has_sig": false, "md5_digest": "2b83e6584660fdc61da5d0315bdd58f6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5092, "upload_time": "2018-09-18T04:11:47", "url": "https://files.pythonhosted.org/packages/31/f4/270bfb9c4ffa700ca176b5e6022bd68e2d6d92ad70be090f1184e0fe40c8/apptuit-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "798722ba2043e5e465f78453440988c4", "sha256": "459d5af2e3d4e2275fd7c19b5c4ed5a097064668d46b476e012e317e48663c2e" }, "downloads": -1, "filename": "apptuit-0.2.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "798722ba2043e5e465f78453440988c4", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13765, "upload_time": "2018-09-24T04:44:57", "url": "https://files.pythonhosted.org/packages/9f/6d/2473074c7798d1bda26969edc9a0ebe617d7251efee5122a3f90582da0aa/apptuit-0.2.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0f9488657c2a6fe1ce6422a54e6525ad", "sha256": "f7579689e49094a3ea96563bfc96f89bf07524af5c4310e448ec04e50f5c0ebb" }, "downloads": -1, "filename": "apptuit-0.2.3.tar.gz", "has_sig": false, "md5_digest": "0f9488657c2a6fe1ce6422a54e6525ad", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9182, "upload_time": "2018-09-24T04:44:58", "url": "https://files.pythonhosted.org/packages/ba/be/5615192c61579459fcb36168b2412b44fef62056ad0b8150e8a77eb3bb9a/apptuit-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "3316b65385845a59c274eece48986abf", "sha256": "dc19712c3caaade94689c5fb571e386077968c839548d18d24322b409b5d2eb9" }, "downloads": -1, "filename": "apptuit-0.2.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3316b65385845a59c274eece48986abf", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13719, "upload_time": "2018-10-30T12:15:08", "url": "https://files.pythonhosted.org/packages/dc/29/89412823517997b0aff2645cee7808e8f1259a7547389c30b4d44ade42a2/apptuit-0.2.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b119ffec54b0dc47a52c0901d3ac4001", "sha256": "10187d5eb99dcfa4bec004749ce5c2b1d037758eecb5aa87b377f005cd404c79" }, "downloads": -1, "filename": "apptuit-0.2.4.tar.gz", "has_sig": false, "md5_digest": "b119ffec54b0dc47a52c0901d3ac4001", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9217, "upload_time": "2018-10-30T12:15:10", "url": "https://files.pythonhosted.org/packages/15/1c/14f56f6a8f3b0f82db3164c87bd65ffec775fd3d961cf5ffd85d19d90c58/apptuit-0.2.4.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "14e0e03fb3e447b7436a7145d912a8b0", "sha256": "294d120dc4b5e15b7f2dfc654c9ee7beb6bcb1e8a94fd1ef3d411933ea83684d" }, "downloads": -1, "filename": "apptuit-0.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "14e0e03fb3e447b7436a7145d912a8b0", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 17946, "upload_time": "2018-12-10T08:31:50", "url": "https://files.pythonhosted.org/packages/61/ab/e50b8d832cb6ae511723d4c22cd99f056282dfccc7616e433157b2f69499/apptuit-0.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "88c9bd3bf78b95cbde35dc57d1fe4bb9", "sha256": "62d5e36bb5b2fdf329c55bbe3bc9ec4788af6cb488d20b59c8d47e4b52f22797" }, "downloads": -1, "filename": "apptuit-0.3.0.tar.gz", "has_sig": false, "md5_digest": "88c9bd3bf78b95cbde35dc57d1fe4bb9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16226, "upload_time": "2018-12-10T08:31:52", "url": "https://files.pythonhosted.org/packages/c0/90/1ec9a5981da8e1d28a8ac0273f78134d89b3ffd0a6cf93b5855b7aed21c6/apptuit-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "ccd06d2bc27f2a4d3e16a9964d26b32d", "sha256": "6242cb1d7b78cc6526749bf328a6074dc0053aa1d345f3f7e6c4e061e2aeec98" }, "downloads": -1, "filename": "apptuit-0.3.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ccd06d2bc27f2a4d3e16a9964d26b32d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 19243, "upload_time": "2018-12-13T12:32:49", "url": "https://files.pythonhosted.org/packages/b6/1f/7ba118ceea96fce4b2ce44663114b6b949e29b449d0d26bd3e15f4319c17/apptuit-0.3.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e974d6570f5301035708c2f72f745011", "sha256": "332a396849dc14a9558a86dc96eed60b43c7a4047b56c827e68e934f69aa278a" }, "downloads": -1, "filename": "apptuit-0.3.1.tar.gz", "has_sig": false, "md5_digest": "e974d6570f5301035708c2f72f745011", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17001, "upload_time": "2018-12-13T12:32:50", "url": "https://files.pythonhosted.org/packages/ae/83/cea3316f420f3fdaaad06e52bb827559a0b35d93100a14b472e9cfd4794c/apptuit-0.3.1.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "5f7f18d9ca8b7d87c35d650bade7a682", "sha256": "d7e527a8d654d52f57f89dfab7008a10e28d96d418f74a4e52c33f097231d1b2" }, "downloads": -1, "filename": "apptuit-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5f7f18d9ca8b7d87c35d650bade7a682", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 24204, "upload_time": "2019-01-03T06:42:23", "url": "https://files.pythonhosted.org/packages/e4/92/ac1a0841cdd6871ba9611d0f8075390db33bc9f04e59ee4914d6199e3802/apptuit-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "804fbfcf74ba65461243fa27f2602000", "sha256": "5114b988f359c44b6c8ad917927a61361492518e8f673639d104fb66c37d94b9" }, "downloads": -1, "filename": "apptuit-1.0.0.tar.gz", "has_sig": false, "md5_digest": "804fbfcf74ba65461243fa27f2602000", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23799, "upload_time": "2019-01-03T06:42:25", "url": "https://files.pythonhosted.org/packages/0c/03/22b334d8fe51c0d09a84ccf9cabb98ea48064b2b90d7cb4d02a1fbc3a81c/apptuit-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "d9854c7bf23e313f2afe356d42191b90", "sha256": "6af4605d2daff82ab2035a00b63cb31f6f11773dbce4b2293afff22565bac596" }, "downloads": -1, "filename": "apptuit-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d9854c7bf23e313f2afe356d42191b90", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 24239, "upload_time": "2019-01-04T11:16:43", "url": "https://files.pythonhosted.org/packages/82/3a/4a958cc53808a4ff9c0383d124a10181031aefacf20863214cb4fbf9ecef/apptuit-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6d2702593dd7cc9188dffefaee12130d", "sha256": "1064fbaae846dc78e8f4bf1375aa5863874fcb573355467407c9b2169c1088a3" }, "downloads": -1, "filename": "apptuit-1.1.0.tar.gz", "has_sig": false, "md5_digest": "6d2702593dd7cc9188dffefaee12130d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23877, "upload_time": "2019-01-04T11:16:45", "url": "https://files.pythonhosted.org/packages/7d/04/7c206f06f7c09fad11456cee69c2ef14bc9b609075e64a5ad9714fd3bc5d/apptuit-1.1.0.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "c0a72b7e7244f3e31fb4e4e1218ead0b", "sha256": "b02e85efc13f77e9a2484ac5123e0350fc15bddec79d4df3b972e799d42b9fea" }, "downloads": -1, "filename": "apptuit-1.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c0a72b7e7244f3e31fb4e4e1218ead0b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 25020, "upload_time": "2019-01-16T10:15:00", "url": "https://files.pythonhosted.org/packages/9e/5f/6068151cc80a8572521fe85fb4258329aa8b2c3995322495e0ceb33fef2e/apptuit-1.2.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "117690db7a580925d14a5e61877056f1", "sha256": "9d254ddf6efb1b28f6e307a33174330dce9f9bce99625204a98bfb4e05900202" }, "downloads": -1, "filename": "apptuit-1.2.0.tar.gz", "has_sig": false, "md5_digest": "117690db7a580925d14a5e61877056f1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24354, "upload_time": "2019-01-16T10:15:02", "url": "https://files.pythonhosted.org/packages/02/ce/c58504e67b6ef9cc363d6a26c833421e5f08fe586173f7bbb9d4d61aa0a6/apptuit-1.2.0.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "88dc2271b4d4d3d7278c83cc9551f000", "sha256": "0d4cad71f60fa64de06f3c2eb93d3d2df6e0e4bdb7f14fcc7163cbbf12aa4573" }, "downloads": -1, "filename": "apptuit-1.2.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "88dc2271b4d4d3d7278c83cc9551f000", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 25596, "upload_time": "2019-01-17T04:44:57", "url": "https://files.pythonhosted.org/packages/47/6b/bc7bd0abbb518bc89c8528f9b48f5770a6a84659e6c8f7ec852c0e3c0c01/apptuit-1.2.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9cbea0ffe1d1eeb2da22829f313ba020", "sha256": "7ff48ab1bd19cf032565d817822c776342185864e32a93f9d048819732ffd1da" }, "downloads": -1, "filename": "apptuit-1.2.1.tar.gz", "has_sig": false, "md5_digest": "9cbea0ffe1d1eeb2da22829f313ba020", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25509, "upload_time": "2019-01-17T04:45:00", "url": "https://files.pythonhosted.org/packages/b6/62/6dd58025532cb2b7a29cc1ccc5f41e2ae17347eb92b49d29e14f8c0f5fd0/apptuit-1.2.1.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "60a79422d6469599de6112465f650db1", "sha256": "0869001e9744d02beb165c7b8ea4bed1fbea1d3c1675d049738afdf56cd45202" }, "downloads": -1, "filename": "apptuit-1.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "60a79422d6469599de6112465f650db1", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 26061, "upload_time": "2019-01-30T08:38:34", "url": "https://files.pythonhosted.org/packages/36/9d/3ce290991a4bf2c023147b92bc4f87c2227143ce8ffc99a713c6c2d2b491/apptuit-1.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9fc5f3ae3eac7bdfb4b55fa3de64e5e6", "sha256": "58ad95945afe598ab8c92538c00c209d7a1e0a5f24a4c225e9757ce25e7ea219" }, "downloads": -1, "filename": "apptuit-1.3.0.tar.gz", "has_sig": false, "md5_digest": "9fc5f3ae3eac7bdfb4b55fa3de64e5e6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26256, "upload_time": "2019-01-30T08:38:35", "url": "https://files.pythonhosted.org/packages/1b/22/18ff98882071e88f36aa59c4bdb35fbf0cf737eae7fdf424e2e948bddb46/apptuit-1.3.0.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "5588505cb8ae0cf54029f33d120e375a", "sha256": "2ccef377df22952231a34e4604074c15cf73cbd8500dc2146c867e8042662da8" }, "downloads": -1, "filename": "apptuit-1.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5588505cb8ae0cf54029f33d120e375a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 26852, "upload_time": "2019-02-14T08:12:00", "url": "https://files.pythonhosted.org/packages/7d/59/d2e2fa517d1adaf5c005a93676e22e9e8b2b39f9026c9e0ed64563788036/apptuit-1.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b2619c042b208e4928384d2e8072114c", "sha256": "05e89eee94bb699bdfe676101a37e0c59f56cbf144d3f3bed7ba15b3fafb4fde" }, "downloads": -1, "filename": "apptuit-1.4.0.tar.gz", "has_sig": false, "md5_digest": "b2619c042b208e4928384d2e8072114c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27140, "upload_time": "2019-02-14T08:12:02", "url": "https://files.pythonhosted.org/packages/42/4f/af7d7f92b77d993dc0a4a5bb59f842b3188ac1448ac1298bea58ee5713e6/apptuit-1.4.0.tar.gz" } ], "2.0.0": [ { "comment_text": "", "digests": { "md5": "63b37cccae3f322c97276b138465bbcf", "sha256": "77d36f8ca50fbd04ee491f17f1f4889af090a54087017d9e603be3bfe790a11c" }, "downloads": -1, "filename": "apptuit-2.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "63b37cccae3f322c97276b138465bbcf", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 33595, "upload_time": "2019-03-29T07:34:10", "url": "https://files.pythonhosted.org/packages/44/87/0e34475e0a51082767c36fa99b4aced1f96f3f2be624f532804f9d79ff0f/apptuit-2.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "153d320f47c74b80f0c9e536a2702241", "sha256": "baff7401e9d0ff4cd5a644a16d9f5e9b9122286b7ef89faa621ce2e0d7b52fbe" }, "downloads": -1, "filename": "apptuit-2.0.0.tar.gz", "has_sig": false, "md5_digest": "153d320f47c74b80f0c9e536a2702241", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34161, "upload_time": "2019-03-29T07:34:12", "url": "https://files.pythonhosted.org/packages/b5/90/d6e192fa46782cc01d4514942eea5bb56cc593c1537e60a3dce5c43dc5e9/apptuit-2.0.0.tar.gz" } ], "2.1.0": [ { "comment_text": "", "digests": { "md5": "82a0189326a3168f07be12d12e50c8e2", "sha256": "6322ed2fda80746d40927ff845858b0a85d0f409d881018d69d9f9e3764d3874" }, "downloads": -1, "filename": "apptuit-2.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "82a0189326a3168f07be12d12e50c8e2", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 33594, "upload_time": "2019-07-23T08:29:32", "url": "https://files.pythonhosted.org/packages/89/af/744d835cb802c5c8a82f0c8683eb29fa5559a2eea055f92fbeb621bc121e/apptuit-2.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "75b824b8692bd64289748054ff9ce374", "sha256": "24c12fe9eb3dc578fe0c3ee4477d61e9a46925e54edfc1c181613a697f660055" }, "downloads": -1, "filename": "apptuit-2.1.0.tar.gz", "has_sig": false, "md5_digest": "75b824b8692bd64289748054ff9ce374", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34172, "upload_time": "2019-07-23T08:29:33", "url": "https://files.pythonhosted.org/packages/d7/01/06ad5e7ed7f76ebde9f35f6851215d4ccce983a88c5b8ded11dad0d7d6e0/apptuit-2.1.0.tar.gz" } ], "2.2.0": [ { "comment_text": "", "digests": { "md5": "578109cc6295694d4dd29d1d8c4c888c", "sha256": "940bdbbd37c42c64c9fb7f0b33b08ee02f8210265d4491e16093e62f985057f2" }, "downloads": -1, "filename": "apptuit-2.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "578109cc6295694d4dd29d1d8c4c888c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 33876, "upload_time": "2019-08-19T09:34:06", "url": "https://files.pythonhosted.org/packages/aa/01/44e47ebe4b3964af127604b09d7e45214addefa2101274b597545b9c6858/apptuit-2.2.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "74bef70dbd330ac0e7cf3f72b0241f9f", "sha256": "824b428d19735eea2ffbe6e4b1182b069cc26fc5ecea5eacff7aa80551296009" }, "downloads": -1, "filename": "apptuit-2.2.0.tar.gz", "has_sig": false, "md5_digest": "74bef70dbd330ac0e7cf3f72b0241f9f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34603, "upload_time": "2019-08-19T09:34:08", "url": "https://files.pythonhosted.org/packages/da/be/ea950dde6adb53802efa0848e22fbf223ce6690a0ad02646529688aa596b/apptuit-2.2.0.tar.gz" } ], "2.3.0": [ { "comment_text": "", "digests": { "md5": "a71206d49081abac169822f94fe0e552", "sha256": "dd7aa5ca0933ba507ac6dae15b00555431fd4dc8112ba7207df4e75a909b87db" }, "downloads": -1, "filename": "apptuit-2.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "a71206d49081abac169822f94fe0e552", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34231, "upload_time": "2019-08-26T10:48:58", "url": "https://files.pythonhosted.org/packages/c9/cc/b3e2cc4859235dc7c3c9e306721cf736ec1d968bb70721f230d474b52268/apptuit-2.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fab5ad48dd9dff2708a228c798cfc204", "sha256": "06934e219a6621945b4efc0f838912fc4331c531d34ec91ed49cc039346da89a" }, "downloads": -1, "filename": "apptuit-2.3.0.tar.gz", "has_sig": false, "md5_digest": "fab5ad48dd9dff2708a228c798cfc204", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35807, "upload_time": "2019-08-26T10:49:00", "url": "https://files.pythonhosted.org/packages/5e/e9/09427e137a05ec9d05788e067160828622f2cba7fb6f80b08966882417fe/apptuit-2.3.0.tar.gz" } ], "2.4.0": [ { "comment_text": "", "digests": { "md5": "fe8d1cce344f899bf7d73a2658ee5555", "sha256": "1c1719622d046f9c73b9ae3b4d8efd52a0ae556cad2ccdf41032926b29d5116d" }, "downloads": -1, "filename": "apptuit-2.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "fe8d1cce344f899bf7d73a2658ee5555", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34274, "upload_time": "2019-10-18T07:04:25", "url": "https://files.pythonhosted.org/packages/53/69/e5cd83f76d3a323c53e54a5265d894c023e72b359027243c9a464ec2259b/apptuit-2.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ca84621d54c76d0b1843e0fa69b2d55a", "sha256": "86eeb01f706c5af1deeb237d157f30011b294fa2023b392c27cdfe4d32035271" }, "downloads": -1, "filename": "apptuit-2.4.0.tar.gz", "has_sig": false, "md5_digest": "ca84621d54c76d0b1843e0fa69b2d55a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35845, "upload_time": "2019-10-18T07:04:27", "url": "https://files.pythonhosted.org/packages/e1/1c/ff6e53c028e4731f6f054441fee7cddc409289b0f79e1f8dce35e5e22fc3/apptuit-2.4.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "fe8d1cce344f899bf7d73a2658ee5555", "sha256": "1c1719622d046f9c73b9ae3b4d8efd52a0ae556cad2ccdf41032926b29d5116d" }, "downloads": -1, "filename": "apptuit-2.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "fe8d1cce344f899bf7d73a2658ee5555", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34274, "upload_time": "2019-10-18T07:04:25", "url": "https://files.pythonhosted.org/packages/53/69/e5cd83f76d3a323c53e54a5265d894c023e72b359027243c9a464ec2259b/apptuit-2.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ca84621d54c76d0b1843e0fa69b2d55a", "sha256": "86eeb01f706c5af1deeb237d157f30011b294fa2023b392c27cdfe4d32035271" }, "downloads": -1, "filename": "apptuit-2.4.0.tar.gz", "has_sig": false, "md5_digest": "ca84621d54c76d0b1843e0fa69b2d55a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35845, "upload_time": "2019-10-18T07:04:27", "url": "https://files.pythonhosted.org/packages/e1/1c/ff6e53c028e4731f6f054441fee7cddc409289b0f79e1f8dce35e5e22fc3/apptuit-2.4.0.tar.gz" } ] }