pax_global_header00006660000000000000000000000064143036573020014515gustar00rootroot0000000000000052 comment=c2cabc3458b577c81e7ac25d82aa427344c32668 afeld-sodapy-464855c/000077500000000000000000000000001430365730200143515ustar00rootroot00000000000000afeld-sodapy-464855c/.github/000077500000000000000000000000001430365730200157115ustar00rootroot00000000000000afeld-sodapy-464855c/.github/dependabot.yml000066400000000000000000000002171430365730200205410ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: pip directory: "/" schedule: interval: daily time: "13:00" open-pull-requests-limit: 10 afeld-sodapy-464855c/.gitignore000066400000000000000000000006431430365730200163440ustar00rootroot00000000000000*.py[cod] MANIFEST # virtualenv venv* # cache .cache .pytest_cache # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # vim *.swp # jupyter notebook examples/.ipynb_checkpoints afeld-sodapy-464855c/.travis.yml000066400000000000000000000005361430365730200164660ustar00rootroot00000000000000language: python python: - "3.5" - "3.6" - "3.7" - "3.8" - "3.9" - "3.10" install: - "pip install -r requirements-dev.txt" - "pip install codecov" - "python setup.py install" before_script: - "flake8 sodapy/" - "flake8 tests/" script: - coverage run --source sodapy -m pytest - coverage report after_success: - codecov afeld-sodapy-464855c/CHANGELOG.md000066400000000000000000000032271430365730200161660ustar00rootroot00000000000000sodapy changes by release ========================== ## 2.2.0 * Dependencies: Upgrade all package dependencies * Cleanup: Update README with info about package deprecation ## 2.1.0 * Cleanup: Code formatting with black * Cleanup: Remove support for python 3.4 * Cleanup: Refactor `__init__.py` into a few different files * Feature: Add `get_all` method * Feature: Add codecov to CI, add badge to README ## 2.0.0 * Cleanup: Remove python 2 support * Cleanup: Update trove classifiers for PyPI * Feature: Add CODE_OF_CONDUCT to the repository ## 1.5.5 * Feature: Support python 3.7 * Cleanup: Update readme ## 1.5.4 * Bugfix: Fix `datasets()` filtering for realzies ## 1.5.3 * Feature: Add support for geojson * Bugfix: Fix `datasets()` filtering ## 1.5.2 * Feature: Render markdown readme in PyPI * Cleanup: Remove deleted files from MANFEST.in ## 1.5.1 * Feature: Add support for the discovery API ## 1.5.0 * Bugfix: Bump requests library version because of CVE-2018-18074 * Feature: Loosen requirements.txt versions ## 1.4.7 * Feature: Add `datasets` method * Bugfix: Clean up contributing documentation ## 1.4.6 * Feature: Use Socrata client with a context manager * Feature: Jupyter notebooks with usage examples * Feature: Support Python 3.6 ## 1.4.5 * Bugfix: Spelling fix `rowid` -> `row_id` ## 1.4.4 * Bugfix: Upsert with csv now working with python2 and 3. ## 1.4.3 * Universal wheels distribution ## 1.4.2 * Bugfix: More flexible regex matching for response content-type. ## 1.4.1 * Separate requirements and dev requirements ## 1.4.0 * New `update_metadata` method * API url-building code refactor * Format code according to Flake8 * Bugfixes afeld-sodapy-464855c/CODE_OF_CONDUCT.md000066400000000000000000000064211430365730200171530ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hi@xmunoz.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq afeld-sodapy-464855c/CONTRIBUTING.md000066400000000000000000000025621430365730200166070ustar00rootroot00000000000000# Contributing This project adheres to the [Contributor Covenant Code of Conduct](http://contributor-covenant.org/version/1/4/). By participating, you are expected to honor this code. ## Getting started The best way to start developing this project is to set up a [virtualenv](https://virtualenv.pypa.io/en/stable/) and install the requirements. git clone cd sodapy virtualenv venv source venv/bin/activate pip install -r requirements-dev.txt Install the package, and run tests to confirm that everything is set up properly. pip install . pytest ## Submitting a pull request 1. Fork this repository 2. Create a branch: `git checkout -b my_feature` 3. Make changes 4. Run `black sodapy tests` to ensure that your changes conform to the coding style of this project 5. Commit: `git commit -am "Great new feature that closes #3"`. Reference any related issues in the first line of the commit message. 6. Push: `git push origin my_feature` 7. Open a pull request 8. Pat yourself on the back for making an open source contribution :) ## Other considerations - Please review the open issues before opening a PR. - Significant changes or new features should be documented in [`README.md`](https://github.com/xmunoz/sodapy/blob/master/README.md). - Writing tests is never a bad idea. Make sure all tests are passing before opening a PR. afeld-sodapy-464855c/LICENSE000066400000000000000000000020711430365730200153560ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Cristina Munoz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. afeld-sodapy-464855c/MANIFEST.in000066400000000000000000000000731430365730200161070ustar00rootroot00000000000000include LICENSE requirements.txt recursive-include tests * afeld-sodapy-464855c/README.md000066400000000000000000000353421430365730200156370ustar00rootroot00000000000000[![PyPI version](https://badge.fury.io/py/sodapy.svg)](http://badge.fury.io/py/sodapy) [![Build Status](https://travis-ci.com/xmunoz/sodapy.svg?branch=master)](https://travis-ci.com/xmunoz/sodapy) [![Code Coverage](https://codecov.io/github/xmunoz/sodapy/coverage.svg?branch=master)](https://codecov.io/github/xmunoz/sodapy) # sodapy - UNMAINTAINED :rotating_light: NOTICE: sodapy still works well, but is unmaintained as of Aug 31, 2022. No new features or bugfixes will be added. Use at your own risk. sodapy is a python client for the [Socrata Open Data API](https://dev.socrata.com/). ## Installation You can install with `pip install sodapy`. If you want to install from source, then clone this repository and run `python setup.py install` from the project root. ## Requirements At its core, this library depends heavily on the [Requests](http://docs.python-requests.org/en/latest/) package. All other requirements can be found in [requirements.txt](https://github.com/xmunoz/sodapy/blob/master/requirements.txt). `sodapy` is currently compatible with Python 3.5, 3.6, 3.7, 3.8, 3.9, and 3.10. ## Documentation The [official Socrata Open Data API docs](http://dev.socrata.com/) provide thorough documentation of the available methods, as well as [other client libraries](https://dev.socrata.com/libraries/). A quick list of eligible domains to use with this API is available via the [Socrata Discovery API](https://socratadiscovery.docs.apiary.io/#reference/0/count-by-domain/count-by-domain?console=1) or [Socrata's Open Data Network](https://www.opendatanetwork.com/). This library supports writing directly to datasets with the Socrata Open Data API. For write operations that use data transformations in the Socrata Data Management Experience (the user interface for creating datasets), use the Socrata Data Management API. For more details on when to use SODA vs the Data Management API, see the [Data Management API documentation](https://socratapublishing.docs.apiary.io/#). A Python SDK for the Socrata Data Management API can be found at [socrata-py](https://github.com/socrata/socrata-py). ## Examples There are some [jupyter](https://jupyter.org/) notebooks in the [examples directory](examples) with usage examples of sodapy in action. ## Interface ### Table of Contents - [client](#client) - [`datasets`](#datasetslimit0-offset0) - [`get`](#getdataset_identifier-content_typejson-kwargs) - [`get_all`](#get_alldataset_identifier-content_typejson-kwargs) - [`get_metadata`](#get_metadatadataset_identifier-content_typejson) - [`update_metadata`](#update_metadatadataset_identifier-update_fields-content_typejson) - [`download_attachments`](#download_attachmentsdataset_identifier-content_typejson-download_dirsodapy_downloads) - [`create`](#createname-kwargs) - [`publish`](#publishdataset_identifier-content_typejson) - [`set_permission`](#set_permissiondataset_identifier-permissionprivate-content_typejson) - [`upsert`](#upsertdataset_identifier-payload-content_typejson) - [`replace`](#replacedataset_identifier-payload-content_typejson) - [`create_non_data_file`](#create_non_data_fileparams-file_obj) - [`replace_non_data_file`](#replace_non_data_filedataset_identifier-params-file_obj) - [`delete`](#deletedataset_identifier-row_idnone-content_typejson) - [`close`](#close) ### client Import the library and set up a connection to get started. >>> from sodapy import Socrata >>> client = Socrata( "sandbox.demo.socrata.com", "FakeAppToken", username="fakeuser@somedomain.com", password="mypassword", timeout=10 ) `username` and `password` are only required for creating or modifying data. An application token isn't strictly required (can be `None`), but queries executed from a client without an application token will be subjected to strict throttling limits. You may want to increase the `timeout` seconds when making large requests. To create a bare-bones client: >>> client = Socrata("sandbox.demo.socrata.com", None) A client can also be created with a context manager to obviate the need for teardown: >>> with Socrata("sandbox.demo.socrata.com", None) as client: >>> # do some stuff The client, by default, makes requests over HTTPS. To modify this behavior, or to make requests through a proxy, take a look [here](https://github.com/xmunoz/sodapy/issues/31#issuecomment-302176628). ### datasets(limit=0, offset=0) Retrieve datasets associated with a particular domain. The optional `limit` and `offset` keyword args can be used to retrieve a subset of the datasets. By default, all datasets are returned. >>> client.datasets() [{"resource" : {"name" : "Approved Building Permits", "id" : "msk6-43c6", "parent_fxf" : null, "description" : "Data of approved building/construction permits",...}, {resource : {...}}, ...] ### get(dataset_identifier, content_type="json", **kwargs) Retrieve data from the requested resources. Filter and query data by field name, id, or using [SoQL keywords](https://dev.socrata.com/docs/queries/). >>> client.get("nimj-3ivp", limit=2) [{u'geolocation': {u'latitude': u'41.1085', u'needs_recoding': False, u'longitude': u'-117.6135'}, u'version': u'9', u'source': u'nn', u'region': u'Nevada', u'occurred_at': u'2012-09-14T22:38:01', u'number_of_stations': u'15', u'depth': u'7.60', u'magnitude': u'2.7', u'earthquake_id': u'00388610'}, {...}] >>> client.get("nimj-3ivp", where="depth > 300", order="magnitude DESC", exclude_system_fields=False) [{u'geolocation': {u'latitude': u'-15.563', u'needs_recoding': False, u'longitude': u'-175.6104'}, u'version': u'9', u':updated_at': 1348778988, u'number_of_stations': u'275', u'region': u'Tonga', u':created_meta': u'21484', u'occurred_at': u'2012-09-13T21:16:43', u':id': 132, u'source': u'us', u'depth': u'328.30', u'magnitude': u'4.8', u':meta': u'{\n}', u':updated_meta': u'21484', u'earthquake_id': u'c000cnb5', u':created_at': 1348778988}, {...}] >>> client.get("nimj-3ivp/193", exclude_system_fields=False) {u'geolocation': {u'latitude': u'21.6711', u'needs_recoding': False, u'longitude': u'142.9236'}, u'version': u'C', u':updated_at': 1348778988, u'number_of_stations': u'136', u'region': u'Mariana Islands region', u':created_meta': u'21484', u'occurred_at': u'2012-09-13T11:19:07', u':id': 193, u'source': u'us', u'depth': u'300.70', u'magnitude': u'4.4', u':meta': u'{\n}', u':updated_meta': u'21484', u':position': 193, u'earthquake_id': u'c000cmsq', u':created_at': 1348778988} >>> client.get("nimj-3ivp", region="Kansas") [{u'geolocation': {u'latitude': u'38.10', u'needs_recoding': False, u'longitude': u'-100.6135'}, u'version': u'9', u'source': u'nn', u'region': u'Kansas', u'occurred_at': u'2010-09-19T20:52:09', u'number_of_stations': u'15', u'depth': u'300.0', u'magnitude': u'1.9', u'earthquake_id': u'00189621'}, {...}] ### get_all(dataset_identifier, content_type="json", **kwargs) Read data from the requested resource, paginating over all results. Accepts the same arguments as [`get()`](#getdataset_identifier-content_typejson-kwargs). Returns a generator. >>> client.get_all("nimj-3ivp") >>> for item in client.get_all("nimj-3ivp"): ... print(item) ... {'geolocation': {'latitude': '-15.563', 'needs_recoding': False, 'longitude': '-175.6104'}, 'version': '9', ':updated_at': 1348778988, 'number_of_stations': '275', 'region': 'Tonga', ':created_meta': '21484', 'occurred_at': '2012-09-13T21:16:43', ':id': 132, 'source': 'us', 'depth': '328.30', 'magnitude': '4.8', ':meta': '{\n}', ':updated_meta': '21484', 'earthquake_id': 'c000cnb5', ':created_at': 1348778988} ... >>> import itertools >>> items = client.get_all("nimj-3ivp") >>> first_five = list(itertools.islice(items, 5)) >>> len(first_five) 5 ### get_metadata(dataset_identifier, content_type="json") Retrieve the metadata associated with a particular dataset. >>> client.get_metadata("nimj-3ivp") {"newBackend": false, "licenseId": "CC0_10", "publicationDate": 1436655117, "viewLastModified": 1451289003, "owner": {"roleName": "administrator", "rights": [], "displayName": "Brett", "id": "cdqe-xcn5", "screenName": "Brett"}, "query": {}, "id": "songs", "createdAt": 1398014181, "category": "Public Safety", "publicationAppendEnabled": true, "publicationStage": "published", "rowsUpdatedBy": "cdqe-xcn5", "publicationGroup": 1552205, "displayType": "table", "state": "normal", "attributionLink": "http://foo.bar.com", "tableId": 3523378, "columns": [], "metadata": {"rdfSubject": "0", "renderTypeConfig": {"visible": {"table": true}}, "availableDisplayTypes": ["table", "fatrow", "page"], "attachments": ... }} ### update_metadata(dataset_identifier, update_fields, content_type="json") Update the metadata for a particular dataset. `update_fields` should be a dictionary containing only the metadata keys that you wish to overwrite. Note: Invalid payloads to this method could corrupt the dataset or visualization. See [this comment](https://github.com/xmunoz/sodapy/issues/22#issuecomment-249971379) for more information. >>> client.update_metadata("nimj-3ivp", {"attributionLink": "https://anothertest.com"}) {"newBackend": false, "licenseId": "CC0_10", "publicationDate": 1436655117, "viewLastModified": 1451289003, "owner": {"roleName": "administrator", "rights": [], "displayName": "Brett", "id": "cdqe-xcn5", "screenName": "Brett"}, "query": {}, "id": "songs", "createdAt": 1398014181, "category": "Public Safety", "publicationAppendEnabled": true, "publicationStage": "published", "rowsUpdatedBy": "cdqe-xcn5", "publicationGroup": 1552205, "displayType": "table", "state": "normal", "attributionLink": "https://anothertest.com", "tableId": 3523378, "columns": [], "metadata": {"rdfSubject": "0", "renderTypeConfig": {"visible": {"table": true}}, "availableDisplayTypes": ["table", "fatrow", "page"], "attachments": ... }} ### download_attachments(dataset_identifier, content_type="json", download_dir="~/sodapy_downloads") Download all attachments associated with a dataset. Return a list of paths to the downloaded files. >>> client.download_attachments("nimj-3ivp", download_dir="~/Desktop") ['/Users/xmunoz/Desktop/nimj-3ivp/FireIncident_Codes.PDF', '/Users/xmunoz/Desktop/nimj-3ivp/AccidentReport.jpg'] ### create(name, **kwargs) Create a new dataset. Optionally, specify keyword args such as: - `description` description of the dataset - `columns` list of fields - `category` dataset category (must exist in /admin/metadata) - `tags` list of tag strings - `row_identifier` field name of primary key - `new_backend` whether to create the dataset in the new backend Example usage: >>> columns = [{"fieldName": "delegation", "name": "Delegation", "dataTypeName": "text"}, {"fieldName": "members", "name": "Members", "dataTypeName": "number"}] >>> tags = ["politics", "geography"] >>> client.create("Delegates", description="List of delegates", columns=columns, row_identifier="delegation", tags=tags, category="Transparency") {u'id': u'2frc-hyvj', u'name': u'Foo Bar', u'description': u'test dataset', u'publicationStage': u'unpublished', u'columns': [ { u'name': u'Foo', u'dataTypeName': u'text', u'fieldName': u'foo', ... }, { u'name': u'Bar', u'dataTypeName': u'number', u'fieldName': u'bar', ... } ], u'metadata': { u'rowIdentifier': 230641051 }, ... } ### publish(dataset_identifier, content_type="json") Publish a dataset after creating it, i.e. take it out of 'working copy' mode. The dataset id `id` returned from `create` will be used to publish. >>> client.publish("2frc-hyvj") {u'id': u'2frc-hyvj', u'name': u'Foo Bar', u'description': u'test dataset', u'publicationStage': u'unpublished', u'columns': [ { u'name': u'Foo', u'dataTypeName': u'text', u'fieldName': u'foo', ... }, { u'name': u'Bar', u'dataTypeName': u'number', u'fieldName': u'bar', ... } ], u'metadata': { u'rowIdentifier': 230641051 }, ... } ### set_permission(dataset_identifier, permission="private", content_type="json") Set the permissions of a dataset to public or private. >>> client.set_permission("2frc-hyvj", "public") ### upsert(dataset_identifier, payload, content_type="json") Create a new row in an existing dataset. >>> data = [{'Delegation': 'AJU', 'Name': 'Alaska', 'Key': 'AL', 'Entity': 'Juneau'}] >>> client.upsert("eb9n-hr43", data) {u'Errors': 0, u'Rows Deleted': 0, u'Rows Updated': 0, u'By SID': 0, u'Rows Created': 1, u'By RowIdentifier': 0} Update/Delete rows in a dataset. >>> data = [{'Delegation': 'sfa', ':id': 8, 'Name': 'bar', 'Key': 'doo', 'Entity': 'dsfsd'}, {':id': 7, ':deleted': True}] >>> client.upsert("eb9n-hr43", data) {u'Errors': 0, u'Rows Deleted': 1, u'Rows Updated': 1, u'By SID': 2, u'Rows Created': 0, u'By RowIdentifier': 0} `upsert`'s can even be performed with a csv file. >>> data = open("upsert_test.csv") >>> client.upsert("eb9n-hr43", data) {u'Errors': 0, u'Rows Deleted': 0, u'Rows Updated': 1, u'By SID': 1, u'Rows Created': 0, u'By RowIdentifier': 0} ### replace(dataset_identifier, payload, content_type="json") Similar in usage to `upsert`, but overwrites existing data. >>> data = open("replace_test.csv") >>> client.replace("eb9n-hr43", data) {u'Errors': 0, u'Rows Deleted': 0, u'Rows Updated': 0, u'By SID': 0, u'Rows Created': 12, u'By RowIdentifier': 0} ### create_non_data_file(params, file_obj) Creates a new file-based dataset with the name provided in the files tuple. A valid file input would be: ``` files = ( {'file': ("gtfs2", open('myfile.zip', 'rb'))} ) ``` >>> with open(nondatafile_path, 'rb') as f: >>> files = ( >>> {'file': ("nondatafile.zip", f)} >>> ) >>> response = client.create_non_data_file(params, files) ### replace_non_data_file(dataset_identifier, params, file_obj) Same as create_non_data_file, but replaces a file that already exists in a file-based dataset. Note: a table-based dataset cannot be replaced by a file-based dataset. Use create_non_data_file in order to replace. >>> with open(nondatafile_path, 'rb') as f: >>> files = ( >>> {'file': ("nondatafile.zip", f)} >>> ) >>> response = client.replace_non_data_file(DATASET_IDENTIFIER, {}, files) ### delete(dataset_identifier, row_id=None, content_type="json") Delete an individual row. >>> client.delete("nimj-3ivp", row_id=2) Delete the entire dataset. >>> client.delete("nimj-3ivp") ### close() Close the session when you're finished. >>> client.close() ## Run tests $ pytest ## Contributing See [CONTRIBUTING.md](https://github.com/xmunoz/sodapy/blob/master/CONTRIBUTING.md). ## Meta This package uses [semantic versioning](https://semver.org/). Source and wheel distributions are available on PyPI. Here is how I create those releases. python3 setup.py bdist_wheel python3 setup.py sdist twine upload dist/* afeld-sodapy-464855c/examples/000077500000000000000000000000001430365730200161675ustar00rootroot00000000000000afeld-sodapy-464855c/examples/basic_queries.ipynb000066400000000000000000000403371430365730200220570ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Example 01: Basic Queries\n", "\n", "Retrieving data from Socrata databases using sodapy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os\n", "import pandas as pd\n", "import numpy as np\n", "\n", "from sodapy import Socrata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Find some data\n", "\n", "Though any organization can host their own data with Socrata's tools, Socrata also hosts several open datasets themselves:\n", "\n", "https://opendata.socrata.com/browse\n", "\n", "The following search options can help you find some great datasets for getting started:\n", "* Limit to data sets (pre-analyzed stuff is great, but if you're using sodapy you probably want the raw numbers!)\n", "* Sort by \"Most Accessed\"\n", "\n", "[Here's](https://opendata.socrata.com/browse?limitTo=datasets&sortBy=most_accessed&utf8=%E2%9C%93&page=1) a link that applies those filters automatically.\n", "\n", "Click on a few listings until you find one that looks interesting. Then click API and extract the following bits of data from the displayed url.\n", "\n", "https://<**opendata.socrata.com**>/dataset/Santa-Fe-Contributors/<**f92i-ik66**>.json\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Socrata Interface](socrata_interface.png)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Enter the information from those sections here\n", "socrata_domain = 'opendata.socrata.com'\n", "socrata_dataset_identifier = 'f92i-ik66'\n", "\n", "# App Tokens can be generated by creating an account at https://opendata.socrata.com/signup\n", "# Tokens are optional (`None` can be used instead), though requests will be rate limited.\n", "#\n", "# If you choose to use a token, run the following command on the terminal (or add it to your .bashrc)\n", "# $ export SODAPY_APPTOKEN=\n", "socrata_token = os.environ.get(\"SODAPY_APPTOKEN\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get all the data" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Domain: opendata.socrata.com\n", "Session: \n", "URI Prefix: https://\n" ] } ], "source": [ "client = Socrata(socrata_domain, socrata_token)\n", "print(\"Domain: {domain:}\\nSession: {session:}\\nURI Prefix: {uri_prefix:}\".format(**client.__dict__))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
amountamount_2citycompanydatejobnamename2partyrecipientstreetupdate
06100Donation of $6,100 to Presidential elections 2008Santa Fe NMThornburg CompaniesQ3/2008FinanceGarrett ThornburgGarrett ThornburgDemocratBarack Obama150 WASHINGTON AVEUpdated
15600Donation of $5,600 to Presidential elections 2008Santa Fe NMFOREST REALTYQ3/2007REAL ESTATEMichael DalyMichael DalyDemocratBill Richardson305 BROWNELL HOWLAND RDUpdated
25100Donation of $5,100 to Presidential elections 2008Santa Fe NMJames Currey PublishersQ3/2008PublisherDouglas JohnsonDouglas JohnsonDemocratBarack Obama48 WOODS LOOPUpdated
35100Donation of $5,100 to Presidential elections 2008Santa Fe NMAcademy for Educational DevelopmQ3/2008Chief of PartyLynn MortensenLynn MortensenDemocratBarack Obama4 AVILA RDUpdated
45100Donation of $5,100 to Presidential elections 2008Santa Fe NMSelf employedQ1/2008SculptorTed FlickerTed FlickerDemocratBarack Obama164 TANO RDUpdated
\n", "
" ], "text/plain": [ " amount amount_2 city \\\n", "0 6100 Donation of $6,100 to Presidential elections 2008 Santa Fe NM \n", "1 5600 Donation of $5,600 to Presidential elections 2008 Santa Fe NM \n", "2 5100 Donation of $5,100 to Presidential elections 2008 Santa Fe NM \n", "3 5100 Donation of $5,100 to Presidential elections 2008 Santa Fe NM \n", "4 5100 Donation of $5,100 to Presidential elections 2008 Santa Fe NM \n", "\n", " company date job \\\n", "0 Thornburg Companies Q3/2008 Finance \n", "1 FOREST REALTY Q3/2007 REAL ESTATE \n", "2 James Currey Publishers Q3/2008 Publisher \n", "3 Academy for Educational Developm Q3/2008 Chief of Party \n", "4 Self employed Q1/2008 Sculptor \n", "\n", " name name2 party recipient \\\n", "0 Garrett Thornburg Garrett Thornburg Democrat Barack Obama \n", "1 Michael Daly Michael Daly Democrat Bill Richardson \n", "2 Douglas Johnson Douglas Johnson Democrat Barack Obama \n", "3 Lynn Mortensen Lynn Mortensen Democrat Barack Obama \n", "4 Ted Flicker Ted Flicker Democrat Barack Obama \n", "\n", " street update \n", "0 150 WASHINGTON AVE Updated \n", "1 305 BROWNELL HOWLAND RD Updated \n", "2 48 WOODS LOOP Updated \n", "3 4 AVILA RD Updated \n", "4 164 TANO RD Updated " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = client.get(socrata_dataset_identifier)\n", "df = pd.DataFrame.from_dict(results)\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Success! Let's do some minimal cleaning and analysis just to justify the bandwidth used." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "df['amount'] = df['amount'].astype(float)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
summeansize
recipient
Bill Richardson1020748.02442.0418.0
Barack Obama602865.02491.0242.0
Hillary Clinton185887.02324.080.0
John McCain60741.02025.030.0
John Edwards25550.02555.010.0
\n", "
" ], "text/plain": [ " sum mean size\n", "recipient \n", "Bill Richardson 1020748.0 2442.0 418.0\n", "Barack Obama 602865.0 2491.0 242.0\n", "Hillary Clinton 185887.0 2324.0 80.0\n", "John McCain 60741.0 2025.0 30.0\n", "John Edwards 25550.0 2555.0 10.0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "by_candidate = df.groupby('recipient').amount.aggregate([np.sum, np.mean, np.size]).round(0)\n", "by_candidate.sort_values('sum', ascending=False).head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple Data Sources\n", "\n", "That was much less annoying than downloading a CSV, though you can always save the dataframe to a CSV if you'd like. Where sodapy really shines though is in grabbing different data sources and mashing them together.\n", "\n", "For example, let's compare 311 calls between [New York City](https://data.cityofnewyork.us/Social-Services/311-Service-Requests-from-2010-to-Present/erm2-nwe9) and [Chattanooga, TN](https://data.chattlibrary.org/Government/311-Service-Requests/9iep-6yhz). Socrata makes it so easy, you'd be crazy _not_ to do it!" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1000, 48)\n", "(1000, 28)\n" ] } ], "source": [ "nyc_domain = 'data.cityofnewyork.us'\n", "nyc_dataset_identifier = 'fhrw-4uyv'\n", "nyc_client = Socrata(nyc_domain, socrata_token)\n", "nyc_results = nyc_client.get(nyc_dataset_identifier)\n", "nyc_df = pd.DataFrame.from_dict(nyc_results)\n", "print(nyc_df.shape)\n", "\n", "chatt_domain = 'data.chattlibrary.org'\n", "chatt_dataset_identifier = 'sf89-4qcw'\n", "chatt_client = Socrata(chatt_domain, socrata_token)\n", "chatt_results = chatt_client.get(chatt_dataset_identifier)\n", "chatt_df = pd.DataFrame.from_dict(chatt_results)\n", "print(chatt_df.shape)\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nycchatt
False0.930.98
True0.070.02
\n", "
" ], "text/plain": [ " nyc chatt\n", "False 0.93 0.98\n", "True 0.07 0.02" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# extract tree-related complaints\n", "tree_related = pd.concat([\n", " nyc_df.complaint_type.str.contains(r'[T|t]ree').value_counts(),\n", " chatt_df.description.str.contains(r'[T|t]ree').value_counts()\n", "], axis=1, keys=['nyc', 'chatt'])\n", "tree_related.div(tree_related.sum()).round(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks like trees are a higher percentage of NYC complaints than Chattanooga's.\n", "\n", "Note that we can only talk about percentages, since our query results got truncated to 1,000 rows.\n", "\n", "What if we want to be smarter about what we ask for, so that we can get 100% of the subset of data\n", "we're most interested in? That's the subject of a future example, so stay tuned!\n", "\n", "If you want to find more data sets, here's Socrata's data finder:\n", "\n", "https://www.opendatanetwork.com/search" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 2 } afeld-sodapy-464855c/examples/basic_queries.py000066400000000000000000000100071430365730200213550ustar00rootroot00000000000000# coding: utf-8 # # Example 01: Basic Queries # # Retrieving data from Socrata databases using sodapy # ## Setup # In[1]: import os import pandas as pd import numpy as np from sodapy import Socrata # ## Find some data # # Though any organization can host their own data with Socrata's tools, Socrata also hosts several open datasets themselves: # # https://opendata.socrata.com/browse # # The following search options can help you find some great datasets for getting started: # * Limit to data sets (pre-analyzed stuff is great, but if you're using sodapy you probably want the raw numbers!) # * Sort by "Most Accessed" # # [Here's](https://opendata.socrata.com/browse?limitTo=datasets&sortBy=most_accessed&utf8=%E2%9C%93&page=1) a link that applies those filters automatically. # # Click on a few listings until you find one that looks interesting. Then click API and extract the following bits of data from the displayed url. # # https://<**opendata.socrata.com**>/dataset/Santa-Fe-Contributors/<**f92i-ik66**>.json # # ![Socrata Interface](socrata_interface.png) # In[4]: # Enter the information from those sections here socrata_domain = "opendata.socrata.com" socrata_dataset_identifier = "f92i-ik66" # App Tokens can be generated by creating an account at https://opendata.socrata.com/signup # Tokens are optional (`None` can be used instead), though requests will be rate limited. # # If you choose to use a token, run the following command on the terminal (or add it to your .bashrc) # $ export SODAPY_APPTOKEN= socrata_token = os.environ.get("SODAPY_APPTOKEN") # ## Get all the data # In[5]: client = Socrata(socrata_domain, socrata_token) print( "Domain: {domain:}\nSession: {session:}\nURI Prefix: {uri_prefix:}".format( **client.__dict__ ) ) # In[6]: results = client.get(socrata_dataset_identifier) df = pd.DataFrame.from_dict(results) df.head() # Success! Let's do some minimal cleaning and analysis just to justify the bandwidth used. # In[7]: df["amount"] = df["amount"].astype(float) # In[8]: by_candidate = ( df.groupby("recipient").amount.aggregate([np.sum, np.mean, np.size]).round(0) ) by_candidate.sort_values("sum", ascending=False).head() # ## Multiple Data Sources # # That was much less annoying than downloading a CSV, though you can always save the dataframe to a CSV if you'd like. Where sodapy really shines though is in grabbing different data sources and mashing them together. # # For example, let's compare 311 calls between [New York City](https://data.cityofnewyork.us/Social-Services/311-Service-Requests-from-2010-to-Present/erm2-nwe9) and [Chattanooga, TN](https://data.chattlibrary.org/Government/311-Service-Requests/9iep-6yhz). Socrata makes it so easy, you'd be crazy _not_ to do it! # In[12]: nyc_domain = "data.cityofnewyork.us" nyc_dataset_identifier = "fhrw-4uyv" nyc_client = Socrata(nyc_domain, socrata_token) nyc_results = nyc_client.get(nyc_dataset_identifier) nyc_df = pd.DataFrame.from_dict(nyc_results) print(nyc_df.shape) chatt_domain = "data.chattlibrary.org" chatt_dataset_identifier = "sf89-4qcw" chatt_client = Socrata(chatt_domain, socrata_token) chatt_results = chatt_client.get(chatt_dataset_identifier) chatt_df = pd.DataFrame.from_dict(chatt_results) print(chatt_df.shape) # In[11]: # extract tree-related complaints tree_related = pd.concat( [ nyc_df.complaint_type.str.contains(r"[T|t]ree").value_counts(), chatt_df.description.str.contains(r"[T|t]ree").value_counts(), ], axis=1, keys=["nyc", "chatt"], ) tree_related.div(tree_related.sum()).round(2) # Looks like trees are a higher percentage of NYC complaints than Chattanooga's. # # Note that we can only talk about percentages, since our query results got truncated to 1,000 rows. # # What if we want to be smarter about what we ask for, so that we can get 100% of the subset of data # we're most interested in? That's the subject of a future example, so stay tuned! # # If you want to find more data sets, here's Socrata's data finder: # # https://www.opendatanetwork.com/search afeld-sodapy-464855c/examples/socrata_interface.png000066400000000000000000003776331430365730200223740ustar00rootroot00000000000000PNG  IHDRx[3iCCPICC ProfileXyXMܛ,)-!RK-H(("b"`#&  }^s߿=s̙sfC#44A@PpP/v@ p6bx˟.?a^ Jnk'Y+ 0] :"XKH[smZ j/4[zF}a94pC)f=c c0ϞsPX_r|LϿ2=<|❹l =RxhG9%(0p#Yoyn![zinc ymoiH#]b.l3 CƜ047ۥk a aK0"XGxx2#weyg밣'/doc 1噋5C9 wxAT#b3Ƕ>0v5ڑ?`GO/o=y!vGFX Gznaebv;=L,wE3 /'Իиi1 |7ܥ ?m@#ov7Rwg5jGq0^G` F'ºuhh}cC4Pj(3WY2J^ z ʍLy~0h;;% KU@al3HᑴQ 0ߺF?ܕ!p,8-ieN#N_kO)6ON1 C=cd+""=ȶ-wm|^F-CH22,@V [`O"ǐ UE@$jCQaDT6UF Q 4͉@оht:])O Ì({9Ɯ4`:0I,ˆc-l: 2.v;]ࡐ0pH(DN1@b G©,p^X \7[T[oTTTT*TVT$TETWQSR3PSRPGRP_~E N|߅ůhx$єܦLզu-AvG'LKAHWBL7B@/CoADM1 AA!a$t DB p0ňaa4fgbge\db`gga*ajccF2 332`M --/jbڗ?Hun,&v! 2zG |   vLrFƵƋ&J& &ݦԦ6Ŧffd&Ok.dlh,-NY c*~hCqdV;QHN{Z{Ze=<R(r0cWv@67Z7hwK?<=K=B⼗W׬wGu<_uS~~~ $]R1iȿ9"bfC`CE{Ps0Cp@pwwHLHDhzXjE): ?_{"E#"ǣ4JVoĊf~3pux3/HxvD(33I )5i#GrTh11ұ 3^O ֳO/:{BDIù5yyqyϛ兔cEfEMgϜ<^WRXq'OQL٭^ϕ7o77CCv/G\F^zyju7ߢf{W09Z^}ØX۸xτěIS邏<kgdgZg fC?*,/=K_}]{?~-gԬ*>Z:v ӍAd^ tv | =GEPsL6OH%E/ALkJNp8L͢Jfgʳ§R`BHF;1 {+6d\d[bTrTe}AgUT`HI_<5]9:v9u874v-u;J4R,=!]/H$Y+,Ց-"x"DGDH}W|IL,O (.5{D'v5_yLANaJѡ3%EM/[Ԏ_[ېx+7yś5[شzŴݽ1Ӊ"t} Pc'O2kLO{Tam˞wX[tsEšen~_zUWom @WH#T:ÄyA ңËъI+0R+YY948I\=<|W%DDk%$%o]֔9"PJL1G_jFSS+JFaQSY3~s 9KU+Uk9A[v;d:;8+plpvvO"r||]HtAB9ɳW##U#GG'hŢb?dOJ(Lt##))NiGQ[Θɺ}'r5N=_YRDU^tXHyC+Vijj/IlPoqűjԵ57oZjZ:-騽WYu;هcSӴgVV' ^Q80L1})5›oK=>~,t<`"h2CTtǒ WZ}zyeQeWf~ Wl8:Ebgo.?G<8C:;461  {I>1 R/kԹh赘X8D\!xtDt$Ì${(ҤN 8)^V~qʜM'ry67N(xRPtLTcj);|Z*/9w|zEjeZ G.&_vYGCբk7?1us6}pӾf;.}]7=O<~ޛS*SPQqSSuP3RSsC44gh6i}inЋW304=nߙ33?e eeab cg8iązcR K0N(HKMU]["hOd29r_(t)V|IV}MSZ:bzY׌ޘ`MeS,NXY5ZiGW'y{x7Z"O1OJACCO}ϷQL1OX$l&OHy>t1c=k&''l>{IQhqQiSYh.՚Է\^qKyե=ޮ >?bp+7ʓSg6>W- .c^Scjњy;~@ p1  x .v0 @APF Bv4F""?8Q  mNCٷ4&4V냭SHQDR08KY*e{*y,Ԛgi$i iѴQtD1zO9X-! S ktVyiv[:".On)3[ϗ+`"(#!L!&]غ~^w f99NyK,ez3jꐆ1z!}YSf3惖4VI6-1ڜΖ.Wy&ZqjW/4+0,AWhT`|Daz[L{IWSޥYwSʨ.a9Q)(8ԭ|O͋ͅ_k^?jh|ޭזּSvvvxIKO_=/Z/<:Ѥ؉1Ӈfb.0²HZ:mOe˕3c[mT2@?A=x&&¾?=o UqфBH29¡45A;f3co`7(L((fpITTW Աxs|4ZR:n*zn7s4"MvcVnsI~q>A!>a1EQc17X=-R &2]h+zeJ>fOt=NC7c*V39" Xؼ3Ϥ&gWsq"Wo$r$?r1L!()q$'5"]h[mJ⹃D8]^^X^}^b*bMѥW>\;#^#3eA''zNf% 1rUӣ|&'Z?--bRwx9oej_k33&nߠذ(x)ٸp9=Mls77*67W[:w>k(m??T kiTXtXML:com.adobe.xmp 1144 499 @IDATx\?-&3p *A 6a נ ۠+T}&m&5-&mPB2IPXc}<9<9~<yz? B@! B@!0f |i̖\ .B@! B@!G:B@! B@! 8xxJB@! B@! ! B@! B@q" ! B@! B@B@! B@! D (B@! B@! <B@! B@!0 3P/B@! B@x! B@! B`g7_! B@! "HB@! B@! ' o@)B@! B@! D> B@! B@1N@1ހR|! B@! B@#}@! B@! c=+vsg}v0a%Ǐ\Bt4,=11qIc 7`4`2_d&HA6tu?7=ɣىv 7l2cfӵjމ;R!RMhtÐX瑲 B@! B`t N9ڊ׮!(9!!3iu ꃯrr!W|5 mh@BF>V-oQ Xᬈ.fYB@! B@!0Bxv"cDrF`QoՊ454ӊΦQK6c]U2 `Ajr`#>sɥùҵiUZYRp#mjEgK , ,rF?z}] L1h$I2z7%V"ȑB@! B@3@O*!.ipISWb1/m9m]G^HӒG;IhDQacJ~J9zcV NdL?i˳ފ, MMHI31驈k&gݍuhjF(u`ZE! B@! I?Є&p1(vw =~b$fb( w+JV!dTRZPjA@ "wld5aNE[yJ?ڸaCUSR$! B@! @HNΔ)S13<lSbyrC4`4"b/U)P>v}O1Qx4)5A;ʵ<%82o;JS G4éMB6E{4, _ t4߂yi#V0j0jPKjZ=R@ovͨo<َvH,yhO//|6'@)eI! B@! %%-hWmBzn.'yG+EFՕh ??`N,:}taSqqQr9%iS6hnD}}1!`C\$yv<]YK{qjņ&N4O93i@7 xKH0 7?S&Q,EI uKa0#ku>jN鵨 8/˃ks֗,R7`֢۵fоE3ǣ}; FDz|UPjIZJum455|Ei3ߵk˪Ц{ze(5W{16WLJr@#M--^iHN]EsY, ! B@! @hBxa{07t+VqXP9M1 fMXcnUAK^p*ԮCӲ)n4M (+hDNi ~;zft{'ρ&Lnldjoو8DEaD/ӀT^/i-cTCuvt +ru4IvhsD3"!%1fMj RWcEvΚ5ȩ!K)%SGmfvނn{' В[J_%?de2KtW3/8fuӶBkk7ȇ4bLdUơW1Eѳ\_jdIFjBЇz))MkאҲl7T䰚PxK~ګѱT?rxTwGmrռk;tI K!ٌPYZ:fFł%h4oʓk4p R[xO! B@!0Bx&&#ud?ӂ5PZ"4gc&S2&ƺLX`wCIu:kКRXWcc@Jn1],}Ұ< K֠>^UE 0~L*cp%jMʀ6V?nב^bh)v+B5͑CJHwL9d2o ih߉jXjP=, )t咾#}RkHE^qd41zJ SZ .h3}L:ꇅF*_7>i$ $&59v_Ř%n⎚xį(D}֜~ܔ:qQLj\zы;ɱi(0S]mƞڞ!i׍ZOzqQXUU?j㏗9Y^."z.S놈$#B@5_\)B`Q&.†Ոs i"g%ags;\ޡR?`#ybR}QGl(<^v94;@!uZPnV'Ʀ#Ň ''P[]>ɼUxEC7Lj.ɻLDɀw&F|2t,!,,ERg!Kvlihq0{hkSR=H&G:kj5^h(BĴk(raƦ;>'d:QjttwO9=2 U#?~xzj+bOO/Y??ʞٍFlt=-u{pp,Zvc~k?L-p)S@Wf|d< *Q^UMv6zm 9~{-̌SxoD>wC A 6Y!-06T"oeTls:Ѵ@RRU)22o#aSLJfb<2+;r)>1~HœTp˘_D)%=vtU8;}? )MMEizamEx!`:Z+I!  F)q$,6ڵ_)✏߰FtF]pfGSO|;s.V.F/tu5E+v=2uhi=apR nudj+Dzko`1M mqċׇ%e7jTxھ /^ԝv ݍxcظ u, !"ԋ"?֦l!+OSmjc4Kwuw+ϯ%hT}nqtC&?<'4XѧRF0ա|n9@^nA8 &eŁ.МK%bMK./u{Prϙ*R{uvqdž6ՏO893&+h g虢)&6S<%u!,!FYZ`E-mhs`Ũ89^rUHEnK*7QU ,1]ĸXLg =l@G1}:v$)^A!m7t]>Mk.vɊw3j'05|2}WuG؊mە{. 7_Eg^ eнuZquX2Ooj;Um;oqG:KIdCΏSf_M?VCWs 3N@i8 nѺq+2~V}~,{{L$͚D1xD9}'>b?{2YsO{͝$adX=un=$-ﮝ+pN*@5MŤ hԹ1< (/!v?~GG/1o]HlEz|^L[~أ}ATge$y@@* ܾ!сӧc„ >?~q|݊AC)hyev99ZaY(4؈R;^7}0CEjKfO"ՇyjBv 3:8ٺؤE_9-c'4_jܰ /M=j>b G=sV*ÔAݮ98Ob)OADu(>A$aDŽ9 7 kUGCH0IL/}>GQCY!=p0@q-[0'lGe%}6ކ2PrXH/L9cԶ/,v~Ђ\9pC:.'[p@xCO7O]ryXGצU+c9n|puu. ƽ x&IL4{^Dѷo}O^k'YIQRw!C M̾,cp&=51ߟÖuxy.[:i,ew9nJAxJEb@xM;;+,jP,9pzqE9ECDDtd C#JINf+QXPAPv+f84nyn,.qjp;.NRY,A5j<+P2e\w~_*\JhS7ߕ# B@$kP|%.oB`vΥ]xK{~2,rlkǢhpq /T-/&;dL{]ˮ 3OEib$tf_UˮIJרW! ˿tmCQ^~5G?|e&~%XChz΂$77OqMĝCʭ~0aE? s8v6}R%es1cxy#Xgctu53\݇4}kܧ{q#0ƞ%qD1}!l|?5 J*`!==_|֯_!hNtt48#ۺɍ#C[`~gL[V`CalCa??Ҳ $ܟO_#6`*yyĝϾߢ>~$d=0 ا8k6LO|oqOM#ƻ 1%&9=FKo1I6.羁m!!Le,ۇhCx{G9R?r7)㴬,\y啨»ヒK.~;PBLb=RE|_zFEN*4tД!%2ßmP]HHD_dt ]jwvqɰ{5uR, )HJJ¼y-Ha;m529X VI}+P.~[_jq2h,qPoPRd7I6W*Ar䦫0ֶZn_'>>sN=9Pe痯v o7~W;uߊ7l4grTZ^u5ֵƢk_ :w(r6 x ƜOaw' SÑ]o~3mc}'zx`j+cY.q|-eώ'\IX U&]hB,e9OPS*e_?^#$4\Wb“, d5(R"z>\I7,4cO+A~9TAn'R򔟏m4 I0jǡ2q:1fE2[8}9!0,RH>i2.:#g}~$M6 ӦL¾7{kpMs)SȦ߰!9-IB -|;ٳEzqvxezTTTP+PIuF=- LDqݕ8{#VS҄<ؙBf/C>Y?@aesgIr+n |/6 a?Ĺ)j*1]q԰n+lʊD.ǻ^vk#ظR?w~|dߔ4E 4GC3 ~9[HIj=D}hS6kq5@[_jr64:DM$(Zpfƺ cA}k N#?J}ޖ{}"&J1eLl Ř7Ng*wypDřY]-Pw/Ŏ*@$$*X`9E3CcO0y8E0cb!+B;o )s?x2o-Ѵ%zv՞{fa1=u%Hci׽D!LK./܇2?+pt=STY#-NC;nhb,kn/^Zwa=[-KB@Q IIB,b#g]rǢ"~~ :@Ԭhg sֽяLsYGp|DH, LE13]ϡ%Me0J=3xlda\ bok܇OMcop~,1ny E9nWŽL}}I7_lU>i:bS#v6XθӲ~ѣ?Rz0[̝;WY׶O<_|Vr>#CZaC8⻝ifK1lx+X1:Y(?s#m#(٠ͶT sg@%շnm VPji_٧XDϮc0Kj8=HCC6Y{ B6|u)X,C{O|KJ{n-YFux[) B-_σ:EUZޜH~6AwzmءHM{|'kk&d&,&q.ǣa{4R[Uۉ)PW:uB}cbYs/u;}c!oFMA6o 737^^y!:8A}*ay߮}QbeQ|ܺ&z $/|!YsWWϑ ՝0?zV'Y_h2}W˦"kEߜI_V6oDrWx+lB@0V9ΡOv+Ө=g8fi !e'i1҇[7(UPۥ&'y#r̼~a>菞."K]ྂ/A{ga硃Y`Q?ˠ{0 1G8xp?~/eC8<1(e>ن[9 2*gDpgMNֽ3ڊgpHK}zv!Ո$e-_i];ɳ~M"'@aaSpͲdEoN8]$4cwkl>G6ބj`LEc)N鮫.w%;7Bzqf/YyG]E-߃ Q1X^r i?>DuH۲Wm#$TUmz8_މCNNl@_m%Iкc* fy]ϹB|ߎ/p:Md>&ϗ?}&|5?ߢgT=.S{~ҽK(Kdu'CL]*sp%'N>p71HyzM煖w[W[﹒[q39$Ds$k&~N 8n2_=pa\q6x8&9HR9J~{2Mޤn>5ώaRLKH {9+%o5gjV&alYAԿKWkP6|t Y^q6*Eqø0ߎ{F"@ |7mCލێaq2eԌ[0wҾfoߘ#*;8;?ڙ!3_ߡfsC/޻ƽދ{€qC1lz!}xJ4_ĤPc>듈9>r {Q@,GjCN 'gVZ;>_wr /BY[&YI*+Yrt_馋wwvaQj@^$ՠ  u8тKO.GOf2b@H9HHNmCjJpIjЉ &I@[Ц 1(͎H56#fK: (k@L2eM`pjEiT2婡-H2R/<+*&ϥOft`-bʝ 3TH3V Ó |>SO(2Ie!t=Pږ3r&$'c[P]zsqt5 [_o^zJDP?>AsW`9?鳰5ג!rn{ϝ U}d6f~ $pG[*IA]qYДґt &~M&Dz]M"u\}d6X&85 Qy(m~?& In{[F'ϣ͐W̹!_ُGlQYD֩T xkH?>Fe<>*solwlUaw Li2c@\G3Ǔo$8+!la1_H%%x%Y,MAʭ#,PxVt%\ mV-l2bR ac}S!s% n=x%n5< ?mkp ?iʚ8 $3V6rkD 32yjF&΋uԘ0m@5 [,lN@n}JЊzx\|(yW/uʤ -O]>s}hBiBzZ4tK~$E;qYr-m$8!E4P;fd"-YU0=j+CGm t>jLx4;2&DMe ZHո%&% 9 o>]vk9dX4џk*Fs233t*CW Gh93Y@ӷʮ4k.{<;7Ϛ[3HAطƈ_=*/}f`Wۯ sKI W9rk!o9j/S=VzbCCxp7;^޹m!gm OE=v_![$hŸX8Ber']5wA@_. **q{Oފz{o_oy6! NEg TcVzu:"&jK?亜 s"0qCw~{6[YP8*G7nP*\/a+mJi[bCl0r1bۏ^HeގO&<2"~o;Jod`GOh$!WmΧm#SQs`4RA\~ߥ< %Zz,||8ާzV4^B'T-2Tf8M$A! Öp͒zL4omU徑lW-4 CY˱P~.cbbPV[:M R9MiHB@! B& OȂ>@Qhhꕝsw#Zcߙ uX(<өřBj.B@! 8E z) J%X"M/B@! B@! N".-)B@! B@! X"M/B@! B@! N".-)B@! B@! X"M/B@! B@! N".-)B@! B@! X"M/B@! B@! N".-)B@! B@! XؚKŅB@! %ӌxuHҒD< zn-Ns΁ 0a-* .ǥ-G P$$i! B@! B@5:%W! B@! B@ 5d(%!! B@`y0$B@/-B@!0H_&B$ Ri! B@wGkK7rB@:,B<.HdE! B`( 4:j{ܷ˺B@3ֶsB@! N/&= EB@!0D.<4B@! N_P5tu4a?}X$5B@!ڟq&y10DB`t l:>5kp9O?U GTT/_^H*Pv?!0}v/\0`_~ettt[nQΑE 7 *B@!pAn]]R@~%p]X0?R%xqNLtq"lD\qe R~ӟzkBn}-ڰe4Kl*"nP#n{:vaQYlJh 4܎6~|TxcKK 6n܈U|_V>,YB[YhSs46T 쟄,%&D0-8o~Kv>s= lWh瞫L3avٳGxW i6A/ 5+*wZؗ [n`Edz{{j83OzetMַo>%?.o?Ory6vՍE)̚sf+/_W,mXW=r-IP+O]c,u+}3f6oެ5,6y \_>n6~f?3e>~ɾ;܇XvcO/h^'nO //B`,& L%7RV[:XIػ!ʴP]A=L+P_>o( TaMD"Pij|:Y0'].g(}SbRM@c8JJ\霙zMoaHE}rbT:iLTЇhR1`@)R׎jE܉IAfJ ^t4QSӄ5U$TUB'5 6(>t6֡:el*`KsZP#^]oc޲mba)jxlV68lY={ R#~lpÿ,N0.oFE8#,NhS60ea~-ׅ-AI. .?nAV`\v0 NGz1y/6T8sPWȧuP3lo=48~&^ t W|+ZW/eq]d~6%kkSqjRb}`xVHGE/XH/={$4s`R,&ÊX7qr︁5NW)l(+iY=o}GpIWTu zٳh"ZH s1]UcET)ؙ:%~5mT8dGxyVbXR|&bA|\{z:(=h@\$FenDEp Exe!? ioBGSG+ :0t0R >"-+uo+/ny]UotG=={&F5-,G03lW~yڒτU&>4~ix[i(p,2qYxo}\| ݧhx&6AڤiVSg8E!A-rݘ L8p>zqܟ5!_Jw8=I{ǫNW=ؒۀ?Wl4?MM|K:ዯq:|MP+_"[w ,qتF+3AOb++^7SyZv ;PZ|86h_$<*Mb-J'cwڥX=3+&[l'AXa5*qlck/q90UakfUAF7Ĥ8҃Ov`'JT7. 9Z[W3j ۱ɈՅXNeZ^_zZ2dyv8=L8 QȞloǪZd>4^GNQR(AyFÎ ipESs#iwAEd<†=+V))A,f@u_Hq DRt_)կQo.5hs2Л(3ڰx|\w괊zi~*u$L)x8{lzN}+3G 3ĸ1"&*PQOŒR%׽ ?[{{?^/CyYu(M _n.FYC+1Q 51xp}fRz<@~}׻(%m Ujt]$<HOy4׋Z |v‚3Peh8BmY[]uq#`9qv%IGj~vlXu|:L,<)KuaGm YI q髑(MO2s[[&ǵU |`;e["Vl-e+6AYyS3צ.l*%VJ9vD?v} ~&ҵgT;SpQF3K/>\kO(R/([^|hQKo-~fkh>]CWء]] W>iQΙ-thB"]Ǖ\\XO7qskxO[{MM`7j Gbzk;Q9lB92]9 xS,Rd'h~mt/AUS/JcJ>+&V϶FJ3D#ݿ*~*iVn &5kIrHƢ~+iA"D@>T9Y螜Od3ٷ3gZ◭!}%)B&0[>РM,B| yJY, {؂;ܯD &hiAۮ%ʋ/O4~S̿'X&,L> oaMp/[zp>DXKG_&mhEhhhP>S}}?ТYx;-[(ϐ?,/^5X_:VdZ5<˚3 &p;rR2דO}dՍ}WX4&CY }O Slb&O)uM>2 -~4naÄy֣23=PTuVg$݄ʪ:Ei*E@&>~wVc7ԋqMh襇,PSAGƠ3ѐ ͿD#) 4ͲEh5( Ey[6n@Jj$-U,B mKg}5jtr>J}**a.PL Vĥ*LTR<؊KH0#pۃz&F3 #*= 14E / H!3=/cte;hjȎߍ&rMNa_zZԺյꪝAڞ v򁒁 ҈N&(K[`oGU=}dM*9d,8$^jd*/8jm =5s=;SVWѹ2'鰐 SO[8vS;iSzzۻaiEzV>a֢&n=ɇʁe$t#5g5MXPyO; QU$0CQXU}doFu$WMJ[w\&#җLzNG!\gc`1~ziwXBI6nA]Rb84 T53^+N/-kۨؕv1&V{diK fuZPQC8Tew.7sMVpKګI4jijvh vpOAJf.)rkRRٳSU&뷔REh*_d:3A}h݃Y\-edmlBFN.2u֕tD^q}:pP{K݌S)N"8p҇x_^c}EOOo3"x3Ovk(S S sM bk@ڔeFا/kNZz~9diXt,$%%9:ȁS4 cj9b[鿪"'3%_&8h,^j_)i?--5Tο9-XD|[n},sX ֦e C!wklmB*1%#*1Ցu i+X~~Ale2^G  Ri0ć](Z@Nqaw(/Y,lT KQ :mdyMO\b"MIsgހ.#Qyp ~=b!HcEMT9|,s:|^rPiѧ&NE늜V$MMUN+ZV΃d.UF,'!1.@ucـV qi*e}Cd K-=SBfTQ܌UVr!T7 nK"g] FUMP !s%j`JȱirJ !J-\'0DMU G))&m(쀂"q zCtA!Z JD:t-REDDTidl"L$!/2D-wklx1d1ylw@z ɾ몜4:%dyUMV$4C eZ [S:# 0u 혷90q{ej]7wx XM ̃ d&M'5|4H1sQV5׹/6:ЀX)pD ZMsYG]gؘ$,3*_u sf@Eĵ)6> vQ=/SAߔEj>$gLr'^H+j 8ddfLowkdXILW 2D 2}t﨡茡V*n~No~9Vj$[IAV샧w You T~J^NTE篢{FlwS3b˗/^E+[_/Mym4mY9~],[k 6=-E+6>I)6gHw[;˱"^ a}YY T+Kl ){v=dqu׽w'A3R[tRtv5eKM.RhM;k  4Sl4hРͦ5ɶ-r`{s';;^'~ry'_Stޔ&umTc'G&W0h ('A7c,LJieY>qVV.n:P)-lO)餭J9!Džs'eXJS,~Ӵ&GY[j%T3W 59?OٱMFv)%x2Hj]9ۿFK~KU"kZ&Ƶ%+HJ͸<'Lj'dYTOIezr۹ǔcJ>Q:y6??ЏJ늵R:yԹYԵ7j[iƮv_Q2><;?~%>R46d\G?UgrO!9?5QVY&4RQtѦRu7ewj9=}OCir刻ڣU4IϒZc:TېsAfdw6ِdfhXSY4P ⲣAo!W4St38Li Ah6͖0>u>MktO~b! -\g5_.M RuR3LۛkP:7IyJ9=K gCGeBRNK#=u(ٰNWQye?uFMu#)%-gЭ6~to*<ľN [.r~SiuO׍2ףLV)֝GPLU6u]H} =䙹3A rذ &)2mOv 꿬 %9RgBZ8sB!4\ՄRL]P'>v{m[_Z`}\sZ ƖuԸ*b̖!EPVpܾVbe*w`Hxrq?#>ӧR(5 ~뱎Mn>I?9 f9E:Kb&,Ophﰛ yЇ:ܪp@iU9BT>n^(vXbBH\'>mD#ecA⃀IJ"% e}}Cbx3-_3>A4pR0Eˆ5'w Tb.b0^AW`(?jGZ ;f!IC~01o{ۜ\9OR,HK_җ)&xZ6/+D;o IAK jI9D\tqplMp.,{ό$y|r5}>DH-ǥ&o{TKB]P*y*V"FѱR&hHTIcCxեHT=o}-W} Y˱6I9gLr5n_wj݂\j8>ЩO }%Znjj3V'rUjNyՔ\R$ZV?BZ:k-zܲ0I]+qA$ pMTzMT6Kҗ8Cji *9X7KYz_x|I}S%eXnlPJ+#OJoǓ*`ܹBW/]X]mrvV\<en%o"ǿHg]84&3_7e C#$J݉減MwJ'CUGMjyt򮶝2.g`I1U >: S.׷D֫UI uC^ [S)Oe[jS}rA¥u\9vMIG{_ySb~c MoӲ.yEyJ61: e%ߦHA

