diff --git a/Pipfile b/Pipfile index b5fb274..f840545 100644 --- a/Pipfile +++ b/Pipfile @@ -11,6 +11,7 @@ python_version = "3.6" [packages] +sodapy = "*" # testpackage = "*" @@ -29,8 +30,8 @@ coverage = "~=4.0" "coverage.space" = "~=0.8" mkdocs = "*" docutils = "*" -Pygments = "*" +pygments = "*" wheel = "*" -PyInstaller = "*" +pyinstaller = "*" twine = "*" sniffer = "*" diff --git a/Pipfile.lock b/Pipfile.lock index c64e206..704585a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2ec956cc13b6a5f81f12486128ac9b0e36cc0134c450592b18b23039c4a9ceec" + "sha256": "18fcd1d4305fced0d31b96329af28e49da74d674cc7ebb41050f812266700345" }, "host-environment-markers": { "implementation_name": "cpython", @@ -27,14 +27,35 @@ } ] }, - "default": {}, + "default": { + "future": { + "hashes": [ + "sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb" + ], + "version": "==0.16.0" + }, + "requests": { + "hashes": [ + "sha256:3f3f27a9d0f9092935efc78054ef324eb9f8166718270aefe036dfa1e4f68e1e", + "sha256:2109ecea94df90980be040490ff1d879971b024861539abb00054062388b612e" + ], + "version": "==2.12.1" + }, + "sodapy": { + "hashes": [ + "sha256:6eb513a5623a1881017df302dda84a093fb61619fe835c0b6edb3bce84c16020", + "sha256:8931f2c2958110c0564d7b6e94051e45a7bec05da323759e37b22eee2e339bc3" + ], + "version": "==1.4.5" + } + }, "develop": { "altgraph": { "hashes": [ - "sha256:b966a3114c7942e2a2b4c2304b2baf1cd2ed7a5a8b2106e64e0709a21d6cfbb4", - "sha256:481bac8feb1716bb8e485e652ed94002cc11304abccb2911f8f4574fc9dc207b" + "sha256:49dc134049903cc73fb76ca3cc9bef5b2b8c01c28732dd29594f99af2b449fc5", + "sha256:fc28b986a68fde8d3ff0e6d6ba3fbdd2cd562d11d45ef7c7735fbd826c9eec2e" ], - "version": "==0.14" + "version": "==0.15" }, "astroid": { "hashes": [ @@ -214,10 +235,10 @@ }, "macholib": { "hashes": [ - "sha256:15151e4dde7c2cb9758cf43f8672585a8d5b261d40a07cb9f679489c52397488", - "sha256:323c9c8b85768244554b3c040808ed6393c783aa6eb1122e04dc8905f442e559" + "sha256:7f76a7ef4f58f85889dec25fb532bad5acfd461c444738dfeb2e7bf855d5906b", + "sha256:9aeec52d7da59912b15445d08b08d95cee48414f01dd035be06f04a825973c08" ], - "version": "==1.8" + "version": "==1.9" }, "markdown": { "hashes": [ diff --git a/cannapy/__init__.py b/cannapy/__init__.py index b3ca1b1..b0ddc22 100644 --- a/cannapy/__init__.py +++ b/cannapy/__init__.py @@ -1,8 +1,8 @@ -"""cannapy package root.""" +"""cannapy module.""" + +from .organization import Organization __project__ = 'cannapy' __version__ = '0.0.0' VERSION = "{0} v{1}".format(__project__, __version__) - -from .organization import Organization diff --git a/cannapy/organization.py b/cannapy/organization.py index fdaf329..531b083 100644 --- a/cannapy/organization.py +++ b/cannapy/organization.py @@ -1,3 +1,6 @@ +"""A class representing an organization.""" + + class Organization(object): """An organization. diff --git a/cannapy/us/__init__.py b/cannapy/us/__init__.py new file mode 100644 index 0000000..a5b3ff7 --- /dev/null +++ b/cannapy/us/__init__.py @@ -0,0 +1 @@ +"""cannapy submodule for the United States.""" diff --git a/cannapy/us/wa/__init__.py b/cannapy/us/wa/__init__.py new file mode 100644 index 0000000..aa24ec0 --- /dev/null +++ b/cannapy/us/wa/__init__.py @@ -0,0 +1 @@ +"""cannapy submodule for Washington state.""" diff --git a/cannapy/us/wa/wslcb/__init__.py b/cannapy/us/wa/wslcb/__init__.py new file mode 100644 index 0000000..8708e86 --- /dev/null +++ b/cannapy/us/wa/wslcb/__init__.py @@ -0,0 +1 @@ +"""cannapy submodule for Washington State Liquor & Cannabis Board (WSLCB).""" diff --git a/cannapy/us/wa/wslcb/portal.py b/cannapy/us/wa/wslcb/portal.py new file mode 100644 index 0000000..5371c10 --- /dev/null +++ b/cannapy/us/wa/wslcb/portal.py @@ -0,0 +1,60 @@ +"""An interface to the WSLCB Socrata-based open data portal.""" + +import os +import time +from sodapy import Socrata + +# WSLCB Socrata Open Data Portal URL +WSLCB_PORTAL_URL = 'data.lcb.wa.gov' + +# WSLCB Socrata Open Data Portal Dataset IDs +WSLCB_PORTAL_DATASETS = { + 'licensed_businesses': 'bhbp-x4eb', +} + + +class WSLCBPortal(object): + """A class interface to the WSLCB open data portal.""" + + def __init__(self, app_token=''): + """Constructor.""" + # Set the user's Socrata app token: + # https://dev.socrata.com/docs/app-tokens.html + if app_token == '': + # See if an environment variable is set + app_token = os.getenv('WSLCB_APP_TOKEN', '') + self._app_token = app_token + + # The Socrata client property will be initialized on first get + self._client = None + + def dataset_last_updated(self, dataset_id): + """Return the requested dataset's last update timestamp.""" + # Retrieve the source dataset's metadata + metadata = self.client.get_metadata(dataset_id) + + # Retrieve dataset last update timestamp in epoch/Unix time + last_updated = metadata['rowsUpdatedAt'] + + # Convert to a localized Python time.struct_time + # https://docs.python.org/3/library/time.html#time.struct_time + last_updated = time.localtime(last_updated) + + return last_updated + + @property + def app_token(self): + """The user's Socrata open data portal app token.""" + return self._app_token + + @app_token.setter + def app_token(self, value): + self._app_token = value + + @property + def client(self): + """A sodapy client interface to the WSLCB Socrata open data portal.""" + if self._client is None: + self._client = Socrata(WSLCB_PORTAL_URL, self.app_token) + + return self._client