| O|⅘<(r K0w󝎨"B$W+OA=yizK]1{,C'gR?,nYqGc% , Oxu}z'LsV=2\{6 brbuAVB{K_Ɣ܁x!s7D.ܩH٪֭$ rǏ`=9 "ΰ )l޸~.E~qe!p%!Gx9ILLTU*5,I٫0[^{LؽS}7>>4B>gR=_ʔУyww$X}2z^H`g'@NY15}6xCjԲ Qy5uwUyfJ$Ea9;Q':yR5hlp-hrUi2}:Vj^ꪼ(˔'TTeTt EjN2hl:?h+%w)m=)UO$s7z!wt1cjK:g3˕oQ92}rGOk`)<8sSUA΍!*5Vːq(C1}KCZXjWuTA]]3})5;d%"HRuO^]ٸs@Ft/]YbW*smL-VuZkPJԔޔvVhL%9. Rr^d U5 6Njq3Wk6INsߏy"|^ei[l3,{n]1tH[t^Ԫ$ZfȕH[\E;鯼 \Pntk-nm+˻iuW̳WΌ辷avS+'8rk KJ=b̄MU9\l/6m?52m5n]Ffzn!raLWWPm4֤s|gJY~ eBT ӌ7éYֺq8&XxWpueeϗTy{ˋwAa^g1mxCpe_?yp$*t|Ga' 2ܒ:}%o2n8oHË2$AB9 H A]/3HAAAC0(el8A!_G>β!\$cx3[9 *N=#F1N\#^fA?4 vNpyV &ob|HG.4pwI:ʑjH ӱK%ŅؓhսbXwˑO&tT27)Lúu2}קszPM4T[jyz|qU=EO8dH:HjWw=CzDh2[T?S}<ӟ/t'ԏt˰n# ﯖ_עÀ\';˷LJRP^c/nMdlܻCJzPUҳARG;f뛽j^UMsK[K@bu([-8]ޥQ}-Ͼkpn=R>-oT qG Y%=9O+@{efo.:MU-^gjvԽwRO{+uLy=.8,$[ _0.!C&O\u&Z'ݷt-Oչuuhgfz"5q[8Z/z,vwj{T&06d*/Pz|p^jv⊹Ý@p_3B.uh+1|Aڪ$-;{efK}[sDI;kuCxrצdj9IKzo'4F6)yF/yzJeC[4tMiur*q<}&>>&HꂛxJOڦ'^F']q:N͋{8?-WK"2ծk_zn\RD-tOOpZSjIJ/q'ge~Vշʶcϵ\*kd18+WOS hhT Kn][Ξ!ٳ3~tPI[_uE%(Vͧ~(eA0I9%M)t#8&moEЬ'O ң۷H*Ҧ)+$DAKtlmI$i=]Oyu6SꅪtQfAe!!}7i;yi=4|gɜoIxV_ItU&m&ח:B;^yd&gjLtMs ٲ[vW; 4zOeuSi64a!թn<{X 8j/ɳ81>yl';are`ׯWGu?w]4x*n:CjFO8ss[e!S&[CL> OL^Xp8n.Xpr t?*Jl!`9$)0H'4JYmzI`qK3ӾMjc0 B`u$Bq3}6%w)~s>gM?d^\1}/E%`>&sX$*@DžXd尙UQ{Hкkx_Q%}f2~HX6ޔ[źR & UOԵUfub ^Iw:oItF0_ ڌ+xiNs7xMXLO;Ee.W4ٵ$VdX.C0 CXH.+V*Z>zf9 <:QKZ9oZ#I?]܃;eN6 DǑi k[&{rc?;d\6>pi,]>^ Mu%KKt05qmρ ;_ R !`!p `U0HY".1 ;1?ņ&ck5h\ʫQTTKC*$5zEvbrɻ]V!-M/%&(ƊT#}a)VD-]I:sHLΩ\ufn}-_.S'OʩSr!Rs*IL1_9k Dsz:o"^/L-d̶$0 C0 C"!bN9/w;dT54ʒ"UP(9u~,[Z9sLTf/̇ JTh_BȎ4ty_LE,yw@ҶcY>KfWzB}in/;F2ߥ^v*ML{;oT9d5⥘֫^%dq! :yBw`tM!gyn+̝Sr~F8J6=yR*7O&}0 C0 C0 .SB"ש1%DJchlgdgokurđ;%5MQKf1RkWy}Sz+P:ܑ"wdǶuJKj%̨#w*dӺzP"no eR@S(vmӷA>"8sH#w6ɮrVUpt]&2_9>);clTו#ݜ%dkba+q[BlX'4IIy՜!C{Ν}ұ_fU(ϥZ3 C0 C0*k@ 2A$NLKBmxWPV%kR k;\('͋)iwBDanqiQN\HKZT_8d&'K.noNOquYScmqFRyt~7aE݌< iV`_dZn,#[g'[&jdE[rVʍI'vylY"GwKgjm.o}>;ۤvu_SOJxk!`!`\AX>MS.ժxWR׼I՗C2SD_^"dtM>==6"3%gNQb&/?+z5MgWF9;*#g+mKT-SFznsrQٴ\ʫ ^u^M-U2ă1U@*ZZA쩔C3\~%j}[T8EX5Mmn2zH{eL|6R*mR.Z~u(WͭJ5)ik*UIj` z:rWF>wLwzٴY>KǢHBeiMcKԆ#W9gTqil& r` tEdrijo>j[+m0⒒QZ@i9sG_pe۠V(奤3^s(?UW#DF[8,30lh"ՋdbXɆ魚'$k^t^U++CV-/~:ԪleliEwyPIHySMWg (ɦCx~dX&qH(;$kdN tVB&5ޅmᑳܶEi#ubyu4U3%Wax37#A}P=O!`!`@QO  »OzP)SrlQhZCrcPܮZ'1Zg\-0ָjrbHwˍ*w>;#oEoj΁EetިtU*eZ?-0ѣAS9djGza̍r-YVntVzS uhjmukVɂiضW6H9v--3[^^jW*%xfVʊUMFJɈ v\65IVFY};w\U*n*fxV]Bg|&. Դ- +M릴+\VbS社{=U1ިJ~I\ 280$lpO ;"Aʊ%_o,03kvuC#xVt4iثV|n)߱QBnr%T:P*RUCjU d;4}_I꒾#.Jbm-Ӌf-2>]&N!yʥkBދj{`.(bņnjeStUk1uތȮIg̐uжIW]FuTi|k*Q{Hc:褬Pbz۷ܷeݗҒ2VxBVxeKkae#xV\!tT8؛!`!`!p!pb:Ejf:=5\M)+QJۛFI6IyMkU)kjb܉ppF9!ؘ*ڪ]8wReC%ue~yL*Rଌ7>64aU2**U[֧z>`Č"v5 TgRqTYA:z^YSQX!.[uZ'NM{U'7. VSڳX*udz%RugzL}%׷ɶ泲|0J2%%wvlݣTa뜔B܎;-Rh*iedYr^8- Seʈ8GSbD-#UNiAuMJm j5,#Z!zАQ/ML,Sf%w,vN"h;ֱZaFIRרO%CFei_J\MʠZnԴ6KpʧM%Ԕ#gSyՂ,{HZLd&EkUcJ1z]r%ws5ɾJ>WdX`ʚ2r j-X3$ IR mpF Q8U-_R.r+j/ŏ!`!`E<S^뿕ҦQG#rP]nl+GZRq.+3g,BqI5Y6ABNLVJ*e]CXĒ X39YBXV1`/JV=ko%۶j\V1mKl]͇hɐ۳˪DyY98:&'+P\*g-P:vJFuxkZ_ߨ.{ZOcZT4&mSq<!`!`U@O-U+,.S\4!JZ"Nuszz,V#Ti)ӆ"gɟWJ~$7!$* E@09v}(GAO.YK&kguЗ2*BEPK[*)*]\A2<$0D zIAHZ'h߱W )қJ?'ꔔqUn>yXv )}#gsIjXKLh|?7Z:UxʚG]H̼ X3H% 9BQ֠*puy4>ޛS;0U32YT.CkF1iiLcT;ηf9HnVk.@TqPSJjQ`jcĕZU+&u2}u]\C\^ 횪tk'-bv9| I Ewv&NvunX--gg).* 쫹Pk+w#:Zii]ΰ= h^,uu)ҭ`өjwȆKRMO2?f%1͗3:'yI 7]1 C0 C0~7뼻UBZ5rԠE8yRN8&-OOˑ;)9n#hN+mRuYͲ;䤖?~߽_\410t舜GV$H5([w'NI:p gэjĜrSvzLGU ]]Tg GS񭶹UկOhvxBUv=YǩvKrv;vr:Reͦ 1toܺb[1aȢDISjqLIkm,7OiRe:X1ۿMz5kM˖Byf9&IvTYƕÇ{&9A%YxirԤ8_IG3,whո$:{oSYNɑ;e@I֦^Hlk#u nM輁Š%sO~mҡ HZS56zhXkėRSk1[ ٪: S:*r֠<ԽUn#5)VIE"~--5:ܷe]ddu}ISvtDX"R^bN^,Ԙ d9KhljDK؉Sttúe5O&M_iz* ]_']!i*<|\OVQ2 C0 C0~(ʂ'څDl+zUAwD*?zS JZ;6qͲGMۏh.uiR%ly*znt9֢yĚIՙ+]5HqlKjѺ}-e}Õq9ӗ߸KKzY.*۶(1U 6K9KIb}7Z:H^9GUT^1y{ZL ,YVRY/[6gHBꫫk! /F嗇ϫV͒R==PP7@*Z|XOK15JL6j0,krְ#Ǝ}iW<^+!`!`.w?M }nR^.轔^I짹6Ⓘ%'EyHyG#D_dN9ZKCゎK*XbgX$ϸNU-eFe*y|ulu27}NΩ8oqEn.GBO}O˭΍L#0J2#F͟{Յ/fdOqct5tۙ˖ZbuIM.=1l˗氫Wkb{Qt=A ^t; /E[j\sz,?1y7_{/~ ӵROdd!` @Q<X$9Č/LCY!A?6bTaێ1/y EޘŔ-1Ui"Gʔ܉k4nLՓJy6Q\U ɭ'gl3Y b[sj{ZnS!4d foD/\_m݈z=~W{-кOP\0(Srǒ!`!`!p#ϵg⣠..z.]XԴ |:`!`!`EE ve'B\\Z.,U6AV&޹X"Y!`!`!`\,l 0 C0 C`< Uh!`"0cc!`!`!`!`#x.6V!`!`!`!`#x.2V!`!`X`_e׮]v~Eу%~O~w{D~3O+?9~ȹs"EpBf?u}wNJ`Ѝ}/-##8S4GƧ|?I~q|[ߊmuuVn^$+SKeA玝4 C0 C%/%n^җ:%v!m&G-))m{{Y/GrM/^kG$/zыx//B}1ʬ_ww#iw~wy{#GJ;v쐻5#" ulْ|6l 555oory+_){2ƒn-[Cq_Jd@ jM'㈪KF\g!`!=qpJg?Y}NłzvrooTN>-_brNAߴiӜE8x_-NȢE+^ yHs0TOOO#8 x D_R HHB%l_W/\ o|8|k^! \'Ą|ߕr{ӑI;Ozғd͚5Io[Pr:׿;dҟٳge޽rAg¼&Ӟ47XkǎO}S2y'>1-xG?* oxCF .G<-///(Iq)X2\2t1c| }t\dyzC0 C0 ^LA(X`y$XAtI9qℳ RF{ pAA-++UC2V|1yLN^%"(*<((},) 5 qy}O! w >?Oum ȧ?҂E;0y 7NFF Q?fܞ ׭[y1G6CW n ^ G׻A a;16}XV4weray,`"c_,w>Yf\Y[OySX***h S̥|cBA : "} A+֡6xυvz}}/~_[g`Dq]1ǿ#Wx?k:|(@_|c.jzr5Io!`!`\S;(Aʼnh- hjjJ7 tHNٍRB5}sLqaUښ!K`=O>80p"|/_\>O:%ܷy7__~BƜ?Y4A*N >gɼ#J>yp= >9+T?~s8n8$HHGߞq3N5FЇGA{ֳ#G҈wcAA[oԽb M_җ\m"V-Ȗ\ A[9H1yt0I:k#Jߎo]puo5Lc CJo0o}>rq_X.kc=qYa]Ɯ'/!АA+r֎0O{\ܐv htC:t/0E.b}ի^%gΜq(,eJ@@H@z7\} QأaEḄ 2][`E K81_@~#?(z#`O }#544\+.~1o=ߌ%9v<w*:Ơϑ7&ѣ(GqM3{yA\#,He<ʳ؋8WyR˸/| qsqx, 66dRp@;] US!21Aʀ/dLgg#zm~ËF{\o}Fvf8yr??9sY1f 18j&!`!`@((2>- O!rP>qeP~a|Ŕ'$Ν;]466:BB$)D<(]p E>A|)+WtHPl9M'a,:u)HVO!gh'(X%M[ 3$,Z@$0=^y>V((̐#UUULGя8}}wbNl b_$GфLA Dg0Xo3dm'!?0󂹊u7aE\ cu?x)Iq)_Q![ !ʼW7֞O>9kk99@j%'kyG$kq)Gqsx#J:` '_Xa򳶰0D>ڥM0!zЃ`f~27QCeȅ~]ݐO)' )1K沦cIbיQ7;nP+?}w'<}%bȸ)io~YlE2̥G'3֢'pb!Hp$OeZt{~mF҆EȀL~s>𱆰 9/o̜+.[]EVؓHe5'Ɠ{735MYB{{5s5n!`!@@q*\ *dD!W*<|q1Q(B‹"AczAy((>!3.d`y︭l߾)oIJA9ŭ0[g0Cq+ =0o:﾿^d/ʼn/x_ݤ?XO'`qDܥ4̱B <7@1Eas{#nk q2%nXXC@>b%@-AAIgOzKu珿; c^k,l`2gORDՙdn6p#X'ou7H,k'xX=AH3Ie`>ryL|IuU=' ˚u6ߥw#x~Fb!`!?S $6"‹D-XqPPPP.OmILc ICa@^)W~׼k_ڌ%1Bp8 7 D PPhd1d܇n,B&ߦp3ށm{8#Sy`:-B ˓ o-5< Zn?7~uƌcGfCv ʬ8)9a r !7yA|}²»ANJ[U+?ߑ x8 oA#Q6Y?XWAv:e2OI6D֡;" jE3I-d{Cp/C>aO E{ A|&sYay2Ww#xQ3 C0 C0F2cpp=&.i d^e)TKq E(Q'.QX"^)k(.'01)GQVPTȢ5dK|+NG AQ&Pi\B\BPxn_H>1ȇ}$VNa0Fo}]ż <5 KA}''91vXq ۘ 1^!]?(B_är~a`I?qeBEY'+.MeNp@i9ɋ +*^{h>HϘ0 |7AVPGT/Ğ}9#Է`y-}_\.c)XTW-D%uR.M3pABsw9;iuXPgt@C~h &tadA[|JWU.HEݟb״}q{/{5s5j!`5JDA8廎'b'~{_\^>A! xc@"qb)Jn]N䁜4!_!4h#9a2|;{x%$Tw0BƆ r&d)k^'e9c9|B޸|bQOD1oq! G.PrPp 0s`.8aN(цG`<ש5)?ؒ!xDZO9F0Q;\X`-?7DuG%ڀLaH| +Წec/K |\‚ސA9DžϜ|<+^58rIvkƂHqlD,hDB{n~={#q )Fa+/` ˁ=}˚q^۽!`!`W <)œi-xae/\_Vpm#7.H(/(Q~R'u{ZO=(Yϗ'XdyP = `>SBB xB8@ca@)nW,d,:OqcF](xa^  `de^2Ql"cɚCᾇ(;!kي'aB=Vqm9X'Lvۆ0HFe9{h:8iu\lELu߿z"c粦d(v/Jf<7C0 C0 C` P' ؤUF8q)Cr{/'CBà̖od풇zYv _onaۍzgˣNΤk w2R?yc2- >?tL|͠Y ^|/LYheϷE*v0 C0 CXpG?*ǎszӦMggڿ;NA!UVV_z X1-u{TUUko~34tX|K_gܐPp:;;]Y\B=OǻuD +^ ? F{W]ɿ;PI)ԉbH=A~zqysu׹N^'Ƃ?\***Hq';@ƀyߞoQam߹aL{s7Kkk> g=YN;s+_ _]z/a"}?cU>,wqرឃK ˆ #Gg?Yyczϑ !25Bb1>zիd7W!oddD>8%C& -ёKSܜh.څ?w1c%}}}n>qʺ3g8LHwKXxe"qd=003vU'9Y}yr!w}y~йN0j>O"#nmosD/xAϟ2>ϖ}c.6CМyLƨ9oo2{dgB>uM^8޶m[gwbW ?ZO/<}k0 C0 C (((,JuuS&QZ-[&֭s OQPbQ0125\☠\>qA~G @|?OUV(7ɵ)_Wݽpȑ9XdB1$"_r-NF"P_. ˔WYg?/K.d8ٱXd# L'1 (96BF2qpsylnj5r 77 ߓ 0 }_;H @%nב ~zt$b ' iĉ}".{dVҶq`<򑏔OnYzԣleGp\4eCbMA;R!\c答#^1|$,>dH+CC$I Z}y{ߛUY_ ZOCtړM2MXD͓qfr>򿢧's\;/ytu]w#x.'ֶ!`!`@"PQ҅b T666;((8GQ &%+^)Aiǂ'(G_4B`^H,-~ȆS`hyN}>E?Ä'T|}2z>kk^ 1hRB ','|b5OqD|cmd7~!^;Ŝ!QW6h `DǩId2ۼNDO7Op+3_paޞ>}K[~[I0!/V"CbHQO9CYs;$/j//|f< \( (*EaM󥨶qD, t|b!2'h}wƎ| Cd~U )@` |u.IƝ:ٟ!B:r}6r!o!`!`$B۷ow OS+"1“n`b'(=/8+;LE&n PI!.ʜO ||$0AE9S@l@Tu H0Iu߿a`p ׳!hwXw@se %_¸,LI2,$a; _op\C yrk}w. rԅrbA"=u/I}++i4LC0 C0 WX1H1F c' Zp:U&x QwzzH,wKT\pʁ,|)@IDAT^I2Ot}d5?O^+$~1Ȁ/'yxҎW0.pn;3yyd8@>y 5XG ;ZW|cXnǒl e :N~0˗s5_uOaEܗBur 7KȇdnfAbϗ{'?b<"6 nVX|~8ō z (~|)MNx 6{{0r㏐&L a!&X1/NRc.BnU'oL]UPpI(\5 [>DU,R|nԓ9:J}g֠?&LT,^F˗ -_ $1('y 7\‰%N%$A B%Wץwes/e֖!`!`$w=/J\l$QʢW|iQ*xNÝ{:!Q6@s{=kب6)L׷׿`=}O{vqS|z|9oXe-Vj3J\ѮWhՓo~%edJOXx@8aeE$oHQy m0O&[;Fc=AAԆOwU~>|!؊cfp9$(3}zYTtOcNcHkm?c a9p|k_sVE9*y+G叻Vh+{)ϥ@0 C0 C7(2?'#%ȗX(t! q$xD ^K9>_!qEe{!قS/l 7 7 W$R$kE5W`}uPb-<۟|m`R:ŷ !Ë >]ec \UUM *@@NƬQcΤ6i5FOifc2gIkD'34RTPq2hWb6DsZ{ݵV/;u 0MQZ)Ui˺^esuxN @ 'y}t7LZ!YCTqM`=xU$AѤlSZr3mKuB6VzŨxx&0%}'`WNSȂG$@$@$@$@$@$p^М23a,梂FgI<5fWY7x#4h`rh^x3y]<+$w&z$Vѭ";I{΅\KHHH1/[^9\  V&Zqs2       zx)G$       j%@Zqs2       zx)G$       j%@Zqs2       zx)G$       j%@Zqs2       zx)G$    AK/5k{h #@̱$@$@$@$@$P!ǏGfffVQVV9RngҖ $@$@&pxnd2 ĸ eB3Vr8ŐPDDGמ˷9XI$@$@$@5 'N@ݺuqE3TO p%Xoc6. :^Zͭ:ﶋ/k.b{rr2ԩSfh}Z+jshĝ'OV^ʲEt\;{ V=qG˦[7VgA*-Iz78l8Fw X:mGG'#˩, V&. *G.febSk3co,R]DL[ ' GxM}ĥX G/V1 Foo/ҘŨQp 7sW4̎;̹?W\qrrr#""yݻ7zꅇ~G!))93˪Uׯˍ8\{~^~elٲŌ=|p#=sf۷O?do6w+QO?緿 ?kX2qƥڢ<cڵ0(oC T jƨ`HʾC 5=k8PP)Wz6ed)koQF!fTʏ[WGNŐ OYiXڛHU1:k #=i 2rQ1R5itr!i&d8i.$@$@$@g 1FE #,\͛7 5 +<# 1bPbb"x\uUؼyw~QO?5wy'nf?sAXXFiQ!iٲeFP(.. xP^=DEE;ۺu+u놘7?{ܹCHբ:*BA*B0zjc]we|wxꩧ5vXmzG 0~j¿l:z3YEݻ)XPN$@g@Wlҧz7?Y)O.YܴH TjS',ǕfNz߶2]ݓ0}ahUY>6Δxr4b0!$u&',1!…^v`IƔf<~dg&#+ca`G&?r8vᙉ sfM[?FJZFt%. 2{pC<-r'H$.ET-xX)+1qv|[69Ak,|'[ʧ_,HoV,l'z2ʓ2HHH3Gźum6#rX!KLZdYxT2:z̨z'cG CS^tT(:pQ=ӗ^SIף}mj?/\1EU9%2Yi}8X$Gh<t) E?AU0c{kD4wIsҵ)F @]*Ak뗮Ѧ-@ބnK-…y۳w'KXzJ2lJB@ldq +N:bռM%auѱ}{D6 @mFck蓆gYEE#u=:udH=[;cf Q*0ibj u?Cّ+-WU6ijwz .4ȾxaFV/ &7# oΓ_FM0Wn,GJ~=pg${8Tv @|hiřF0K("40iH͒QlfGQ[ͥ" #i=˯qOLaUhdY(:04[HHH n'†~Ae_NXZ4zbO[ l2l9nAQ?-u+uRUz![Zʛ[C35R_z%#>n_Ң;Уɐoy!PŋMHP0{Q3f@éXomV-*fuS|4h`%HHZ>Q3.C_TOas# .[%j sHtYZ\p9`_Aʳ}vP|ec:,s,iD+ɇwmY˷,X8}5ЮTrt'$)"N-q]yixRr _X[I!̻y:I!QB"cp:ұeX5C.%`[QX0$\awbw|c؂HHH$FIEE4U4LHCXnz]æ4l^(ηmysbV5@ͷ*s4Uc{;[||9 %P`YҰr[`8/D)ٲRW%k ҜҧnDLGhƄdyon@nq[Eғvo;vXqDe:̷E/iTInñm,?PlOc e3x%)woBr,cM].,N}6r"ָS;rwmdi)ݽ (&-a>:Iw8N{$@$@$@,M~yuƌe- R-FCT7Y=^ŝt7wY~$@$@$P <ٞ슶]{ޔ8C7wM&FvSf'+]/$/«!+yΝSFmt5 9!$@$@$@?|޼y/~{GÉ:\ves=gd kOƖf3Gã]HHH.()ɁK+^tכWimNzysG$@$@!r{ãglYn[pnI?{8)ʀ_2aZH9p h.0[71S_@7Gi %Bw$q#[c1nTOgɮbKz+Ųz1kHKAV*REIr#:'4[W?":y@Ć?Dtc Udԁf~8f"XZ k.QFp1ubcEHE'CE9YzS5}_RInw]7>K-Ͼ~e59S3[R۟y׳Yi}0?r\?(;uj!U`6 )AV-^풕{yײx]2M;߃KijKւ2a!87 ЃܼoHHHH>?xiA7m*]qw~\>kuE;5q(-< |G՘?ތw }^X7-.+Vчr $WbzvVw[p'#XTO?Ǻ6⥡;o@VḶƛdOcLؐ/dT~eS~jVv=} -ۘ&xOiFW7OSj6?zj6ccw~O$s6gʧ0&\L/2C=oH!>K;bګ]s=.p 凕>_ĕni}qY@-x0M;E T4ԆEKj8hxՠ@n|,\~vSBv-vxL$pn(UƹZM$@$@$@$@UB@7> }5<\kؕKPÒVŌ)q<ȫzmwq=w2`oՏ֌-k S#hGu79TPD%:{"]-p%ɩ^SZiZyەtP4:0-ٔz~G0ⵍf*TҢbBȚKd1}\.7R\lJ+M<6=z;WNi{,$#zuL̾72KӜM*_v",݁v &z4}+x`z%;:Q]=SMh$yTW֪K"|m6@?"R` E\!&Z`O5و*Mm>ebC A Mz?k4 G$@$@$@$@*9%*R5,ns^RAA==4q.PfdDzت+OP:6]ZG=ܲOѦ> #jw+B )p:{ȏԱ@^o?df8~ҝYĎFDAwai‚!R]i{1}GaM*Y$iT(tPFòTQFCfaxڴ0* SOn]OޣśOxҷ\%G$VҊS굤 #o2ƦN1ScL9I#waHwRKXtMZ4S³Sǃ-;4 ssp    &aW˶3I9yׇ\;^4͡A|9,"k~:e׷?AA=54oQA[S7,f4"SN5B$p~>dPO>J'uԕмK'Yb܂yJX%38* 䳭R^+\wt'X97VPؐHe*hovI;j*28e I!GЬa:.-o/X@BJct׮^iOrќ;,K>kv0U]#j7V-5)La4%&h)= 'u@!7걢kRg߈>qM&[Z7JQ.ٶ^] w"TTlEtӾzo7a`O߇KT00߲E<TnV:c=cMe1QBZz;Y k{r?tO$_zՔ$@8jx|hc/R7Vnne˕ *pWP0!hXT997hʍ$@ =8z }W UDF8?c! ^"+0' G Lfur*xXh2ek"F4HZ!-wm្$Xsh9!p\()bt7t9^T<5mMKsGs{Vȼv57~EŌ&ICp6J~&!f-vqg@!bŒQ}i!Kc"hLI?_gV^E])ۮoMbrz1k z(ypԋfbv Dy޾!l: z|6TkKeǔ MK،E.)^Ce^ ٺ禖xJ4r?Qt.=T[.Z![훇}f~_5L=Tj{u^PZIiҢX=fnxL$pn&G<$uAeW\< CD\%IJi; @?1M [?+&-D.9!GV j͠a89m(HHh)a/P^4lX`lh.k"<"Vû]T@2SRQƺk;wZ4F=645/-cxED;LTLЗͻ3ֹ^ݑt]&3,޴YxiȘj4R'\vA;aKnY@fEټ,1l*GGχavԊ[$EфV]Ǵh\D1M^HίdًƉ/sI=t5-*i ߬CEv2 ;TRyqha~zSc2 `Xr %G&V>g1& Y~J$@5EqPv9vBBBnTfmޛ0+ԃg^ WHK( SAD9U 1Ѷ}{Ú[cPg_W/}IB&P8fN-!& p &3fav}|ACwkW,ò ;Mk V"Hn f&!dyynJ Bq0WYY'=5HZ @ TQxsX|Gñ+fOA0Upeg•Y"^{i㘰%˙e8 .kc0{gP>ډܟkAAA2W\ā;ST 8 ŢaAl^BBRb]T9ƒĘ +ɹ#J\+ユ6gE}SH>oc]:6qn6^F;1h䙱X&MLQ.ȴ_LŞ‘\ a P b,fn#c&1nP 1^46;w ¶w}y¤B?ƊAνx}2d \ ++KL9˗a ػl!;tn 3' @ZyX!Cmt_bG2j&Q>1Ety@̈A=px!՟$@$@$pPHj*_TM׶hVq]+_7ܢIh7⪫ZYux|=Z6#DG.HEiա9ik[]+/yĨE ?͌B ?*uY<ǍB{`rH*N s8xЈݸ67'pc a41oyc|wz;Zjk$tK&-z{|-' @Q?ѷX~xF$@g@leKS]ֲH*so&zI& }iFRgvBr:۰ AS92GNB<`vXcph/-/BŒXTQhHH$%%L.ۊlqUE7 6lb>o9 󤭣z jx$k[6N܁CW`baE>o}xL$@$@$&WnZdB$@5@ xMF?0c^X4r"6Kf.ΖډZUF$.#0LBlޓ@IDAT|"&U$@$P. GՒK(rzf(>"lwO&2wC黯yld+ěѱsmi'$@$@$@$@$@j Ѫ1"8Q>yn 6ltӲyꉷ 1}q&'Npm$D!ʖEd#GOoVXw٥m][14<"[s˾HJ#"³ۼw'3qh Cm$|};4K?,}SN>ozw<#/i_a+6v:&tq{1 S     s@y>b1>l<y`. *au1~f& "I7c޼͂4]dBZb:C/HmYuc$Q7;Ӽn¯D@>lKhq-ú1GĢu$ {71$aEusY8G~h& @5}:yg}/?0~^ NѶm$/|v\6Mw6wM&9ּ2%v[]P혻A>IoW'vJ1-!cc$HHHHHjxPF<7Syuyy߲ &۶" 8d笉 cd--cLdO,̖#--ѫW$Bv?V+ D@5YhL]$acuat0;>7}m0 <@]ufq`uٺIyP>2c[N$@30S|~D4&`|$wj} }NܗczM==-ճQ?o6KLNW4"3Z3^g!    8\sy&;vL )Y5L|_$DђcFcM0o@ Rm([/ņ2m %e nSlrm_'el]{)|' rX7>UO״EpZgOF6FQe>t>7KIH~gԩS(((.agQs*bM58!o`DETp2C9i-Dz5Ʉ[~Sϒ]Tx<О€.wu" H:ԩm/,S$@$@$@ON:\.%7!>|812;CZȻt9;Fdw~݇rXSXkwXㇴŌa2w< 6ʫFw.+{:CsډO'arm;yDx@$@$@$@$@$@$pn8 x)|0s qw.kv_&s>y0 #Œd|1F$zc<|RP/ZP;}'nD)WZ;bm;͑,ooR\_݈~Z=\{Svmܼ4׃JA*[{VTpuǷhsK/R5U'.3"ujwbDNñ ؘ$|`cŇU}YE$@$@$@$@$@$@5@he`{ѩ5ZtjE; zx.gJ̕o` u7^Di5  U8~(=6Ʊ}PcH`췍U5fe]lխ5ιmC[NlUmW }*)6 e~h=X= =*6/ @ "PL~8ӶI6E !q===b}w`cb;JE+3>3:u~Z7H@X>ylխfpd5 mbdq+8>^襖~  ()c-\ ~ɶDEJ7E4.9?vĠGm/ax,$@$@$@$@$@$@$p>fo֛T7q&ѶPR%",Envr@"hxJzvUwe'p$"=~&H!k9CDaNLyz$@!aV' .HrxTYvU9 ]+HT no/ݟr`$%qNj:lONhP/]٤hrH~Klݖ?u"&?_tt۰iTNk1D$@$@$@$@$@$@5@h9DYQqʏ%&ΫN6oC=(zFEOzbW& ad*ڷrgE߸:(t24Czfa_US*\HHHHHHH|"P9WxhtnQ!,ֻՂvxY-l[?"yW)'X¯ AFwjm{dNs&« m)^ ǡǡ5c#xR9\a-zI"ʨb;8u"[^9r3w`-EQY]+^S2؜HHHHHH!oRpMh,;K5Ivڝ幼'/m)61|" Fh;~ W[eٶu k v%PNx.#@+Xe~w+9waY6>z4.~ >m۾W .#i9;=rv7F,N$@$@$@$@$@$@$ {sWCI0ɖk6:BԢ[+ 1c@XΣ]lL(S7#}nLM/#!m;>b'Z˥:kaMp]&ҝNx~Лo`ǎk# xɭS0?e+zpP.\{#!Yԗ$`\JA4@wYg8`Gv]9V8h+M0Ӫ/-h;@ğKOzQw˘g1}d؇5uoEx.F¼G9yN|) H~mF @u矡~ NBAA\.N8|\|qy_~UHH6L-[|5%X͌2n=O?uO/ēCbDg_QqG6rvn]/2Av+M5*QRb ,H \$4;޶a^5yϦDށ ݾC=ioWP[a=1kߧ]p .< L+4 8,G}sI#YC8: \ |0j8U[BSdy)CGpBE )4D"uT#-134.[E*sV@!p-XECHHzӮ]1 @y9:\CvMeAέ]w?x|:aex67uP'D^%֕r1( рJyHR&e=uK5+'*Q[*1 @̇h-&_%ZܒT,ś]WVre`~/T \7 x6 ZI%^!mʕ<X=1ny פHhnHHHfp8[.jժUPM欻tYEw]&zJV:E]d ]wsĉp5ToYgפ<a yC<x xav,!K?݁ѕ Ųr>S9*D$@$@2Gbر,%-- O=Tkկ?|5k`ŶjWqg̘1fǯ"ܹsq^йnTiQi9t :… ,hCAʛ$@$@gy,Tx~ u䖉YHHH.Μ, 0rUYY>sW.d `! Ucf l۶\ݽ{M ǎ GW\aČe˖_Dpp0"""[WOI$;fg}s,Pc<*"?&O{]vY^' jchAt[!/ N >Zkw%] "඾}Ѧqn:#C +iVmE@CqN41 (~:<'|8SĔ9 hV|ɕZ<T2'A$@$psB.H pK&s5/}ў/i{$YII,jO-zpZAmJ^^^o}yy'>U#'/ތ]FP<$$ڶƨn*$ 3 <dm#G׹ ] {Rx1js09S v꩑*‰_u9tE4+4w@14X*8(Q>8\;Ȥ!~֖=:K}>ȾrDߖ坫`Duk'yE۽eC4H'ωχY/[ɚu,kg˖-Xn4h`&=WqG ڿ?|"S)))D:uxbkFM{LxGM{ >ƞ]8ڠ/zMK<3a h@!Kcr4['1Ӯ7m]d~2 i_$%6}VRߩ>ʕ@ 2"uHxՓqZ =15KK ֕0(;wQHM9ۺPe-UxOL%O܇ׯGeX(dn xmJoS8#OyDŽizXX 7qX<]c`æo=_}:t`DN:aժUF\5jwTTфղtRiSV8h&/ DV>|8:wlN5 /D^ҹ}]4l=5kf4xӧ ;$˗/7pWwt)~2& ^ZȈ;}'=m`YAL<Ј1L/zIDI0K<"d̙M/Coϳ^Į{yĩ s巸6wJT]\8-n/&+6}7,&Jt!<[q'<Hͪ[L܁3SDq)#⑻Ea [RJ}s ^ĉ;9"=/BPki__}SFD?dGe\ks0gm"KY)" ##9~*HG{1O8V=:7e`>qw>nKޙ3 9(8]-M8Wvda;=c="7#Y~agVp+^<9~]2ǡG`ץV kHz狆Gi-СC,b~gZYȐ!FcQAEa~T]T@kEXX9+YD&D*ɻS,'xz,۴^R&Ӂ?HH xΉD#Iwy'?Hؕw6uhj58';.G'j=tRV%QۮF=cs:N"-qQ jiV.C&$=q2>06Cd邨?=:PA,/g{<<^",# w qKOevJ>׊(&*քW9,}S|%/xuLWڶN$W0(S.sFAJϒXidą>cKeh!r$qtT"?{eS񂺀k^Vi]qWVj}fyI[^*X%a+~+&8 w0HQf\yg=yIYjnfr&| 5쀟B3z(J=u|BB ɓxj:t|sDר4ON5,W*-jĴ]M0q[F3j׉NXt Qh:V 0YֻօlҦg0 T#skRZbL0>9Q.]lʡ p,腄loᆡ1j'"+iBG5v?'#$=emS* ߰5r S=DKr2z_EV|4v0\ȹ˺r[o<,ƈ `qxԇH)xsT "j8M7CGT>},-_%3i\MEz7E7YZLl<+>T,R6ֱ8<*fRU8OjxI {c  Pkh-ڃxF;tϫ?-x*ܰEzca5?Iֈι &.xlpRB|BOkC-pi{C"ŷhjؔDQI }[He-NbxTs6H0]}hYh)5I$o,A·܉;WqQ4#"y}EG>I}NbSN1]g% Ֆ-aZQMۀ$=N=KYa2eVI|hp7[k0 -`咊ksQE1EFFj#%EY2<)DYڸJ\m)vZGٴieuRK>}vӚ2e 5Y[)զ]vz,`2劄QIzӧOҥKѤI̛74ސm53屖=֭[uPgEHA@w n7u.((ށCF"@!PTp Јkaޟ& o 2 {-V߉<3$qk3Gƅb4.ZJࣘiẒݶ֘kn51B3MutbYVO):;jQgIԨ?,$9 pxR"'`S25#&1q^Ms+xx-f Ӿxs2GTxS:.(e %IG-pX*,ּ1[J)KX7, 3rIBn: ei2*MSvRRm(<@&5Rzn?wGsbݢm˸}+W@[m“z?,5e fT<fcZw c|g-Db~>^xA#|Ͻ),YmH(ڵ {"U=ooo,~]I+/Y <\HA!pu$]cz?e.5lKCs;Qɱ~bmC<~6!l{=F\Ypx 3׭;݋I[.gc;(-kHx*eRV3IGJNy=trRV}WtVT:՘Ի"$ԟ!a%=H0QMݏI<*n2V8#F2fA@[GA@j~$[)kzwΑQ59šh^ˌNP<~%ap]YIEX\Yo":Wt>JD[15>fΊ{Xf?L뼢LHJFIJOٕ۟U|a$[ehIXUފbG?zĪ@:dpWaN . ꃁ}"I8dac(ݻj:\q*Ⱦ䍨>'*Y)zQ%_ .\wXP27$Kҷɝ聈7x{,Ѻc% d6X"{$,6d"QX;nD4 Sp\& T-8ʣ+hR;D=6ǩ<]ŊQؤ@ITÁ`cy\w2GrOQGXJr@oѼf$b/_%08:[Of?-*FJJ& `jf1hoΎĥK0]*w)HKD(N{b!.C@Mb1EXl4'gDl/eTXHDp(=0)N+Bp!.]椄N\Iz<SJеo,fWr܂Ֆۆ̌ @XNRm=8A+9UMG Zط/]cݐt{GuEP%W^aN QmN023qŜw}#-x.ڋx)wK'o22OI۸P=׏tt\[^xR(  }%xxhS ¶AEY,JL2<[ !%˶y]IŒ ȲP;@@/Zd. UA`ƏE ͈$7YG-7(TIqÅs8w)hb! A;8SEc.Q͋2Oj'd|o}fWeSt$۶ 仡ysZ;7Cֈ B&, ŹcVWp,]I `v0Sg9(?Wc|$+2twז6E`@?'.d{r~9A@A@A@ 5JlK &EصQ;>k TX]XbYfT!c5~9ݹ\'kSkir'j Ć5DYXlebZ79Iqj4YqXBMT%LdXԵ"oěme9"BVE:nv1[X.p)]qؑο VVjGotjmt9[O Ա"ñSf5gEs^tr&wcL,Ǔ&kQ+wm~Nek<͂H#VծHi%  T>,֮][=D P(S=r&c3[ߴEp({JL)#Ɋp0sLVƩ}p -H2iQ!N)¾;jޠIZ3J$XMYXga枵  ca?<,'aߡ4V! K3EJAFR6Bw}ۄ+TqV nF뼠 <[pn6 ˆ )$؇xk}⣀S) r 1TgϲTseeQdd?Ǡ baƕĢW[r O4a͹w}39XRtuܡPE-^aѸ`~# ޹̣WA@A@A@[ja\ZŸ}⮐ lUNw`[}ICqByDBc$c)bf$>$HH6>ecbn^=HS"Č8q)F p",չCf"C@[SmHDXeױ-]xoay$ħ'.ѧD#c")+sHB͠fc8\y9lx4'!ic:]ytf20rl;e@ޝ!m&!J$pXR-0Ld1z[ SEsmh2''0L_\-k<:yA@A@A@p@gyͺM v\DkmrQ  8SD:.MvQcClk2sQ J τS^[}wm5, #Pssiߠb{yyyԩ}3gDXX8znݺ.jll<ȫ(G/"3O7e_hݸqׯ_ѣG5ƕ޳Nl~~Tw} }||Y"OW[)ZaÆNS]ڕ~/}ڥm7i㸱Cp(#^t;682L] )q$Cʊ8&9>W괭eR>qp&};OybǪUG:5t8U4;QQ`1ߓזRkƢ(#:2*[8k 2%0uU>:~-~\AgX=,ϧWlϻKMDb~w~k1g==lZ,D3{FL:ÌX) Y|H9fw/>>MBN ҿ'5KS=G ~ۼH@IDAT:=|BLi7qp186ۄ#Y ~ 3^nCqؔtxj trS>_.]]vٶm.pg典8Mcǎ!99?`oʰ 111MݻwiӦ3gvR`>WA@'zuP2Bl0(u ˭%궮7k}?|<[3 `kA1bӄ7vW %rϻ*~d2$3iFf|zc]>GąQAHLqӴO',ڐB~zL-<}>< ߼O@vJA d #Ml|nOBH` c O3)B},Jߥo_}&w|A'"mZhQJ9rN0l0 8uF>tR%e d0*-8Ds]㕦i9=5[Iն P3;bkS@#$;E{Fc| to(3⫴,{@YI';]wi!@%ŸFn M{o˒=ޜej$|0r8U6%TM秂yN.^:u6ːҞu놈sXooo4ok֬&Et4iRN|t Kήx< }F+.ٻM 2s5#jw{ps{;邀 #p7?*;oѥ@a 0{&j"X76`2REYZ-; w;W?biz=|2,]XWn?~ ʽhpb igkB{`?[6jOK;a%x'|m|`?#w%,fF5Ed>袵Eʙo1!_Z`%rz]l ?s?)f~gֻMX LӕqR'& n)c}_N:Ǣ|șᓁ%*cVgZzl1/ϒk@Ok*~?Qq(F7F!M3K1a9rmǢM3Ez(Wq8ަ7L#U,4 f, 9Sp4_"c'ьg/´ D!;-~l\JqdBx6,St2cW"1L)' sیx6ł02XP]t#M;;֪쨑Zs9(?fQuoD!8|?/N < LNoѮ?saʊ̭l⠧Y`>3`T$mN4Dc 9y M,01T9ƭO0=b@Z;A_ׁ1}RɂaӼ=,Keuo2?G:_5.85 o +s{ißo0>X29X_a.ݟ,""O']<)4:܀L%3e1 d^:jMK6=Iϡ!A!ho'e .QSbβy-O?7|j>xf8dzڢJX)8TWE0R$rRV}6{giQ?\;oEYZ>a+EhxwכS蹡׹jKGM1p_qU< :Ǿg] @mG .~A'`SO0 <)? [:Oxb I;ģC^8~5Xȝ>٢ߏD7^Ą:XBbw ΜJlx{']ԣ!/s~\ Ri|/p6*.&u|=/%p:Tq &7ow35ܠ M?B[é/sɗsڊmՒ?V1kɝzo "Px 8s{t13caW+oK;mTN^(>_+xY` Oj׮tͱsio^7Ow|SCqz _ޞqz-8h/ Pߗd+"IxnXdc+攲`R1rb5!qoaƊ2!7?G7פa>8xi#]?)_F! +?־&?Gc ZfΜ'otG3lo!gj{4f ?|\8?"> q/X !ObH‰`Z$cf; J;SV1Z), $27| sio k{ 3_׍!Ӆ=75qf!\䉉Ke/ճ@Iې\SR7`FSb$<G!c;d{~5Ǘjb1Q$w1|,0x"\_aNrDfE`gx4{ɋbNdYS=g`S>iMD ~\)s9|χ}+:Ш⻅*s]V4A@Qhظ w"e҇OO4l՜2,/zA.=ѿyKG*4c?O:jD,7'9TG WhS aCySg=ۂDRy}Mr:ZjA]i[uUZ˹Ml0Пzی"q`Ed6Ka&ͥ"}Oݨ!z~=7pg$=e "CkO#|udd!9%*[ʐAti"Ӹ>)p OraYBG JQE{ |zܹ~B8\<)08?]Ã;K: #H̘8ptFrq SY3u}"f@l>Ú,J[O?J\)p>u`9C]s6BDLm2诮P5DZ_})7FЌ^gh''bg^P䨡1SL]cbCXVz?= +c>d W&r'u2U?`0ـ9:_T$`p$qѶySQ|;&rv튇zH<ׯʕ+5acud˖nК]gI ݫ9+˞-Zsz<{q>y-nF9E~6DHs>aAl/uKupF8x<'OhӷChRs^[8sˑ  ,gF$p㲵K!n\-1!wX4w.^'mI&,$(͞Q=7L?eƽ) z v uO)r$J6r*mdQTRծeǘ U(ֱKWj c=bڗF롂"u01ef|K,e%1^UM=k wIt:[0-v{Ͷ=6%Vmj9_3Wۏdq;CtXŏXWm1ph1"Zx^%ytcmN)1uY^DYcl,5[1oZ;pN :?O!0'HQY;,B΢n0q"#-r|B *ҿbi__KXԳf>_^xr,pSGbF0$VjqኾM&L7Uƫ'H55*-6cb)F E&$m>1lTk,| {@ 8%+$؍#/mFى3b{EB:$ph%(Nb[S:D b20eͨt\M!UL{OaeFS ꬑxvTgḳ;W3emQV wP}*$w‚bo3}m_~@Z[uW$wh/]|"T7Ot ! zi牽k_LUJjZj炛oc<_JWؓ]{Uic&WmmIMבQ>(@F5jjQlKgmkŵm! _tLMG¥uv&wX'C̫O_ymZ¿Ii2u1+(tZ0$2,קmPccͨU֭oڥwrZeŪ%bRV`7%ӫAg%,:NSLQ.E  T6L2B_CeyVhr,G)|hv=pRX3qbWfԾ':3/e0DY#1񽤲xNMg^XbE)y\3Ԕ]RF3U$,NE3)Ơ,h /b;.Oh)Wq)V3[۾};Ξ=]V7*sLPdu,N:Ȁ/E)sh;m yyjD[(K=l(w/kRč1{t;rN3F'0-3?3V,A@jqkfjt'!~dvΞLLg_|{c<ָƵG! ւp(*f/ T\ӹS9xk>SsNկ_r\˾{tcIȥǕl86^ gܝ隷vL\FǙ)a]wbH'MdKJpVB׊"/Wqۨw{X 58Hgs"E~K>VLjMXw/]Aԛgy=UYWDzt'V-uyA1Ҹub]djݟ]'M_d0Y-ZtG2G$yu^z[d{13 ilMg.~kY^Sxy=mkSNsnS,:Ct }B!j8z/bUaM;;YԻ >~h3l]s0gjzP0~2ֺ=l~.t{E:)yMeӕ(3|y36фg'`إϏZ~k NY|b|:KYJO>ީKY(2~JfŇqD:figŐ(D>!ݺ _EFI,Ygm ɱ2jEe+[imnf[}y9R}VMe9YQTܨCδӭKocLnw6~Sգjl7;R*.G\caͯyۥk1]}i@5 r^s]ԯOBٟ]eD껺&<ȦFILJ[?Q|Tyu0e]Oջh X K*z)ENXo1 Dϝ5_[ uaKaB pW/|ߚxK0Zɰ!a5 #U zr bqHP~Eát 叧05a!F$BĄ!⿥H(DO~鶲)ĵCUOʊ0 M5eb e>5# ѯ.~ &2r25ٲ1uc*xQ m&,27Xx;Ӛ%ҔWZZ\8#' zYt ^.!9U9~äu`Bfb<[/v$Q nLd`2y9L-qc'bvbuDׇ='r1P:EYjwKk9ܾܚZAaq<[+Yލi~emkncClW1w?Jd}_T?e{ݗ1̑&a)HK[sk6?#ZQ5U+XSx㴥s/K}V>?7|;7lu;[? ++Km*QF={`ݺu EPPTl}*вu"Ii߾=>lͺoޓ-l 3W?5xϧ$WKwDe#g>;YZM"_䱦e$Rv!-TNuW6.+S.[fEJd!ad z3cƝHFd~N;޼Nhibι#׃[Ư#X>o)uiùks_Ö"b6oǵYecؕ7I!4_@ -/yG˜6oK,7I5^\׾%6]?nnD;1o>2'.C1mxy0wٲ}8":E]([yeZUťbj_5C.ʦk=Z\\7 16fa9UgTgDam=/^N6ϑs3' /*Ϗ>Xvb|e=j&*(W/]vغuʶƍI80.z&E(kgȜܲKskRqqqhذai:X~m[nwU<{1O:MM;lI6v"ރlRˉvmUw6OK°fI~Qڽwu\l<v7N-{ 1%Ӿ9y9yh&{ \ \KS߰;1]ct;OBLvFP}Vun\u0^~Hdߎ.Br'b(ILGĒUT|q a&5vI87vZVxhsۡ{ڠq6("ڄS%RE#t؍DJ%n&rZyփӎ3+SL[- YE𙽹8f*NkM;SYw2q9m03fNӖ ]DEpԼ@_R0Ⅱ&ɖ>u1sfg}^w(za6bؗzad4WHcr8t1|tٚǫ ^[} $LH$?he\Z-|Ɩ-Y2n؂-ߌH6s2ى`y~,fjK$9 ' äe5uiX4o#]^^ N5zܸQ<|2-Q?߀I<^ma7vC+W&-c̲]A |,|B2 ܡa?t[i~A>$yH2e=ۥ7QhR$u<{qy ئo]+( qN2I-j =u1m/RmIL?lQdF6£j PF~ҹN\9 7ln$3|e@rw4L䋜T{߯ڭ7t~Sc':]8ŹYtۢ}ucwD`wy\ϧh*ɫx-'w1zMs WmxvАnC~Op-t~dMPMUK0;XUѬY37Ѯw_ے5wrL ۣ6~yڍV{OTa ѵg%t{aB!E-iuv!>'buۓ Mf;+VHooؠɡr[ZRFPZbjyZp#lc,@˗cs ݍ-tGN F$/#9Ӂ0i|BbZzq-90wMQiT+%WA@A@A@pDftYKs!Xwi{Mˎ8N6,v4G[2'/Lzmo\: Bz\5kwuqv2O_Às1jCYlZTs]uK0}I;">#8.sRn5)x~1t TIv ɶ; qٍ͔Y\Nԃ70;n U;f3o ӱeL\5I݄޹)FXȝ1eтQx[!*F*cǾV.   p"P&~| b:sj6c6rͻiC%L{4a^?(u_~֣z_T*jgLCZvx!]':Ўs|ns_ZJK1w9y`P;O\6t#P]]z0<$ӚD븋c-rq3wF51ߠԜR䨭רDV#$[-Q.Ϝ\Cڑ4qS;tF.{!8}q/ B1dۊq1ďĪ x^E^+GKEIzǷ`vww7TA@A@A#P<ز5mx } $O&7e,.j7}Ҳ] L_!K򂎉K+,b_Cc}-)˲7k#$&-C\xoLtlO7m7)dq'({13P8꾔E04u fuy ;X&\ؚXT6J;yXQ18{ ?AYz}Ю٪G靝+ sMevرېŏ$K/'G/`r =8KKl,43CŴjk]z`٪6 cx ]4S]|Z2r$   #P>YAA|}bn/`wF:9/JWy:dI0ܲ`/ΪJ1\x(ݚg~7*/L*ϊP"xuc9o].WNGRNݓ+Ðz’5lxCG.e\;ː{ir*Ʉ%kêb%ĔVQk*]9A@~^:w+)) CNtD:x M& [F +TA57n߃GwHkRRb=Qq]ˍ,x2Ý}I ką8ypDw͵˷Jqty"&mQdWrGtt%k0h ܉nV,g2r,$ps8oƧc@UQHsݵ₀ Xuqc%y]>`"i$  @!PrGij A sMUL(wP 9^h]5w5OE`Xf^۠ܝ 6s߆\݇@@@ݧh,@5!`%x*1])u1" =%IIj9C 7yj ,<EߑSA@A@X #e%=g ;KIjZO҇  @F@97n?BIȝ=_ GD@D[A@A@OR;i$@m@ӓe`^t{PduC),)  P[]gjL ~*AcH4%4A@A@A@A6! Om EA@A@A@* O@&    &M!    PhDA@A@A@ڄtsq+u\I5!}Fkn^=;z~*h 3sU3Fr* @ `%z A@<#ѫ{A^VNl܈̎#رrRNe5ܼwHt 2.'Lx#nxDx᫵+X4oZ4236jr \ڵ{/F[M݈>Q{7NxP@&>ZŏB|ۖ%;g:ҡXѸpL}& ErҢ#jHF4ѲR.AB 5:yۏ@d//r%cthFZfCQê¸q ~&V&MCyŒ'Y1a nb&?DttG4KG5-2xWsSdVZܱ@ˢl R|4)"mTrV~u\Cꁬ$lMQں1PKIYhۼ .Ze/dvr&w9i>r$F| iFDZUؿ+t|ϳc.ߵYwZ[ YY0g!'Q8ƌ1c`tRx(v(<\J<>Ū9ДC2)ZiU[';##ϝ YAEF G/uf owmжR=MӫWWZ\±tr EtTk=mɧdfC'ƍ.Yvјx8@WNAEPT0DvE#3nҢ님=aA:z6腎Jg -p% [Xhvm %8wgxcaA˒A@0Gt2(w4]B;jp[Y]z EVbEJ pC%dsٽ*fΜ [.xM;2j?o]n~BP1߀fbb4'cMXY?77\" @u!pG]3+t}ݳJ:NNĤ!8C| fVGQ&()7H%($ϰJeS+رd>UiĢIc2>2Q̹`Ak04iʋ ?f!++4idfVlƚ lA<>`Q]7 bDԟr)6%m-SGlk,x!>b+jƆJ~G#m$'p~Jbl+Ag3ߵaf|?{>k# A NN߁B5Y G|r*fOQ5mA)cGIq)(anR1kPa*ެ F$Ê Mvmk*êRĤ%(=S)dztk\ڋU/hf{^Q.P(0s@ R2kqaiHhׅp7~HMI@6,(MgKɎFpR`PEr \@7p믵܋B@! :*"=c/>dJ̧SG%aKKA%f7w8_ }P)kKOv##(}?U=б#F13N6s踖˵;a $uvcY~ qP-//u{~RWH@IDATnpY"DT˛u +JoU#\_J!0Wit͎ B 1L@45:A\¼0+DS(% 䁔; kOi=gO0noAC ( hB,oC 3&D1QDCXdS4CKe1+Mh}Kfuau|;Mړ id RXYҒZY2G= `lbobO~as &X5.9J>Lr!֤8!xmŌL36anj3v$6vYQXv>!gMn}]ف%yF7ڪXu9qjĢL)oK\Fl'!"ËuML ! 3 sΟȓ F}K9uvٯĢ.pl_ >8">$N=%|d<\gښxc\/;̐/7??Fxu!4?g;w3bұQ(.]|c8d#~;Piah4`8mcWB@!|Fp9\tq'.-Oț@ PM C:=uIXEf)P%Sd*=6f3k9JmDU(nomgi>+To<:"%+ vPBB  XX>LGƂ<ڋA9N/Fŧ##?mY!uu-0g^Ǐz9 C 5{`?wTwYzy.l7=pvGg8]XXZP FR.BU>RzԗWGUM+Q 3`q bj\F3yH@}TF]]ՕCQv9gLJYs7OL ! n+W\=Cn`~Rsx",p2?O2òu`ˌ.ܪ;}<=K煅1Q)JüQ72ѰJqٹl}-A W!(AG&! 70W1 Su  Ozޘz5*m&-H ?s%/*̙cf^;Ł.j*gκ쾴JS1)eJ?:x>Q'斩sXfԱvR{nudL*N`s83WER}M:U]LF%^--wroDZqڳ?q(LU5盛GrPAq^uv\kB@| @H 8fRpSrB@$ xޢMנ!ߐ$F(k= s6>%ꑿd>"bӋud`sbC& 0q,ӻ_9ՕIw}WsgEE΃"⑖6G8>sE>r{KsvGk=+>\K鈌_9t<2JS5qvv "5O*ya,Ĩf*Gz57J6숽zdT *;~FQI-S؅騎 GSE4~fF)w )uvC8?;nq娎B ٪:5()^-Sڽȣ!8x]Sڿwg! F@;mů=_7Ե"B@$@WZ嗣eA}={Fչ@A)V.cIM0(Zn{hN838G;b2vZ08ΧJn^F"m|ml}o&=Zss(5"G.4U`U^ 洅jIDjT% xm{ To+X\{n,277-ydQ5ś0nuN&jmłR}1QB9W1i(Wq1dMPa?ɮ#r-"un}+ ɩ(-,unv; T䡚UR"9ބbn0?>) lqh/m`F!Kaߊ &6Dk:Ti:Ÿ;x9YLJP`xS=况]! H@\eR@gb&}NZ]xAA6;i,UB@!pSlS>Zo]cbWuv-z;|=7Ek*_DX7mKtn瞱$MjS\ʒ%8r`Kh+H,1q(="" j0e\RJ,ox٫Q啪m e`DcFCI~^,< [QaؽDl*&bٹ0}G]1>y7dd`VB 2U`Lq#)-%"PYjF\F? lS"Q]gŁX)ȘJ,8K+B@! B@! GW؋^m(nTPS㳡^ LQ|"Lzd AQTZ(0')6r@! B@! =IWЋdiJpXz(Z"0>5+"LݦZy[%8% 3F ŠHϊKpفCbPXNTHg݄$WIӓ9S0(8X )I)>`vjOa?wV7~V>`*[v+R$ T<1Y? !!XZFhs%$4ń6u#,iNi4A~mp.3 EL<=\;!MD6SJ! B@! JBp%=9(܌4y`򰨴 )%aS (RǕфcM_4FIG/K$U*@RNޥzr jI--zljWɌ7m%;`.9ƽX^PM 3I)vm؆6ګSeW҄1ij2T+[ϥy՜ )K豴w#("q( 4jA! B@! ?t fe9t㋙Hd,^尨\Ǩc(c(Z)?!Elkp :X:!oNdԖ7ZhPEi a6=1=:eJnt/AMYRoZ;[l(,,Pi:bkpEAS5N4DL4S*w sL^! B@! =NWB.5`fû(F#ȟaKq޼UZȖ gOY`Q/) ƻ70?r<1?H唢wVm9l5F\JE~ gkJQL'1&!37A kj` Wk!P+yRTeɓB[BU3?9ăVثM1;#2>ƪ7Oo+1#2)ql.2PdFjLH[J! B@! )T0fC !sW#% 97T#gQ^gBƆYU;Vrg|,֡dlVQLV^#`.A6Ȕ,$h3sf25n|2ǹ3*`B͎bԛ m9hM?j6Rj9E(RlZrkTBR8Ǥ!}>BKM1cU;KDi(B@! B@-w}__kH/^DPkVkfiaEFnfPsi`|[1.;z̴յhW+B,뭮! B@n&0bĈn(@gu! `g/SB@! B7 HVoҖB@! B@!DbR! B@! I@ޤ-} ! B@! BPŤB@! B@! zyz|Kx>{ ֬} h!w-ٛ…pwJjι>ހ hc ״~kxegcϾ]X%_!]X2nW1@yB@|<---lQ ! N ((oUKu/,w 5~T !)XG`p Nvvѫ@PF{v=If#m.ZMa/b F7(mΜjp`y!V9sp3+ő8X6oW Cγ>p嚶73@jFD7ڼ|\|qSz} A!З s[\U !'`x?b߿f6g|;ޛMZn֛U/a!p  ҧ]XRp1j~iA+?y,'q>=L93/toiWi&}|c~<ݖ:')}>gۛ?Ys'wvةqq;]9hw]?kzcB#?q;:THѫh }ձ-Lk6OZ\q4Do_l;#_?m-J}1^þsTOyט5|OIwT>k'OO34iFA! @<2R1"C ~,;Prco#+|fxhzh1r- Wܹ& ;mۍsBds`҅1 =[^F1"Z};2c~\#+d8 cRpdO֗}MJأ9vq׊89{% +9rc:FA\V3 ׵gš؜WV5Et@3vM[0$N];=|μǫjZs3aN>ܲ_5ë+pҳmRʧ~'7ma>R΅x.WQ Ξ" jqA G` 4|]2l_c\ޯ{l|i .וx!qM Qx;ږ.<[^cإX;G];mWn=1HB@"SŮB@tYJ9};]ywF$,>cwN#);et9K{+Ȍ ܎9x}VoA+x?{#s6⇣q2Zw>߯ĝ)=F iRd+2z".;N4א3sG8>z1^N kӈ;;(G+瘫g va9͟P;C8y+oCZaƎ粱mkL֣=*5g}1}6ӼNB4Z-;JW) I@ 0xu<#BƝ8miM+2|l$8bk2,Fe.^l4!{ oG ǧ;XLi_SQ''/ƤkprBd==lN;Xvz,ǞxuNzJ[( mj<Bg}> 5z\H^ػ>>Crʷ| )Bl<13cQܹlq)`S cxw6a 63ʉcbrhݕE >)(nnT+> W۪][9qz#\,v*NicL~`ù<~Gbyy^GmcR2S7.褹8@7+3Cg?>jvR]r׾-}$6J! $YVbL!SШc{Pxy,RCa9V~8*tkăYY2z^<j30TC5m}0M *ϝo J+}')BO8x0a Qt/<6ռC4Ofn9ӆ#JXŊ^E$;4@3ѼUz`1-F6pHe6V4ΙL-y1?2g[q%` a_ UKЗ!"z2=c`~Z[8~Hy܏%:GgkJmH>JC|d0r8kV?_EO G`5؇S^r@ϨDZq…ߐ=Y\q48گ5F3֏k^E"pDT#KaDA}۪psQܡzhaRi\+iњ?!)БfDOt S^?Gt b[`;q9IBC=MP֋}9KK8|2fq?%׋^݈>6 $c5Vu=fY ! @uv_9v4JQe6v\i7f ?KmgÇt2:^ń};~ %0a.ѓ)sBbހ#`,RL# @=Z٥7|"M7ŊfuNy%[7ؔ3S  8D$zqaq͞5N/c,EOA0iQ 9k8Z`sX}jlcKqJk<8n^@!|^`+O:gT-בT<ː+Q46EiO[p6@zj'&8O3l64WxȦ;nZcb8+zG-,/Sb jbʥƦ`1QDeC7٣m3(2u W!N/ > kߙP<ȶ)D/ע:P~5jC! "T1)aΛ/6ǡUxE[+1:I̕o 0nW:\rHs'Xvq9hCB%K~B h&ZOz^Ρ?|65Zp\ZadMHSm"j]븾^֗B@& !ZMT ! nr91ǎ~>o͌C~8Ȑ) >WrN xg=*>>};^}cڛ"2Hi/;cc[H{c?ٿcm"63Z.Y˴ 4}G!(bמ#A^7?ľؽ8n赍] .4L9Be;q [|moeRٿ ʹkoqW'=ڙD/ͼIx6;B`H cUǜBf^o=^-1u`7BzT$#Ae61Ŏ*2Z9n0H~d \hnowY;8 C4F]j_a RHpRnŇ\+C nļ@`84[ڹ4[ [*Ezl{C495 =tk<_5LuARV)= )%3jy~zgh%wSф8۴Ɇǚŝoq6G{q2JO1#9Xz 9#AI)w1s,üԏa~;zVmA,d-ޣWqס|oP+7Ţ}ln{ bRe#Uȱ2 sl4=>uv< [H^Lt3%Ċ4&I4K1ݏL<K?]fEqDfnM7}7-XfƖ%aᑴgqIoy:Hf\Y>m`|$ I96 #My2 <(۫AzpפN/"7H>$3K=?Bqa ;;*t\_ YHԌu PebBYy&' &h*&-+u `8lu(Ý&04E :R*/Fk׈vY93~(F,zh;GЧJYkĜD^l;Zg+=-l,&x]dt妝d6ɛ}Q|Hѱ-Y$kHo׋˅.8aI+t5{D;<^?zMj[YIh.>\/m` =B/kYx"LꅀBƻNJ^:z:;={-ɔkD?Z&gbg͊}፸_4tc+Oz!󕇉znY͞α Ռ\_{7[r6t9ŗj W\0ޱλ-m};pKVtf۵B_oiozK7ZfKhׄƝ\/uaw[ӰEx^;oi/n=Iݽߖ 8O; V!p{*R w{Q P7]m빝GAM޳ݫsGzu,vqix2s=bˮͽsұλ-olw5љmױwB05IXU;yj[%͎/=d@ ! @wH! B@RVY[3ʉnuK&1t@BGz+ˈ۵ݰ=9Q! zhjH;@φhi4eB@tÉ^4kU'caP 3G. {h,_ăE! B@""" q疬t*YMB@! B@!p s@B@! B@! f B@! B@! D ! B@! Bf H%( ! /a! B@N`Ĉ}}2>!F@7F!}CAKB@! B@tN@B:#B@! B@! DK$B@! B@! @zEX,ؼ uFecMj-^ Z/ׅtXp/ ֥c]٩#B@! B@! c;E9hLCoCY"G`ua:Qxl* v-m/ sQ2G"u|ĎԮڭM0a[ͨ BO҅B@! B@#$El30e:Dw\KÒӑWƶjlYXҫCՋE"B@! BxrEy{\R4&).B4f$L( ،pVQL(3FAذLP\Zn{0:|aU-lzh]#9"B@! @{pcTƜ-{ 5 2\"2 9smlؙ %F@)Vcb/jvf!" P5]:{mZnx ٰ"lkI¦8TVF ꬧AsR\ 4q.kSRg`Nxyvt"KN,ʫD<qbO.r됲zFAx7 s[ 26aHb[Q1"Wcn<y%2SD<4boqʫE&.Cp,+G(21FD#p gl6rpKw4+/J땏90WRߊ_ݍ!a//z_Ǩ{ {y!BZzŃAbos>g' `Pdw gڢL;~HL@V|DU#?35t x=EwH]f6g.~P5q- t廕fGI9^+6^Lc7[a/C(]LG ͔dj6ܞފL;V:˲u-ŪEvڧhuI>ԅ 7/QN`$2V##=-%E:%׏J{u`"gΫE+^Cԣ=ެltP-ե_z RQշ'qrj}2*Jcc`h]ٌ҆~tdit _^u^BLX=A~w;/]EB@!N <>=;ŗR"qcqCm)E90gx! :X%Uʙ3F[}5{⧇ڢ>!"\ǔ9X9ғ.`BWBFƆL̚hϦ,`meq1+t)[c%**T_lP"_t雕gB~(S`Zs^9fC9,(\AȰ\u٠e3h aά)ϵOX0J~ԅ|ǹ.nj0~Jr~= !=EV )X?GEܸtlysxͬ1\__{o4t/Ow)#w]^}i}K;G7Wi )ﯮix:kV"nƎGZbhQ†?Ɠ?iw_~}~ޯ{:pgrB@ܑ|zk־!y_0j,8Ya5taxV*wOu5Hf#C o>),eX+yOaz?FGDi> 2$$!‘^uL 7 ө651iSl+3N AFF8rr Qd>!JMK,8D^~A^zh)-SD:IzlgqRiAQs8v-4`Mr|w9>^/ @;;i{(ڔ-P#݃K.w '/.|1Fdv8ں4lrV|WGo^9 `R@0+Wl*=y̎pO[W,d[( y,W. ch|7>/y u~c~*X׿gmwaZ붶*\Wrcpz 7Ge{:YGW=ի]\;sm8ifĀ~{όr9r xobUl+! 3xk0W1̮9;f1j:(~Z?PR\SO\ sٲp3*N Dp.yDdnEcM5J}R9?ڔ1"yv#`Ę`ݏǡ5j&|z 㫊Q]SR3wKTخ &)CSob׎:޵M\m4bLT(.9#a| r΃^n J|QSmi)s^¯MQ߽T%%IDATᆁ]+[kzGS6X<ބKU 0:q__`Zo)`ۚcuLdDMO`+y_i!$(zϽRe[ HoDc0#KX/lośܼ!rC}r: 'xw/_ِsku iB>mȌ?k<@hC۱~>Cd?>}56ɷ.4?~.cf9[8p[lv O#1+m_ؑSrSx/#Wqf<^j}~<H{ˊcI6r{q¼}@įA[xk(܂xңF 'mGY4s&6eG#ZC* rEލnuhNek<{e?8{ph+5K`wg0_۟iE7^/9W֊$\k郫䋡wcGB@ k!ZjLc'ֻy(zWx)W_p8O.?cW( C詃AgOi~D;ti'V(a?O//)ڹJL[#osYjmU#w|M S͂5w)cZӡgTy%M0Ex/l0j1RyUpA"gٱ A7zGp{-EK. &O ;Iw+ A ߤ s|,SfSr 75 -<Nn} hORܡZ )[2ϋ)w7FZL=R_|}Lxw o0p|g짱_oolh 6>Q8?Lm0+0qFoC {:? K #F ?† FVB6$ FC0ZkT~(; oM7FtN_?w룻r}GSS¸7H:WQ1R[&qxa?[}pv/?c˝W.a;W5\gvbEō&X;YqlގSAX.EcmwR|{dcwރ}~5}.f8vp=hAY;CVb烷j\ռ3w6N΀|+xe֩B@!UsaR\x+Eغ8qU˰uV0}C"5orc7vGLCԡFPS{7`k{QUsNՠ\I⾡5#gR|_[s3\-%,7l|| 4!o:|Ps 'cC*MXII1F!.R'jbex9YhuG|*,IϰF8E͙Gͩ^*C-xX?)gVTͩ]Dö"O y3|7~_Ɗ)x!])m) k~4rXqIݿ]uܭ=^ w&9 #Ow'T?XLHL "tѿKk ħݧ8Lc;[Vb+5VW)K㹇pO~C|h?a\Qн4m&LHH BbZzQP(Gޅ?1 LJ*>~Vx}CO{/x@x1ז8 R݅-S!Çkw+gb]ZXq_CR+B3H//IQ(\Ĩm%ԐB`j3ʇ!WQdBJnw3^!E08E$Wi1)H=@g;9 *I`8NCg`C[o<#J0R2rEaY zX|s4#'Uؙ'|~ErG]ڨmNY&&Q.gXގ랑#5% yE[O*T\uj O%QnƪRү8懪,AFvK o{W۾{l`ַp!v:lT}^ZȀ+O 8.ŃQV| t,w"^=~/.ϐGy9ީǟwLd!p1׉f 8@SB1Eֹ֜fM0h R" jo(6R(֣Î/PVwSOPQOWqqcJNƘ-JżO̘uEnLtlWMMּUb4%}a?.fs.lBQGɵ3nǓޓzAsuW(] M| wS^Ժa]P}.5*MB@n /v7DȌ%(dwZ? s\ EVMApmL\P@]'!7E0;?;}; <1 GQ'eДQXt9rLy.c3}c8ˎd|>אV: 2%ׇa~q%ozpI_-~< 8gqѸߩ0GyG9Ԁ *s<|/ kyA?:VEv܇sB#l9­ a!‡.9-.7kǵ} DŽG=MwOww/yw}?KZ;6hz\oL}kk:/êT`ՌGs(_]q4`xhLrI'\鯯c זý1Jh(n\_lo[SoB:9$B' |Փܤm J BUL[s_0l]N< gj|{ 4yFo8+ޡQsux@ģ 8/lGg(yS?`~t 4T58u} uuaBuits0, ){~'~fr\t s9y;%1H-_}voGZhN\mD2)(?kw63T ÎXWK1FA1IR}R~tȿ9){=?]m~1f쿿&p|> +͘(tŌ>g筸ݥBڐ:}>s`OZ;d|dRJ 7 Ṙt"vVqTֳk_c87~xQ[k?LJQY=8N>֋({뼶6S:xD\! @uޟ(lg`+ ^a^GGQRܩ7 2l] ' J?X EP8+<N K[Wgƶp<^%k1GfAɡU ~vXy$Rms1?tq5o0`*'WFw >-y%:!vbtKKN1vF?p1;) <>Xڌy_G;Ip75|_-)z}W8G|~~ʍ&?|ؐ/o1!p+b0FOoQym~ȵ/O i{c#승(|vb^ma.}NJɉ{ǃ|ҏm}:kLHB&9QGm~ɕ~MPet\; c0%V1ay?O/0O E̓A97d).כrlRy+_^b䥱B@!Ў]_~2FY$s;V! n%2N:Kx] `")MO}|ihi+=4bnWt{WK?&`>x_{zn&ڽ~;KƱyhs咸zWH\䯲aWTڵ4:2Prw ɻ>Q=?ϻʣ!9(7@KK t|L tOj7*愀B#k)7ބ=jYWB/mz67o @ps\o`茛w볧<l#@P?OjXڋ%LH! ?ΖB@! 7v771扟3_婑B@!p$D&ɩB@! 7 !ۤ_7G%ܺJ ! h];xܹk/3B@! "oZnB@-6}F! B@! }<}j9d0B@! B@! ~"\?39C! B@! }@i99 _ᓫ:5 ?@S|>U <ڵ&5S[1?}a C\1C D{*c6v 80_n$> ל7.nzg?hzoRӼVZg#hTgń:Ф[SDˍeV0PI*˦ޥMvR- V kumB ern,16M !̙9g~$qb>9e\>(rUSR꒩fL?ڑ?zW\<'Od[ O !LQLm  -^̝DzZPQ^(S`4= ^XnL(|e[Nb:0VөgV]D "mwƵs6Җ[]PBu`pGv^P'.~HmQII UB:L"]xF}21֡:Y~'nfTu}wAI5-RcmJZ9ģ]|"   Q$ܒj])HjY|B5oLFsƻɳ2TWKww&4 ֯Wn~X۩CivỴXrYZ+]fci-߮#%x5?W^8+;9GY~}'My:?h9ZG԰].Z|"   pZe 4UnQUZ.[{˗g- f I\3>JZ=™qr+*";<qLd^]plMAK3@@@ ts;Gͪ MUc>T__y[%:qA^_Bލ:}d>=z8NG#|B VrݮryHg_q/˲re^fݺUuwv9ŸK:|zYrõI{JEiU1.B   ߻#܍7أ@l+gZad1u>"vPU2^'{VkpFëhqåirHݯm k_x}Ȇ@@@E )_zYβ{uv"쐑+=39?}ODWK %:LּggZԭ;ܱ/+0vK7.YV1>"?s=٭F|~Y"5{݈nD    R )K2Wy2mZhgj/۬gcR̉LUv2D &Nι[4W͵5:\3s`T98z@7[zKLgoNpt>|9Tş;w1=   -%Z\x_n< @@@@hxr( ȏt@@@` hMCUH KBfgaQwya7w   @D+u#A@@@ '!   #@'u#A@@@ '!   #@'u#A@@@ '!   #@'u#A@@@ '!   #@'u#A@@@ '!   #@'u#A@@@ '!   #@'u#A@@@ '!   #$2\)B@@@`$%Ïu>@@@@ XO1!   w!@.h   MHv:IENDB`afeld-sodapy-464855c/examples/soql_queries.ipynb000066400000000000000000000355201430365730200217520ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Example 02: SoSQL Queries\n", "\n", "Constructing custom queries to conserve bandwith and computational resources" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os\n", "# Note: we don't need Pandas\n", "# Filters allow you to accomplish many basic operations automatically\n", "\n", "from sodapy import Socrata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Find Some Data\n", "\n", "As in the first example, I'm using the Santa Fe political contribution dataset.\n", "\n", "`https://opendata.socrata.com/dataset/Santa-Fe-Contributors/f92i-ik66.json`" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "socrata_domain = 'opendata.socrata.com'\n", "socrata_dataset_identifier = 'f92i-ik66'\n", "\n", "# If you choose to use a token, run the following command on the terminal (or add it to your .bashrc)\n", "# $ export SODAPY_APPTOKEN=\n", "socrata_token = os.environ.get(\"SODAPY_APPTOKEN\")\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "client = Socrata(socrata_domain, socrata_token)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use Metadata to Plan Your Query\n", "You've probably looked through the column names and descriptions in the web UI,\n", "but it can be nice to have them right in your workspace as well." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['NAME2',\n", " 'AMOUNT-2',\n", " 'PARTY',\n", " 'NAME',\n", " 'JOB',\n", " 'COMPANY',\n", " 'UPDATE',\n", " 'DATE',\n", " 'RECIPIENT',\n", " 'AMOUNT',\n", " 'STREET',\n", " 'CITY']" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "metadata = client.get_metadata(socrata_dataset_identifier)\n", "[x['name'] for x in metadata['columns']]" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'cachedContents': {'average': '2433.2075',\n", " 'largest': '6100',\n", " 'non_null': 800,\n", " 'null': 0,\n", " 'smallest': '1250',\n", " 'sum': '1946566',\n", " 'top': [{'count': 426, 'item': '2300'},\n", " {'count': 72, 'item': '4600'},\n", " {'count': 34, 'item': '1500'},\n", " {'count': 33, 'item': '2000'},\n", " {'count': 20, 'item': '1250'},\n", " {'count': 14, 'item': '2100'},\n", " {'count': 12, 'item': '1300'},\n", " {'count': 11, 'item': '3300'},\n", " {'count': 10, 'item': '1750'},\n", " {'count': 10, 'item': '2500'},\n", " {'count': 7, 'item': '1400'},\n", " {'count': 6, 'item': '1800'},\n", " {'count': 5, 'item': '1550'},\n", " {'count': 4, 'item': '2800'},\n", " {'count': 4, 'item': '2050'},\n", " {'count': 4, 'item': '1450'},\n", " {'count': 4, 'item': '1950'},\n", " {'count': 4, 'item': '2200'},\n", " {'count': 4, 'item': '1350'},\n", " {'count': 4, 'item': '1700'}]},\n", " 'dataTypeName': 'number',\n", " 'fieldName': 'amount',\n", " 'format': {'aggregate': 'sum'},\n", " 'id': 2303155,\n", " 'name': 'AMOUNT',\n", " 'position': 10,\n", " 'renderTypeName': 'number',\n", " 'tableColumnId': 1263296,\n", " 'width': 142}" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "meta_amount = [x for x in metadata['columns'] if x['name'] == 'AMOUNT'][0]\n", "meta_amount" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Efficiently Query for Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Restrict rows to above-average donations" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'2433.2075'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Get the average from the metadata. Note that it's a string by default\n", "meta_amount['cachedContents']['average']" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total number of non-null results: 800\n", "Number of results downloaded: 143\n" ] }, { "data": { "text/plain": [ "[{'amount': '6100',\n", " 'amount_2': 'Donation of $6,100 to Presidential elections 2008',\n", " 'city': 'Santa Fe NM',\n", " 'company': 'Thornburg Companies',\n", " 'date': 'Q3/2008',\n", " 'job': 'Finance',\n", " 'name': 'Garrett Thornburg',\n", " 'name2': 'Garrett Thornburg',\n", " 'party': 'Democrat',\n", " 'recipient': 'Barack Obama',\n", " 'street': '150 WASHINGTON AVE',\n", " 'update': 'Updated'},\n", " {'amount': '5600',\n", " 'amount_2': 'Donation of $5,600 to Presidential elections 2008',\n", " 'city': 'Santa Fe NM',\n", " 'company': 'FOREST REALTY',\n", " 'date': 'Q3/2007',\n", " 'job': 'REAL ESTATE',\n", " 'name': 'Michael Daly',\n", " 'name2': 'Michael Daly',\n", " 'party': 'Democrat',\n", " 'recipient': 'Bill Richardson',\n", " 'street': '305 BROWNELL HOWLAND RD',\n", " 'update': 'Updated'},\n", " {'amount': '5100',\n", " 'amount_2': 'Donation of $5,100 to Presidential elections 2008',\n", " 'city': 'Santa Fe NM',\n", " 'company': 'James Currey Publishers',\n", " 'date': 'Q3/2008',\n", " 'job': 'Publisher',\n", " 'name': 'Douglas Johnson',\n", " 'name2': 'Douglas Johnson',\n", " 'party': 'Democrat',\n", " 'recipient': 'Barack Obama',\n", " 'street': '48 WOODS LOOP',\n", " 'update': 'Updated'}]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Use the 'where' argument to filter the data before downloading it\n", "results = client.get(socrata_dataset_identifier, where=\"amount >= 2433\")\n", "print(\"Total number of non-null results: {}\".format(meta_amount['cachedContents']['non_null']))\n", "print(\"Number of results downloaded: {}\".format(len(results)))\n", "results[:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Restrict columns and order rows\n", "Often, you know which columns you want, so you can further simplify the download.\n", "\n", "It can also be valuable to have results in order, so that you can quickly grab the\n", "largest or smallest." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'amount': '1250', 'job': 'QA Architect'},\n", " {'amount': '1250', 'job': 'Artist'},\n", " {'amount': '1250', 'job': 'investor'}]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = client.get(socrata_dataset_identifier,\n", " where=\"amount < 2433\",\n", " select=\"amount, job\",\n", " order=\"amount ASC\")\n", "results[:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Perform basic operations\n", "You can even accomplish some basic analytics operations like finding sums.\n", "\n", "If you're planning on doing further processing, note that the numeric outputs\n", "are strings by default." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'recipient': 'Bill Richardson', 'sum_amount': '1020748'},\n", " {'recipient': 'Barack Obama', 'sum_amount': '602865'},\n", " {'recipient': 'Hillary Clinton', 'sum_amount': '185887'},\n", " {'recipient': 'John McCain', 'sum_amount': '60741'},\n", " {'recipient': 'John Edwards', 'sum_amount': '25550'},\n", " {'recipient': 'Christopher Dodd', 'sum_amount': '14200'},\n", " {'recipient': 'Rudy Giuliani', 'sum_amount': '13800'},\n", " {'recipient': 'Ron Paul', 'sum_amount': '11500'},\n", " {'recipient': 'Mitt Romney', 'sum_amount': '4600'},\n", " {'recipient': 'Joe Biden', 'sum_amount': '4600'},\n", " {'recipient': 'Fred Thompson', 'sum_amount': '2075'}]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = client.get(socrata_dataset_identifier,\n", " group=\"recipient\",\n", " select=\"sum(amount), recipient\",\n", " order=\"sum(amount) DESC\")\n", "results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Break download into managable chunks\n", "Sometimes you do want all the data, but it would be too big for one download.\n", "\n", "By default, all queries have a limit of 1000 rows, but you can manually set it\n", "higher or lower. If you want to loop through results, just use `offset`" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'amount': '6100', 'name': 'Garrett Thornburg'},\n", " {'amount': '5600', 'name': 'Michael Daly'},\n", " {'amount': '5100', 'name': 'Douglas Johnson'},\n", " {'amount': '5100', 'name': 'Lynn Mortensen'},\n", " {'amount': '5100', 'name': 'Ted Flicker'},\n", " {'amount': '4906', 'name': 'Jere Smith'}]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = client.get(socrata_dataset_identifier, limit=6, select=\"name, amount\")\n", "results" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "> Loop number: 0\n", "{'amount': '6100', 'name': 'Garrett Thornburg'}\n", "{'amount': '5600', 'name': 'Michael Daly'}\n", "{'amount': '5100', 'name': 'Douglas Johnson'}\n", "\n", "> Loop number: 1\n", "{'amount': '5100', 'name': 'Lynn Mortensen'}\n", "{'amount': '5100', 'name': 'Ted Flicker'}\n", "{'amount': '4906', 'name': 'Jere Smith'}\n" ] } ], "source": [ "loop_size = 3\n", "num_loops = 2\n", "\n", "for i in range(num_loops):\n", " results = client.get(socrata_dataset_identifier,\n", " select=\"name, amount\",\n", " limit=loop_size,\n", " offset=loop_size * i)\n", " print(\"\\n> Loop number: {}\".format(i))\n", " \n", " # This simply formats the output nicely\n", " for result in results:\n", " print(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query strings\n", "All of the queries above were made with method parameters,\n", "but you could also pass all the parameters at once in a\n", "SQL-like format" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'amount': '1995', 'name': 'Shelley Silverstein'},\n", " {'amount': '1974', 'name': 'Marvin Godner'},\n", " {'amount': '1954', 'name': 'Stuart Ashman'},\n", " {'amount': '1950', 'name': 'Hope Curtis'},\n", " {'amount': '1950', 'name': 'David Harwell'}]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = \"\"\"\n", "select \n", " name, \n", " amount\n", "where\n", " amount > 1000\n", " and amount < 2000\n", "limit\n", " 5\n", "\"\"\"\n", "\n", "results = client.get(socrata_dataset_identifier, query=query)\n", "results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Free text search\n", "My brother just got a dog named Slider, so we were curious about how many other New York City dogs had that name.\n", "\n", "Searches with `q` match anywhere in the row, which allows you to quickly search through data with several free text columns of interest." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'animalname': 'SLIDER', 'breedname': 'American Pit Bull Terrier/Pit Bull'},\n", " {'animalname': 'SLIDER', 'breedname': 'Cavalier King Charles Spaniel'},\n", " {'animalname': 'SLIDER ', 'breedname': 'Shih Tzu'},\n", " {'animalname': 'SLIDER', 'breedname': 'Wheaton Terrier'}]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nyc_dogs_domain = 'data.cityofnewyork.us'\n", "nyc_dogs_dataset_identifier = 'nu7n-tubp'\n", "\n", "nyc_dogs_client = Socrata(nyc_dogs_domain, socrata_token)\n", "results = nyc_dogs_client.get(nyc_dogs_dataset_identifier,\n", " q=\"Slider\",\n", " select=\"animalname, breedname\")\n", "results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Going Further\n", "\n", "There's plenty more to do! Check out [Queries using SODA](https://dev.socrata.com/docs/queries/) for additional functionality" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 2 } afeld-sodapy-464855c/examples/soql_queries.py000066400000000000000000000103651430365730200212610ustar00rootroot00000000000000# coding: utf-8 # # Example 02: SoSQL Queries # # Constructing custom queries to conserve bandwith and computational resources # ## Setup # In[1]: import os # Note: we don't need Pandas # Filters allow you to accomplish many basic operations automatically from sodapy import Socrata # ## Find Some Data # # As in the first example, I'm using the Santa Fe political contribution dataset. # # `https://opendata.socrata.com/dataset/Santa-Fe-Contributors/f92i-ik66.json` # In[2]: socrata_domain = "opendata.socrata.com" socrata_dataset_identifier = "f92i-ik66" # If you choose to use a token, run the following command on the terminal (or add it to your .bashrc) # $ export SODAPY_APPTOKEN= socrata_token = os.environ.get("SODAPY_APPTOKEN") # In[3]: client = Socrata(socrata_domain, socrata_token) # ## Use Metadata to Plan Your Query # You've probably looked through the column names and descriptions in the web UI, # but it can be nice to have them right in your workspace as well. # In[4]: metadata = client.get_metadata(socrata_dataset_identifier) [x["name"] for x in metadata["columns"]] # In[5]: meta_amount = [x for x in metadata["columns"] if x["name"] == "AMOUNT"][0] meta_amount # ## Efficiently Query for Data # ### Restrict rows to above-average donations # In[6]: # Get the average from the metadata. Note that it's a string by default meta_amount["cachedContents"]["average"] # In[7]: # Use the 'where' argument to filter the data before downloading it results = client.get(socrata_dataset_identifier, where="amount >= 2433") print( "Total number of non-null results: {}".format( meta_amount["cachedContents"]["non_null"] ) ) print("Number of results downloaded: {}".format(len(results))) results[:3] # ### Restrict columns and order rows # Often, you know which columns you want, so you can further simplify the download. # # It can also be valuable to have results in order, so that you can quickly grab the # largest or smallest. # In[8]: results = client.get( socrata_dataset_identifier, where="amount < 2433", select="amount, job", order="amount ASC", ) results[:3] # ### Perform basic operations # You can even accomplish some basic analytics operations like finding sums. # # If you're planning on doing further processing, note that the numeric outputs # are strings by default. # In[10]: results = client.get( socrata_dataset_identifier, group="recipient", select="sum(amount), recipient", order="sum(amount) DESC", ) results # ### Break download into managable chunks # Sometimes you do want all the data, but it would be too big for one download. # # By default, all queries have a limit of 1000 rows, but you can manually set it # higher or lower. If you want to loop through results, just use `offset` # In[11]: results = client.get(socrata_dataset_identifier, limit=6, select="name, amount") results # In[11]: loop_size = 3 num_loops = 2 for i in range(num_loops): results = client.get( socrata_dataset_identifier, select="name, amount", limit=loop_size, offset=loop_size * i, ) print("\n> Loop number: {}".format(i)) # This simply formats the output nicely for result in results: print(result) # ### Query strings # All of the queries above were made with method parameters, # but you could also pass all the parameters at once in a # SQL-like format # In[13]: query = """ select name, amount where amount > 1000 and amount < 2000 limit 5 """ results = client.get(socrata_dataset_identifier, query=query) results # ### Free text search # My brother just got a dog named Slider, so we were curious about how many other New York City dogs had that name. # # Searches with `q` match anywhere in the row, which allows you to quickly search through data with several free text columns of interest. # In[20]: nyc_dogs_domain = "data.cityofnewyork.us" nyc_dogs_dataset_identifier = "nu7n-tubp" nyc_dogs_client = Socrata(nyc_dogs_domain, socrata_token) results = nyc_dogs_client.get( nyc_dogs_dataset_identifier, q="Slider", select="animalname, breedname" ) results # # Going Further # # There's plenty more to do! Check out [Queries using SODA](https://dev.socrata.com/docs/queries/) for additional functionality afeld-sodapy-464855c/requirements-dev.txt000066400000000000000000000001631430365730200204110ustar00rootroot00000000000000-r requirements.txt flake8>=5.0.4 pytest>=7.1.2 requests-mock>=1.9.3 black; python_version > '3.5' coverage>=6.4.4 afeld-sodapy-464855c/requirements.txt000066400000000000000000000000211430365730200176260ustar00rootroot00000000000000requests>=2.28.1 afeld-sodapy-464855c/setup.cfg000066400000000000000000000000731430365730200161720ustar00rootroot00000000000000[flake8] max-line-length = 100 [bdist_wheel] universal = 1 afeld-sodapy-464855c/setup.py000066400000000000000000000022741430365730200160700ustar00rootroot00000000000000#!/usr/bin/env python3 from setuptools import setup with open("sodapy/version.py") as f: exec(f.read()) with open("README.md") as f: long_description = f.read() with open("requirements.txt") as requirements: required = requirements.read().splitlines() kwargs = { "name": "sodapy", "version": __version__, "packages": ["sodapy"], "description": "Python library for the Socrata Open Data API", "long_description": long_description, "long_description_content_type": "text/markdown", "author": "Cristina Muñoz", "maintainer": "Cristina Muñoz", "author_email": "hi@xmunoz.com", "maintainer_email": "hi@xmunoz.com", "license": "MIT", "install_requires": required, "url": "https://github.com/xmunoz/sodapy", "download_url": "https://github.com/xmunoz/sodapy/archive/master.tar.gz", "keywords": "soda socrata opendata api", "classifiers": [ "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Development Status :: 5 - Production/Stable", ], } setup(**kwargs) afeld-sodapy-464855c/sodapy/000077500000000000000000000000001430365730200156505ustar00rootroot00000000000000afeld-sodapy-464855c/sodapy/__init__.py000066400000000000000000000001761430365730200177650ustar00rootroot00000000000000from sodapy.socrata import Socrata from sodapy import version __all__ = [ "Socrata", ] __version__ = version.__version__ afeld-sodapy-464855c/sodapy/constants.py000066400000000000000000000001361430365730200202360ustar00rootroot00000000000000DEFAULT_API_PATH = "/resource/" OLD_API_PATH = "/api/views" DATASETS_PATH = "/api/catalog/v1" afeld-sodapy-464855c/sodapy/socrata.py000066400000000000000000000530611430365730200176630ustar00rootroot00000000000000import csv from io import StringIO, IOBase import json import logging import os import re import requests from sodapy.constants import DATASETS_PATH import sodapy.utils as utils class Socrata: """ The main class that interacts with the SODA API. Sample usage: from sodapy import Socrata client = Socrata("opendata.socrata.com", None) """ # https://dev.socrata.com/docs/paging.html#2.1 DEFAULT_LIMIT = 1000 def __init__( self, domain, app_token, username=None, password=None, access_token=None, session_adapter=None, timeout=10, ): """ The required arguments are: domain: the domain you wish you to access app_token: your Socrata application token Simple requests are possible without an app_token, though these requests will be rate-limited. For write/update/delete operations or private datasets, the Socrata API currently supports basic HTTP authentication, which requires these additional parameters. username: your Socrata username password: your Socrata password The basic HTTP authentication comes with a deprecation warning, and the current recommended authentication method is OAuth 2.0. To make requests on behalf of the user using OAuth 2.0 authentication, follow the recommended procedure and provide the final access_token to the client. More information about authentication can be found in the official docs: http://dev.socrata.com/docs/authentication.html """ if not domain: raise Exception("A domain is required.") self.domain = domain # set up the session with proper authentication crendentials self.session = requests.Session() if not app_token: logging.warning( "Requests made without an app_token will be" " subject to strict throttling limits." ) else: self.session.headers.update({"X-App-token": app_token}) utils.authentication_validation(username, password, access_token) # use either basic HTTP auth or OAuth2.0 if username and password: self.session.auth = (username, password) elif access_token: self.session.headers.update( {"Authorization": "OAuth {}".format(access_token)} ) if session_adapter: self.session.mount(session_adapter["prefix"], session_adapter["adapter"]) self.uri_prefix = session_adapter["prefix"] else: self.uri_prefix = "https://" if not isinstance(timeout, (int, float)): raise TypeError("Timeout must be numeric.") self.timeout = timeout def __enter__(self): """ This runs as the with block is set up. """ return self def __exit__(self, exc_type=None, exc_value=None, traceback=None): """ This runs at the end of a with block. It simply closes the client. Exceptions are propagated forward in the program as usual, and are not handled here. """ self.close() def datasets(self, limit=0, offset=0, order=None, **kwargs): """ Returns the list of datasets associated with a particular domain. WARNING: Large limits (>1000) will return megabytes of data, which can be slow on low-bandwidth networks, and is also a lot of data to hold in memory. This method performs a get request on these type of URLs: https://data.edmonton.ca/api/catalog/v1 limit: max number of results to return, default is all (0) offset: the offset of result set order: field to sort on, optionally with ' ASC' or ' DESC' suffix ids: list of dataset IDs to consider domains: list of additional domains to search categories: list of categories tags: list of tags only: list of logical types to return, among `api`, `calendar`, `chart`, `datalens`, `dataset`, `federated_href`, `file`, `filter`, `form`, `href`, `link`, `map`, `measure`, `story`, `visualization` shared_to: list of users IDs or team IDs that datasets have to be shared with, or the string `site` meaning anyone on the domain. Note that you may only specify yourself or a team that you are on. Also note that if you search for assets shared to you, assets owned by you might be not be returned. column_names: list of column names that must be present in the tabular datasets q: text query that will be used by Elasticsearch to match results min_should_match: string specifying the number of words from `q` that should match. Refer to Elasticsearch docs for the format, the default is '3<60%', meaning that 60% of the terms must match, or all of them if there are 3 or fewer. attribution: string specifying the organization datasets must come from license: string used to filter on results having a specific license derived_from: string containing the ID of a dataset that must be a parent of the result datasets (for example, charts are derived from a parent dataset) provenance: string 'official' or 'community' for_user: string containing a user ID that must own the returned datasets visibility: string 'open' or 'internal' public: boolean indicating that all returned datasets should be public (True) or private (False) published: boolean indicating that returned datasets should have been published (True) or not yet published (False) approval_status: string 'pending', 'rejected', 'approved', 'not_ready' filtering results by their current status in the approval pipeline explicitly_hidden: boolean filtering out datasets that have been explicitly hidden on a domain (False) or returning only those (True) derived: boolean allowing to search only for derived datasets (True) or only those from which other datasets were derived (False) """ # Those filters can be passed multiple times; this function expects # an iterable for them filter_multiple = set( [ "ids", "domains", "categories", "tags", "only", "shared_to", "column_names", ] ) # Those filters only get a single value filter_single = set( [ "q", "min_should_match", "attribution", "license", "derived_from", "provenance", "for_user", "visibility", "public", "published", "approval_status", "explicitly_hidden", "derived", ] ) all_filters = filter_multiple.union(filter_single) for key in kwargs: if key not in all_filters: raise TypeError("Unexpected keyword argument %s" % key) params = [("domains", self.domain)] if limit: params.append(("limit", limit)) for key, value in kwargs.items(): if key in filter_multiple: for item in value: params.append((key, item)) elif key in filter_single: params.append((key, value)) # TODO: custom domain-specific metadata # https://socratadiscovery.docs.apiary.io/ # #reference/0/find-by-domain-specific-metadata if order: params.append(("order", order)) results = self._perform_request( "get", DATASETS_PATH, params=params + [("offset", offset)] ) num_results = results["resultSetSize"] # no more results to fetch, or limit reached if ( limit >= num_results or limit == len(results["results"]) or num_results == len(results["results"]) ): return results["results"] if limit != 0: raise Exception( "Unexpected number of results returned from endpoint.\ Expected {}, got {}.".format( limit, len(results["results"]) ) ) # get all remaining results all_results = results["results"] while len(all_results) != num_results: offset += len(results["results"]) results = self._perform_request( "get", DATASETS_PATH, params=params + [("offset", offset)] ) all_results.extend(results["results"]) return all_results def create(self, name, **kwargs): """ Create a dataset, including the field types. Optionally, specify args such as: description : description of the dataset columns : list of columns (see docs/tests for list structure) category : must exist in /admin/metadata tags : list of tag strings row_identifier : field name of primary key new_backend : whether to create the dataset in the new backend WARNING: This api endpoint might be deprecated. """ new_backend = kwargs.pop("new_backend", False) resource = utils.format_old_api_request(content_type="json") if new_backend: resource += "?nbe=true" payload = {"name": name} if "row_identifier" in kwargs: payload["metadata"] = {"rowIdentifier": kwargs.pop("row_identifier", None)} payload.update(kwargs) payload = utils.clear_empty_values(payload) return self._perform_update("post", resource, payload) def set_permission( self, dataset_identifier, permission="private", content_type="json" ): """ Set a dataset's permissions to private or public Options are private, public """ resource = utils.format_old_api_request( dataid=dataset_identifier, content_type=content_type ) params = { "method": "setPermission", "value": "public.read" if permission == "public" else permission, } return self._perform_request("put", resource, params=params) def get_metadata(self, dataset_identifier, content_type="json"): """ Retrieve the metadata for a particular dataset. """ resource = utils.format_old_api_request( dataid=dataset_identifier, content_type=content_type ) return self._perform_request("get", resource) def update_metadata(self, dataset_identifier, update_fields, content_type="json"): """ Update the metadata for a particular dataset. update_fields is a dictionary containing [metadata key:new value] pairs. This method performs a full replace for the key:value pairs listed in `update_fields`, and returns all of the metadata with the updates applied. """ resource = utils.format_old_api_request( dataid=dataset_identifier, content_type=content_type ) return self._perform_update("put", resource, update_fields) def download_attachments( self, dataset_identifier, content_type="json", download_dir="~/sodapy_downloads" ): """ Download all of the attachments associated with a dataset. Return the paths of downloaded files. """ metadata = self.get_metadata(dataset_identifier, content_type=content_type) files = [] attachments = metadata["metadata"].get("attachments") if not attachments: logging.info("No attachments were found or downloaded.") return files download_dir = os.path.join( os.path.expanduser(download_dir), dataset_identifier ) if not os.path.exists(download_dir): os.makedirs(download_dir) for attachment in attachments: file_path = os.path.join(download_dir, attachment["filename"]) has_assetid = attachment.get("assetId", False) if has_assetid: base = utils.format_old_api_request(dataid=dataset_identifier) assetid = attachment["assetId"] resource = "{}/files/{}?download=true&filename={}".format( base, assetid, attachment["filename"] ) else: base = "/api/assets" assetid = attachment["blobId"] resource = "{}/{}?download=true".format(base, assetid) uri = "{}{}{}".format(self.uri_prefix, self.domain, resource) utils.download_file(uri, file_path) files.append(file_path) logging.info( "The following files were downloaded:\n\t%s", "\n\t".join(files) ) return files def publish(self, dataset_identifier, content_type="json"): """ The create() method creates a dataset in a "working copy" state. This method publishes it. """ base = utils.format_old_api_request(dataid=dataset_identifier) resource = "{}/publication.{}".format(base, content_type) return self._perform_request("post", resource) def get(self, dataset_identifier, content_type="json", **kwargs): """ Read data from the requested resource. Options for content_type are json, csv, and xml. Optionally, specify a keyword arg to filter results: select : the set of columns to be returned, defaults to * where : filters the rows to be returned, defaults to limit order : specifies the order of results group : column to group results on limit : max number of results to return, defaults to 1000 offset : offset, used for paging. Defaults to 0 q : performs a full text search for a value query : full SoQL query string, all as one parameter exclude_system_fields : defaults to true. If set to false, the response will include system fields (:id, :created_at, and :updated_at) More information about the SoQL parameters can be found at the official docs: http://dev.socrata.com/docs/queries.html More information about system fields can be found here: http://dev.socrata.com/docs/system-fields.html """ resource = utils.format_new_api_request( dataid=dataset_identifier, content_type=content_type ) headers = utils.clear_empty_values({"Accept": kwargs.pop("format", None)}) # SoQL parameters params = { "$select": kwargs.pop("select", None), "$where": kwargs.pop("where", None), "$order": kwargs.pop("order", None), "$group": kwargs.pop("group", None), "$limit": kwargs.pop("limit", None), "$offset": kwargs.pop("offset", None), "$q": kwargs.pop("q", None), "$query": kwargs.pop("query", None), "$$exclude_system_fields": kwargs.pop("exclude_system_fields", None), } # Additional parameters, such as field names params.update(kwargs) params = utils.clear_empty_values(params) response = self._perform_request( "get", resource, headers=headers, params=params ) return response def get_all(self, *args, **kwargs): """ Read data from the requested resource, paginating over all results. Accepts the same arguments as get(). Returns a generator. """ params = {} params.update(kwargs) if "offset" not in params: params["offset"] = 0 limit = params.get("limit", self.DEFAULT_LIMIT) while True: response = self.get(*args, **params) for item in response: yield item if len(response) < limit: return params["offset"] += limit def upsert(self, dataset_identifier, payload, content_type="json"): """ Insert, update or delete data to/from an existing dataset. Currently supports json and csv file objects. See here for the upsert documentation: http://dev.socrata.com/publishers/upsert.html """ resource = utils.format_new_api_request( dataid=dataset_identifier, content_type=content_type ) return self._perform_update("post", resource, payload) def replace(self, dataset_identifier, payload, content_type="json"): """ Same logic as upsert, but overwrites existing data with the payload using PUT instead of POST. """ resource = utils.format_new_api_request( dataid=dataset_identifier, content_type=content_type ) return self._perform_update("put", resource, payload) def create_non_data_file(self, params, file_data): """ Creates a new file-based dataset with the name provided in the files tuple. A valid file input would be: files = ( {'file': ("gtfs2", open('myfile.zip', 'rb'))} ) """ api_prefix = "/api/imports2/" if not params.get("method", None): params["method"] = "blob" return self._perform_request("post", api_prefix, params=params, files=file_data) def replace_non_data_file(self, dataset_identifier, params, file_data): """ Same as create_non_data_file, but replaces a file that already exists in a file-based dataset. WARNING: a table-based dataset cannot be replaced by a file-based dataset. Use create_non_data_file in order to replace. """ resource = utils.format_old_api_request( dataid=dataset_identifier, content_type="txt" ) if not params.get("method", None): params["method"] = "replaceBlob" params["id"] = dataset_identifier return self._perform_request("post", resource, params=params, files=file_data) def _perform_update(self, method, resource, payload): """ Execute the update task. """ if isinstance(payload, (dict, list)): response = self._perform_request(method, resource, data=json.dumps(payload)) elif isinstance(payload, IOBase): headers = { "content-type": "text/csv", } response = self._perform_request( method, resource, data=payload, headers=headers ) else: raise Exception( "Unrecognized payload {}. Currently only list-, dictionary-," " and file-types are supported.".format(type(payload)) ) return response def delete(self, dataset_identifier, row_id=None, content_type="json"): """ Delete the entire dataset, e.g. client.delete("nimj-3ivp") or a single row, e.g. client.delete("nimj-3ivp", row_id=4) """ if row_id: resource = utils.format_new_api_request( dataid=dataset_identifier, row_id=row_id, content_type=content_type ) else: resource = utils.format_old_api_request( dataid=dataset_identifier, content_type=content_type ) return self._perform_request("delete", resource) def _perform_request(self, request_type, resource, **kwargs): """ Utility method that performs all requests. """ request_type_methods = set(["get", "post", "put", "delete"]) if request_type not in request_type_methods: raise Exception( "Unknown request type. Supported request types are" ": {}".format(", ".join(request_type_methods)) ) uri = "{}{}{}".format(self.uri_prefix, self.domain, resource) # set a timeout, just to be safe kwargs["timeout"] = self.timeout response = getattr(self.session, request_type)(uri, **kwargs) # handle errors if response.status_code not in (200, 202): utils.raise_for_status(response) # when responses have no content body (ie. delete, set_permission), # simply return the whole response if not response.text: return response # for other request types, return most useful data content_type = response.headers.get("content-type").strip().lower() if re.match(r"application\/(vnd\.geo\+)?json", content_type): return response.json() if re.match(r"text\/csv", content_type): csv_stream = StringIO(response.text) return list(csv.reader(csv_stream)) if re.match(r"application\/rdf\+xml", content_type): return response.content if re.match(r"text\/plain", content_type): try: return json.loads(response.text) except ValueError: return response.text raise Exception("Unknown response format: {}".format(content_type)) def close(self): """ Close the session. """ self.session.close() afeld-sodapy-464855c/sodapy/utils.py000066400000000000000000000056331430365730200173710ustar00rootroot00000000000000import requests from .constants import DEFAULT_API_PATH, OLD_API_PATH # Utility methods def raise_for_status(response): """ Custom raise_for_status with more appropriate error message. """ http_error_msg = "" if 400 <= response.status_code < 500: http_error_msg = "{} Client Error: {}".format( response.status_code, response.reason ) elif 500 <= response.status_code < 600: http_error_msg = "{} Server Error: {}".format( response.status_code, response.reason ) if http_error_msg: try: more_info = response.json().get("message") except ValueError: more_info = None if more_info and more_info.lower() != response.reason.lower(): http_error_msg += ".\n\t{}".format(more_info) raise requests.exceptions.HTTPError(http_error_msg, response=response) def clear_empty_values(args): """ Scrap junk data from a dict. """ result = {} for param in args: if args[param] is not None: result[param] = args[param] return result def format_old_api_request(dataid=None, content_type=None): if dataid is not None: if content_type is not None: return "{}/{}.{}".format(OLD_API_PATH, dataid, content_type) return "{}/{}".format(OLD_API_PATH, dataid) if content_type is not None: return "{}.{}".format(OLD_API_PATH, content_type) raise Exception( "This method requires at least a dataset_id or content_type." ) def format_new_api_request(dataid=None, row_id=None, content_type=None): if dataid is not None: if content_type is not None: if row_id is not None: return "{}{}/{}.{}".format( DEFAULT_API_PATH, dataid, row_id, content_type ) return "{}{}.{}".format(DEFAULT_API_PATH, dataid, content_type) raise Exception("This method requires at least a dataset_id or content_type.") def authentication_validation(username, password, access_token): """ Only accept one form of authentication. """ if bool(username) is not bool(password): raise Exception("Basic authentication requires a username AND" " password.") if (username and access_token) or (password and access_token): raise Exception( "Cannot use both Basic Authentication and" " OAuth2.0. Please use only one authentication" " method." ) def download_file(url, local_filename): """ Utility function that downloads a chunked response from the specified url to a local path. This method is suitable for larger downloads. """ response = requests.get(url, stream=True) with open(local_filename, "wb") as outfile: for chunk in response.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks outfile.write(chunk) afeld-sodapy-464855c/sodapy/version.py000066400000000000000000000001161430365730200177050ustar00rootroot00000000000000version_info = (2, 2, 0) __version__ = ".".join(str(v) for v in version_info) afeld-sodapy-464855c/tests/000077500000000000000000000000001430365730200155135ustar00rootroot00000000000000afeld-sodapy-464855c/tests/test_data/000077500000000000000000000000001430365730200174635ustar00rootroot00000000000000afeld-sodapy-464855c/tests/test_data/403_response_json.txt000066400000000000000000000001131430365730200234740ustar00rootroot00000000000000{"message": "You must be logged in to access this resource", "error": true}afeld-sodapy-464855c/tests/test_data/bike_counts_page_1.json000066400000000000000000002336371430365730200241150ustar00rootroot00000000000000[{"id":"100009424","date":"2016-09-21T15:45:00.000","counts":"100","status":"0"} ,{"id":"100009424","date":"2016-09-21T16:00:00.000","counts":"77","status":"0"} ,{"id":"100009424","date":"2016-09-21T16:15:00.000","counts":"61","status":"0"} ,{"id":"100009424","date":"2016-09-21T16:30:00.000","counts":"72","status":"0"} ,{"id":"100009424","date":"2016-09-21T16:45:00.000","counts":"73","status":"0"} ,{"id":"100009424","date":"2016-09-21T17:00:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-21T17:15:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-21T17:30:00.000","counts":"73","status":"0"} ,{"id":"100009424","date":"2016-09-21T17:45:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-21T18:00:00.000","counts":"59","status":"0"} ,{"id":"100009424","date":"2016-09-21T18:15:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-21T18:30:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-21T18:45:00.000","counts":"52","status":"0"} ,{"id":"100009424","date":"2016-09-21T19:00:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-21T19:15:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-21T19:30:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-21T19:45:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-21T20:00:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-21T20:15:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-21T20:30:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-21T20:45:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-21T21:00:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-21T21:15:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-21T21:30:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-21T21:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-21T22:00:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-21T22:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-21T22:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-21T22:45:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-21T23:00:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-21T23:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-21T23:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-21T23:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-22T00:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-22T00:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-22T00:30:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-22T00:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-22T01:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-22T01:15:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-22T01:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-22T01:45:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-22T02:00:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-22T02:15:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-22T02:30:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-22T02:45:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-22T03:00:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-22T03:15:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-09-22T03:30:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-22T03:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-22T04:00:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-22T04:15:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-22T04:30:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-22T04:45:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-22T05:00:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-22T05:15:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-22T05:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-22T05:45:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-22T06:00:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-22T06:15:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-22T06:30:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-22T06:45:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-22T07:00:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-22T07:15:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-22T07:30:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-22T07:45:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-22T08:00:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-22T08:15:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-22T08:30:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-22T08:45:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-22T09:00:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-22T09:15:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-22T09:30:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-22T09:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-22T10:00:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-22T10:15:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-22T10:30:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-22T10:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-22T11:00:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-22T11:15:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-22T11:30:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-22T11:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-22T12:00:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-22T12:15:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-22T12:30:00.000","counts":"65","status":"0"} ,{"id":"100009424","date":"2016-09-22T12:45:00.000","counts":"74","status":"0"} ,{"id":"100009424","date":"2016-09-22T13:00:00.000","counts":"99","status":"0"} ,{"id":"100009424","date":"2016-09-22T13:15:00.000","counts":"97","status":"0"} ,{"id":"100009424","date":"2016-09-22T13:30:00.000","counts":"115","status":"0"} ,{"id":"100009424","date":"2016-09-22T13:45:00.000","counts":"143","status":"0"} ,{"id":"100009424","date":"2016-09-22T14:00:00.000","counts":"123","status":"0"} ,{"id":"100009424","date":"2016-09-22T14:15:00.000","counts":"144","status":"0"} ,{"id":"100009424","date":"2016-09-22T14:30:00.000","counts":"142","status":"0"} ,{"id":"100009424","date":"2016-09-22T14:45:00.000","counts":"97","status":"0"} ,{"id":"100009424","date":"2016-09-22T15:00:00.000","counts":"108","status":"0"} ,{"id":"100009424","date":"2016-09-22T15:15:00.000","counts":"109","status":"0"} ,{"id":"100009424","date":"2016-09-22T15:30:00.000","counts":"74","status":"0"} ,{"id":"100009424","date":"2016-09-22T15:45:00.000","counts":"80","status":"0"} ,{"id":"100009424","date":"2016-09-22T16:00:00.000","counts":"75","status":"0"} ,{"id":"100009424","date":"2016-09-22T16:15:00.000","counts":"85","status":"0"} ,{"id":"100009424","date":"2016-09-22T16:30:00.000","counts":"62","status":"0"} ,{"id":"100009424","date":"2016-09-22T16:45:00.000","counts":"64","status":"0"} ,{"id":"100009424","date":"2016-09-22T17:00:00.000","counts":"59","status":"0"} ,{"id":"100009424","date":"2016-09-22T17:15:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-22T17:30:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-22T17:45:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-22T18:00:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-22T18:15:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-22T18:30:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-22T18:45:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-22T19:00:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-22T19:15:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-22T19:30:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-22T19:45:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-22T20:00:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-22T20:15:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-22T20:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-22T20:45:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-22T21:00:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-22T21:15:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-22T21:30:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-22T21:45:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-22T22:00:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-22T22:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-22T22:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-22T22:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-22T23:00:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-22T23:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-22T23:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-22T23:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-23T00:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-23T00:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-23T00:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-23T00:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-23T01:00:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-23T01:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-23T01:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-23T01:45:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-23T02:00:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-23T02:15:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-23T02:30:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-23T02:45:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-23T03:00:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-23T03:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-23T03:30:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-23T03:45:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-23T04:00:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-23T04:15:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-23T04:30:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-23T04:45:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-23T05:00:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-23T05:15:00.000","counts":"52","status":"0"} ,{"id":"100009424","date":"2016-09-23T05:30:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-23T05:45:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-23T06:00:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-23T06:15:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-09-23T06:30:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-23T06:45:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-23T07:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-23T07:15:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-23T07:30:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-23T07:45:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-23T08:00:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-23T08:15:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-23T08:30:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-23T08:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-23T09:00:00.000","counts":"62","status":"0"} ,{"id":"100009424","date":"2016-09-23T09:15:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-23T09:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-23T09:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-23T10:00:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-09-23T10:15:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-23T10:30:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-23T10:45:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-23T11:00:00.000","counts":"61","status":"0"} ,{"id":"100009424","date":"2016-09-23T11:15:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-23T11:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-23T11:45:00.000","counts":"62","status":"0"} ,{"id":"100009424","date":"2016-09-23T12:00:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-23T12:15:00.000","counts":"66","status":"0"} ,{"id":"100009424","date":"2016-09-23T12:30:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-23T12:45:00.000","counts":"77","status":"0"} ,{"id":"100009424","date":"2016-09-23T13:00:00.000","counts":"100","status":"0"} ,{"id":"100009424","date":"2016-09-23T13:15:00.000","counts":"119","status":"0"} ,{"id":"100009424","date":"2016-09-23T13:30:00.000","counts":"118","status":"0"} ,{"id":"100009424","date":"2016-09-23T13:45:00.000","counts":"118","status":"0"} ,{"id":"100009424","date":"2016-09-23T14:00:00.000","counts":"142","status":"0"} ,{"id":"100009424","date":"2016-09-23T14:15:00.000","counts":"113","status":"0"} ,{"id":"100009424","date":"2016-09-23T14:30:00.000","counts":"114","status":"0"} ,{"id":"100009424","date":"2016-09-23T14:45:00.000","counts":"119","status":"0"} ,{"id":"100009424","date":"2016-09-23T15:00:00.000","counts":"86","status":"0"} ,{"id":"100009424","date":"2016-09-23T15:15:00.000","counts":"79","status":"0"} ,{"id":"100009424","date":"2016-09-23T15:30:00.000","counts":"72","status":"0"} ,{"id":"100009424","date":"2016-09-23T15:45:00.000","counts":"78","status":"0"} ,{"id":"100009424","date":"2016-09-23T16:00:00.000","counts":"70","status":"0"} ,{"id":"100009424","date":"2016-09-23T16:15:00.000","counts":"71","status":"0"} ,{"id":"100009424","date":"2016-09-23T16:30:00.000","counts":"75","status":"0"} ,{"id":"100009424","date":"2016-09-23T16:45:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-23T17:00:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-23T17:15:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-23T17:30:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-23T17:45:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-23T18:00:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-23T18:15:00.000","counts":"46","status":"0"} ,{"id":"100009424","date":"2016-09-23T18:30:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-23T18:45:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-23T19:00:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-23T19:15:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-23T19:30:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-23T19:45:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-23T20:00:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-23T20:15:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-23T20:30:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-23T20:45:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-23T21:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-23T21:15:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-23T21:30:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-23T21:45:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-23T22:00:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-23T22:15:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-23T22:30:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-23T22:45:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-23T23:00:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-23T23:15:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-23T23:30:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-23T23:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-24T00:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-24T00:15:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-24T00:30:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-24T00:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-24T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-24T01:15:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-24T01:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-24T01:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-24T02:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-24T02:15:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-24T02:30:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-24T02:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-24T03:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-24T03:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-24T03:30:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-24T03:45:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-24T04:00:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-24T04:15:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-24T04:30:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-24T04:45:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-24T05:00:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-24T05:15:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-24T05:30:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-24T05:45:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-24T06:00:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-24T06:15:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-24T06:30:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-24T06:45:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-24T07:00:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-24T07:15:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-24T07:30:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-24T07:45:00.000","counts":"61","status":"0"} ,{"id":"100009424","date":"2016-09-24T08:00:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-24T08:15:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-24T08:30:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-24T08:45:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-24T09:00:00.000","counts":"62","status":"0"} ,{"id":"100009424","date":"2016-09-24T09:15:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-24T09:30:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-24T09:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-24T10:00:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-24T10:15:00.000","counts":"65","status":"0"} ,{"id":"100009424","date":"2016-09-24T10:30:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-24T10:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-24T11:00:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-24T11:15:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-24T11:30:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-24T11:45:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-24T12:00:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-24T12:15:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-24T12:30:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-24T12:45:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-24T13:00:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-24T13:15:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-24T13:30:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-24T13:45:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-24T14:00:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-24T14:15:00.000","counts":"58","status":"0"} ,{"id":"100009424","date":"2016-09-24T14:30:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-24T14:45:00.000","counts":"62","status":"0"} ,{"id":"100009424","date":"2016-09-24T15:00:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-24T15:15:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-24T15:30:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-24T15:45:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-24T16:00:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-24T16:15:00.000","counts":"64","status":"0"} ,{"id":"100009424","date":"2016-09-24T16:30:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-24T16:45:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-24T17:00:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-24T17:15:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-24T17:30:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-24T17:45:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-24T18:00:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-24T18:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-24T18:30:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-24T18:45:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-24T19:00:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-24T19:15:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-24T19:30:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-24T19:45:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-24T20:00:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-24T20:15:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-24T20:30:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-24T20:45:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-24T21:00:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-24T21:15:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-24T21:30:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-24T21:45:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-24T22:00:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-24T22:15:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-24T22:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-24T22:45:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-24T23:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-24T23:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-24T23:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-24T23:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-25T00:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T00:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T00:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T00:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-25T01:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-25T01:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T01:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-25T02:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-25T02:15:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-25T02:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-25T02:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-25T03:00:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-25T03:15:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-25T03:30:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-25T03:45:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-25T04:00:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-25T04:15:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-25T04:30:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-25T04:45:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-25T05:00:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-25T05:15:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-25T05:30:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-25T05:45:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-25T06:00:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-25T06:15:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-25T06:30:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-25T06:45:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-25T07:00:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-25T07:15:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-25T07:30:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-25T07:45:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-25T08:00:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-25T08:15:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-25T08:30:00.000","counts":"59","status":"0"} ,{"id":"100009424","date":"2016-09-25T08:45:00.000","counts":"66","status":"0"} ,{"id":"100009424","date":"2016-09-25T09:00:00.000","counts":"46","status":"0"} ,{"id":"100009424","date":"2016-09-25T09:15:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-25T09:30:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-25T09:45:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-25T10:00:00.000","counts":"73","status":"0"} ,{"id":"100009424","date":"2016-09-25T10:15:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-25T10:30:00.000","counts":"58","status":"0"} ,{"id":"100009424","date":"2016-09-25T10:45:00.000","counts":"65","status":"0"} ,{"id":"100009424","date":"2016-09-25T11:00:00.000","counts":"59","status":"0"} ,{"id":"100009424","date":"2016-09-25T11:15:00.000","counts":"58","status":"0"} ,{"id":"100009424","date":"2016-09-25T11:30:00.000","counts":"89","status":"0"} ,{"id":"100009424","date":"2016-09-25T11:45:00.000","counts":"72","status":"0"} ,{"id":"100009424","date":"2016-09-25T12:00:00.000","counts":"58","status":"0"} ,{"id":"100009424","date":"2016-09-25T12:15:00.000","counts":"79","status":"0"} ,{"id":"100009424","date":"2016-09-25T12:30:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-25T12:45:00.000","counts":"75","status":"0"} ,{"id":"100009424","date":"2016-09-25T13:00:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-25T13:15:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-09-25T13:30:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-25T13:45:00.000","counts":"89","status":"0"} ,{"id":"100009424","date":"2016-09-25T14:00:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-09-25T14:15:00.000","counts":"61","status":"0"} ,{"id":"100009424","date":"2016-09-25T14:30:00.000","counts":"75","status":"0"} ,{"id":"100009424","date":"2016-09-25T14:45:00.000","counts":"70","status":"0"} ,{"id":"100009424","date":"2016-09-25T15:00:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-25T15:15:00.000","counts":"74","status":"0"} ,{"id":"100009424","date":"2016-09-25T15:30:00.000","counts":"72","status":"0"} ,{"id":"100009424","date":"2016-09-25T15:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-25T16:00:00.000","counts":"72","status":"0"} ,{"id":"100009424","date":"2016-09-25T16:15:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-25T16:30:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-25T16:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-25T17:00:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-25T17:15:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-25T17:30:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-25T17:45:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-25T18:00:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-25T18:15:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-25T18:30:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-25T18:45:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-25T19:00:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-25T19:15:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-25T19:30:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-25T19:45:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-25T20:00:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-25T20:15:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-25T20:30:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-25T20:45:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-25T21:00:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-25T21:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-25T21:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-25T21:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-25T22:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T22:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-25T22:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-25T22:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-25T23:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-25T23:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-25T23:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-25T23:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-26T00:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T00:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-26T00:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-26T00:45:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T01:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-26T01:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-26T01:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-26T02:00:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-26T02:15:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-26T02:30:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-26T02:45:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-26T03:00:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-26T03:15:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-26T03:30:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-26T03:45:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-26T04:00:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-26T04:15:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-26T04:30:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-26T04:45:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-26T05:00:00.000","counts":"61","status":"0"} ,{"id":"100009424","date":"2016-09-26T05:15:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-26T05:30:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-26T05:45:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-26T06:00:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-26T06:15:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-26T06:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-26T06:45:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-26T07:00:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-26T07:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-26T07:30:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-26T07:45:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-26T08:00:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-26T08:15:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-26T08:30:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-26T08:45:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-26T09:00:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-26T09:15:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-09-26T09:30:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-26T09:45:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-26T10:00:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-26T10:15:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-26T10:30:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-26T10:45:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-26T11:00:00.000","counts":"52","status":"0"} ,{"id":"100009424","date":"2016-09-26T11:15:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-26T11:30:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-26T11:45:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-26T12:00:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-09-26T12:15:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-26T12:30:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-26T12:45:00.000","counts":"70","status":"0"} ,{"id":"100009424","date":"2016-09-26T13:00:00.000","counts":"74","status":"0"} ,{"id":"100009424","date":"2016-09-26T13:15:00.000","counts":"110","status":"0"} ,{"id":"100009424","date":"2016-09-26T13:30:00.000","counts":"143","status":"0"} ,{"id":"100009424","date":"2016-09-26T13:45:00.000","counts":"134","status":"0"} ,{"id":"100009424","date":"2016-09-26T14:00:00.000","counts":"134","status":"0"} ,{"id":"100009424","date":"2016-09-26T14:15:00.000","counts":"138","status":"0"} ,{"id":"100009424","date":"2016-09-26T14:30:00.000","counts":"144","status":"0"} ,{"id":"100009424","date":"2016-09-26T14:45:00.000","counts":"119","status":"0"} ,{"id":"100009424","date":"2016-09-26T15:00:00.000","counts":"92","status":"0"} ,{"id":"100009424","date":"2016-09-26T15:15:00.000","counts":"83","status":"0"} ,{"id":"100009424","date":"2016-09-26T15:30:00.000","counts":"102","status":"0"} ,{"id":"100009424","date":"2016-09-26T15:45:00.000","counts":"99","status":"0"} ,{"id":"100009424","date":"2016-09-26T16:00:00.000","counts":"93","status":"0"} ,{"id":"100009424","date":"2016-09-26T16:15:00.000","counts":"72","status":"0"} ,{"id":"100009424","date":"2016-09-26T16:30:00.000","counts":"71","status":"0"} ,{"id":"100009424","date":"2016-09-26T16:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-26T17:00:00.000","counts":"64","status":"0"} ,{"id":"100009424","date":"2016-09-26T17:15:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-26T17:30:00.000","counts":"52","status":"0"} ,{"id":"100009424","date":"2016-09-26T17:45:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-26T18:00:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-26T18:15:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-26T18:30:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-26T18:45:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-26T19:00:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-26T19:15:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-26T19:30:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-26T19:45:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-26T20:00:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-26T20:15:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-26T20:30:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-26T20:45:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-26T21:00:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-26T21:15:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-26T21:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-26T21:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-26T22:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-26T22:15:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T22:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-26T22:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-26T23:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T23:15:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T23:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-26T23:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-27T00:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-27T00:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-27T00:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-27T00:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-27T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-27T01:15:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-27T01:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-27T01:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-27T02:00:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-27T02:15:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-27T02:30:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-27T02:45:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-27T03:00:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-27T03:15:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-27T03:30:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-27T03:45:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-27T04:00:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-27T04:15:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-27T04:30:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-27T04:45:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-27T05:00:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-27T05:15:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-27T05:30:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-27T05:45:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-27T06:00:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-27T06:15:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-27T06:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-27T06:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-27T07:00:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-27T07:15:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-27T07:30:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-27T07:45:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-09-27T08:00:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-27T08:15:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-27T08:30:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-27T08:45:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-27T09:00:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-27T09:15:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-27T09:30:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-27T09:45:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-27T10:00:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-27T10:15:00.000","counts":"59","status":"0"} ,{"id":"100009424","date":"2016-09-27T10:30:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-27T10:45:00.000","counts":"46","status":"0"} ,{"id":"100009424","date":"2016-09-27T11:00:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-27T11:15:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-27T11:30:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-27T11:45:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-27T12:00:00.000","counts":"61","status":"0"} ,{"id":"100009424","date":"2016-09-27T12:15:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-27T12:30:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-27T12:45:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-27T13:00:00.000","counts":"63","status":"0"} ,{"id":"100009424","date":"2016-09-27T13:15:00.000","counts":"112","status":"0"} ,{"id":"100009424","date":"2016-09-27T13:30:00.000","counts":"92","status":"0"} ,{"id":"100009424","date":"2016-09-27T13:45:00.000","counts":"101","status":"0"} ,{"id":"100009424","date":"2016-09-27T14:00:00.000","counts":"128","status":"0"} ,{"id":"100009424","date":"2016-09-27T14:15:00.000","counts":"137","status":"0"} ,{"id":"100009424","date":"2016-09-27T14:30:00.000","counts":"121","status":"0"} ,{"id":"100009424","date":"2016-09-27T14:45:00.000","counts":"88","status":"0"} ,{"id":"100009424","date":"2016-09-27T15:00:00.000","counts":"115","status":"0"} ,{"id":"100009424","date":"2016-09-27T15:15:00.000","counts":"105","status":"0"} ,{"id":"100009424","date":"2016-09-27T15:30:00.000","counts":"85","status":"0"} ,{"id":"100009424","date":"2016-09-27T15:45:00.000","counts":"75","status":"0"} ,{"id":"100009424","date":"2016-09-27T16:00:00.000","counts":"74","status":"0"} ,{"id":"100009424","date":"2016-09-27T16:15:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-27T16:30:00.000","counts":"68","status":"0"} ,{"id":"100009424","date":"2016-09-27T16:45:00.000","counts":"65","status":"0"} ,{"id":"100009424","date":"2016-09-27T17:00:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-27T17:15:00.000","counts":"46","status":"0"} ,{"id":"100009424","date":"2016-09-27T17:30:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-27T17:45:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-27T18:00:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-27T18:15:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-27T18:30:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-09-27T18:45:00.000","counts":"46","status":"0"} ,{"id":"100009424","date":"2016-09-27T19:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-27T19:15:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-27T19:30:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-27T19:45:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-27T20:00:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-27T20:15:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-27T20:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-27T20:45:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-27T21:00:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-09-27T21:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-27T21:30:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-27T21:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-27T22:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-27T22:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-27T22:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-27T22:45:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-27T23:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-27T23:15:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-27T23:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-27T23:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-28T00:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-28T00:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-28T00:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-28T00:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-28T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-28T01:15:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-28T01:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-28T01:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-28T02:00:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-28T02:15:00.000","counts":"12","status":"0"} ,{"id":"100009424","date":"2016-09-28T02:30:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-28T02:45:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-28T03:00:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-28T03:15:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-28T03:30:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-28T03:45:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-28T04:00:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-28T04:15:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-28T04:30:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-28T04:45:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-28T05:00:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-28T05:15:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-28T05:30:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-28T05:45:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-28T06:00:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-28T06:15:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-28T06:30:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-28T06:45:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-28T07:00:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-28T07:15:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-28T07:30:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-28T07:45:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-28T08:00:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-28T08:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-28T08:30:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-28T08:45:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-28T09:00:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-28T09:15:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-28T09:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-28T09:45:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-28T10:00:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-28T10:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-28T10:30:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-28T10:45:00.000","counts":"53","status":"0"} ,{"id":"100009424","date":"2016-09-28T11:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-28T11:15:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-28T11:30:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-28T11:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-28T12:00:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-09-28T12:15:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-28T12:30:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-28T12:45:00.000","counts":"70","status":"0"} ,{"id":"100009424","date":"2016-09-28T13:00:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-28T13:15:00.000","counts":"96","status":"0"} ,{"id":"100009424","date":"2016-09-28T13:30:00.000","counts":"109","status":"0"} ,{"id":"100009424","date":"2016-09-28T13:45:00.000","counts":"115","status":"0"} ,{"id":"100009424","date":"2016-09-28T14:00:00.000","counts":"126","status":"0"} ,{"id":"100009424","date":"2016-09-28T14:15:00.000","counts":"135","status":"0"} ,{"id":"100009424","date":"2016-09-28T14:30:00.000","counts":"108","status":"0"} ,{"id":"100009424","date":"2016-09-28T14:45:00.000","counts":"115","status":"0"} ,{"id":"100009424","date":"2016-09-28T15:00:00.000","counts":"88","status":"0"} ,{"id":"100009424","date":"2016-09-28T15:15:00.000","counts":"97","status":"0"} ,{"id":"100009424","date":"2016-09-28T15:30:00.000","counts":"69","status":"0"} ,{"id":"100009424","date":"2016-09-28T15:45:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-28T16:00:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-28T16:15:00.000","counts":"65","status":"0"} ,{"id":"100009424","date":"2016-09-28T16:30:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-28T16:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-28T17:00:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-09-28T17:15:00.000","counts":"54","status":"0"} ,{"id":"100009424","date":"2016-09-28T17:30:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-28T17:45:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-28T18:00:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-28T18:15:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-28T18:30:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-28T18:45:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-28T19:00:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-28T19:15:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-28T19:30:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-28T19:45:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-09-28T20:00:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-28T20:15:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-28T20:30:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-28T20:45:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-28T21:00:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-28T21:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-28T21:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-28T21:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-28T22:00:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-28T22:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-28T22:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-28T22:45:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-28T23:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-28T23:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-28T23:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-28T23:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-29T00:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-29T00:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-29T00:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-29T00:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-29T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-29T01:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-29T01:30:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-29T01:45:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-29T02:00:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-29T02:15:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-29T02:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-29T02:45:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-29T03:00:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-29T03:15:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-29T03:30:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-29T03:45:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-29T04:00:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-29T04:15:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-29T04:30:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-29T04:45:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-09-29T05:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-29T05:15:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-29T05:30:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-29T05:45:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-29T06:00:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-29T06:15:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-29T06:30:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-29T06:45:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-29T07:00:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-29T07:15:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-29T07:30:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-29T07:45:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-29T08:00:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-29T08:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-29T08:30:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-29T08:45:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-29T09:00:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-09-29T09:15:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-29T09:30:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-09-29T09:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-29T10:00:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-29T10:15:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-09-29T10:30:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-09-29T10:45:00.000","counts":"28","status":"0"} ,{"id":"100009424","date":"2016-09-29T11:00:00.000","counts":"33","status":"0"} ,{"id":"100009424","date":"2016-09-29T11:15:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-29T11:30:00.000","counts":"43","status":"0"} ,{"id":"100009424","date":"2016-09-29T11:45:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-09-29T12:00:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-09-29T12:15:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-29T12:30:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-29T12:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-29T13:00:00.000","counts":"71","status":"0"} ,{"id":"100009424","date":"2016-09-29T13:15:00.000","counts":"80","status":"0"} ,{"id":"100009424","date":"2016-09-29T13:30:00.000","counts":"87","status":"0"} ,{"id":"100009424","date":"2016-09-29T13:45:00.000","counts":"81","status":"0"} ,{"id":"100009424","date":"2016-09-29T14:00:00.000","counts":"99","status":"0"} ,{"id":"100009424","date":"2016-09-29T14:15:00.000","counts":"128","status":"0"} ,{"id":"100009424","date":"2016-09-29T14:30:00.000","counts":"105","status":"0"} ,{"id":"100009424","date":"2016-09-29T14:45:00.000","counts":"92","status":"0"} ,{"id":"100009424","date":"2016-09-29T15:00:00.000","counts":"85","status":"0"} ,{"id":"100009424","date":"2016-09-29T15:15:00.000","counts":"80","status":"0"} ,{"id":"100009424","date":"2016-09-29T15:30:00.000","counts":"76","status":"0"} ,{"id":"100009424","date":"2016-09-29T15:45:00.000","counts":"81","status":"0"} ,{"id":"100009424","date":"2016-09-29T16:00:00.000","counts":"69","status":"0"} ,{"id":"100009424","date":"2016-09-29T16:15:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-29T16:30:00.000","counts":"70","status":"0"} ,{"id":"100009424","date":"2016-09-29T16:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-29T17:00:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-09-29T17:15:00.000","counts":"55","status":"0"} ,{"id":"100009424","date":"2016-09-29T17:30:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-09-29T17:45:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-29T18:00:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-29T18:15:00.000","counts":"52","status":"0"} ,{"id":"100009424","date":"2016-09-29T18:30:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-29T18:45:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-29T19:00:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-29T19:15:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-29T19:30:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-29T19:45:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-29T20:00:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-29T20:15:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-29T20:30:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-29T20:45:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-29T21:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-29T21:15:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-29T21:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-29T21:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-29T22:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-29T22:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-29T22:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-29T22:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-29T23:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-29T23:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-29T23:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-29T23:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-30T00:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-09-30T00:15:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-30T00:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-30T00:45:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-30T01:00:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-30T01:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-30T01:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-30T01:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-30T02:00:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-30T02:15:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-30T02:30:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-30T02:45:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-30T03:00:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-09-30T03:15:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-30T03:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-30T03:45:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-30T04:00:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-09-30T04:15:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-09-30T04:30:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-30T04:45:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-30T05:00:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-30T05:15:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-30T05:30:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-30T05:45:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-30T06:00:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-30T06:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-30T06:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-30T06:45:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-09-30T07:00:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-30T07:15:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T07:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-09-30T07:45:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-09-30T08:00:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-30T08:15:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-09-30T08:30:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-30T08:45:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-30T09:00:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-30T09:15:00.000","counts":"24","status":"0"} ,{"id":"100009424","date":"2016-09-30T09:30:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-30T09:45:00.000","counts":"32","status":"0"} ,{"id":"100009424","date":"2016-09-30T10:00:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-30T10:15:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T10:30:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T10:45:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-30T11:00:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-09-30T11:15:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-30T11:30:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-09-30T11:45:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-09-30T12:00:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-09-30T12:15:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-30T12:30:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T12:45:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-30T13:00:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-09-30T13:15:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-09-30T13:30:00.000","counts":"29","status":"0"} ,{"id":"100009424","date":"2016-09-30T13:45:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-30T14:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-30T14:15:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-09-30T14:30:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-30T14:45:00.000","counts":"67","status":"0"} ,{"id":"100009424","date":"2016-09-30T15:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-30T15:15:00.000","counts":"46","status":"0"} ,{"id":"100009424","date":"2016-09-30T15:30:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-09-30T15:45:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-30T16:00:00.000","counts":"52","status":"0"} ,{"id":"100009424","date":"2016-09-30T16:15:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-09-30T16:30:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-09-30T16:45:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-09-30T17:00:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-30T17:15:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-09-30T17:30:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-09-30T17:45:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-09-30T18:00:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-09-30T18:15:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-09-30T18:30:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T18:45:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T19:00:00.000","counts":"23","status":"0"} ,{"id":"100009424","date":"2016-09-30T19:15:00.000","counts":"13","status":"0"} ,{"id":"100009424","date":"2016-09-30T19:30:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-30T19:45:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-09-30T20:00:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-30T20:15:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-09-30T20:30:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-09-30T20:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-30T21:00:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-30T21:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-09-30T21:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-30T21:45:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-09-30T22:00:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-30T22:15:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-09-30T22:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-30T22:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-30T23:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-09-30T23:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-09-30T23:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-09-30T23:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T00:00:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-10-01T00:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-10-01T00:30:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T00:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-10-01T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-10-01T01:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-10-01T01:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-10-01T01:45:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-10-01T02:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T02:15:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-10-01T02:30:00.000","counts":"1","status":"0"} ,{"id":"100009424","date":"2016-10-01T02:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-10-01T03:00:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-10-01T03:15:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-10-01T03:30:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-10-01T03:45:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-10-01T04:00:00.000","counts":"16","status":"0"} ,{"id":"100009424","date":"2016-10-01T04:15:00.000","counts":"17","status":"0"} ,{"id":"100009424","date":"2016-10-01T04:30:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-10-01T04:45:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-10-01T05:00:00.000","counts":"10","status":"0"} ,{"id":"100009424","date":"2016-10-01T05:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T05:30:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-10-01T05:45:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-10-01T06:00:00.000","counts":"22","status":"0"} ,{"id":"100009424","date":"2016-10-01T06:15:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-10-01T06:30:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-10-01T06:45:00.000","counts":"18","status":"0"} ,{"id":"100009424","date":"2016-10-01T07:00:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-10-01T07:15:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-10-01T07:30:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-10-01T07:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-10-01T08:00:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-10-01T08:15:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-10-01T08:30:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-10-01T08:45:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-10-01T09:00:00.000","counts":"40","status":"0"} ,{"id":"100009424","date":"2016-10-01T09:15:00.000","counts":"49","status":"0"} ,{"id":"100009424","date":"2016-10-01T09:30:00.000","counts":"25","status":"0"} ,{"id":"100009424","date":"2016-10-01T09:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-10-01T10:00:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-10-01T10:15:00.000","counts":"57","status":"0"} ,{"id":"100009424","date":"2016-10-01T10:30:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-10-01T10:45:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-10-01T11:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-10-01T11:15:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-10-01T11:30:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-10-01T11:45:00.000","counts":"44","status":"0"} ,{"id":"100009424","date":"2016-10-01T12:00:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-10-01T12:15:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-10-01T12:30:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-10-01T12:45:00.000","counts":"26","status":"0"} ,{"id":"100009424","date":"2016-10-01T13:00:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-10-01T13:15:00.000","counts":"51","status":"0"} ,{"id":"100009424","date":"2016-10-01T13:30:00.000","counts":"38","status":"0"} ,{"id":"100009424","date":"2016-10-01T13:45:00.000","counts":"34","status":"0"} ,{"id":"100009424","date":"2016-10-01T14:00:00.000","counts":"41","status":"0"} ,{"id":"100009424","date":"2016-10-01T14:15:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-10-01T14:30:00.000","counts":"60","status":"0"} ,{"id":"100009424","date":"2016-10-01T14:45:00.000","counts":"50","status":"0"} ,{"id":"100009424","date":"2016-10-01T15:00:00.000","counts":"36","status":"0"} ,{"id":"100009424","date":"2016-10-01T15:15:00.000","counts":"62","status":"0"} ,{"id":"100009424","date":"2016-10-01T15:30:00.000","counts":"48","status":"0"} ,{"id":"100009424","date":"2016-10-01T15:45:00.000","counts":"45","status":"0"} ,{"id":"100009424","date":"2016-10-01T16:00:00.000","counts":"56","status":"0"} ,{"id":"100009424","date":"2016-10-01T16:15:00.000","counts":"47","status":"0"} ,{"id":"100009424","date":"2016-10-01T16:30:00.000","counts":"39","status":"0"} ,{"id":"100009424","date":"2016-10-01T16:45:00.000","counts":"42","status":"0"} ,{"id":"100009424","date":"2016-10-01T17:00:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-10-01T17:15:00.000","counts":"35","status":"0"} ,{"id":"100009424","date":"2016-10-01T17:30:00.000","counts":"37","status":"0"} ,{"id":"100009424","date":"2016-10-01T17:45:00.000","counts":"14","status":"0"} ,{"id":"100009424","date":"2016-10-01T18:00:00.000","counts":"30","status":"0"} ,{"id":"100009424","date":"2016-10-01T18:15:00.000","counts":"31","status":"0"} ,{"id":"100009424","date":"2016-10-01T18:30:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-10-01T18:45:00.000","counts":"21","status":"0"} ,{"id":"100009424","date":"2016-10-01T19:00:00.000","counts":"27","status":"0"} ,{"id":"100009424","date":"2016-10-01T19:15:00.000","counts":"20","status":"0"} ,{"id":"100009424","date":"2016-10-01T19:30:00.000","counts":"8","status":"0"} ,{"id":"100009424","date":"2016-10-01T19:45:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-10-01T20:00:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-10-01T20:15:00.000","counts":"19","status":"0"} ,{"id":"100009424","date":"2016-10-01T20:30:00.000","counts":"15","status":"0"} ,{"id":"100009424","date":"2016-10-01T20:45:00.000","counts":"11","status":"0"} ,{"id":"100009424","date":"2016-10-01T21:00:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-10-01T21:15:00.000","counts":"6","status":"0"} ,{"id":"100009424","date":"2016-10-01T21:30:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-10-01T21:45:00.000","counts":"9","status":"0"} ,{"id":"100009424","date":"2016-10-01T22:00:00.000","counts":"7","status":"0"} ,{"id":"100009424","date":"2016-10-01T22:15:00.000","counts":"5","status":"0"} ,{"id":"100009424","date":"2016-10-01T22:30:00.000","counts":"4","status":"0"} ,{"id":"100009424","date":"2016-10-01T22:45:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T23:00:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T23:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-01T23:30:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-10-01T23:45:00.000","counts":"2","status":"0"} ,{"id":"100009424","date":"2016-10-02T00:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-10-02T00:15:00.000","counts":"3","status":"0"} ,{"id":"100009424","date":"2016-10-02T00:30:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-10-02T00:45:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-10-02T01:00:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-10-02T01:15:00.000","counts":"0","status":"0"} ,{"id":"100009424","date":"2016-10-02T01:30:00.000","counts":"3","status":"0"}] afeld-sodapy-464855c/tests/test_data/bike_counts_page_2.json000066400000000000000000000001201430365730200240710ustar00rootroot00000000000000[{"id":"100009424","date":"2016-10-02T01:45:00.000","counts":"3","status":"0"}] afeld-sodapy-464855c/tests/test_data/create_foobar.txt000066400000000000000000000030031430365730200230130ustar00rootroot00000000000000{ "id": "2frc-hyvj", "name": "Foo Bar", "averageRating": 0, "createdAt": 1448018696, "description": "test dataset", "downloadCount": 0, "newBackend": false, "numberOfComments": 0, "oid": 14929734, "publicationAppendEnabled": false, "publicationGroup": 5638965, "publicationStage": "unpublished", "rowIdentifierColumnId": 230641051, "rowsUpdatedAt": 1448018697, "rowsUpdatedBy": "gxfh-uqsf", "tableId": 5638965, "totalTimesRated": 0, "viewCount": 0, "viewLastModified": 1448018697, "viewType": "tabular", "columns": [ { "id": 230641050, "name": "Foo", "dataTypeName": "text", "fieldName": "foo", "position": 1, "renderTypeName": "text", "tableColumnId": 32762225, "format": {} }, { "id": 230641051, "name": "Bar", "dataTypeName": "number", "fieldName": "bar", "position": 2, "renderTypeName": "number", "tableColumnId": 32762226, "format": {} } ], "metadata": { "rowIdentifier": 230641051 }, "owner": {}, "query": {}, "rights": [ "read", "write", "add", "delete", "grant", "add_column", "remove_column", "update_column", "update_view", "delete_view" ], "tableAuthor": {}, "flags": [ "default" ] }afeld-sodapy-464855c/tests/test_data/empty.txt000066400000000000000000000000001430365730200213500ustar00rootroot00000000000000afeld-sodapy-464855c/tests/test_data/get_datasets.txt000066400000000000000000001314311430365730200226760ustar00rootroot00000000000000{ "results" : [ { "resource" : { "name" : "Approved Building Permits", "id" : "msk6-43c6", "parent_fxf" : null, "description" : "Data of approved building/construction permits", "attribution" : "Inspectional Services Division", "type" : "dataset", "updatedAt" : "2017-07-21T10:01:47.000Z", "createdAt" : "2013-04-05T18:11:45.000Z", "page_views" : { "page_views_last_week" : 0, "page_views_last_month" : 0, "page_views_total" : 7778210, "page_views_last_week_log" : 0.0, "page_views_last_month_log" : 0.0, "page_views_total_log" : 22.891006940702717 }, "columns_name" : [ "ISSUED_DATE", "CITY", "PermitNumber", "Location", "STATE", "PermitTypeDescr", "Parcel_ID", "OCCUPANCYTYPE", "STATUS", "TOTAL_FEES", "Property_ID", "ADDRESS", "DECLARED_VALUATION", "ZIP", "WORKTYPE", "EXPIRATION_DATE", "Sq_feet", "APPLICANT", "OWNER", "DESCRIPTION", "Comments" ], "columns_field_name" : [ "issued_date", "city", "permitnumber", "location", "state", "permittypedescr", "parcel_id", "occupancytype", "status", "total_fees", "property_id", "address", "declared_valuation", "zip", "worktype", "expiration_date", "sq_feet", "applicant", "owner", "description", "comments" ], "columns_datatype" : [ "Calendar date", "Text", "Text", "Location", "Text", "Text", "Text", "Text", "Text", "Number", "Text", "Text", "Number", "Text", "Text", "Calendar date", "Number", "Text", "Text", "Text", "Text" ], "columns_description" : [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" ], "columns_format" : [ {}, {}, {}, {}, {}, {}, {}, {}, {}, { "precisionStyle" : "currency", "currencyStyle" : "USD", "precision" : "2" }, {}, {}, { "precisionStyle" : "currency", "currencyStyle" : "USD", "precision" : "2" }, {}, {}, {}, {}, {}, {}, {}, {} ], "download_count" : 19038, "provenance" : "official" }, "classification" : { "categories" : [ "housing & development", "infrastructure" ], "tags" : [], "domain_category" : "Permitting", "domain_tags" : [ "permits", "construction", "building" ], "domain_metadata" : [ { "key" : "Data-Owner_Owner", "value" : "Environmental & Energy Services" } ] }, "metadata" : { "domain" : "data.cityofboston.gov", "license" : "Creative Commons Attribution 3.0 Unported" }, "permalink" : "https://data.cityofboston.gov/d/msk6-43c6", "link" : "https://data.cityofboston.gov/Permitting/Approved-Building-Permits/msk6-43c6", "owner" : { "id" : "2pz3-cb5i", "display_name" : "Curt Savoie" } }, { "resource" : { "name" : "Lottery Cash 4 Life Winning Numbers: Beginning 2014", "id" : "kwxv-fwze", "parent_fxf" : null, "description" : "Go to http://on.ny.gov/1xRIvPz on the New York Lottery website for past Cash 4 Life results and payouts.", "attribution" : "New York State Gaming Commission", "type" : "dataset", "updatedAt" : "2018-08-31T10:01:19.000Z", "createdAt" : "2014-06-17T19:47:54.000Z", "page_views" : { "page_views_last_week" : 406, "page_views_last_month" : 2143, "page_views_total" : 5769673, "page_views_last_week_log" : 8.668884984266247, "page_views_last_month_log" : 11.066089190457774, "page_views_total_log" : 22.460058374872556 }, "columns_name" : [ "Cash Ball", "Winning Numbers", "Draw Date" ], "columns_field_name" : [ "cash_ball", "winning_numbers", "draw_date" ], "columns_datatype" : [ "Text", "Text", "Calendar date" ], "columns_description" : [ "Cash ball", "Winning numbers", "" ], "columns_format" : [ { "align" : "center" }, { "align" : "center" }, { "view" : "date", "align" : "center" } ], "download_count" : 58522, "provenance" : "official" }, "classification" : { "categories" : [], "tags" : [], "domain_category" : "Government & Finance", "domain_tags" : [ "winning", "cash 4 life", "new york lottery", "results" ], "domain_metadata" : [ { "key" : "Common-Core_Contact-Email", "value" : "opendata@its.ny.gov" }, { "key" : "Common-Core_Contact-Name", "value" : "Open Data NY" }, { "key" : "Common-Core_Publisher", "value" : "State of New York" }, { "key" : "Additional-Resources_See-Also", "value" : "http://www.gaming.ny.gov/" }, { "key" : "Dataset-Summary_Dataset-Owner", "value" : "New York State Gaming Commission" }, { "key" : "Dataset-Summary_Contact-Information", "value" : "Info@gaming.ny.gov" }, { "key" : "Dataset-Summary_Granularity", "value" : "By draw" }, { "key" : "Dataset-Summary_Coverage", "value" : "Statewide" }, { "key" : "Dataset-Summary_Data-Frequency", "value" : "Twice weekly" }, { "key" : "Dataset-Summary_Posting-Frequency", "value" : "Twice weekly" }, { "key" : "Dataset-Summary_Organization", "value" : "The New York Lottery" }, { "key" : "Dataset-Summary_Time-Period", "value" : "Beginning 6/16/2014" }, { "key" : "Dataset-Information_Agency", "value" : "Gaming Commission, New York State" }, { "key" : "Notes_Notes", "value" : "The information contained on these pages is believed to be accurate. In the event of a discrepancy between the information displayed on this Web site concerning winning numbers and payouts and the information contained in the official and certified files maintained by the New York Lottery's Drawing Unit, those maintained by the Drawing Unit shall prevail." } ] }, "metadata" : { "domain" : "data.ny.gov" }, "permalink" : "https://data.ny.gov/d/kwxv-fwze", "link" : "https://data.ny.gov/Government-Finance/Lottery-Cash-4-Life-Winning-Numbers-Beginning-2014/kwxv-fwze", "owner" : { "id" : "xzik-pf59", "display_name" : "NY Open Data" } }, { "resource" : { "name" : "Order and Referring", "id" : "qcn7-gc3g", "parent_fxf" : null, "description" : "Order and Referring data file has National Provider Identifier (NPI) and legal name (last name, first name) of all physicians and non-physician practitioners who are of a type/specialty that is legally eligible to order and refer in the Medicare program and who have current enrollment records in Medicare (i.e., they have enrollment records in PECOS)", "attribution" : "CMS", "type" : "dataset", "updatedAt" : "2018-08-30T21:24:19.000Z", "createdAt" : "2013-12-18T18:27:02.000Z", "page_views" : { "page_views_last_week" : 27497, "page_views_last_month" : 123695, "page_views_total" : 3736001, "page_views_last_week_log" : 14.74703907109547, "page_views_last_month_log" : 16.916439322580775, "page_views_total_log" : 21.83306379669223 }, "columns_name" : [ "FIRST NAME", "LAST NAME", "NPI" ], "columns_field_name" : [ "first_name", "last_name", "npi" ], "columns_datatype" : [ "Text", "Text", "Number" ], "columns_description" : [ "", "", "" ], "columns_format" : [ {}, {}, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "right" } ], "download_count" : 1670710, "provenance" : "official" }, "classification" : { "categories" : [ "health" ], "tags" : [], "domain_category" : "Medicare - Enrollment", "domain_tags" : [ "order and referring" ], "domain_metadata" : [ { "key" : "Common-Core_Contact-Email", "value" : "cardinal.williams@cms.hhs.gov" }, { "key" : "Common-Core_Contact-Name", "value" : "CMS" }, { "key" : "Common-Core_Program-Code", "value" : "009:000" }, { "key" : "Common-Core_Bureau-Code", "value" : "009:38" } ] }, "metadata" : { "domain" : "data.cms.gov", "license" : "Public Domain" }, "permalink" : "https://data.cms.gov/d/qcn7-gc3g", "link" : "https://data.cms.gov/Medicare-Enrollment/Order-and-Referring/qcn7-gc3g", "owner" : { "id" : "xg9z-qwbg", "display_name" : "Rupinder Singh" } }, { "resource" : { "name" : "Howard County Police Department Call For Service - 2014-2017", "id" : "qccx-65fg", "parent_fxf" : null, "description" : "Calls for Service by computer aided dispatch (CAD) event type, date, time, location, statistical reporting area (SRA), and beat.", "attribution" : null, "type" : "dataset", "updatedAt" : "2018-02-15T15:31:08.000Z", "createdAt" : "2015-05-04T15:33:31.000Z", "page_views" : { "page_views_last_week" : 20, "page_views_last_month" : 54, "page_views_total" : 3672157, "page_views_last_week_log" : 4.392317422778761, "page_views_last_month_log" : 5.78135971352466, "page_views_total_log" : 21.80819670348781 }, "columns_name" : [ "Beat", "Statistical_Reporting_Area", "Location", "Time_Reported", "Date_Reported", "Computer_Aided_Dispatch_Event_Type" ], "columns_field_name" : [ "beat", "statistical_reporting_area", "location", "time_reported", "date_reported", "computer_aided_dispatch_event_type" ], "columns_datatype" : [ "Text", "Number", "Text", "Text", "Calendar date", "Text" ], "columns_description" : [ "", "", "", "", "", "" ], "columns_format" : [ {}, {}, {}, {}, { "view" : "date", "align" : "left" }, {} ], "download_count" : 302, "provenance" : "official" }, "classification" : { "categories" : [], "tags" : [], "domain_category" : "Public Safety", "domain_tags" : [ "calls", "sra", "beat", "dispatch", "cad" ], "domain_metadata" : [ { "key" : "Additional-Metadata_Frequency", "value" : "Annually" }, { "key" : "Additional-Metadata_Contributor", "value" : "Police Department" }, { "key" : "Additional-Metadata_Coverage", "value" : "2014-2017" }, { "key" : "Additional-Metadata_Publisher", "value" : "Howard County, MD" } ] }, "metadata" : { "domain" : "opendata.howardcountymd.gov" }, "permalink" : "https://opendata.howardcountymd.gov/d/qccx-65fg", "link" : "https://opendata.howardcountymd.gov/Public-Safety/Howard-County-Police-Department-Call-For-Service-2/qccx-65fg", "owner" : { "id" : "n9w7-tmda", "display_name" : "Jeanne Upchurch" } }, { "resource" : { "name" : "Building, Electrical, Fire, Grading, Mechanical, Plumbing & Sign Permits: 2010 - Present", "id" : "kvz2-j5cj", "parent_fxf" : null, "description" : "Issued Permits: 01/01/2010 - Present", "attribution" : "Department of Inspections, Licenses & Permits", "type" : "dataset", "updatedAt" : "2017-12-04T15:23:39.000Z", "createdAt" : "2015-04-30T17:13:40.000Z", "page_views" : { "page_views_last_week" : 9, "page_views_last_month" : 21, "page_views_total" : 2389094, "page_views_last_week_log" : 3.3219280948873626, "page_views_last_month_log" : 4.459431618637297, "page_views_total_log" : 21.188032791600588 }, "columns_name" : [ "FILE_DATE", "TYPE", "CITY", "ISSUE_DATE", "CENSUS_TRACT", "PERMIT_TYPE", "WATER", "SEWER", "ZIP", "CATEGORY", "PERMIT_NUMBER" ], "columns_field_name" : [ "file_date", "type", "city", "issue_date", "census_tract", "permit_type", "water", "sewer", "zip", "category", "permit_number" ], "columns_datatype" : [ "Calendar date", "Text", "Text", "Calendar date", "Text", "Text", "Text", "Text", "Text", "Text", "Text" ], "columns_description" : [ "", "", "", "", "", "", "", "", "", "", "" ], "columns_format" : [ { "view" : "date", "align" : "left" }, {}, {}, { "view" : "date", "align" : "left" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "right" }, {}, {}, {}, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "right" }, {}, {} ], "download_count" : 352, "provenance" : "official" }, "classification" : { "categories" : [ "infrastructure" ], "tags" : [], "domain_category" : "Licenses and Permits", "domain_tags" : [ "building permit", "issued permits", "permits", "electrical permit", "fire permit", "grading permit", "mechanical permit", "hvac permit", "plumbing permit", "sign permit" ], "domain_metadata" : [ { "key" : "Additional-Metadata_Frequency", "value" : "Monthly" }, { "key" : "Additional-Metadata_Contributor", "value" : "Department of Inspections, Licenses & Permits" }, { "key" : "Additional-Metadata_Coverage", "value" : "2010 - Present" }, { "key" : "Additional-Metadata_Publisher", "value" : "Howard County, Maryland" } ] }, "metadata" : { "domain" : "opendata.howardcountymd.gov", "license" : "Public Domain" }, "permalink" : "https://opendata.howardcountymd.gov/d/kvz2-j5cj", "link" : "https://opendata.howardcountymd.gov/Licenses-and-Permits/Building-Electrical-Fire-Grading-Mechanical-Plumbi/kvz2-j5cj", "owner" : { "id" : "y48e-rpnp", "display_name" : "Walker, C." } }, { "resource" : { "name" : "DOB Job Application Filings", "id" : "ic3t-wcy2", "parent_fxf" : null, "description" : "A list of job applications filed for a particular day and associated data. Prior weekly and monthly reports are archived at DOB and are not available on NYC Open Data.\r\n\r\nNote: These job applications are submitted through the Borough Offices, through eFiling, or through the HUB. This dataset does not include jobs submitted through DOB NOW.", "attribution" : "Department of Buildings (DOB)", "type" : "dataset", "updatedAt" : "2018-09-01T20:13:18.000Z", "createdAt" : "2013-04-18T15:18:56.000Z", "page_views" : { "page_views_last_week" : 956, "page_views_last_month" : 4110, "page_views_total" : 2179028, "page_views_last_week_log" : 9.902375114486025, "page_views_last_month_log" : 12.005273656563938, "page_views_total_log" : 21.055253966272616 }, "columns_name" : [ "GIS_COUNCIL_DISTRICT", "Bin #", "Community - Board", "Applicant's First Name", "Paid", "Street Frontage", "Vertical Enlrgmt", "ExistingNo. of Stories", "Proposed Dwelling Units", "SPECIAL_ACTION_DATE", "Zoning Dist1", "GIS_NTA_NAME", "Job #", "House #", "Applicant's Last Name", "Enlargement SQ Footage", "Existing Dwelling Units", "Owner'sHouse Street Name", "DOBRunDate", "Fuel Storage", "Pre- Filing Date", "Owner'sPhone #", "Fee Status", "Borough", "Sprinkler", "Fire Alarm", "Existing Occupancy", "Job Description", "GIS_LATITUDE", "Zip", "Proposed No. of Stories", "Existing Zoning Sqft", "Fully Permitted", "Landmarked", "Owner Type", "GIS_LONGITUDE", "Owner's House Number", "Owner's Business Name", "TOTAL_CONSTRUCTION_FLOOR_AREA", "Street Name", "GIS_BIN", "Loft Board", "Fire Suppression", "Mechanical", "Proposed Height", "Owner's First Name", "Zoning Dist2", "Special District 1", "Job Type", "SPECIAL_ACTION_STATUS", "JOB_NO_GOOD_COUNT", "Job Status", "Standpipe", "eFiling Filed", "Building Type", "Applicant License #", "Initial Cost", "Proposed Occupancy", "Horizontal Enlrgmt", "Lot", "Non-Profit", "BUILDING_CLASS", "Latest Action Date", "Total Est. Fee", "Site Fill", "Professional Cert", "Job Status Descrp", "Zoning Dist3", "Applicant Professional Title", "Block", "Adult Estab", "PC Filed", "Existing Height", "WITHDRAWAL_FLAG", "Plumbing", "Other", "Equipment", "Owner's Last Name", "Proposed Zoning Sqft", "Fuel Burning", "GIS_CENSUS_TRACT", "Cluster", "Doc #", "Little e", "Assigned", "SIGNOFF_DATE", "Other Description", "Fully Paid", "JOB_S1_NO", "City ", "Approved", "Special District 2", "State", "City Owned", "Curb Cut", "Boiler" ], "columns_field_name" : [ "gis_council_district", "bin__", "community___board", "applicant_s_first_name", "paid", "street_frontage", "vertical_enlrgmt", "existingno_of_stories", "proposed_dwelling_units", "special_action_date", "zoning_dist1", "gis_nta_name", "job__", "house__", "applicant_s_last_name", "enlargement_sq_footage", "existing_dwelling_units", "owner_shouse_street_name", "dobrundate", "fuel_storage", "pre__filing_date", "owner_sphone__", "fee_status", "borough", "sprinkler", "fire_alarm", "existing_occupancy", "job_description", "gis_latitude", "zip", "proposed_no_of_stories", "existing_zoning_sqft", "fully_permitted", "landmarked", "owner_type", "gis_longitude", "owner_s_house_number", "owner_s_business_name", "total_construction_floor_area", "street_name", "gis_bin", "loft_board", "fire_suppression", "mechanical", "proposed_height", "owner_s_first_name", "zoning_dist2", "special_district_1", "job_type", "special_action_status", "job_no_good_count", "job_status", "standpipe", "efiling_filed", "building_type", "applicant_license__", "initial_cost", "proposed_occupancy", "horizontal_enlrgmt", "lot", "non_profit", "building_class", "latest_action_date", "total_est__fee", "site_fill", "professional_cert", "job_status_descrp", "zoning_dist3", "applicant_professional_title", "block", "adult_estab", "pc_filed", "existing_height", "withdrawal_flag", "plumbing", "other", "equipment", "owner_s_last_name", "proposed_zoning_sqft", "fuel_burning", "gis_census_tract", "cluster", "doc__", "little_e", "assigned", "signoff_date", "other_description", "fully_paid", "job_s1_no", "city_", "approved", "special_district_2", "state", "city_owned", "curb_cut", "boiler" ], "columns_datatype" : [ "Text", "Text", "Text", "Text", "Text", "Number", "Text", "Number", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Number", "Text", "Text", "Calendar date", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Number", "Number", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Number", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Calendar date", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Number", "Text", "Text", "Text", "Text", "Text", "Number", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text", "Text" ], "columns_description" : [ "Council District", "Number assigned by City Planning to a specific building", "3-digit identifier: Borough code = first position, last 2 = community board", "First Name of Applicant", "Date when job is paid", "Street Frontage", "Vertical Enlrgmt", "ExistingNo. of Stories", "Proposed Dwelling Units", "Special Action Date", "Zoning Distr 1", "NTA Name", "Number assigned by DOB to Job Filing", "House Number of Residence or Commercial Property", "Last Name of Applicant", "Enlargement SQ Footage", "Existing Dwelling Units", "House Street Name of Property Owner", "Date when query is run and pushed to Open Data. Could be used to differentiate report dates.", "Fuel Storage Work Type? (X=Yes, Blank=No)", "Date when job is prefiled", "Owner's Phone #", "Type of Fee", "1= Manhattan, 2= Bronx, 3 = Brooklyn, 4 = Queens, 5 = Staten Island", "Sprinkler Work Type? (X=Yes, Blank=No)", "Fire Alarm Work Type? (X=Yes, Blank=No)", "Existing Occupancy", "Job Description", "Latitude", "Zip", "Proposed No. of Stories", "Existing Zoning Sqft", "Date when job is fully permitted", "L code indicates that the building has been assigned landmark status", "Owner Type", "Longitude", "House Number of Property Owner", "Business Name of Property Owner", "Total Construction Floor Area", "Street Name where Property is located", "BIN", "Loft Board", "Fire Suppression Work Type? (X=Yes, Blank=No)", "Mechanical Work Type? (X=Yes, Blank=No)", "Proposed Height", "First Name of property owner", "Zoning Distr 2", "Special Distr 1", "Job Type, based on DOB Job Code (NB-New Building, A1, A2, A3- Alterations 1-3, SG-Sign, etc.)", "Special Action Status", "Job No Good Count", "DOB Status code of job (A-Pre Filed, I-Sign Off, P- Approved, R-Permit Entire) Complete List - http://www.nyc.gov/html/dob/downloads/pdf/bisjobstatus.pdf", "Standpipe Work Type? (X=Yes, Blank=No)", "Application Filed electronically, rather than manually", "1-2-3 Family or Other", "Number assigned to the skilled trade person/contractor or licensed professional", " Estimated cost of job", "Proposed Occupancy", "Horizontal Enlrgmt", "Tax lot assigned by Department of Finance", "Non-Profit", "Building Class", "Latest status date", "Estimated fee of job", "Site Fill", "Job is Professionally Certified by Licensed Professional instead of having it reviewed by Department of Building's Plan Examiners", "Status code description", "Zoning Distr 3", "Applicant's Professional Title", "Tax block assigned by Department of Finance", "Adult Estab", "Application Filed electronically, rather than manually", "Existing Height", "Withdrawal Indicator", "Plumbing Work Type? (X=Yes, Blank=No)", "Other? (X=Yes, Blank=No)", "Equipment Work Type? (X=Yes, Blank=No)", "Last Name of property owner", "Proposed Zoning Sqft", "Fuel Burning Work Type? (X=Yes, Blank=No)", "Census Tract", "Cluster", "Document Number", "Hazardous", "Date when job is assigned to plan examiner", "Sign-off Date", "Other Description", "Date when job is paid and entered", "JOB_S1_NO", "City ", "Date when job is approved", "Special District 2", "State", "City Owned", "Curb Cut Work Type? (X=Yes, Blank=No)", "Boiler Work Type? (X=Yes, Blank=No)" ], "columns_format" : [ { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "left" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" }, { "align" : "right" } ], "download_count" : 18332, "provenance" : "official" }, "classification" : { "categories" : [ "finance", "economy", "environment" ], "tags" : [], "domain_category" : "Housing & Development", "domain_tags" : [ "job", "buildings", "dob" ], "domain_metadata" : [ { "key" : "Update_Automation", "value" : "Yes" }, { "key" : "Update_Date-Made-Public", "value" : "2014-01-16 21:12:51" }, { "key" : "Update_Update-Frequency", "value" : "Daily" }, { "key" : "Dataset-Information_Agency", "value" : "Department of Buildings (DOB)" } ] }, "metadata" : { "domain" : "data.cityofnewyork.us" }, "permalink" : "https://data.cityofnewyork.us/d/ic3t-wcy2", "link" : "https://data.cityofnewyork.us/Housing-Development/DOB-Job-Application-Filings/ic3t-wcy2", "owner" : { "id" : "5fuc-pqz2", "display_name" : "NYC OpenData" } }, { "resource" : { "name" : "Fire And Rescue Apparatus Responses: 2012 - 2017", "id" : "xvpn-2pnt", "parent_fxf" : null, "description" : "One record for each emergency response by a Fire Department unit, including dispatch and response times, action taken, and counts of personnel which responded. This is an expansion of data available in the Howard County Fire And Rescue Incidents data set. Further detail is available in the Howard County Fire and Rescue Personnel Responses data set.", "attribution" : "Department of Fire and Rescue Services", "type" : "dataset", "updatedAt" : "2017-12-05T21:59:49.000Z", "createdAt" : "2015-04-24T14:34:49.000Z", "page_views" : { "page_views_last_week" : 0, "page_views_last_month" : 2, "page_views_total" : 1872117, "page_views_last_week_log" : 0.0, "page_views_last_month_log" : 1.5849625007211563, "page_views_total_log" : 20.83623994048996 }, "columns_name" : [ "Responded_From_Quarters", "First_Arriving_Unit", "Action_Taken", "Action_Taken_Code", "Number_Of_People", "Canceled_Enroute", "Arrival_Time", "Enroute_Scene_Time", "Dispatch_Time", "Apparartus_Type", "Apparatus_Type_Code", "Apparatus_ID", "Incident_ID" ], "columns_field_name" : [ "responded_from_quarters", "first_arriving_unit", "action_taken", "action_taken_code", "number_of_people", "canceled_enroute", "arrival_time", "enroute_scene_time", "dispatch_time", "apparartus_type", "apparatus_type_code", "apparatus_id", "incident_id" ], "columns_datatype" : [ "Text", "Text", "Text", "Number", "Number", "Text", "Text", "Calendar date", "Calendar date", "Text", "Number", "Text", "Number" ], "columns_description" : [ "Was this unit located in its assigned station when alerted? (Often, a unit is dispatched while still returning from a previous call.)", "Was this unit reported as first unit to arrive on scene?", "", "", "", "Was the unit's response canceled by 911 or Incident Command prior to arrival?", "The time the unit arrived at the location of the incident", "The time the unit began travel to the scene", "The time that the unit was alerted by the 911 Center", "", "", "", "" ], "columns_format" : [ { "align" : "left" }, { "align" : "left" }, {}, {}, {}, { "align" : "left" }, { "align" : "left" }, { "view" : "date_time", "align" : "left" }, { "view" : "date_time", "align" : "left" }, {}, {}, {}, { "precisionStyle" : "standard", "noCommas" : "true", "align" : "right" } ], "download_count" : 177, "provenance" : "official" }, "classification" : { "categories" : [ "public safety" ], "tags" : [], "domain_category" : "Public Safety", "domain_tags" : [ "apparatus", "fire", "rescue", "ems" ], "domain_metadata" : [ { "key" : "Additional-Metadata_Frequency", "value" : "Continuous" }, { "key" : "Additional-Metadata_Contributor", "value" : "Department of Fire and Rescue Services" }, { "key" : "Additional-Metadata_Coverage", "value" : "2012 - 2017" }, { "key" : "Additional-Metadata_Publisher", "value" : "Howard County, Maryland" } ] }, "metadata" : { "domain" : "opendata.howardcountymd.gov" }, "permalink" : "https://opendata.howardcountymd.gov/d/xvpn-2pnt", "link" : "https://opendata.howardcountymd.gov/Public-Safety/Fire-And-Rescue-Apparatus-Responses-2012-2017/xvpn-2pnt", "owner" : { "id" : "u68m-5jft", "display_name" : "Wilson, S." } } ], "resultSetSize" : 150680, "timings" : { "serviceMillis" : 39, "searchMillis" : [ 7, 31 ] } }afeld-sodapy-464855c/tests/test_data/get_song_metadata.txt000066400000000000000000000013071430365730200236720ustar00rootroot00000000000000{"newBackend": false, "licenseId": "CC0_10", "publicationDate": 1436655117, "viewLastModified": 1451289003, "owner": {"roleName": "administrator", "rights": [], "displayName": "Brett", "id": "cdqe-xcn5", "screenName": "Brett"}, "query": {}, "id": "songs", "createdAt": 1398014181, "category": "Public Safety", "publicationAppendEnabled": true, "publicationStage": "published", "rowsUpdatedBy": "cdqe-xcn5", "publicationGroup": 1552205, "displayType": "table", "state": "normal", "attributionLink": "http://foo.bar.com", "tableId": 3523378, "columns": [], "metadata": {"rdfSubject": "0", "renderTypeConfig": {"visible": {"table": true}}, "availableDisplayTypes": ["table", "fatrow", "page"], "attachments": []}} afeld-sodapy-464855c/tests/test_data/get_songs.txt000066400000000000000000000024031430365730200222130ustar00rootroot00000000000000[{"theme": "Love", "year": "1982", "spotify_url": {"url": "http://open.spotify.com/track/78j3qTBdzcIiT3eS7XymoD"}, "artist": "ABC", "title": "The Look of Love"}, {"theme": "Love", "year": "2000", "spotify_url": {"url": "http://open.spotify.com/track/2PojSoZ94AIzp7fsz6wtMt"}, "artist": "Badly Drawn Boy", "title": "The Shining"}, {"theme": "Love", "year": "1966", "spotify_url": {"url": "http://open.spotify.com/track/0ObrXLrfrqJUNc8RfmIBHP"}, "artist": "The Beach Boys", "title": "God Only Knows"}, {"theme": "Love", "year": "1966", "spotify_url": {"url": "http://open.spotify.com/track/2oF7FZHIJbzjeEXZ3D0Ku4"}, "artist": "The Beach Boys", "title": "Good Vibrations"}, {"theme": "Love", "year": "1966", "spotify_url": {"url": "http://open.spotify.com/track/0cx32rX0uZvcJUP92Wkj2y"}, "artist": "The Beach Boys", "title": "Wouldn\u2019t It Be Nice"}, {"theme": "Love", "year": "1964", "artist": "The Beatles", "title": "Eight Days a Week"}, {"theme": "Love", "year": "1965", "artist": "The Beatles", "title": "Girl"}, {"theme": "Love", "year": "1963", "artist": "The Beatles", "title": "I Want to Hold Your Hand"}, {"theme": "Love", "year": "1963", "artist": "The Beatles", "title": "She Loves You"}, {"theme": "Love", "year": "1969", "artist": "The Beatles", "title": "Something"}] afeld-sodapy-464855c/tests/test_data/get_songs_unicode.txt000066400000000000000000000024131430365730200237220ustar00rootroot00000000000000[{"theme": "Schön", "year": "1982", "spotify_url": {"url": "http://open.spotify.com/track/78j3qTBdzcIiT3eS7XymoD"}, "artist": "ABC", "title": "The Look of Love"}, {"theme": "Love", "year": "2000", "spotify_url": {"url": "http://open.spotify.com/track/2PojSoZ94AIzp7fsz6wtMt"}, "artist": "Badly Drawn Boy", "title": "The Shining"}, {"theme": "Love", "year": "1966", "spotify_url": {"url": "http://open.spotify.com/track/0ObrXLrfrqJUNc8RfmIBHP"}, "artist": "The Beach Boys", "title": "God Only Knows"}, {"theme": "Love", "year": "1966", "spotify_url": {"url": "http://open.spotify.com/track/2oF7FZHIJbzjeEXZ3D0Ku4"}, "artist": "The Beach Boys", "title": "Good Vibrations"}, {"theme": "€", "year": "1966", "spotify_url": {"url": "http://open.spotify.com/track/0cx32rX0uZvcJUP92Wkj2y"}, "artist": "The Beach Boys", "title": "Wouldn\u2019t It Be Nice"}, {"theme": "Love", "year": "1964", "artist": "The Beatles", "title": "Eight Days a Week"}, {"theme": "Love", "year": "1965", "artist": "The Beatles", "title": "Girl"}, {"theme": "Love", "year": "1963", "artist": "The Beatles", "title": "I Want to Hold Your Hand"}, {"theme": "Love", "year": "1963", "artist": "The Beatles", "title": "She Loves You"}, {"theme": "Love", "year": "1969", "artist": "☂ The Beatles❄", "title": "Something"}] afeld-sodapy-464855c/tests/test_data/nondatasetfile.zip000066400000000000000000000007601430365730200232120ustar00rootroot00000000000000PKv]Hnondatasetfile/PK v]H2 nondatasetfile/file1.csvsomething1,2PK ie]Hnondatasetfile/file2.csvPK?v]H$nondatasetfile/ f.2sf.2sV2sPK? v]H2 $ -nondatasetfile/file1.csv F2sаv !sаv !sPK? ie]H$ onondatasetfile/file2.csv аv !sx A!sx A!sPK5afeld-sodapy-464855c/tests/test_data/replace_songs.txt000066400000000000000000000001521430365730200230460ustar00rootroot00000000000000{"Errors": 0, "Rows Deleted": 0, "Rows Updated": 0, "By SID": 0, "Rows Created": 2, "By RowIdentifier": 0}afeld-sodapy-464855c/tests/test_data/successblobres.txt000066400000000000000000000042441430365730200232510ustar00rootroot00000000000000{ "blobFileSize": 496, "publicationStage": "published", "newBackend": false, "publicationDate": 1456778834, "viewLastModified": 1456781085, "owner": { "emailUnsubscribed": false, "profileLastModified": 1383601187, "displayName": "TestUser", "rights": ["create_datasets", "edit_others_datasets", "edit_nominations", "approve_nominations", "moderate_comments", "manage_stories", "feature_items", "change_configurations", "view_domain", "view_others_datasets", "create_pages", "edit_pages", "view_goals", "view_dashboards", "edit_goals", "edit_dashboards", "manage_provenance", "view_story", "view_unpublished_story", "view_all_dataset_status_logs"], "roleName": "publisher", "id": "64k6-dman", "screenName": "TestUser" }, "id": "9hh8-5vbg", "createdAt": 1456778834, "publicationAppendEnabled": false, "blobId": "db343818-49c0-4354-8ff8-9ccf135ef6ca", "publicationGroup": 8526274, "tableId": 8526274, "oid": 18351712, "flags": ["default"], "blobMimeType": "application/zip; charset=binary", "viewCount": 0, "numberOfComments": 0, "name": "Nondatasetfile", "rights": ["read", "write", "add", "delete", "grant", "add_column", "remove_column", "update_column", "update_view", "delete_view"], "totalTimesRated": 0, "indexUpdatedAt": 1456779216, "blobFilename": "gtfs2", "downloadCount": 0, "rowsUpdatedAt": 1456778834, "viewType": "blobby", "tableAuthor": { "emailUnsubscribed": false, "profileLastModified": 1383601187, "displayName": "TestUser", "rights": ["create_datasets", "edit_others_datasets", "edit_nominations", "approve_nominations", "moderate_comments", "manage_stories", "feature_items", "change_configurations", "view_domain", "view_others_datasets", "create_pages", "edit_pages", "view_goals", "view_dashboards", "edit_goals", "edit_dashboards", "manage_provenance", "view_story", "view_unpublished_story", "view_all_dataset_status_logs"], "roleName": "publisher", "id": "64k6-dman", "screenName": "TestUser" }, "averageRating": 0 }afeld-sodapy-464855c/tests/test_data/update_song_metadata.txt000066400000000000000000000013101430365730200243670ustar00rootroot00000000000000{"newBackend": false, "licenseId": "CC0_10", "publicationDate": 1436655117, "viewLastModified": 1451289003, "owner": {"roleName": "administrator", "rights": [], "displayName": "Brett", "id": "cdqe-xcn5", "screenName": "Brett"}, "query": {}, "id": "songs", "createdAt": 1398014181, "category": "Education", "publicationAppendEnabled": true, "publicationStage": "published", "rowsUpdatedBy": "cdqe-xcn5", "publicationGroup": 1552205, "displayType": "table", "state": "normal", "attributionLink": "https://testing.updates", "tableId": 3523378, "columns": [], "metadata": {"rdfSubject": "0", "renderTypeConfig": {"visible": {"table": true}}, "availableDisplayTypes": ["table", "fatrow", "page"], "attachments": []}} afeld-sodapy-464855c/tests/test_data/upsert_songs.txt000066400000000000000000000001531430365730200227560ustar00rootroot00000000000000{"Errors": 0, "Rows Deleted": 0, "Rows Updated": 0, "By SID": 0, "Rows Created": 1, "By RowIdentifier": 0} afeld-sodapy-464855c/tests/test_socrata.py000066400000000000000000000376371430365730200206000ustar00rootroot00000000000000import inspect import json import logging import os.path import requests import requests_mock import pytest from sodapy import Socrata from sodapy.constants import DEFAULT_API_PATH, OLD_API_PATH, DATASETS_PATH PREFIX = "https://" DOMAIN = "fakedomain.com" DATASET_IDENTIFIER = "songs" APPTOKEN = "FakeAppToken" USERNAME = "fakeuser" PASSWORD = "fakepassword" TEST_DATA_PATH = os.path.join( os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))), "test_data", ) LOGGER = logging.getLogger(__name__) def test_client(): client = Socrata(DOMAIN, APPTOKEN) assert isinstance(client, Socrata) client.close() def test_client_warning(caplog): with caplog.at_level(logging.WARNING): client = Socrata(DOMAIN, None) assert "strict throttling limits" in caplog.text client.close() def test_context_manager(): with Socrata(DOMAIN, APPTOKEN) as client: assert isinstance(client, Socrata) def test_context_manager_no_domain_exception(): with pytest.raises(Exception): with Socrata(None, APPTOKEN): pass def test_context_manager_timeout_exception(): with pytest.raises(TypeError): with Socrata(DOMAIN, APPTOKEN, timeout="fail"): pass def test_client_oauth(): client = Socrata(DOMAIN, APPTOKEN, access_token="AAAAAAAAAAAA") assert client.session.headers.get("Authorization") == "OAuth AAAAAAAAAAAA" def test_get(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) response_data = "get_songs.txt" setup_mock(adapter, "GET", response_data, 200) response = client.get(DATASET_IDENTIFIER) assert isinstance(response, list) assert len(response) == 10 client.close() def test_get_all(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) setup_mock(adapter, "GET", "bike_counts_page_1.json", 200, query="$offset=0") setup_mock(adapter, "GET", "bike_counts_page_2.json", 200, query="$offset=1000") response = client.get_all(DATASET_IDENTIFIER) assert inspect.isgenerator(response) data = list(response) assert len(data) == 1001 assert data[0]["date"] == "2016-09-21T15:45:00.000" assert data[-1]["date"] == "2016-10-02T01:45:00.000" client.close() def test_get_unicode(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) response_data = "get_songs_unicode.txt" setup_mock(adapter, "GET", response_data, 200) response = client.get(DATASET_IDENTIFIER) assert isinstance(response, list) assert len(response) == 10 client.close() def test_get_datasets(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) setup_datasets_mock(adapter, "get_datasets.txt", 200, params={"limit": "7"}) response = client.datasets(limit=7) assert isinstance(response, list) assert len(response) == 7 def test_get_metadata_and_attachments(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) response_data = "get_song_metadata.txt" setup_old_api_mock(adapter, "GET", response_data, 200) response = client.get_metadata(DATASET_IDENTIFIER) assert isinstance(response, dict) assert "newBackend" in response assert "attachments" in response["metadata"] response = client.download_attachments(DATASET_IDENTIFIER) assert isinstance(response, list) assert len(response) == 0 client.close() def test_update_metadata(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) response_data = "update_song_metadata.txt" setup_old_api_mock(adapter, "PUT", response_data, 200) data = {"category": "Education", "attributionLink": "https://testing.updates"} response = client.update_metadata(DATASET_IDENTIFIER, data) assert isinstance(response, dict) assert response.get("category") == data["category"] assert response.get("attributionLink") == data["attributionLink"] client.close() def test_upsert_exception(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata(DOMAIN, APPTOKEN, session_adapter=mock_adapter) response_data = "403_response_json.txt" setup_mock(adapter, "POST", response_data, 403, reason="Forbidden") data = [ { "theme": "Surfing", "artist": "Wavves", "title": "King of the Beach", "year": "2010", } ] try: client.upsert(DATASET_IDENTIFIER, data) except Exception as e: assert isinstance(e, requests.exceptions.HTTPError) else: raise AssertionError("No exception raised for bad request.") def test_upsert(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "upsert_songs.txt" data = [ { "theme": "Surfing", "artist": "Wavves", "title": "King of the Beach", "year": "2010", } ] setup_mock(adapter, "POST", response_data, 200) response = client.upsert(DATASET_IDENTIFIER, data) assert isinstance(response, dict) assert response.get("Rows Created") == 1 client.close() def test_replace(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "replace_songs.txt" data = [ { "theme": "Surfing", "artist": "Wavves", "title": "King of the Beach", "year": "2010", }, { "theme": "History", "artist": "Best Friends Forever", "title": "Abe Lincoln", "year": "2008", }, ] setup_mock(adapter, "PUT", response_data, 200) response = client.replace(DATASET_IDENTIFIER, data) assert isinstance(response, dict) assert response.get("Rows Created") == 2 client.close() def test_delete(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) uri = "{}{}{}/{}.json".format(PREFIX, DOMAIN, OLD_API_PATH, DATASET_IDENTIFIER) adapter.register_uri("DELETE", uri, status_code=200) response = client.delete(DATASET_IDENTIFIER) assert response.status_code == 200 try: client.delete("foobar") except Exception as e: assert isinstance(e, requests_mock.exceptions.NoMockAddress) finally: client.close() def test_create(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "create_foobar.txt" setup_mock(adapter, "POST", response_data, 200, dataset_identifier=None) columns = [ {"fieldName": "foo", "name": "Foo", "dataTypeName": "text"}, {"fieldName": "bar", "name": "Bar", "dataTypeName": "number"}, ] tags = ["foo", "bar"] response = client.create( "Foo Bar", description="test dataset", columns=columns, tags=tags, row_identifier="bar", ) request = adapter.request_history[0] request_payload = json.loads(request.text) # can't figure out how to use .json # Test request payload for dataset_key in ["name", "description", "columns", "tags"]: assert dataset_key in request_payload for column_key in ["fieldName", "name", "dataTypeName"]: assert column_key in request_payload["columns"][0] # Test response assert isinstance(response, dict) assert len(response.get("id")) == 9 client.close() def test_set_permission(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "empty.txt" setup_old_api_mock(adapter, "PUT", response_data, 200) # Test response response = client.set_permission(DATASET_IDENTIFIER, "public") assert response.status_code == 200 # Test request request = adapter.request_history[0] query_string = request.url.split("?")[-1] params = query_string.split("&") assert len(params) == 2 assert "method=setPermission" in params assert "value=public.read" in params client.close() def test_publish(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "create_foobar.txt" setup_publish_mock(adapter, "POST", response_data, 200) response = client.publish(DATASET_IDENTIFIER) assert isinstance(response, dict) assert len(response.get("id")) == 9 client.close() def test_import_non_data_file(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "successblobres.txt" nondatasetfile_path = "tests/test_data/nondatasetfile.zip" setup_import_non_data_file(adapter, "POST", response_data, 200) with open(nondatasetfile_path, "rb") as f: files = {"file": ("nondatasetfile.zip", f)} response = client.create_non_data_file({}, files) assert isinstance(response, dict) assert response.get("blobFileSize") == 496 client.close() def test_replace_non_data_file(): mock_adapter = {} mock_adapter["prefix"] = PREFIX adapter = requests_mock.Adapter() mock_adapter["adapter"] = adapter client = Socrata( DOMAIN, APPTOKEN, username=USERNAME, password=PASSWORD, session_adapter=mock_adapter, ) response_data = "successblobres.txt" nondatasetfile_path = "tests/test_data/nondatasetfile.zip" setup_replace_non_data_file(adapter, "POST", response_data, 200) with open(nondatasetfile_path, "rb") as fin: file = {"file": ("nondatasetfile.zip", fin)} response = client.replace_non_data_file(DATASET_IDENTIFIER, {}, file) assert isinstance(response, dict) assert response.get("blobFileSize") == 496 client.close() def setup_publish_mock( adapter, method, response, response_code, reason="OK", dataset_identifier=DATASET_IDENTIFIER, content_type="json", ): path = os.path.join(TEST_DATA_PATH, response) with open(path, "r") as response_body: body = json.load(response_body) uri = "{}{}{}/{}/publication.{}".format( PREFIX, DOMAIN, OLD_API_PATH, dataset_identifier, content_type ) headers = {"content-type": "application/json; charset=utf-8"} adapter.register_uri( method, uri, status_code=response_code, json=body, reason=reason, headers=headers, ) def setup_import_non_data_file( adapter, method, response, response_code, reason="OK", dataset_identifier=DATASET_IDENTIFIER, content_type="json", ): path = os.path.join(TEST_DATA_PATH, response) with open(path, "r") as response_body: body = json.load(response_body) uri = "{}{}/api/imports2/?method=blob".format(PREFIX, DOMAIN) headers = {"content-type": "application/json; charset=utf-8"} adapter.register_uri( method, uri, status_code=response_code, json=body, reason=reason, headers=headers, ) def setup_replace_non_data_file( adapter, method, response, response_code, reason="OK", dataset_identifier=DATASET_IDENTIFIER, content_type="json", ): path = os.path.join(TEST_DATA_PATH, response) with open(path, "r") as response_body: body = json.load(response_body) uri = "{}{}{}/{}.{}?method=replaceBlob&id={}".format( PREFIX, DOMAIN, OLD_API_PATH, dataset_identifier, "txt", dataset_identifier, ) headers = {"content-type": "text/plain; charset=utf-8"} adapter.register_uri( method, uri, status_code=response_code, json=body, reason=reason, headers=headers, ) def setup_old_api_mock( adapter, method, response, response_code, reason="OK", dataset_identifier=DATASET_IDENTIFIER, content_type="json", ): path = os.path.join(TEST_DATA_PATH, response) with open(path, "r") as response_body: try: body = json.load(response_body) except ValueError: body = None uri = "{}{}{}/{}.{}".format( PREFIX, DOMAIN, OLD_API_PATH, dataset_identifier, content_type ) headers = {"content-type": "application/json; charset=utf-8"} adapter.register_uri( method, uri, status_code=response_code, json=body, reason=reason, headers=headers, ) def setup_datasets_mock(adapter, response, response_code, reason="OK", params={}): path = os.path.join(TEST_DATA_PATH, response) with open(path, "r") as response_body: body = json.load(response_body) uri = "{}{}{}".format(PREFIX, DOMAIN, DATASETS_PATH) if "offset" not in params: params["offset"] = 0 uri = "{}?{}".format( uri, "&".join(["{}={}".format(k, v) for k, v in params.items()]) ) headers = {"content-type": "application/json; charset=utf-8"} adapter.register_uri( "get", uri, status_code=response_code, json=body, reason=reason, headers=headers ) def setup_mock( adapter, method, response, response_code, reason="OK", dataset_identifier=DATASET_IDENTIFIER, content_type="json", query=None, ): path = os.path.join(TEST_DATA_PATH, response) with open(path, "r") as response_body: body = json.load(response_body) if dataset_identifier is None: # for create endpoint uri = "{}{}{}.{}".format(PREFIX, DOMAIN, OLD_API_PATH, "json") else: # most cases uri = "{}{}{}{}.{}".format( PREFIX, DOMAIN, DEFAULT_API_PATH, dataset_identifier, content_type ) if query: uri += "?" + query headers = {"content-type": "application/json; charset=utf-8"} adapter.register_uri( method, uri, status_code=response_code, json=body, reason=reason, headers=headers, complete_qs=True, ) afeld-sodapy-464855c/tests/test_utils.py000066400000000000000000000060661430365730200202740ustar00rootroot00000000000000import pytest import requests import requests_mock import sodapy.utils as utils @pytest.mark.parametrize( ("status_code", "status_type", "reason", "raises_exception"), [ (200, "Success", "OK", False), (300, "Redirection", "Multiple Choices", False), (400, "Client Error", "Bad Request", True), (500, "Server Error", "Internal Server Error", True), (600, "Foo Bar", "Here be dragons", False), ], ) def test_raise_for_status(status_code, status_type, reason, raises_exception): response = requests.models.Response() response.status_code = status_code response.reason = reason if raises_exception: with pytest.raises( requests.exceptions.HTTPError, match="{} {}: {}".format(status_code, status_type, reason), ): utils.raise_for_status(response) else: utils.raise_for_status(response) @pytest.mark.parametrize( ("elems", "result"), [ ({}, {}), ({"a": 1, "b": None, "c": "d"}, {"a": 1, "c": "d"}), ({"s": "", "c": 0}, {"s": "", "c": 0}), ], ) def test_clear_empty_values(elems, result): assert utils.clear_empty_values(elems) == result def test_format_old_api_request_exception(): with pytest.raises(Exception): utils.format_old_api_request() @pytest.mark.parametrize( ("dataid", "content_type", "path"), [ ("abcd", None, "/api/views/abcd"), ("abcd", "json", "/api/views/abcd.json"), (None, "json", "/api/views.json"), ], ) def test_format_old_api_request(dataid, content_type, path): assert ( utils.format_old_api_request(dataid=dataid, content_type=content_type) == path ) @pytest.mark.parametrize( ("dataid", "row_id", "content_type", "path"), [ ("abcd", None, "json", "/resource/abcd.json"), ("abcd", 123, "json", "/resource/abcd/123.json"), ], ) def test_format_new_api_request(dataid, row_id, content_type, path): assert ( utils.format_new_api_request( dataid=dataid, row_id=row_id, content_type=content_type ) == path ) def test_format_new_api_request_exception(): with pytest.raises(Exception): utils.format_new_api_request() @pytest.mark.parametrize( ("username", "password", "token"), [("me", None, "123456"), (None, "pass", "123456"), ("me", "pass", "123456")], ) def test_authentication_validation_exceptions(username, password, token): with pytest.raises(Exception): utils.authentication_validation(username, password, token) @pytest.mark.parametrize( ("username", "password", "token"), [("me", "pass", None), (None, None, "93738")], ) def test_authentication_validation(username, password, token): utils.authentication_validation(username, password, token) def test_download_file(tmp_path): path = tmp_path / "myfile.txt" url = "http://fileserver.dev/file" text = "the response data" with requests_mock.Mocker() as mock: mock.get(url, text=text) utils.download_file(url, str(path)) assert path.read_text() == text afeld-sodapy-464855c/tests/test_version.py000066400000000000000000000004161430365730200206120ustar00rootroot00000000000000""" Validate the length and format of the version string. """ import sodapy def test_version(): version = sodapy.__version__ components = version.split(".") assert len(components) == 3 for component in components: assert component.isnumeric()