From a70893c8339dfb742038eca8d095e56126ee1af4 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:32:41 -0800 Subject: [PATCH 01/13] add precommit and fix formatting --- .github/workflows/pre-commit.yaml | 38 + .github/workflows/release.yaml | 4 +- .pre-commit-config.yaml | 51 + LICENSE.txt | 4 +- MANIFEST.in | 2 +- Makefile | 2 +- README.md | 2 +- btrdbextras/__init__.py | 2 +- btrdbextras/conn.py | 8 +- btrdbextras/dq.py | 102 +- btrdbextras/eventproc/__init__.py | 11 +- btrdbextras/eventproc/eventproc.py | 108 +- btrdbextras/eventproc/protobuff/api_pb2.py | 1481 ++++++++++------- .../eventproc/protobuff/api_pb2_grpc.py | 238 +-- docs/source/conf.py | 23 +- docs/source/eventproc.rst | 2 +- docs/source/installing.rst | 1 - release.sh | 2 +- requirements.txt | 2 +- setup.cfg | 58 + setup.py | 75 +- tests/requirements.txt | 2 +- tests/test_base.py | 7 +- 23 files changed, 1436 insertions(+), 789 deletions(-) create mode 100644 .github/workflows/pre-commit.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 setup.cfg diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000..858e465 --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,38 @@ +name: pre-commit + +on: + pull_request: + branches: + - master + types: + - opened + - reopened + - ready_for_review + - synchronize + +env: + SKIP: pytest-check + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 # get full git history + - uses: actions/setup-python@v3 + with: + cache: 'pip' + - name: Install pre-commit + run: | + pip install pre-commit + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v21 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Run pre-commit + uses: pre-commit/action@v2.0.3 + with: + extra_args: --files ${{ steps.changed-files.outputs.all_changed_files }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 87de83e..09c37a5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: ["3.8", "3.9", "3.10", "3.11"] os: [ubuntu-latest, macos-latest, windows-latest] steps: @@ -75,4 +75,4 @@ jobs: uses: pypa/gh-action-pypi-publish@master with: user: __token__ - password: ${{ secrets.PYPI_DEPLOYMENT_TOKEN }} \ No newline at end of file + password: ${{ secrets.PYPI_DEPLOYMENT_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c62cb0d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,51 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + exclude: setup.cfg +- repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black-jupyter + args: [--line-length=88] +- repo: https://github.com/pycqa/isort + rev: 5.11.5 + hooks: + - id: isort + name: isort (python) + args: [--profile=black, --line-length=88] +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: # Enforce that python3.6+ type annotations are used instead of type comments + - id: python-use-type-annotations +- repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 +# args: [] +#- repo: https://github.com/PyCQA/pydocstyle +# rev: 6.1.1 +# hooks: +# - id: pydocstyle +- repo: https://github.com/kynan/nbstripout + rev: 0.6.1 + hooks: + - id: nbstripout + name: nbstripout + description: 'nbstripout: strip output from Jupyter and IPython notebooks' + entry: nbstripout + language: python + types: [jupyter] + files: kinesis/dominion_apps/ # Run only on notebooks in dominion_apps directory + # exclude: # add files here to keep output cells in Jupyter during commit +- repo: local + hooks: + - id: pytest-check + name: pytest-check + entry: pytest + language: system + pass_filenames: false + always_run: true diff --git a/LICENSE.txt b/LICENSE.txt index e07e178..87f9ad8 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -6,6 +6,6 @@ Redistribution and use in source and binary forms, with or without modification, 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in index cd0cdb5..99d1fdb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -19,4 +19,4 @@ global-exclude *.py[co] global-exclude .ipynb_checkpoints global-exclude .DS_Store global-exclude .env -global-exclude .coverage.* \ No newline at end of file +global-exclude .coverage.* diff --git a/Makefile b/Makefile index 489fafa..e80d39d 100644 --- a/Makefile +++ b/Makefile @@ -59,4 +59,4 @@ deploy: # Build html version of docs html: - $(SPHINXBUILD) -b html $(SPHINXOPTS) $(SPHINXSOURCEDIR) $(SPHINXBUILDDIR) \ No newline at end of file + $(SPHINXBUILD) -b html $(SPHINXOPTS) $(SPHINXSOURCEDIR) $(SPHINXBUILDDIR) diff --git a/README.md b/README.md index 7cf7bac..3c544c5 100644 --- a/README.md +++ b/README.md @@ -43,4 +43,4 @@ Note that the documentation also requires Sphix and other dependencies to succes ## Versioning -This codebases uses a form of [Semantic Versioning](http://semver.org/) to structure version numbers. In general, the major version number will track with the BTrDB codebase to transparently maintain version compatibility. Planned features between major versions will increment the minor version while any special releases (bug fixes, etc.) will increment the patch number. \ No newline at end of file +This codebases uses a form of [Semantic Versioning](http://semver.org/) to structure version numbers. In general, the major version number will track with the BTrDB codebase to transparently maintain version compatibility. Planned features between major versions will increment the minor version while any special releases (bug fixes, etc.) will increment the patch number. diff --git a/btrdbextras/__init__.py b/btrdbextras/__init__.py index 5a9dad2..5e701f5 100644 --- a/btrdbextras/__init__.py +++ b/btrdbextras/__init__.py @@ -1,5 +1,5 @@ from .conn import Connection -__version__ = 'v5.11.9' +__version__ = "v5.11.9" __all__ = ["__version__", "Connection"] diff --git a/btrdbextras/conn.py b/btrdbextras/conn.py index 506dfb3..da2ad8c 100644 --- a/btrdbextras/conn.py +++ b/btrdbextras/conn.py @@ -21,9 +21,13 @@ ## Classes ########################################################################## -class Connection(): - def __init__(self, endpoint=os.environ.get("BTRDB_ENDPOINTS"), apikey=os.environ.get("BTRDB_API_KEY")): +class Connection: + def __init__( + self, + endpoint=os.environ.get("BTRDB_ENDPOINTS"), + apikey=os.environ.get("BTRDB_API_KEY"), + ): if endpoint is None or endpoint.strip() == "": raise ValueError("invalid endpoint or BTRDB_ENDPOINTS env variable not set") diff --git a/btrdbextras/dq.py b/btrdbextras/dq.py index 75bcd0c..6dfc141 100644 --- a/btrdbextras/dq.py +++ b/btrdbextras/dq.py @@ -1,21 +1,24 @@ import re import warnings -from tabulate import tabulate import btrdb -from btrdb.stream import StreamSet, Stream +from btrdb.stream import Stream, StreamSet from btrdb.utils.general import pointwidth from btrdb.utils.timez import ns_delta, to_nanoseconds +from tabulate import tabulate KNOWN_DISTILLER_TYPES = ["repeats", "duplicate-times", "zeros"] + class AmbiguousDistillateError(ValueError): """ Raised when a distillate stream's name contains references to multiple known data quality distiller names """ + pass + class DQDistillate(Stream): """ Subsets a Stream object and allows for identfication of data quality events @@ -24,21 +27,26 @@ class DQDistillate(Stream): ---------- btrdb : BTrDB A reference to the BTrDB object connecting this stream back to the - physical server. + physical server. uuid : UUID The unique UUID identifier for this stream. """ + def __init__(self, btrdb, uu): # gives all same attrs/methods as Stream super().__init__(btrdb, uu) - # NOTE: this involves determining distiller type based on the distillate + # NOTE: this involves determining distiller type based on the distillate # stream name, so we will need to be careful how we name distillates - types = re.findall(r"(?=("+'|'.join(KNOWN_DISTILLER_TYPES)+r"))", self.name) + types = re.findall(r"(?=(" + "|".join(KNOWN_DISTILLER_TYPES) + r"))", self.name) if len(types) == 0: - raise ValueError(f"unknown distiller type. Must be one of [{', '.join(KNOWN_DISTILLER_TYPES)}]") + raise ValueError( + f"unknown distiller type. Must be one of [{', '.join(KNOWN_DISTILLER_TYPES)}]" + ) if len(types) > 1: - raise AmbiguousDistillateError(f"ambiguous distiller name. contains references to [{', '.join(types)}]") + raise AmbiguousDistillateError( + f"ambiguous distiller name. contains references to [{', '.join(types)}]" + ) self.type = types[0] def contains_issue(self, start=None, end=None, depth=30): @@ -69,7 +77,7 @@ def contains_issue(self, start=None, end=None, depth=30): if end is None: latest = self.latest() # adding 1 to end time because end is exclusive in windows() - end = latest[0].time +1 if latest else None + end = latest[0].time + 1 if latest else None start, end = to_nanoseconds(start), to_nanoseconds(end) # There's no event if there's no data @@ -82,6 +90,7 @@ def contains_issue(self, start=None, end=None, depth=30): def __repr__(self): return f"DQDistillate collection={self.collection}, name={self.name}, type={self.type}" + class DQStream(Stream): """ Subsets Stream object. Contains a regular BTrDB stream along with its distillate Streams @@ -90,11 +99,12 @@ class DQStream(Stream): ---------- stream: btrdb.stream.Stream """ + def __init__(self, stream): # gives all same attrs/methods as Stream super().__init__(stream._btrdb, stream.uuid) self._distillates = self._get_distillates() - + def _get_distillates(self): """ Finds distillate Streams for each of the underlying source Streams @@ -107,7 +117,9 @@ def _get_distillates(self): # NOTE: This involves looking up distillate streams by their source_uuid annotation, so we # need to make sure that all distillers give output streams this annotation distillates = [] - for stream in self._btrdb.streams_in_collection(annotations={"source_uuid": str(self.uuid)}): + for stream in self._btrdb.streams_in_collection( + annotations={"source_uuid": str(self.uuid)} + ): try: distillates.append(DQDistillate(stream._btrdb, stream.uuid)) # we only want to raise this error if we aren't sure which distiller the stream is @@ -118,16 +130,18 @@ def _get_distillates(self): else: continue if len(distillates) < 1: - warnings.warn(f"Could not find any data quality distillates for stream {str(self.uuid)}") + warnings.warn( + f"Could not find any data quality distillates for stream {str(self.uuid)}" + ) return distillates - + @property def distillates(self): """ Returns list of distillate Streams """ return self._distillates - + def list_distillates(self): """ Outputs dict that shows which distillates the DQStream has available @@ -144,8 +158,8 @@ def list_distillates(self): "name": self.name, } distillers = { - distiller: True if self[distiller] - else False for distiller in KNOWN_DISTILLER_TYPES + distiller: True if self[distiller] else False + for distiller in KNOWN_DISTILLER_TYPES } # combine basic info with distiller info return {**info, **distillers} @@ -163,7 +177,7 @@ def contains_any_issue(self, start=None, end=None, depth=30): depth: (optional) int The precision of the window duration as a power of 2 in nanoseconds. e.g 30 would make the window duration accurate to roughly 1 second - + Returns ------- bool @@ -192,7 +206,7 @@ def contains_issue(self, distil_type, start=None, end=None, depth=30): depth: (optional) int The precision of the window duration as a power of 2 in nanoseconds. e.g 30 would make the window duration accurate to roughly 1 second - + Returns ------- bool @@ -204,7 +218,7 @@ def contains_issue(self, distil_type, start=None, end=None, depth=30): except KeyError: return None return distillate.contains_issue(start=start, end=end, depth=depth) - + def __getitem__(self, item): for distillate in self._distillates: if distillate.type == item: @@ -213,7 +227,8 @@ def __getitem__(self, item): def __repr__(self): return f"DQStream collection={self.collection}, name={self.name}" - + + class DQStreamSet(StreamSet): """ Subsets a StreamSet object. Contains a list of Streams along with each of their @@ -224,7 +239,8 @@ class DQStreamSet(StreamSet): streams: list list[btrdb.stream.Stream] """ - def __init__(self, streams): + + def __init__(self, streams): dq_streams = [] for stream in streams: if not isinstance(stream, DQStream): @@ -259,12 +275,21 @@ def describe(self, notebook=False, *additional_cols): KNOWN_TAGS = ["name", "unit", "ingress", "distiller"] contains_annotations = False - table = [["Collection", "Name", "Unit", "UUID", "Version", "Available Data Quality Info"]] + table = [ + [ + "Collection", + "Name", + "Unit", + "UUID", + "Version", + "Available Data Quality Info", + ] + ] # add args as table columns if user provides them if additional_cols: if not all(a in KNOWN_TAGS for a in additional_cols): - contains_annotations=True + contains_annotations = True table[0].extend(additional_cols) # query for metadata for all streams upfront @@ -273,7 +298,10 @@ def describe(self, notebook=False, *additional_cols): uu_str = ",".join(f"'{uu}'" for uu in uuids) if not contains_annotations: query = f"SELECT uuid, name, unit, distiller, ingress FROM streams WHERE uuid IN ({uu_str})" - meta = {res["uuid"]: {tag: res.get(tag) for tag in KNOWN_TAGS} for res in self._conn.query(query)} + meta = { + res["uuid"]: {tag: res.get(tag) for tag in KNOWN_TAGS} + for res in self._conn.query(query) + } else: query = f""" SELECT uuid, annotations, name, unit, distiller, ingress @@ -281,9 +309,12 @@ def describe(self, notebook=False, *additional_cols): WHERE uuid IN ({uu_str}) """ meta = { - res["uuid"]: {**res["annotations"], **{tag: res.get(tag) for tag in KNOWN_TAGS}} - for res in self._conn.query(query) - } + res["uuid"]: { + **res["annotations"], + **{tag: res.get(tag) for tag in KNOWN_TAGS}, + } + for res in self._conn.query(query) + } # iterate through streams, lookup metadata by uuid for stream in self._streams: @@ -319,10 +350,7 @@ def list_distillates(self): as well as bool values for each distiller that denote which distillates are available to the DQStream """ - return [ - stream.list_distillates() - for stream in self._streams - ] + return [stream.list_distillates() for stream in self._streams] def contains_any_issue(self, start=None, end=None, depth=30): """ @@ -337,7 +365,7 @@ def contains_any_issue(self, start=None, end=None, depth=30): depth: (optional) int The precision of the window duration as a power of 2 in nanoseconds. e.g 30 would make the window duration accurate to roughly 1 second - + Returns ------- dict[str, bool] @@ -345,7 +373,9 @@ def contains_any_issue(self, start=None, end=None, depth=30): contain any event """ return { - str(stream.uuid): stream.contains_any_issue(start=start, end=end, depth=depth) + str(stream.uuid): stream.contains_any_issue( + start=start, end=end, depth=depth + ) for stream in self._streams } @@ -364,7 +394,7 @@ def contains_issue(self, distil_type, start=None, end=None, depth=30): depth: (optional) int The precision of the window duration as a power of 2 in nanoseconds. e.g 30 would make the window duration accurate to roughly 1 second - + Returns ------- dict[str, bool] @@ -372,10 +402,12 @@ def contains_issue(self, distil_type, start=None, end=None, depth=30): a certain event """ return { - str(stream.uuid): stream.contains_issue(distil_type, start=start, end=end, depth=depth) + str(stream.uuid): stream.contains_issue( + distil_type, start=start, end=end, depth=depth + ) for stream in self._streams } - + def __getitem__(self, index): """ Returns the DQStream contained at a given index within the set diff --git a/btrdbextras/eventproc/__init__.py b/btrdbextras/eventproc/__init__.py index ae8d7e7..ddc489b 100644 --- a/btrdbextras/eventproc/__init__.py +++ b/btrdbextras/eventproc/__init__.py @@ -1,3 +1,10 @@ -from .eventproc import hooks, list_handlers, register, deregister, upload_file, _uploads +from .eventproc import _uploads, deregister, hooks, list_handlers, register, upload_file -__all__ = ['hooks', 'list_handlers', 'register', 'deregister', 'upload_file', '_uploads'] \ No newline at end of file +__all__ = [ + "hooks", + "list_handlers", + "register", + "deregister", + "upload_file", + "_uploads", +] diff --git a/btrdbextras/eventproc/eventproc.py b/btrdbextras/eventproc/eventproc.py index f80b7d7..95b157b 100644 --- a/btrdbextras/eventproc/eventproc.py +++ b/btrdbextras/eventproc/eventproc.py @@ -17,26 +17,34 @@ import io import os -import warnings import uuid +import warnings from collections import namedtuple +import certifi import dill import grpc -import certifi from btrdb.utils.timez import ns_to_datetime -from btrdbextras.eventproc.protobuff import api_pb2 -from btrdbextras.eventproc.protobuff import api_pb2_grpc -__all__ = ['hooks', 'list_handlers', 'register', 'deregister', 'upload_file', '_uploads'] +from btrdbextras.eventproc.protobuff import api_pb2, api_pb2_grpc + +__all__ = [ + "hooks", + "list_handlers", + "register", + "deregister", + "upload_file", + "_uploads", +] _uploads = {} -PATH_PREFIX="/eventproc" +PATH_PREFIX = "/eventproc" ########################################################################## ## Helper Functions ########################################################################## + def connect(conn): parts = conn.endpoint.split(":", 2) endpoint = conn.endpoint + PATH_PREFIX @@ -67,7 +75,9 @@ def connect(conn): except Exception: if env_bundle != "": # The user has given us something but we can't use it, we need to make noise - raise Exception("BTRDB_CA_BUNDLE(%s) env is defined but could not read file" % ca_bundle) + raise Exception( + "BTRDB_CA_BUNDLE(%s) env is defined but could not read file" % ca_bundle + ) else: contents = None @@ -75,8 +85,8 @@ def connect(conn): endpoint, grpc.composite_channel_credentials( grpc.ssl_channel_credentials(contents), - grpc.access_token_call_credentials(apikey) - ) + grpc.access_token_call_credentials(apikey), + ), ) @@ -84,7 +94,11 @@ def connect(conn): ## Helper Classes ########################################################################## -HandlerBase = namedtuple("HandlerBase", "id name hook version notify_on_success notify_on_failure tags created_at created_by updated_at updated_by") +HandlerBase = namedtuple( + "HandlerBase", + "id name hook version notify_on_success notify_on_failure tags created_at created_by updated_at updated_by", +) + class Handler(HandlerBase): """ @@ -95,11 +109,20 @@ class Handler(HandlerBase): @classmethod def from_grpc(cls, h): return cls( - h.id, h.name, h.hook, h.version, h.notify_on_success, h.notify_on_failure, - h.tag, ns_to_datetime(h.created_at), h.created_by, - ns_to_datetime(h.updated_at), h.updated_by + h.id, + h.name, + h.hook, + h.version, + h.notify_on_success, + h.notify_on_failure, + h.tag, + ns_to_datetime(h.created_at), + h.created_by, + ns_to_datetime(h.updated_at), + h.updated_by, ) + class Service(object): """ Helper class to integrate with GRPC generated code. @@ -120,7 +143,17 @@ def ListHandlers(self, hook): for result in response.handlers: yield result - def Register(self, name, hook, func, apikey, notify_on_success, notify_on_failure, dependencies, tags): + def Register( + self, + name, + hook, + func, + apikey, + notify_on_success, + notify_on_failure, + dependencies, + tags, + ): # convert decorated function to bytes buff = io.BytesIO() dill.dump(func, buff) @@ -128,14 +161,14 @@ def Register(self, name, hook, func, apikey, notify_on_success, notify_on_failur params = api_pb2.RegisterRequest( registration=api_pb2.Registration( - name=name, - hook=hook, - blob=buff.read(), - api_key=apikey, - notify_on_success=notify_on_success, - notify_on_failure=notify_on_failure, - dependencies=dependencies, - tags=tags, + name=name, + hook=hook, + blob=buff.read(), + api_key=apikey, + notify_on_success=notify_on_success, + notify_on_failure=notify_on_failure, + dependencies=dependencies, + tags=tags, ) ) @@ -153,6 +186,7 @@ def Deregister(self, handler_id): ## Public Functions ########################################################################## + def hooks(conn): """ List registered hooks. @@ -165,6 +199,7 @@ def hooks(conn): s = Service(connect(conn)) return s.ListHooks() + def list_handlers(conn, hook=""): """ List registered handlers. An optional hook name is allowed to filter @@ -227,16 +262,25 @@ def register(conn, name, hook, notify_on_success, notify_on_failure, tags=None): # inner will actually receive the decorated func but we still have access # to the args & kwargs due to closure/scope. def inner(func): - # call grpc service to register event handler s = Service(connect(conn)) - _ = s.Register(name, hook, func, conn.apikey, notify_on_success, notify_on_failure, dependencies, tags) + _ = s.Register( + name, + hook, + func, + conn.apikey, + notify_on_success, + notify_on_failure, + dependencies, + tags, + ) # return original func back to user return func return inner + def upload_file(file, file_name): """ Uploads file to S3. Returns a link to download the file. @@ -256,12 +300,12 @@ def upload_file(file, file_name): TypeError: file_name must be a string. ValueError: file must be a path to a file, relative to the home directory. ValueError: file_name cannot be longer than 200 characters, is . - + Returns ---------- string: Download link to the object. None if upload was not attempted. """ - + # check the inputs if not isinstance(file, str): raise TypeError("file must be a string.") @@ -270,16 +314,20 @@ def upload_file(file, file_name): if not os.path.exists(file): raise ValueError("file must be a path to a file.") if len(file_name) > 200: - raise ValueError("file_name cannot be longer than 200 characters, is {0}.".format(len(file_name))) + raise ValueError( + "file_name cannot be longer than 200 characters, is {0}.".format( + len(file_name) + ) + ) # check the context if not os.getenv("EXECUTOR_CONTEXT") == "true": m = "upload_file is running in an execution context without the appropriate AWS credentials and will not upload to S3." warnings.warn(m) return None - + # queue the upload, to be completed by the executor when the handler completes code = str(uuid.uuid4().hex) _uploads[code] = [file, file_name] - - return "https://{0}/{1}".format(os.getenv("DOWNLOADS_ENDPOINT"), code) \ No newline at end of file + + return "https://{0}/{1}".format(os.getenv("DOWNLOADS_ENDPOINT"), code) diff --git a/btrdbextras/eventproc/protobuff/api_pb2.py b/btrdbextras/eventproc/protobuff/api_pb2.py index b08d400..bd69e75 100644 --- a/btrdbextras/eventproc/protobuff/api_pb2.py +++ b/btrdbextras/eventproc/protobuff/api_pb2.py @@ -6,651 +6,1006 @@ from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database + # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - DESCRIPTOR = _descriptor.FileDescriptor( - name='api.proto', - package='eventprocapi', - syntax='proto3', - serialized_options=None, - create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\tapi.proto\x12\x0c\x65ventprocapi\"#\n\x13ListHandlersRequest\x12\x0c\n\x04hook\x18\x01 \x01(\t\"?\n\x14ListHandlersResponse\x12\'\n\x08handlers\x18\x01 \x03(\x0b\x32\x15.eventprocapi.Handler\"\xe7\x01\n\x07Handler\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04hook\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\x05\x12\x10\n\x08\x63\x61llable\x18\x05 \x01(\t\x12\x0b\n\x03tag\x18\x06 \x03(\t\x12\x19\n\x11notify_on_success\x18\x07 \x01(\t\x12\x19\n\x11notify_on_failure\x18\x08 \x01(\t\x12\x12\n\ncreated_by\x18\t \x01(\t\x12\x12\n\ncreated_at\x18\n \x01(\x03\x12\x12\n\nupdated_by\x18\x0b \x01(\t\x12\x12\n\nupdated_at\x18\x0c \x01(\x03\"\x12\n\x10ListHooksRequest\"6\n\x11ListHooksResponse\x12!\n\x05hooks\x18\x01 \x03(\x0b\x32\x12.eventprocapi.Hook\"\x14\n\x04Hook\x12\x0c\n\x04name\x18\x01 \x01(\t\"\xb1\x01\n\x0cRegistration\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04hook\x18\x02 \x01(\t\x12\x0c\n\x04tags\x18\x03 \x03(\t\x12\x0c\n\x04\x62lob\x18\x04 \x01(\x0c\x12\x19\n\x11notify_on_success\x18\x05 \x01(\t\x12\x19\n\x11notify_on_failure\x18\x06 \x01(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x07 \x01(\t\x12\x0c\n\x04user\x18\x08 \x01(\t\x12\x0f\n\x07\x61pi_key\x18\t \x01(\t\"C\n\x0fRegisterRequest\x12\x30\n\x0cregistration\x18\x01 \x01(\x0b\x32\x1a.eventprocapi.Registration\":\n\x10RegisterResponse\x12&\n\x07handler\x18\x01 \x01(\x0b\x32\x15.eventprocapi.Handler\"\x1f\n\x11\x44\x65registerRequest\x12\n\n\x02id\x18\x01 \x01(\x05\" \n\x12\x44\x65registerResponse\x12\n\n\x02id\x18\x01 \x01(\x05\x32\xe1\x02\n\x16\x45ventProcessingService\x12N\n\tListHooks\x12\x1e.eventprocapi.ListHooksRequest\x1a\x1f.eventprocapi.ListHooksResponse\"\x00\x12W\n\x0cListHandlers\x12!.eventprocapi.ListHandlersRequest\x1a\".eventprocapi.ListHandlersResponse\"\x00\x12K\n\x08Register\x12\x1d.eventprocapi.RegisterRequest\x1a\x1e.eventprocapi.RegisterResponse\"\x00\x12Q\n\nDeregister\x12\x1f.eventprocapi.DeregisterRequest\x1a .eventprocapi.DeregisterResponse\"\x00\x62\x06proto3' + name="api.proto", + package="eventprocapi", + syntax="proto3", + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\tapi.proto\x12\x0c\x65ventprocapi"#\n\x13ListHandlersRequest\x12\x0c\n\x04hook\x18\x01 \x01(\t"?\n\x14ListHandlersResponse\x12\'\n\x08handlers\x18\x01 \x03(\x0b\x32\x15.eventprocapi.Handler"\xe7\x01\n\x07Handler\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04hook\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\x05\x12\x10\n\x08\x63\x61llable\x18\x05 \x01(\t\x12\x0b\n\x03tag\x18\x06 \x03(\t\x12\x19\n\x11notify_on_success\x18\x07 \x01(\t\x12\x19\n\x11notify_on_failure\x18\x08 \x01(\t\x12\x12\n\ncreated_by\x18\t \x01(\t\x12\x12\n\ncreated_at\x18\n \x01(\x03\x12\x12\n\nupdated_by\x18\x0b \x01(\t\x12\x12\n\nupdated_at\x18\x0c \x01(\x03"\x12\n\x10ListHooksRequest"6\n\x11ListHooksResponse\x12!\n\x05hooks\x18\x01 \x03(\x0b\x32\x12.eventprocapi.Hook"\x14\n\x04Hook\x12\x0c\n\x04name\x18\x01 \x01(\t"\xb1\x01\n\x0cRegistration\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04hook\x18\x02 \x01(\t\x12\x0c\n\x04tags\x18\x03 \x03(\t\x12\x0c\n\x04\x62lob\x18\x04 \x01(\x0c\x12\x19\n\x11notify_on_success\x18\x05 \x01(\t\x12\x19\n\x11notify_on_failure\x18\x06 \x01(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x07 \x01(\t\x12\x0c\n\x04user\x18\x08 \x01(\t\x12\x0f\n\x07\x61pi_key\x18\t \x01(\t"C\n\x0fRegisterRequest\x12\x30\n\x0cregistration\x18\x01 \x01(\x0b\x32\x1a.eventprocapi.Registration":\n\x10RegisterResponse\x12&\n\x07handler\x18\x01 \x01(\x0b\x32\x15.eventprocapi.Handler"\x1f\n\x11\x44\x65registerRequest\x12\n\n\x02id\x18\x01 \x01(\x05" \n\x12\x44\x65registerResponse\x12\n\n\x02id\x18\x01 \x01(\x05\x32\xe1\x02\n\x16\x45ventProcessingService\x12N\n\tListHooks\x12\x1e.eventprocapi.ListHooksRequest\x1a\x1f.eventprocapi.ListHooksResponse"\x00\x12W\n\x0cListHandlers\x12!.eventprocapi.ListHandlersRequest\x1a".eventprocapi.ListHandlersResponse"\x00\x12K\n\x08Register\x12\x1d.eventprocapi.RegisterRequest\x1a\x1e.eventprocapi.RegisterResponse"\x00\x12Q\n\nDeregister\x12\x1f.eventprocapi.DeregisterRequest\x1a .eventprocapi.DeregisterResponse"\x00\x62\x06proto3', ) - - _LISTHANDLERSREQUEST = _descriptor.Descriptor( - name='ListHandlersRequest', - full_name='eventprocapi.ListHandlersRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='hook', full_name='eventprocapi.ListHandlersRequest.hook', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=27, - serialized_end=62, + name="ListHandlersRequest", + full_name="eventprocapi.ListHandlersRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="hook", + full_name="eventprocapi.ListHandlersRequest.hook", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=27, + serialized_end=62, ) _LISTHANDLERSRESPONSE = _descriptor.Descriptor( - name='ListHandlersResponse', - full_name='eventprocapi.ListHandlersResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='handlers', full_name='eventprocapi.ListHandlersResponse.handlers', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=64, - serialized_end=127, + name="ListHandlersResponse", + full_name="eventprocapi.ListHandlersResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="handlers", + full_name="eventprocapi.ListHandlersResponse.handlers", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=64, + serialized_end=127, ) _HANDLER = _descriptor.Descriptor( - name='Handler', - full_name='eventprocapi.Handler', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='eventprocapi.Handler.id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='hook', full_name='eventprocapi.Handler.hook', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='name', full_name='eventprocapi.Handler.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='version', full_name='eventprocapi.Handler.version', index=3, - number=4, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='callable', full_name='eventprocapi.Handler.callable', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='tag', full_name='eventprocapi.Handler.tag', index=5, - number=6, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='notify_on_success', full_name='eventprocapi.Handler.notify_on_success', index=6, - number=7, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='notify_on_failure', full_name='eventprocapi.Handler.notify_on_failure', index=7, - number=8, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='created_by', full_name='eventprocapi.Handler.created_by', index=8, - number=9, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='created_at', full_name='eventprocapi.Handler.created_at', index=9, - number=10, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='updated_by', full_name='eventprocapi.Handler.updated_by', index=10, - number=11, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='updated_at', full_name='eventprocapi.Handler.updated_at', index=11, - number=12, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=130, - serialized_end=361, + name="Handler", + full_name="eventprocapi.Handler", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="id", + full_name="eventprocapi.Handler.id", + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="hook", + full_name="eventprocapi.Handler.hook", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="eventprocapi.Handler.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="version", + full_name="eventprocapi.Handler.version", + index=3, + number=4, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="callable", + full_name="eventprocapi.Handler.callable", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="tag", + full_name="eventprocapi.Handler.tag", + index=5, + number=6, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="notify_on_success", + full_name="eventprocapi.Handler.notify_on_success", + index=6, + number=7, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="notify_on_failure", + full_name="eventprocapi.Handler.notify_on_failure", + index=7, + number=8, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="created_by", + full_name="eventprocapi.Handler.created_by", + index=8, + number=9, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="created_at", + full_name="eventprocapi.Handler.created_at", + index=9, + number=10, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="updated_by", + full_name="eventprocapi.Handler.updated_by", + index=10, + number=11, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="updated_at", + full_name="eventprocapi.Handler.updated_at", + index=11, + number=12, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=130, + serialized_end=361, ) _LISTHOOKSREQUEST = _descriptor.Descriptor( - name='ListHooksRequest', - full_name='eventprocapi.ListHooksRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=363, - serialized_end=381, + name="ListHooksRequest", + full_name="eventprocapi.ListHooksRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=363, + serialized_end=381, ) _LISTHOOKSRESPONSE = _descriptor.Descriptor( - name='ListHooksResponse', - full_name='eventprocapi.ListHooksResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='hooks', full_name='eventprocapi.ListHooksResponse.hooks', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=383, - serialized_end=437, + name="ListHooksResponse", + full_name="eventprocapi.ListHooksResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="hooks", + full_name="eventprocapi.ListHooksResponse.hooks", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=383, + serialized_end=437, ) _HOOK = _descriptor.Descriptor( - name='Hook', - full_name='eventprocapi.Hook', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='eventprocapi.Hook.name', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=439, - serialized_end=459, + name="Hook", + full_name="eventprocapi.Hook", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="name", + full_name="eventprocapi.Hook.name", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=439, + serialized_end=459, ) _REGISTRATION = _descriptor.Descriptor( - name='Registration', - full_name='eventprocapi.Registration', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='eventprocapi.Registration.name', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='hook', full_name='eventprocapi.Registration.hook', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='tags', full_name='eventprocapi.Registration.tags', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='blob', full_name='eventprocapi.Registration.blob', index=3, - number=4, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='notify_on_success', full_name='eventprocapi.Registration.notify_on_success', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='notify_on_failure', full_name='eventprocapi.Registration.notify_on_failure', index=5, - number=6, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='dependencies', full_name='eventprocapi.Registration.dependencies', index=6, - number=7, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='user', full_name='eventprocapi.Registration.user', index=7, - number=8, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='api_key', full_name='eventprocapi.Registration.api_key', index=8, - number=9, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=462, - serialized_end=639, + name="Registration", + full_name="eventprocapi.Registration", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="name", + full_name="eventprocapi.Registration.name", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="hook", + full_name="eventprocapi.Registration.hook", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="tags", + full_name="eventprocapi.Registration.tags", + index=2, + number=3, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="blob", + full_name="eventprocapi.Registration.blob", + index=3, + number=4, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="notify_on_success", + full_name="eventprocapi.Registration.notify_on_success", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="notify_on_failure", + full_name="eventprocapi.Registration.notify_on_failure", + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="dependencies", + full_name="eventprocapi.Registration.dependencies", + index=6, + number=7, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="user", + full_name="eventprocapi.Registration.user", + index=7, + number=8, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="api_key", + full_name="eventprocapi.Registration.api_key", + index=8, + number=9, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=462, + serialized_end=639, ) _REGISTERREQUEST = _descriptor.Descriptor( - name='RegisterRequest', - full_name='eventprocapi.RegisterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='registration', full_name='eventprocapi.RegisterRequest.registration', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=641, - serialized_end=708, + name="RegisterRequest", + full_name="eventprocapi.RegisterRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="registration", + full_name="eventprocapi.RegisterRequest.registration", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=641, + serialized_end=708, ) _REGISTERRESPONSE = _descriptor.Descriptor( - name='RegisterResponse', - full_name='eventprocapi.RegisterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='handler', full_name='eventprocapi.RegisterResponse.handler', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=710, - serialized_end=768, + name="RegisterResponse", + full_name="eventprocapi.RegisterResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="handler", + full_name="eventprocapi.RegisterResponse.handler", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=710, + serialized_end=768, ) _DEREGISTERREQUEST = _descriptor.Descriptor( - name='DeregisterRequest', - full_name='eventprocapi.DeregisterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='eventprocapi.DeregisterRequest.id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=770, - serialized_end=801, + name="DeregisterRequest", + full_name="eventprocapi.DeregisterRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="id", + full_name="eventprocapi.DeregisterRequest.id", + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=770, + serialized_end=801, ) _DEREGISTERRESPONSE = _descriptor.Descriptor( - name='DeregisterResponse', - full_name='eventprocapi.DeregisterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='eventprocapi.DeregisterResponse.id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=803, - serialized_end=835, + name="DeregisterResponse", + full_name="eventprocapi.DeregisterResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="id", + full_name="eventprocapi.DeregisterResponse.id", + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=803, + serialized_end=835, ) -_LISTHANDLERSRESPONSE.fields_by_name['handlers'].message_type = _HANDLER -_LISTHOOKSRESPONSE.fields_by_name['hooks'].message_type = _HOOK -_REGISTERREQUEST.fields_by_name['registration'].message_type = _REGISTRATION -_REGISTERRESPONSE.fields_by_name['handler'].message_type = _HANDLER -DESCRIPTOR.message_types_by_name['ListHandlersRequest'] = _LISTHANDLERSREQUEST -DESCRIPTOR.message_types_by_name['ListHandlersResponse'] = _LISTHANDLERSRESPONSE -DESCRIPTOR.message_types_by_name['Handler'] = _HANDLER -DESCRIPTOR.message_types_by_name['ListHooksRequest'] = _LISTHOOKSREQUEST -DESCRIPTOR.message_types_by_name['ListHooksResponse'] = _LISTHOOKSRESPONSE -DESCRIPTOR.message_types_by_name['Hook'] = _HOOK -DESCRIPTOR.message_types_by_name['Registration'] = _REGISTRATION -DESCRIPTOR.message_types_by_name['RegisterRequest'] = _REGISTERREQUEST -DESCRIPTOR.message_types_by_name['RegisterResponse'] = _REGISTERRESPONSE -DESCRIPTOR.message_types_by_name['DeregisterRequest'] = _DEREGISTERREQUEST -DESCRIPTOR.message_types_by_name['DeregisterResponse'] = _DEREGISTERRESPONSE +_LISTHANDLERSRESPONSE.fields_by_name["handlers"].message_type = _HANDLER +_LISTHOOKSRESPONSE.fields_by_name["hooks"].message_type = _HOOK +_REGISTERREQUEST.fields_by_name["registration"].message_type = _REGISTRATION +_REGISTERRESPONSE.fields_by_name["handler"].message_type = _HANDLER +DESCRIPTOR.message_types_by_name["ListHandlersRequest"] = _LISTHANDLERSREQUEST +DESCRIPTOR.message_types_by_name["ListHandlersResponse"] = _LISTHANDLERSRESPONSE +DESCRIPTOR.message_types_by_name["Handler"] = _HANDLER +DESCRIPTOR.message_types_by_name["ListHooksRequest"] = _LISTHOOKSREQUEST +DESCRIPTOR.message_types_by_name["ListHooksResponse"] = _LISTHOOKSRESPONSE +DESCRIPTOR.message_types_by_name["Hook"] = _HOOK +DESCRIPTOR.message_types_by_name["Registration"] = _REGISTRATION +DESCRIPTOR.message_types_by_name["RegisterRequest"] = _REGISTERREQUEST +DESCRIPTOR.message_types_by_name["RegisterResponse"] = _REGISTERRESPONSE +DESCRIPTOR.message_types_by_name["DeregisterRequest"] = _DEREGISTERREQUEST +DESCRIPTOR.message_types_by_name["DeregisterResponse"] = _DEREGISTERRESPONSE _sym_db.RegisterFileDescriptor(DESCRIPTOR) -ListHandlersRequest = _reflection.GeneratedProtocolMessageType('ListHandlersRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTHANDLERSREQUEST, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.ListHandlersRequest) - }) +ListHandlersRequest = _reflection.GeneratedProtocolMessageType( + "ListHandlersRequest", + (_message.Message,), + { + "DESCRIPTOR": _LISTHANDLERSREQUEST, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.ListHandlersRequest) + }, +) _sym_db.RegisterMessage(ListHandlersRequest) -ListHandlersResponse = _reflection.GeneratedProtocolMessageType('ListHandlersResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTHANDLERSRESPONSE, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.ListHandlersResponse) - }) +ListHandlersResponse = _reflection.GeneratedProtocolMessageType( + "ListHandlersResponse", + (_message.Message,), + { + "DESCRIPTOR": _LISTHANDLERSRESPONSE, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.ListHandlersResponse) + }, +) _sym_db.RegisterMessage(ListHandlersResponse) -Handler = _reflection.GeneratedProtocolMessageType('Handler', (_message.Message,), { - 'DESCRIPTOR' : _HANDLER, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.Handler) - }) +Handler = _reflection.GeneratedProtocolMessageType( + "Handler", + (_message.Message,), + { + "DESCRIPTOR": _HANDLER, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.Handler) + }, +) _sym_db.RegisterMessage(Handler) -ListHooksRequest = _reflection.GeneratedProtocolMessageType('ListHooksRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTHOOKSREQUEST, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.ListHooksRequest) - }) +ListHooksRequest = _reflection.GeneratedProtocolMessageType( + "ListHooksRequest", + (_message.Message,), + { + "DESCRIPTOR": _LISTHOOKSREQUEST, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.ListHooksRequest) + }, +) _sym_db.RegisterMessage(ListHooksRequest) -ListHooksResponse = _reflection.GeneratedProtocolMessageType('ListHooksResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTHOOKSRESPONSE, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.ListHooksResponse) - }) +ListHooksResponse = _reflection.GeneratedProtocolMessageType( + "ListHooksResponse", + (_message.Message,), + { + "DESCRIPTOR": _LISTHOOKSRESPONSE, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.ListHooksResponse) + }, +) _sym_db.RegisterMessage(ListHooksResponse) -Hook = _reflection.GeneratedProtocolMessageType('Hook', (_message.Message,), { - 'DESCRIPTOR' : _HOOK, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.Hook) - }) +Hook = _reflection.GeneratedProtocolMessageType( + "Hook", + (_message.Message,), + { + "DESCRIPTOR": _HOOK, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.Hook) + }, +) _sym_db.RegisterMessage(Hook) -Registration = _reflection.GeneratedProtocolMessageType('Registration', (_message.Message,), { - 'DESCRIPTOR' : _REGISTRATION, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.Registration) - }) +Registration = _reflection.GeneratedProtocolMessageType( + "Registration", + (_message.Message,), + { + "DESCRIPTOR": _REGISTRATION, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.Registration) + }, +) _sym_db.RegisterMessage(Registration) -RegisterRequest = _reflection.GeneratedProtocolMessageType('RegisterRequest', (_message.Message,), { - 'DESCRIPTOR' : _REGISTERREQUEST, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.RegisterRequest) - }) +RegisterRequest = _reflection.GeneratedProtocolMessageType( + "RegisterRequest", + (_message.Message,), + { + "DESCRIPTOR": _REGISTERREQUEST, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.RegisterRequest) + }, +) _sym_db.RegisterMessage(RegisterRequest) -RegisterResponse = _reflection.GeneratedProtocolMessageType('RegisterResponse', (_message.Message,), { - 'DESCRIPTOR' : _REGISTERRESPONSE, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.RegisterResponse) - }) +RegisterResponse = _reflection.GeneratedProtocolMessageType( + "RegisterResponse", + (_message.Message,), + { + "DESCRIPTOR": _REGISTERRESPONSE, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.RegisterResponse) + }, +) _sym_db.RegisterMessage(RegisterResponse) -DeregisterRequest = _reflection.GeneratedProtocolMessageType('DeregisterRequest', (_message.Message,), { - 'DESCRIPTOR' : _DEREGISTERREQUEST, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.DeregisterRequest) - }) +DeregisterRequest = _reflection.GeneratedProtocolMessageType( + "DeregisterRequest", + (_message.Message,), + { + "DESCRIPTOR": _DEREGISTERREQUEST, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.DeregisterRequest) + }, +) _sym_db.RegisterMessage(DeregisterRequest) -DeregisterResponse = _reflection.GeneratedProtocolMessageType('DeregisterResponse', (_message.Message,), { - 'DESCRIPTOR' : _DEREGISTERRESPONSE, - '__module__' : 'api_pb2' - # @@protoc_insertion_point(class_scope:eventprocapi.DeregisterResponse) - }) +DeregisterResponse = _reflection.GeneratedProtocolMessageType( + "DeregisterResponse", + (_message.Message,), + { + "DESCRIPTOR": _DEREGISTERRESPONSE, + "__module__": "api_pb2" + # @@protoc_insertion_point(class_scope:eventprocapi.DeregisterResponse) + }, +) _sym_db.RegisterMessage(DeregisterResponse) - _EVENTPROCESSINGSERVICE = _descriptor.ServiceDescriptor( - name='EventProcessingService', - full_name='eventprocapi.EventProcessingService', - file=DESCRIPTOR, - index=0, - serialized_options=None, - create_key=_descriptor._internal_create_key, - serialized_start=838, - serialized_end=1191, - methods=[ - _descriptor.MethodDescriptor( - name='ListHooks', - full_name='eventprocapi.EventProcessingService.ListHooks', + name="EventProcessingService", + full_name="eventprocapi.EventProcessingService", + file=DESCRIPTOR, index=0, - containing_service=None, - input_type=_LISTHOOKSREQUEST, - output_type=_LISTHOOKSRESPONSE, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='ListHandlers', - full_name='eventprocapi.EventProcessingService.ListHandlers', - index=1, - containing_service=None, - input_type=_LISTHANDLERSREQUEST, - output_type=_LISTHANDLERSRESPONSE, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='Register', - full_name='eventprocapi.EventProcessingService.Register', - index=2, - containing_service=None, - input_type=_REGISTERREQUEST, - output_type=_REGISTERRESPONSE, serialized_options=None, create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='Deregister', - full_name='eventprocapi.EventProcessingService.Deregister', - index=3, - containing_service=None, - input_type=_DEREGISTERREQUEST, - output_type=_DEREGISTERRESPONSE, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), -]) + serialized_start=838, + serialized_end=1191, + methods=[ + _descriptor.MethodDescriptor( + name="ListHooks", + full_name="eventprocapi.EventProcessingService.ListHooks", + index=0, + containing_service=None, + input_type=_LISTHOOKSREQUEST, + output_type=_LISTHOOKSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name="ListHandlers", + full_name="eventprocapi.EventProcessingService.ListHandlers", + index=1, + containing_service=None, + input_type=_LISTHANDLERSREQUEST, + output_type=_LISTHANDLERSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name="Register", + full_name="eventprocapi.EventProcessingService.Register", + index=2, + containing_service=None, + input_type=_REGISTERREQUEST, + output_type=_REGISTERRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name="Deregister", + full_name="eventprocapi.EventProcessingService.Deregister", + index=3, + containing_service=None, + input_type=_DEREGISTERREQUEST, + output_type=_DEREGISTERRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + ], +) _sym_db.RegisterServiceDescriptor(_EVENTPROCESSINGSERVICE) -DESCRIPTOR.services_by_name['EventProcessingService'] = _EVENTPROCESSINGSERVICE +DESCRIPTOR.services_by_name["EventProcessingService"] = _EVENTPROCESSINGSERVICE # @@protoc_insertion_point(module_scope) diff --git a/btrdbextras/eventproc/protobuff/api_pb2_grpc.py b/btrdbextras/eventproc/protobuff/api_pb2_grpc.py index ec3abe4..ac02e3d 100644 --- a/btrdbextras/eventproc/protobuff/api_pb2_grpc.py +++ b/btrdbextras/eventproc/protobuff/api_pb2_grpc.py @@ -4,6 +4,7 @@ import btrdbextras.eventproc.protobuff.api_pb2 as api__pb2 + class EventProcessingServiceStub(object): """Missing associated documentation comment in .proto file.""" @@ -14,25 +15,25 @@ def __init__(self, channel): channel: A grpc.Channel. """ self.ListHooks = channel.unary_unary( - '/eventprocapi.EventProcessingService/ListHooks', - request_serializer=api__pb2.ListHooksRequest.SerializeToString, - response_deserializer=api__pb2.ListHooksResponse.FromString, - ) + "/eventprocapi.EventProcessingService/ListHooks", + request_serializer=api__pb2.ListHooksRequest.SerializeToString, + response_deserializer=api__pb2.ListHooksResponse.FromString, + ) self.ListHandlers = channel.unary_unary( - '/eventprocapi.EventProcessingService/ListHandlers', - request_serializer=api__pb2.ListHandlersRequest.SerializeToString, - response_deserializer=api__pb2.ListHandlersResponse.FromString, - ) + "/eventprocapi.EventProcessingService/ListHandlers", + request_serializer=api__pb2.ListHandlersRequest.SerializeToString, + response_deserializer=api__pb2.ListHandlersResponse.FromString, + ) self.Register = channel.unary_unary( - '/eventprocapi.EventProcessingService/Register', - request_serializer=api__pb2.RegisterRequest.SerializeToString, - response_deserializer=api__pb2.RegisterResponse.FromString, - ) + "/eventprocapi.EventProcessingService/Register", + request_serializer=api__pb2.RegisterRequest.SerializeToString, + response_deserializer=api__pb2.RegisterResponse.FromString, + ) self.Deregister = channel.unary_unary( - '/eventprocapi.EventProcessingService/Deregister', - request_serializer=api__pb2.DeregisterRequest.SerializeToString, - response_deserializer=api__pb2.DeregisterResponse.FromString, - ) + "/eventprocapi.EventProcessingService/Deregister", + request_serializer=api__pb2.DeregisterRequest.SerializeToString, + response_deserializer=api__pb2.DeregisterResponse.FromString, + ) class EventProcessingServiceServicer(object): @@ -41,124 +42,173 @@ class EventProcessingServiceServicer(object): def ListHooks(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") def ListHandlers(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") def Register(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") def Deregister(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") def add_EventProcessingServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'ListHooks': grpc.unary_unary_rpc_method_handler( - servicer.ListHooks, - request_deserializer=api__pb2.ListHooksRequest.FromString, - response_serializer=api__pb2.ListHooksResponse.SerializeToString, - ), - 'ListHandlers': grpc.unary_unary_rpc_method_handler( - servicer.ListHandlers, - request_deserializer=api__pb2.ListHandlersRequest.FromString, - response_serializer=api__pb2.ListHandlersResponse.SerializeToString, - ), - 'Register': grpc.unary_unary_rpc_method_handler( - servicer.Register, - request_deserializer=api__pb2.RegisterRequest.FromString, - response_serializer=api__pb2.RegisterResponse.SerializeToString, - ), - 'Deregister': grpc.unary_unary_rpc_method_handler( - servicer.Deregister, - request_deserializer=api__pb2.DeregisterRequest.FromString, - response_serializer=api__pb2.DeregisterResponse.SerializeToString, - ), + "ListHooks": grpc.unary_unary_rpc_method_handler( + servicer.ListHooks, + request_deserializer=api__pb2.ListHooksRequest.FromString, + response_serializer=api__pb2.ListHooksResponse.SerializeToString, + ), + "ListHandlers": grpc.unary_unary_rpc_method_handler( + servicer.ListHandlers, + request_deserializer=api__pb2.ListHandlersRequest.FromString, + response_serializer=api__pb2.ListHandlersResponse.SerializeToString, + ), + "Register": grpc.unary_unary_rpc_method_handler( + servicer.Register, + request_deserializer=api__pb2.RegisterRequest.FromString, + response_serializer=api__pb2.RegisterResponse.SerializeToString, + ), + "Deregister": grpc.unary_unary_rpc_method_handler( + servicer.Deregister, + request_deserializer=api__pb2.DeregisterRequest.FromString, + response_serializer=api__pb2.DeregisterResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( - 'eventprocapi.EventProcessingService', rpc_method_handlers) + "eventprocapi.EventProcessingService", rpc_method_handlers + ) server.add_generic_rpc_handlers((generic_handler,)) - # This class is part of an EXPERIMENTAL API. +# This class is part of an EXPERIMENTAL API. class EventProcessingService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def ListHooks(request, + def ListHooks( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/eventprocapi.EventProcessingService/ListHooks', + "/eventprocapi.EventProcessingService/ListHooks", api__pb2.ListHooksRequest.SerializeToString, api__pb2.ListHooksResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) @staticmethod - def ListHandlers(request, + def ListHandlers( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/eventprocapi.EventProcessingService/ListHandlers', + "/eventprocapi.EventProcessingService/ListHandlers", api__pb2.ListHandlersRequest.SerializeToString, api__pb2.ListHandlersResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) @staticmethod - def Register(request, + def Register( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/eventprocapi.EventProcessingService/Register', + "/eventprocapi.EventProcessingService/Register", api__pb2.RegisterRequest.SerializeToString, api__pb2.RegisterResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) @staticmethod - def Deregister(request, + def Deregister( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/eventprocapi.EventProcessingService/Deregister', + "/eventprocapi.EventProcessingService/Deregister", api__pb2.DeregisterRequest.SerializeToString, api__pb2.DeregisterResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3e27d73..d44492a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,15 +16,16 @@ import os import sys -sys.path.insert(0, os.path.abspath('../..')) + +sys.path.insert(0, os.path.abspath("../..")) import sphinx_glpi_theme # -- Project information ----------------------------------------------------- -project = 'btrdbextras' -copyright = '2020, PingThings, Inc.' -author = 'PingThings, Inc.' +project = "btrdbextras" +copyright = "2020, PingThings, Inc." +author = "PingThings, Inc." # -- General configuration --------------------------------------------------- @@ -33,19 +34,19 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - 'sphinx.ext.githubpages', - 'sphinx.ext.intersphinx', + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.githubpages", + "sphinx.ext.intersphinx", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- @@ -57,4 +58,4 @@ html_theme = "glpi" -html_theme_path = sphinx_glpi_theme.get_html_themes_path() \ No newline at end of file +html_theme_path = sphinx_glpi_theme.get_html_themes_path() diff --git a/docs/source/eventproc.rst b/docs/source/eventproc.rst index cd7c4d5..d0a73b1 100644 --- a/docs/source/eventproc.rst +++ b/docs/source/eventproc.rst @@ -313,4 +313,4 @@ Assuming you chose to use variadic arguments (i.e., `**kwargs`) in your event ha 'File': 'comtrade_samples.zip', 'RequestID': '0bbcacdb-e7aa-4e01-af88-9ba865ea32bd', 'SubmittedAt': '2020-10-29T21:58:15.09246084Z' - } \ No newline at end of file + } diff --git a/docs/source/installing.rst b/docs/source/installing.rst index 50dfba4..b6c4948 100644 --- a/docs/source/installing.rst +++ b/docs/source/installing.rst @@ -17,4 +17,3 @@ To upgrade using pip: .. code-block:: bash $ pip install -U btrdbextras - diff --git a/release.sh b/release.sh index ba08e6e..d53028c 100755 --- a/release.sh +++ b/release.sh @@ -45,4 +45,4 @@ git tag v$1.$2.$3 git push origin v$1.$2.$3 sleep 10 -git push \ No newline at end of file +git push diff --git a/requirements.txt b/requirements.txt index a613877..1c1920a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,4 @@ tabulate==0.8.9 certifi # Readthedocs -sphinx_glpi_theme \ No newline at end of file +sphinx_glpi_theme diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..00eb245 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,58 @@ +[metadata] +description-file = README.md +license_file = LICENSE.txt + +[test] +local_freetype = True +tests = True + +[aliases] +test=pytest + +[flake8] +max-line-length = 88 +exclude = .git build dist +statistics = True +count = True +select = + E9 + F63 + F7 + F82 +extend-ignore = + E111 + E114 + E121 + E122 + E123 + E124 + E128 + E201 + E202 + E203 + E221 + E222 + E225 + E226 + E231 + E241 + E251 + E266 + E271 + E302 + E303 + E402 + E501 + E502 + E701 + E731 + F401 + W292 + W293 + W391 + W605 + W503 + W504 + +[coverage:run] +omit = */tests/* diff --git a/setup.py b/setup.py index d5c5592..49bdf75 100644 --- a/setup.py +++ b/setup.py @@ -16,11 +16,10 @@ ## Imports ########################################################################## -import os import codecs +import os -from setuptools import setup -from setuptools import find_packages +from setuptools import find_packages, setup from btrdbextras import __version__ @@ -29,59 +28,63 @@ ########################################################################## ## Basic information -NAME = "btrdbextras" -DESCRIPTION = "Enhancements additional features to interact with the Berkeley Tree Database" -AUTHOR = "Allen Leis" -EMAIL = "allen@pingthings.io" -MAINTAINER = "Allen Leis" -LICENSE = "BSD-3-Clause" -REPOSITORY = "https://github.com/PingThingsIO/btrdbextras" -PACKAGE = "btrdb" -URL = "https://btrdbextras.readthedocs.io/en/latest/" -DOCS_URL = "https://btrdbextras.readthedocs.io/en/latest/" +NAME = "btrdbextras" +DESCRIPTION = ( + "Enhancements additional features to interact with the Berkeley Tree Database" +) +AUTHOR = "Allen Leis" +EMAIL = "allen@pingthings.io" +MAINTAINER = "Allen Leis" +LICENSE = "BSD-3-Clause" +REPOSITORY = "https://github.com/PingThingsIO/btrdbextras" +PACKAGE = "btrdb" +URL = "https://btrdbextras.readthedocs.io/en/latest/" +DOCS_URL = "https://btrdbextras.readthedocs.io/en/latest/" ## Define the keywords -KEYWORDS = ('btrdb', 'timeseries', 'database') +KEYWORDS = ("btrdb", "timeseries", "database") ## Define the classifiers ## See https://pypi.python.org/pypi?%3Aaction=list_classifiers -CLASSIFIERS = ( - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Topic :: Database', - 'Topic :: Software Development :: Libraries :: Python Modules', +CLASSIFIERS = ( + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Topic :: Database", + "Topic :: Software Development :: Libraries :: Python Modules", ) ## Important Paths -PROJECT = os.path.abspath(os.path.dirname(__file__)) +PROJECT = os.path.abspath(os.path.dirname(__file__)) REQUIRE_PATH = "requirements.txt" VERSION_PATH = os.path.join(PACKAGE, "version.py") PKG_DESCRIBE = "DESCRIPTION.md" ## Directories to ignore in find_packages -EXCLUDES = ( - "tests", "docs", +EXCLUDES = ( + "tests", + "docs", ) ########################################################################## ## Helper Functions ########################################################################## + def read(*parts): """ Assume UTF-8 encoding and return the contents of the file located at the absolute path from the REPOSITORY joined with *parts. """ - with codecs.open(os.path.join(PROJECT, *parts), 'rb', 'utf-8') as f: + with codecs.open(os.path.join(PROJECT, *parts), "rb", "utf-8") as f: return f.read() @@ -92,7 +95,7 @@ def get_requires(path=REQUIRE_PATH): """ for line in read(path).splitlines(): line = line.strip() - if line and not line.startswith('#'): + if line and not line.startswith("#"): yield line @@ -144,8 +147,8 @@ def get_description_type(path=PKG_DESCRIBE): }, "install_requires": list(get_requires()), "python_requires": ">=3.6, <4", - "setup_requires":["pytest-runner"], - "tests_require":["pytest"], + "setup_requires": ["pytest-runner"], + "tests_require": ["pytest"], } @@ -153,5 +156,5 @@ def get_description_type(path=PKG_DESCRIBE): ## Run setup script ########################################################################## -if __name__ == '__main__': +if __name__ == "__main__": setup(**config) diff --git a/tests/requirements.txt b/tests/requirements.txt index 60b339e..94366ed 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ pytest==6.1.1 pytest-cov==2.10.1 -pytest-flakes==4.0.2 \ No newline at end of file +pytest-flakes==4.0.2 diff --git a/tests/test_base.py b/tests/test_base.py index 81ef9b6..4f76033 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -16,24 +16,25 @@ ########################################################################## import re + import pytest -from btrdbextras import __version__, Connection +from btrdbextras import Connection, __version__ ########################################################################## ## Initialization Tests ########################################################################## -class TestPackage(object): +class TestPackage(object): def test_version(self): """ Assert that the test version smells valid. """ assert bool(re.match(r"^v\d+\.\d+\.\d+$", __version__)) -class TestConnection(object): +class TestConnection(object): def test_properties(self): """ Connection contains apikey and endpoint From 36d12ea572e8fa9779bfcb16f25e72bc71fb89ff Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:49:17 -0800 Subject: [PATCH 02/13] update actions v2->v3 --- .github/workflows/release.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 09c37a5..8951694 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -18,9 +18,9 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} ${{ matrix.os }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -39,7 +39,7 @@ jobs: if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create Release id: create_release uses: actions/create-release@v1 @@ -59,9 +59,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: '3.8' - name: Install dependencies From 874b95a70a79498b06f02acb3cd974cc1233770c Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:17:59 -0800 Subject: [PATCH 03/13] add pyproject.toml --- .gitignore | 14 +++++++++---- pyproject.toml | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index d5ef9f4..4629a15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -# Local development -local-setup.sh -driver.py -ignore.py # OS trash .DS_Store @@ -122,3 +118,13 @@ dmypy.json # Pyre type checker .pyre/ + +# arrow parquet files +*.parquet + +.idea +.idea/misc.xml +.idea/vcs.xml +.idea/inspectionProfiles/profiles_settings.xml +.idea/inspectionProfiles/Project_Default.xml +/.idea/ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b34b2df --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,54 @@ +[project] +name = "btrdbextras" +version = "5.31.0" +description = "Enhancements additional features to interact with the Berkeley Tree Database" +authors = [ + {name="PingThingsIO", email="support@pingthings.io"}, +] +dependencies = [ + "btrdb>=5.11.7", + "certifi", + "dill==0.3.2", + "grpcio-tools>=1.16.1", + "grpcio>=1.16.1", + "sphinx_glpi_theme", + "tabulate==0.8.9", +] +requires-python = ">=3.6, <4" +readme = "README.md" +license = {file="LICENSE.txt"} +keywords = ["btrdb", "timeseries", "database"] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + "Topic :: Database", + "Topic :: Software Development :: Libraries :: Python Modules", +] + +[project.urls] +Homepage = "https://btrdbextras.readthedocs.io/en/latest/" +Downloads = "https://github.com/PingThingsIO/btrdbextras/tarball/v5.31.0" +Documentation = "https://btrdbextras.readthedocs.io/en/latest/" +Download = "https://github.com/PingThingsIO/btrdbextras/tarball/v5.31.0" +Source = "https://github.com/PingThingsIO/btrdbextras" +Tracker = "https://github.com/PingThingsIO/btrdbextras/issues" +[build-system] +requires = ["setuptools>=61"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.exclude-package-data] +drop = [ + "tests", + "docs" +] From acf5ec1094bbb30d891fa295ec5fbf7d24a7c1fb Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:24:10 -0800 Subject: [PATCH 04/13] update test requirements.txt --- tests/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 94366ed..4a08de2 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ -pytest==6.1.1 +pytest==6.2.5 pytest-cov==2.10.1 pytest-flakes==4.0.2 From 325395018da906c089d8f3d1319bbe8d8ca223e2 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:30:30 -0800 Subject: [PATCH 05/13] update pre-commit hooks --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c62cb0d..69de93a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,18 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace exclude: setup.cfg - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.11.0 hooks: - id: black-jupyter args: [--line-length=88] - repo: https://github.com/pycqa/isort - rev: 5.11.5 + rev: 5.12.0 hooks: - id: isort name: isort (python) @@ -22,7 +22,7 @@ repos: hooks: # Enforce that python3.6+ type annotations are used instead of type comments - id: python-use-type-annotations - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8 # args: [] From d92055f00f0f902f244950d9565d02446e6bc8b0 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:35:30 -0800 Subject: [PATCH 06/13] update Makefile --- Makefile | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index e80d39d..01a5a59 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ SPHINXBUILDDIR = docs/build SPHINXSOURCEDIR = docs/source # Export targets not associated with files -.PHONY: test grpc +.PHONY: test coverage pip clean publish uml build deploy install # Clean build files clean: @@ -31,7 +31,12 @@ clean: -rm -rf docs/build -rm -rf platform-builds meta.yaml -# Generate new grpc code + +# Publish to gh-pages +publish: + git subtree push --prefix=deploy origin gh-pages + +# Autogenerate GRPC/PB files grpc: @echo Generating files: python -m grpc_tools.protoc -I btrdbextras/eventproc/protobuff --python_out=btrdbextras/eventproc/protobuff --grpc_python_out=btrdbextras/eventproc/protobuff btrdbextras/eventproc/protobuff/api.proto @@ -54,9 +59,11 @@ install: # Deploy to PyPI deploy: - # python setup.py register + python setup.py register twine upload dist/* --verbose # Build html version of docs html: $(SPHINXBUILD) -b html $(SPHINXOPTS) $(SPHINXSOURCEDIR) $(SPHINXBUILDDIR) + @echo + @echo "Build finished. The HTML pages are in $(SPHINXBUILDDIR)/html." From 62fe4a45cfaff5102639ccc69fa3da944af9dd4b Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:38:46 -0800 Subject: [PATCH 07/13] update docs/ --- docs/make.bat | 35 ----------------------------------- docs/requirements.txt | 7 +++++-- 2 files changed, 5 insertions(+), 37 deletions(-) delete mode 100644 docs/make.bat diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 6247f7e..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/requirements.txt b/docs/requirements.txt index d535bde..49e71d8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,5 @@ -sphinx -sphinx_glpi_theme +alabaster>=0.7.12 +Sphinx>=1.7 +sphinx-rtd-theme +numpydoc +pydata-sphinx-theme From ae22e5379b15125fd23d7a3cc3d2cdaf1e096e2a Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:00:28 -0800 Subject: [PATCH 08/13] update __version__ and some of docs/sourc/conf.py --- btrdbextras/__init__.py | 2 +- .../IEEE_13_-_Create_Streams_Add_Data.ipynb | 5042 +++++++++++++++++ .../Intro_to_Simulation_with_OpenDSS.ipynb | 405 ++ docs/source/conf.py | 117 +- 4 files changed, 5556 insertions(+), 10 deletions(-) create mode 100644 btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb create mode 100644 btrdbextras/opendss_ingest/Intro_to_Simulation_with_OpenDSS.ipynb diff --git a/btrdbextras/__init__.py b/btrdbextras/__init__.py index 5e701f5..6d18cfe 100644 --- a/btrdbextras/__init__.py +++ b/btrdbextras/__init__.py @@ -1,5 +1,5 @@ from .conn import Connection -__version__ = "v5.11.9" +__version__ = "v5.31.0" __all__ = ["__version__", "Connection"] diff --git a/btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb b/btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb new file mode 100644 index 0000000..f0ec190 --- /dev/null +++ b/btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb @@ -0,0 +1,5042 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import opendssdirect as dss\n", + "import matplotlib.pyplot as plt\n", + "from tqdm.notebook import tqdm_notebook, tqdm\n", + "import btrdb as db\n", + "import uuid\n", + "from btrdb.utils.timez import datetime_to_ns\n", + "import simulation_utils as sims\n", + "from datetime import datetime, timedelta\n", + "\n", + "%matplotlib inline\n", + "\n", + "import importlib\n", + "\n", + "importlib.reload(sims);" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": {}, + "outputs": [], + "source": [ + "# Connect to the database\n", + "conn = db.connect(profile=\"collab\")" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": {}, + "outputs": [], + "source": [ + "model_loc = \"./Models/13Bus/IEEE13Nodeckt.dss\"\n", + "dss.run_command(\"Redirect \" + model_loc);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create output streams\n", + "The following cells create the output streams or retrieve them if they have already been created. " + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": {}, + "outputs": [], + "source": [ + "prefix = \"simulated/ieee13\"\n", + "collections, names, tags, annotations = sims.get_stream_info(base_col=prefix)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nstreams = len(collections)\n", + "print(\"Creating\", nstreams, \"streams\")\n", + "for i in range(nstreams):\n", + " print(collections[i] + \"/\" + names[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 234 streams under simulated/ieee13\n", + "Found 234 streams. Created 0 streams.\n" + ] + } + ], + "source": [ + "streams_dict = sims.create_streams(prefix, collections, names, tags, annotations, conn)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generate simulated measurements\n", + "The following cell generates data that will be converted into streams. \n", + "For convenience, we back-calculate the number of samples from a user specified sample rate (`fs`) and simulation duration (`start_time` to `end_time`). However, keep in mind that the simulation has no inherent sense of time - we are abitrarily assigning timestamps to each simulation result. " + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We will generate 1800 samples.\n" + ] + } + ], + "source": [ + "# The number of samples to generate\n", + "start_time = datetime(2022, 1, 1, 0, 0, 0)\n", + "end_time = datetime(2022, 1, 1, 0, 1, 0)\n", + "fs = 30 # Hz\n", + "T = int(((end_time - start_time).total_seconds()) * fs)\n", + "print(\"We will generate\", T, \"samples.\")\n", + "\n", + "# Generate the nanosecond timestamps for the data\n", + "start_ns = datetime_to_ns(start_time)\n", + "end_ns = datetime_to_ns(end_time)\n", + "timestamps = np.arange(start_ns, end_ns, 1e9 / 30, dtype=\"int\")" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": {}, + "outputs": [], + "source": [ + "# Get the original loads\n", + "load, load_names = sims.get_loads()\n", + "nloads = len(load_names)\n", + "\n", + "# Generate the randomized scaling factors\n", + "mu = 1.1\n", + "sig = 0.1\n", + "s = np.random.normal(loc=mu, scale=sig, size=[nloads, T])\n", + "\n", + "# Generate the new load values\n", + "new_load = s * load[:, np.newaxis]" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Running simulation: 0%| | 0/1799 [00:00]" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(timestamps, V[\"646/VCM\"])\n", + "plt.plot(timestamps, V[\"646/VBM\"])\n", + "\n", + "plt.figure()\n", + "plt.plot(timestamps, V[\"646/VCA\"])\n", + "plt.plot(timestamps, V[\"646/VBA\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of streams simulated:\n", + "234\n" + ] + } + ], + "source": [ + "print(\"Number of streams simulated:\")\n", + "print(len(V.keys()) + len(I.keys()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Push data to streams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Put voltage data into the corresponding stream\n", + "sims.add_all_data(timestamps, V, streams_dict, prefix)\n", + "# Put current data into the corresponding stream\n", + "sims.add_all_data(timestamps, I, streams_dict, prefix)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add long period of data\n", + "The following cell runs a loop which will generate and push data over a much longer period. \n", + "The purpose of the loop is to avoid generating all the data at once - instead, the full time period is divided into smaller time chunks, with data generated and inserted for each chunk sequentially. \n", + "\n", + "The code is divided into two cells below - the first is the initialization step. The second runs the loop. If there is an error during the loop, the second cell can be re-run and will pick up where it left off. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Collection prefix in which data will be added\n", + "prefix = \"simulated/ieee13\"\n", + "# Get the desired output streams to which data will be pushed\n", + "collections, names, tags, annotations = sims.get_stream_info(base_col=prefix)\n", + "# If the desired streams exist, retrieve them. Otherwise create them.\n", + "streams_dict = sims.create_streams(prefix, collections, names, tags, annotations, conn)\n", + "\n", + "\n", + "# Get the original loads\n", + "load, load_names = sims.get_loads()\n", + "nloads = len(load_names)\n", + "\n", + "# Simulation time window - the FULL time range over which we want to generate data\n", + "start_time = datetime(2022, 1, 2, 0, 0, 0)\n", + "end_time = datetime(2022, 1, 3, 0, 0, 0)\n", + "fs = 30 # Hz\n", + "Ttotal = int(((end_time - start_time).total_seconds()) * fs)\n", + "print(\"We will generate\", Ttotal, \"samples.\")\n", + "\n", + "# The simulation time step - this is the amount of data we insert at once.\n", + "step = timedelta(minutes=5)\n", + "nsteps = int((end_time - start_time) / step)\n", + "\n", + "# Create progress bar\n", + "pbar = tqdm(total=nsteps, desc=\"Adding simulated data\")\n", + "\n", + "t0 = start_time" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c1f55605fad2421f944ee0fc466c5752", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Adding simulated data: 0%| | 0/110 [00:00" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "T = 100 # Number of time points to simulate\n", + "\n", + "# Get the original loads\n", + "load, load_names = sims.get_loads()\n", + "nloads = len(load_names)\n", + "\n", + "# Generate the randomized scaling factors\n", + "mu = 1.1\n", + "sig = 0.1\n", + "s = np.random.normal(loc=mu, scale=sig, size=[nloads, T])\n", + "\n", + "# Generate the new load values by scaling the original loads with randomized s\n", + "new_load = s * load[:, np.newaxis]\n", + "\n", + "# Visualize some of the load curves\n", + "plt.plot(new_load[0:5, :].T);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now to iteratively run the simulation for each load setting and retrieve the resulting voltages and currents...\n", + "\n", + "The function `simulate_network` does this all for you. It has the signature `simulate_network(loads, load_names, contypes=['Line', 'Transformer'])`. The first argument is the desired load values in an $n\\times T$ matrix (as created above), where $n$ is the number of loads we want to set. The second argument is the names of the loads to set (as returned by `get_loads()` above). Finally, the optional argument `contypes` specifies which types of connections we want to return current data for. \n", + "\n", + "The function returns dictionaries of voltage and current, with the sames keys as `v2dict` and `i2dict` respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Running simulation: 0%| | 0/99 [00:00" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "key_list = [key for key in V.keys()]\n", + "\n", + "plt.plot(V[key_list[0]])\n", + "plt.title(key_list[0]);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And one of the resulting current magnitudes. " + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXAAAAEICAYAAABGaK+TAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABr/ElEQVR4nO29e7wsZ1Um/Kyq6vu+n7PPOcnJueRGQggCEiCIIERAQB3m09ERL+DgyHwzjle+8fJ5GZ1xHMfxgjrzjTIgIIPoiCiKiILghJgYSCAmgYSQ5Jzk5Fz3/dLXqur3++OtVfVWdVV3dXfV3rv3fp/fL7+c3bt399tdVaue91nPWouEENDQ0NDQmDwYu70ADQ0NDY3RoAO4hoaGxoRCB3ANDQ2NCYUO4BoaGhoTCh3ANTQ0NCYUOoBraGhoTCh0ANfQ0NCYUOgArtEDIjpKRHcS0RYR/dpurycPENHfEdG/3O11ZAEieiURPbPb69DYeegAvsdARNvKf10iaio/f9cOLeNtAJYBzAgh3r5D77nnQERvIqI/2O117DaI6MtE9Cwiei8R/WLkd1PeuflXMX93log6RHQ48vgXiEgQ0emcl77voQP4HoMQYor/A/A0gG9WHvsAP4+IrByXcQrAl8QIZbo5ryv314/gGwF8bCfeaIc/V2oQ0fUATCHEYwlP+VYAbQCvIaJjMb8/A+BNyus9F0A184UeUOgAPiHgbTIR/QQRXQLwHiKaJ6KPEtESEa15/75G+Zu/I6L/SER/78khf8NsiIjKRPS/iGiFiNaJ6HOedPJeAG8B8OMes3o1EZWI6B1EdMH77x1EVOqzrp8noj/2Xn+LiB7yGNxPEdEVIjpHRK9V1jlLRO8mootEdJ6IfpGITO933+ut/zeIaAXAz0e+l+8loie99znDuxRvDf9Led5pj/WpgfJ6IvosEW0S0UeIaEF5vgHgNQA+nvRdec+7moj+nIhWiehxIvp+5TVCjDUqdXgM9SeI6EEAdSKyiOhriehu733OEdH3es8tEdGvEtHTRHSZiH6HiCqR7+L/JaJl73W/S3k8JBd539ld3r/J+26veN/DQ0R0q/Kyg25ibwHwOwAeBPDdMb9/P4A3R57/+31eT2MI6AA+WTgGYAGSIb8N8vi9x/v5JIAmgP8W+ZvvBPAvABwBUATw/3iPvwXALIATAA4B+L8BNIUQ3wvgAwB+xWP9nwTw0wBuB/B8AM8D8GIAP9NnXQDwzZAX7zyALwD4a2+9xwH8BwC/q/z9ewE4AG4A8AIArwWg6tMvAfAkgKMA/hM/SEQ1AL8F4PVCiGkAXwPggdhvLh5vBvBWAFd57/9byu9eDOBJIcQyEr4r73l/COAZAFcD+GcAfomI7hhiDW+CDJJzkN/NXwH4bQCLkN83f55fBvAs77EbvOf+nPI6xwAc9h5/C4B3EtFNKd7/tQBe4b32LIBvB7Ci/P4NAP4y7g+J6BSAV0KeLx9AOFAz/gHADBE927spfweA/xXzPI0RoAP4ZKEL4N8LIdpCiKYQYkUI8SdCiIYQYgsyuH1d5G/eI4R4TAjRBPC/IQMAANiQwegGIYQrhLhfCLGZ8L7fBeA/CCGuCCGWAPwCgO9JWpf32GeEEH8thHAA/DFkQPplIYQNGfROE9Gcx2TfAOBHhBB1IcQVAL8BeaEzLgghflsI4Sivr773rURUEUJcFEJ8ceC3GOD9QoiHhRB1AD8L4NuZ+SPMPGO/KyI6AeBlAH5CCNESQjwA4F2ID2RJ+C0hxDnvc30ngE8KIT4ohLC94/sAERHkjfFHhRCr3rH+JYS/IwD4We8Y/B/IoPvtKd7fBjAN4GYAJIR4RAhxEQCIqArgRQD+LuFvvwfAg0KIL0Ee0+cQ0Qtinscs/DUAHgFwPsW6NFJAB/DJwpIQosU/EFGViH6XiJ4iok0AdwKYU4IQAFxS/t0AMOX9+/2QrPgPPVnkV4iokPC+VwN4Svn5Ke+x2HV5uKz8uwlgWQjhKj/DW8spAAUAFz3ZYB2SnR9R/v5c3KK8wPvPIRnxRSL6SyK6OeEzxEF93ae8dXDC7Q0IAnjSd3U1AA6o6uscH3ENJwA8EfOcRUjd+H7lO/q49zhjzfs+1HWoxygWQohPQe7a/juAK0T0TiKa8X799QDuFkK0E/78zZDMG0KI8wD+DyT7j+L9kDen74WWTzKFDuCThWhS8e0AbgLwEiHEDORWGABo4AtJhvcLQohbIKWHb0Iyc7wAGWgZJ73HktY1DM5BJsEOCyHmvP9mhBDPSfP6Hst/DaQM8iiA/+n9qo5wsiwuwXZC+fdJSDa6TDIZdxWAz3vvkfRdXQCwQETTkddhhplmDepnOwfg+pjnLEPe9J6jfEezXqKbMe9JSuo6+Bj1XYcQ4reEEC8EcAuklPLvvF+pN7EQiOhrANwI4KeI6JKX/3gJgO+M5BkghHgKMpn5BgAfjns9jdGgA/hkYxrywl73EnD/Pu0fEtGriOi5HlvfhAxe3YSnfxDAzxDRIskk6M8hIx3T267/DYBfI6IZIjKI6HoiikpBcZ/hKBG90QtcbQDbymd4AMAriOgkEc0C+KmYl/huIrrFkwr+A4APebuE1wP4OLtwkr4rIcQ5AHcD+M9eovOrAHwfgu/mAQBvIKIF76bwIwM+0gcAvJqIvt1LaB4ioucLIbqQN6bfIKIj3pqOE9E3RP7+F4ioSEQvh7zJ/LGyjm/xdmw3eGvk7/BFRPQSb0dRB9BSvsPXI0H/hmTan4AM+s/3/rsVQMX7uyi+D8AdkV2CxpjQAXyy8Q7IC2YZMln08SH+9hiAD0EGpEcgt7/vT3juLwK4D9Jp8BAkM/3FhOeOgjdDJli/BGDNW9dVcU8kou8iIta5DQA/Bsk0VyH1/38NAEKITwD4I2/N9wP4aMzLvR8ygXoJQBnAD3mPR50X/b6rNwE47a3hTyFzAZ9UXv8fAZyFvEn9Ub8vQQjxNCRLfbv3eR6ATBoDwE8AeBzAP3hy2Schd1+MS5Df3QXIG8H/LYR41PvdbwDoQMpa7/N+z5iBvDmsQcouKwD+q+dE2fbWFAIRlSH19d8WQlxS/jvjfeYeGUUI8YQQ4r5+n19jeJCeyKOhEcDb/l8CcF2fpO6+BxH9OKSs9eO7vRaNZOzJ4gENjV3EAqSb48AGbw9nAfzFbi9Coz80A9fQ0NCYUGgNXENDQ2NCsaMSyuHDh8Xp06d38i01NDQ0Jh7333//shBiMfr4jgbw06dP4777dCJaQ0NDYxgQ0VNxj2sJRUNDQ2NCoQO4hoaGxoRCB3ANDQ2NCcXAAE5EJ4jo00T0JSL6IhH9sPf4fyWiR4noQSL6UyKay321GhoaGho+0jBwB8DbvUY+twP4ASK6BbIPwq1CiK8C8Bjie01oaGhoaOSEgQHc67HMXdm2IHtBHBdC/I3X6xmQfTiuSXoNDQ0NDY3sMZQGTnII6QsA3Bv51VshJ4nE/c3biOg+IrpvaWlppEVqaGhoaPQidQAnoikAfwI5OWVTefynIWWWD8T9nRDinUKI24QQty0u9vjQNTQ0NHButYG/+/KV3V7GxCFVAPd6Bf8JgA8IIT6sPP69kH2Hv2uUCeYaGhoaAPDeu8/iBz/4hd1exsQhjQuFALwbwCNCiF9XHn8dgB8H8E+EEI38lqihobHf0ei4aHbcnsc3mjY++uCFmL/QANIx8JdBDi+9g4ge8P57A+QcvWkAn/Ae+508F6qhobGzuLgRnR+dH9qOC6cr4LjhoVAfffAC/u0ffAEr20ljOQ82BvZCEULchfgZi7Gz8jQ0NCYfX760hW94x534yA+8DM87MZf7+7Wdrv9/ywx4Zb3thH6vEYauxNTQ0OjBuVWpil7ebO3I+3WUAK6ibcufHVen2OKgA7iGhkYP1ps2AKBp9+rSeYADdyvyfi1H/mx3NQOPgw7gGhoaPdjwAng0oOaFtvc+moEPBx3Ah8CZ5TqeWqnv9jI0NHLHRqMDAD3OECEE3n3XmcyllY47gIG7moHHQQfwIfBTH34QP//nX9ztZWho5I5AQgkHzqXtNv7jR7+Ejz10MdP3Y6adyMC7moHHQU+lHwJrdRvdym6vQkMjf6w34jVwZuRZu0KYgbd7GDhLKJqBx0Ez8CGw3XZ0MkXjQGA9QQNvcAC3s70O2p5U0uph4CyhaAYeBx3Ah0C942gtTuNAgJOYUQ286Scbs01u+hJKEgPXxCkWOoAPgXrb0dlwjQMBP4mZIKF08pJQEhi4vu7ioQN4SrQdF7YrNAPXOBBIklDy0sCZgfe6UOTj+rqLhw7gKVFvay1O42Cg2xWJPvBGThLKQAauXSix0AE8Jbgng86Ga+x3bLUccHPoqITSyoGBO24XrhegewK4ZuB9oQN4Smx7AdzWTEBjn4PZN9CbxGx05HWQpQauBu0eCUVr4H2hA3hKMAPXTEBjv2O9KROYpkE9hTzNhIKbcaDeDJIYuHahxEMH8JTY9iUUzQQ09je4iOfodCkmicntXbPTwNWg3WMj1D7wvtABPCW2NQPXOCBgB8qx2XKyDzzDQh71ZpDIwPV1FwsdwFNCSygaBwXsAb9qttKTxORKzE6G10FYQgnez1aSm9qFEg8dwFNi27MRdoW0WWlo7FdwEvPoTLlXQsmFgatJzHg9XEso8dABPCWYgQO6ubzG/sZ6w0a1aGKmYqHtdEOEpZWDD7ydwMDVm4eWUOKhA3hKhAK4ZgMa+xjrTRtzlQIqBRNA0JMbUCSUTG2EigaexMD1rjcWOoCnxLYSwDUb0NjPWG/YmK0WUfYCuJrIzKOUnl/LoPDNQjPwwdABPCXGZeBCCL8IQkNjL2Oj2QkxcDWR2UwYfTYOmM3PVAphBq78Wycx46EDeEpwEhMYzYnyqUev4LZf/CTWvQy/hsZexUbTxmylgHLRk1DsOAaevQY+Uy6EE5rOeNfcQYAO4ClRD0kow7OBC+tNNDounlppZLksDY3Msd6wMVdVGHgnCJ6sgduuyMyNFTBwK3SzCDFwnXeKhQ7gKVHvjOdC6Xgn4KWMh8FqaGQJIQTWmzZmq/ESihpgs/KCM5vvx8B1KX08dABPie22A9MgAKNt55hlZD3NW0MjS7TsLjpOF3OVIipFw3ss7ELh6yArLzi/jgzg8QxcO7/ioQN4StTbDmYrBQCjbec46OsArrGXwY2s5qqFwIXiBXAhBJq2iznvOshKB2cmLyWUeE+4dqHEQwfwlKi3gxN3FAbOf3Npo53pujQ0sgRXYYZ84F4A5+A6V+UAvjMMvFIwtQ88ATqAp4AQAvWO45+4o2znOpqBa0wAuBPhbKXQ4wNnJj5XLQLIMIA7LiyDUC2aaNldCG+aBGvgtZKlGXgCdABPgUbHhRDBiTvKyWQ78qTUAVxjL8MP4DFJTK5jyFxCcbooWQZK3vsx2WHmP122tAslATqApwBbCH0JZYTtnC+h6ACusYex4WvgRVSK4QDOAXU2awnF6aJoGShZRuh1WUKplbSEkgQdwFOAy+jHYeDsQtlqOboiU2PPghn4XKXgB1Seg8ke8HnvOsiqH4pk4KbPwH3N3ZNWypapJZQE6ACeAjyRPtDAR09iAsClDc3CNfYmNpo2CqbUo4kIlYLpM3DWwgMJJTsNPMTA7YCBlywDlklaQkmADuApEDDw8ZOYAHB5UztR8kaz4+Lf/sHncW5VV74Og/WmjdlKEUTS610pBgG8YYeJTHT82ahoexo4J035xtByXJQLJgqmoVs4J0AH8BSoRyWUEU4m25U6H6ATmTuBh85v4KMPXsRnz6zu9lJywcWNJtbq2ffV2WjYmK1Y/s+VgunbB1lKmWUJJSNZo+N0USoEDLylDI0oWQYsQzPwJOgAngL1SPadHSXDwHYFrpmrANCJzJ0AM+/6Ps03/Kv3349/96EHM3/d9WbHJyoAUC4YiguFNXBm4BkmMc3eJGbL6aJcMGGZhm5mlQBr8FM0eiSUERn4XLWAqZKlNfAdwNNeAFf7uO8nnFtt4PEr27DdLgpmdjxso2nj6HTZ/7lcMH3m7fvAK9n6wDmJGUgozMClNl4wSbeTTYBm4CnAEsq870IZ/mRqO/JCOzpTwpUtHcDzxrk1j4HvwwBuu12sNWw0Oi4efGYj09eWwxwK/s+xScxqtj7wtuOGJBRm9j4DNwztQkmADuApwL3AZ8rjuVCKloGjM2XNwHcAvoTSzibI7CWsKtr3PU8sZ/raGw3bZ9hAOInZjPjAs7IRBhJKLwNnF4puZhWPgQGciE4Q0aeJ6EtE9EUi+mHv8W/zfu4S0W35L3X3UG87qBZNlAry6xrlZOKt7rGZsnah7ADOrTYB7E8GvrQVnD/3PLmS2evabhdb7aBlBCAllKbiAy+aBqoRt8i4kElME+UCJzHDDLxgGLqdbALSMHAHwNuFELcAuB3ADxDRLQAeBvAtAO7McX17AvW2g1rJguW10Ry1lL5oGjg6W8blzVZmzfA1etGyXT9RvB+TmMvbMoA/75pZ3Hd2LTMpY7MZ9EFhSBdKUIlZLhiwTAOmQRlKKB4Dj9HAtQ+8PwYGcCHERSHE571/bwF4BMBxIcQjQogv573AvYDttoOpkjVWP3Db7aJgSQbudAVWB4xWE0Lg9+85i5VtzdaHxfn1pv/v7X0ooaxsy3Pnm593NdpOF194ej2T1/U7EfbRwKtF6XsoWUaGLhSpgZejpfTMwLULJRFDaeBEdBrACwDcO8TfvI2I7iOi+5aWloZc3t6AZOCyMq1oGiP1Zei4XRRMwtGZEoDB1ZhXttr4uY98EX/50MWR1nyQwfp3uWCgsQ8lFGbgr3/uVSAC7nkiGxllPY6BFwMfeMN2/f4oJcvIcCJPuJlV4AN3Ax+43rHGInUAJ6IpAH8C4EeEEJtp/04I8U4hxG1CiNsWFxdHWeOOo9FxQjJJve2i5jEPuZ0brRdK0ZRJTGBwMU8j0n9CIz04gN90dHpf2giXt9soWQauni3j1qtnM9PBNxq9AbwcYeDcobCYKQPvxpbSqz5wLaHEI1UAJ6ICZPD+gBDiw/kuaXchhMDrf/Mz+I1PPuY/xhIKAFjGaBlxP4k5ywG8vzTCOqAO4MPj3FoTJcvA6cO1faqBd3B4qgQiwkuvP4QHnl73E43jgN0tC7VwIU/H6cLtCjRtR2HgZiYauBDC94EXfG2de6FIBl4wSZfSJyCNC4UAvBvAI0KIX89/SbuLM8t1PLXSwD+eC/y19Y5MYgIYWY+zXYGCaWBxqgSiwdWYfiY+o34TBwlPrzRwYqGKqZK1L22Ey9ttHJ6WUtxLrzuEjtvF/U+t9f2b1XoH9z/Vv63AmpeXmVcCuDqVR2rg2Uoo/BrMvkuWoXQjDHzgQgCullF6kIaBvwzA9wC4g4ge8P57AxH9X0T0DICXAvhLIvrrXFe6Q/jcWXmSP7m07T/GLhRABvBRtnMdzwdumQYOT5VweYAG3oo00ddIj3NrDZyYr3gBfP99f8vbHSxOySD7omsXYBqEe57s7wd/12eexHe/67N9n7Na78AyCNMlpReK0hO80XH9aslSIRsJhdm2GsDbThe2K1m/dL2Mbh7Y7xhYSi+EuAsAJfz6T7Ndzu7js2ckk7mw0UKj46BatDwJRZ64sqhguBNJCCELebwT8dhMeSAD5xO72dEnLQBc2WrhnidW8MbnHx/43KdXG3jhqXlUixbaTheO24WVYbn5bmN5u43nXTMLAJgqWbjlqhk8cG69799c3myjabt9S+/XGh3M14JOhABCY9VadsDAi6aRiQ+8Ewng5YKUZoLAbkJAEiadyOzF/jmrM8Lnzq7628Yzy3U4bhctuxuWUIY8kdyugBDwL5yjM+WBScyWX/229xmkEAJ/8Y8XfHdEHvjw58/jh//wAWy27L7P22jY2Go5OLlQRc276e4nGaXbFVitSw2cMVctDPyMLI80+0hyq/UOFpRGVkBYQmkoScysNHAO1MWQhNL1z/9ywYBlyN/pcvpeTFwAv7LZwqcevZzLa1/ebOHp1Qa+8auuAiADeN1LDk35AXx4FwrrfAXvJD02W0odwLNOYj56aRN/9oXzmb7mg89s4Ac/+AV8+PPPZPq6KlgKYadEErgHyjXzVf+YbWcoQ3384Yv4759+PLPXGxZrjQ7crsChqSDQlixjYFn7ipegbPU5n9bqNuZrhdBj6lzMpmojLPR/zw/d/wzuTlHmzz3FuYyebwwqAy/4Eopm4FFMXAB/3z1n8f2/f38uCQ3Wv7/thdcAAJ5cqvuBo+a7UIZPYnL7WZ+BT5ex1rD7JihZX8zCXaDi/fc8hZ/9yMOZvuYffu4cAGCzmd9ugb8HLjZJAnchlAxcHrMsveAfffAi3vP3ZzN7vWGx7BXxqAy8aBkD2TD3Du/LwBsdHKqVQo9xwG7Z3ZCNkLXqJLzjk4/h9+4623dNQG8Ss1wIM/CSV/kJjNaHf1j8/j1n8e67zuT+Pllh4gL4yrZkIHnYwz53ZhXVookXnprH8bkKnlza7gnghREa6/BJyhr4Uc9KeKWPlZAvyH4X3Ciot51MnS2NjoO/+McLAPJt3crTYAZJKOwBP7FQCRh4hutyXIG1RieTVgijSE5cmRsK4OZgR8hqigC+Vu/0MHDWwLdaNpyuCDRwy+wbwNtON1XXTSYqgYTiMXA7YOBBC4v8GfhHHriAjzyQ7Q41T0xcAOcTsZGDrvnZs2v46pPzsEwD1y3W8ORy3b/4OYlZMIdvrMOMnU/SI54FbKnPBdwak4H/3l1nYrf6jY4L2xWZZfQ/9tAlbLcdGCQHNucF3vpvpmDgc9UCpssFP9hkqYGzO2LQTmAQzq028OL/9El/15cWfM4sTqsSitlXzmg7rn8eJ51P3a68MUU1cG4wxRJMWWXgfW4GHaebqutmwMAVd4vTRcsJNHDeue6EC6XRcSeq+GviAjhPzc76S95o2nj00iZedHoBAHDd4ZonocgTSa3EHHYiD594fCLyNrVfn5NxNfCPP3wJH4spw+fXy4qF/+/PncO1h2u48cg0tgaw43HAzHFQ4Dy31sTJhSqAYNeU5bnCCeyVMceZXdlqoysw9MzOZAklObit1YPvLImBb7ZsdEXYAw4EGjhLMGovlH6sv+24WN5uD8wX+Uy7EDDwlt0NM3Bv57oTLpRGxxmZHD56aRPf/a57d9T6O3EBnJtAZe3v/fxTaxACeNHpeQDAdYtT2G47OLNSB4CIC2U0Bs4BfMFLQK32CQL+WKkRA22948QGfz65spBmnljaxmfPruLbbzuB6bKVK3NJHcBXGzgxLwM4SyhZXlC2d1z6Hbs0YPY6aEcRxfJ2G5ZBoXL34oAkprrWpPMprgoTCDRw/n2laPjvmeQD5+rKrghuOElgqbDoXRuSgbshBs4ulJ1i4KPGljsfW8Jdjy/j0UtbGa8qGRMXwJkJZB3AP3d2FZZBeMFJDuA1AMDD3sSTqTEKeTgYBwxcXiT9WNy4DDzpRPQZeAb+8v993zmYBuFbX3gcUzkH8IYvoSS/R7crcH6tiWsW5OxRvulmea6wfLZaH88yyefE5pCy0/JWG4emwl7t4QJ4/PP8KswEG6EfwEM2wvjXcroCTJYH1Tv4PnCPgZctE+0IA2cXyk5o4I22g3rHgRDDv9eFdflZh91VjYOJCuDdrvA7pmUdLD53dhW3Hp/1Gce1h2UAf/C8DOC1UC+UYRm4PBmKljwRywUT1aLZl8W1lCTmKAmz7XYSA+cy5fEYuO128Sf3n8erbjqCI9NlTJcL2M5TA0/BwC9vtdBxu4qEIo9lli1lO242Egozz2EZ+ErEAw5IOcPpikRnltq6OEkDX/VkligDL0cDeERCiQt0amAfZJf1feARBt5WGfgOuVCEEGjYLrpitB3qM2uyjbEO4AnYajn+SZqlC8XtCvzjMxu47dS8/9jVsxWUCwYeuyy3Q1Nj9EKJSigAcGiq2D+AK0xplIq3JCbhSyhj2hPPrTawvN3GNzznKAD5/WzlKaGksBE+cUXKXacW5M23UjBhUMYM3DuWqwOkgUEIGPjwEko0gHNyPImFryq5lqTAxDvbqAZesgwQBTeBilJKr34OFZ0hAnjAwNXkqMLACyYKxs74wFt2F3y5jEIQuQ/90zqAx0NlElmyqpXtNjpOF6c81g0AhkE4fagGtytgUJCNH2VCth1hGQCwUCv1tZGpF8awGm63K1DvuBCid8vMDHxcDZwdJ8zYpsvWjiQx+wW8e8+swDQIzzshy8yJCLWilenN3smKgXvHZVjnzvJWTAA3BwRwpfgpUQP3rq2oC4WIUCmYShIzKKUH4gO46kkf5ETh54ZL6RUXiqUw8JwDuHqdjeJcurDODLw54JnZYaIC+JoSwLNkVRe9k+wqr1c34/rFKQBSPmHN0TINPyCnRbQSE5A6eH8GHpxAwwZb9flq8Op2ReYBfNob9DxVstCyu7klmtJIKHc/sYLnHp/11wTIY5flucKfb+wkpjP4hhSFEEK2kp2OsGQeReYmySNtv0FV0s5rrd5BuWD4EqKKSiGQ+4JmVuHxZyrU5Obgtsm9pfQdt+ufp6VC4ELJu6WsKjkOe85stx3/3NQMPAFr9XwDOPfqZrAOPqV0Z5O9iYe1EXoaeIiBDxHAh5Q71KCtWqJU3btfSXUabLflyTpdlt/NVA4JQxWNARJKve3gH8+t46XXHwo9XiuZ2frAu1kFcE9CGaJ6dbPloON2sRjVwAcw8LW6jcWZEiyDEm/ccX1QGOWC6Sdb1XaySe+p2gvTauAlpZAHCHIDZctAwdgpBj56AD/v6d/XHa7h4kZzxzonTlYAV7aCWSYxL23IL/+qSABnJ0otFMCN4XuhODEaeK2IlXonMdsdllCGDODteAauPj4uA+cLmgM3B/I8inmEEIGEkhDwPnd2FU5X4Gt6Ani27hiuARg/iTm8Bs51A2ofFCBgr0m5kpV6G4dqxdB8yyi4E2EcWD4EECqlT3pPv7rSNIZOYvJ7bTRtWAbBMoN2snk3swpdK0PKbufXJet+yXWH0BWBnJI3JiuAexdNrWhmy8A3WyiaRk8G/jpFQmHIXiijFvIE1q9DU0V0nG5icGnbrn9SDxts1e9G1fVUJp+VhDLjyRUcwPOwErYdmVwySDKzuJvePU+uoGASbju1EHq8VrQy9YFnZiMcwQceV8QDDE5irtVtzFeLKBfNvj7w6PnPUGWVSoSBx3nBO56Uc81CJUXbZBdFy/AlSmbgG03bfw+/mVXOhTzq9TFsju28ZyG8/Tp5/u2UjDJRAXy1IRvOH50tZ7otvrTRwrHZcshbCwQMnMvoAe6FMqIGHkliAslb8Zbd9ftSDC2htONZt8oqkvzAacEJyylfQil4j2cfwDnoLE6X0PHa+0bxD0+s4AUn5ns0XMnAM7QRKoU8o3iFGcw8t1rpPcfLMX1QgP5yBiB3C4emJANP9oHbPR5wBrNuol6po58GfnKhiq2W0/cG2vEGGkc/y0bT9vX2nWonG75uhpdQCibhttM6gCdi3dvmTWW8Lb7oBfAoZsoFHJ4q+WX0APdCGY2BlyJJTCB5K95yXP+CGpYtq5KLevGoj49bSr/VclArmjA9i9eUz8Czd6LwunkgdFQH32zZeOj8Bm6PyCeAvPlmW8gjXUm2K8ayTXIAd7oi9fFNCuA+A48JcELIHifzVU9CSfSBJzNwDqTVgqkw5eSbBn829uP3S2S2IwGc32tTYeDWDhXyhJL/wwbw9Saumq3gqpkyiqaxY06UiQrgnGipFbN1FlzaaPXo34yf/aZn461fe63/s2US3K4YqrjGjtHA+WJJ8hO37a4fwIfWwBN07yQ5ZRRstxw/aAOBFp4HA+cLiwN4VDf+7JOr6Ar06N9APi6URa8Z2ThecPUGmjaRubzVBlFvsY1v6Yth15tNWTuxUJMSStzNwna72GgOZuDq7qaf7h4N4P2shDzQmBHHwP1mVjm7UEK71SF3bRfWmzg+V4FhEK6Zr+xYMc9EBfC1uo25aiHTxJQQApc2Wzg2Ex/A3/j847j9uiAwxJ1Mttvtu01kzVy1EfoBvB8DZwklIw28kaUG3rZDdr2ZHDVwvtkcS2Dgdz+xgpJl4AUn53r+NutzxXaFv46VMXRwNfClTWQuewSGdz2MgIH3HlPf310rolIwYo87N4hbiLSSZXDgVgN4IKEk+8BPeAG8X1vZRAbecvzPtVPtZBvjJDHXmrh6TrZwuGah6g8WyRuTFcAbcps3VTIzK85Ya9joON1YCSUOcX0Zfu1vHsO3/H93J/5NJyGJCQDLCUGgZbuYYwllyM8a1r0TGHgGEsq0ysA5gOeogfMxik7luefJFdx2ej7E5Bg1ZS7muGDp7AgH8DEYuKodp01kxhXxAEEwjZMzONm6UCuiXIhPYsZNo1fhM/CCEsALfWyEQzDwtu36gVp93ZAGvkPtZJkozFYKQ930O04Xl7daOD4vA/jJhYrWwOOw1uhgrlr0tsXZJKYuJlgIkxDXGe38ehOPXtpKPOi+jdAIvu5q0ZIFEjFBQAiBttPFfFUyouFthKoPvJeBF01jbB/4ZssJMXAuW89DQolq4CpjXa138MjFTbz0ul75BAj6odTH/LxAcNNmBj6OF1yVO9J+Z8vb7Z4iHqC/nKH2OEnSwP1OhH184EDQBwVQbYQxSUxvHYdqRdSKZl8nSseNT2K6XdHjQsm7nWy9I51fcsZo+vP48mYLQgDXzHEAr2K9YQ/dJmEUTEwAl8kYGwu1QqZJzEt+EU8l1fPj5vMxq3niynbs39huF5ZBMCJb36RiHtkkSAb5ohW/7e0HPhGnSlaYgXs3vUNTxbEZ+HbL9qv7AFlynXVymcFrjZNQPv/UGgDpv41DlgVGvJM6OuP1cx8ngCs36NQSynZvIyugvydbZeBJPvCkPigMP4ArfvA0GnjRMnB0ttx/8pQd1sDLhd5/75QLpdlxUCmaQ+fYuIkVM3BuZ7wTOvjEBPBNr5HVvMfAO042Zdt+GX1qCaW3MxoHcG58FYXtdkPbRMahqWJsEGjZgWuln3MgCY2Og2pJdjyM08AXasXULhQhBB72OjKqiEoogCyrz9NGeMQLnGoA563qdUofGxXVDAM4BxCe9jMWA3dcPxmaRkJx3C4ubkinQxT9HCEqA0/ygas6eRxYOqmGGLingce8nt+gyjJxbKY8kIEXYxi4+u+dGmpc77ioFU3USuZQRISbWLEGztq/DuAKfJbgBXAgm7FqlzZaMA2KZTZxiGusw9vhxxMZuAg5UBiyGrOXnQStNGUQHjaAb7cd1IpWj9TU6DgoWQZqJSs1A7/r8WV802/f1RPE4wO4lYuNkD//VMnCVMkKuTYurDdRLvQWYTGmMpRQ/GS0V/Q1XgAP3CxpeoKfX2/CdoVfm6CiXyHPar2NcsHwJbs4HzhfW3PVpCSmfP2QBt7HuugPabAMHJ0p963GbDtuJGj3MnAigmlQ7u1kmx1XMvAhJVouo2cSePKQDOA7oYNPTgBXWAJflNuhwhQXn3r08tCve3GjhaPTpZ7MfhKYDagnLgfDryQE8I7bjQ3gC7VSrAbON4RywUSlYPoDfdOi0XZRS2Dg1aKJcsFEM2Uhz5cubAIIlwbbbhdN2w1p4AByk1B451AtmpitFEIM/MKGzP5Hi7AY7OHPgoHzjs8yyW+FMCradhcz5QKKlpGKgT+5LFvlxu00BmngrG2zhBItHFqt25gqWbFJYP47IMFGGFeJ6UjJ0DQIR2ekhJLYMsIOM3C1bF8N7JZBubtQ6h0HVSY+QxgHLqw3sThd8m84M+UCZiuFHfGCT1wAZxshEL4o/+rhi3jre+/DGe9ET4tLm83UDhRAkVBiNPCvXImXUDpO159Ir4IllOjJza9XsmR3uGETjv6JWLR6KjElE0ufxORdRVwnSLXJFyCdKHn6wMsFE9NlKxTAz69J/20SspyLyUm0os/Ax7ERSuY5Uy6k0sDPeuf1tXEBvE8zq9V62x/hVymacLuiR4qQfVDi2TegauBBALcMgkHJGjgH36Mzsno2abfSm8SM18NlH/78m1lViyamhtTAz6/3noMnF6qagatQtby4i5KrvdJMwlZxcaMVqysmwfKbyysauLdlfGatGesHt91uyAPOWKgV0Xa6PS6TlsLAJYse3oUyVbJQLZk9xTvVotm3qVEUjy/JAL6qDMYNWslGAnjJys1GyGXcs5VCiLGeX2/1DeBZJjFVBp60e0qLtlfAMlOxUkkoZ5brmC5bsVKRZRowDUrwgQcFOhwQo8e+XydCIGDeVYWBE5E3Vi3OheL67WY58ZxUjRlNYsZp4PIz5i+hNDoOqqNIKDEB/MTCzhTzTEwAX1e8qnEXJd/hl/oMSYhCCOH3QUmLQowntdmRjgIhgCeXencAdqKEEl/Mo46TKo8ioXiBWg4zCHvCqyULlYSKvCiEEL6zRmWbzBijEsp0uZDLVJ5mx0XFK+OerQSMtWXLyef9Ang1Uw08qKhN2j2lRdvpolTwGHgKCeXMch3XHa4lSkVFM34u5qrXiRAIGHQ0kdmvE6H6dyojBqRnO8kHzruCI34AjydW3MyKYRikdCZUGf8OMfCSJVsQp5yLKYSQAXw+GsCreGatOdI4xGEwMQF8tS4bWU2XrFhdk4sqlrbSB/DNlpwbmdaBAih9GbpqEtPFrcflFJg4GaXjiFAvcMZhLuaJ3HSiDHxYCWXbY+CVohnygTc7DqoFqYGnec2l7bbPDuMY+ExcEjMPH7jt+uxvRtHA2UF09Y4xcE5iUuLuKS1atiehVAqpGPiTS/VY+YRRtIxYOWOtbvvBmfXlaFJ8EAP3e6FEGoUVzfj35JsTEBRfJQXwaDMrQJ3OEzxeMCl3G2Gj7aJakAxciHT1F8vbHXScbqyE0nFlgU+emJgAvtawMVct+n5jINzykTXaYQL4pYRBDv0Qx8Bbjoubj03DMghfudybyEyWUOI7EoY08IKJhj1c8JFMwpRtd0NN6mVys1IwUw01Vl01qgbOQXoqRkJp2m7mFXOtjusHETWJeSFi34pDlnMxHYWBD2qFMAi+hFK2sDWAgbdsFxc2mrj28FTic+Im07cdF9ttp4eBR3dfa/X+DLwck8QEeABxfD9wn4F7TpskK6Ea7NXXBcJ6uDXCKMNh0eg4qJWs2BxbEthCGBfAAeDplXxllMkJ4PWO36vBr65TGXh9+AA+bBUm0OtJddwubFdgulzAtYdrsU4U201IYiZ0JOSLolwwUSlaaHaGC4h1z0ZYLVk95fOVooVywYTtioGBluWTG49MhQLVVjteQslrKk/Tdv3gM1spoNGRNwm2b10znxzAeS5mFklMdh5ZhtFz7OptBw890+uXj4Pjdv1Kw+mYJGYjsn1/aqUBIYBrYyyEjFJMAF/zdk0+Ay/2BvCW7aLecRNtmEB8Kb18z3gNvOMGQblgGjg8VYxl4G5XwOkKFM3e1wUiDNwYfpj4sGh4NsJhrKdJJIID+FM56+ATE8BXvTJ6IN5ZwBrtMBr4sFWYQG9VWMsPtgZuPDoV6wXvOMNp4MzAyxb7wNMHH8ftou10JZMoykDNF7YM7GaiFhrF41e2USuaePZVMyEGnpjEzGkqj7zxsEVLvsdm08b59SaIghL7JNRKViY1A05EQgGC8+4//9Uj+NbfuTvVNt8fI1YwepKYzY6L23/pb/EHn33af4ydVdceGiChRN6bz6t+Gjg3skrqRAjIwQzPvmoGz7l6NvR43E1Dfj43xJ6PzZb9akUVHeV7CL1uEgPPUQPvOF04XSELeYawngYBPHwOHp+rwDJIM3DGeiPQ6UqWzLqHkpgjaOAXN1ogCrZ5aRBIKPJkaikWtxuOTOOplXpPYExKYlaLJkqW4Y/KYrC8USoYvg88bbKsrnimuXKOWTgXKsQxsTg8sVTH9UemeopWkgJ4Xh0JGx2FgXvFJhteAD86XY6tclVRLZmhmoFREUpievLXynYHzY6Lj3zhAjpOt8e90HZc/PUXL0UeCyoVZ8oFdJyuf848tVrHZsvBXz0U/A0H8NOHq4lrK5pGjyd7VSl+A+IDuN8HpY+NcKZcwF/98Mtxy9Uz4fdM0N3VJCYA3HZqAZ87u9pzXfgFP5Fro2zx4GTVB27k6kLha6RStBSJdvA5s9m0YVAwmYphmQaOz1dwdmU4W/OwmJgAvlq3fa+q3BYHjfp5GwgMr4EvTpVig2sSAgml6783IAP4jUem0I1xonQSKjGJZAVoVELxk5iWiUrRhBDJ8w6jUD3aaiMnIQTqHSmt+BfyAGnm8SvbuGFRBvCtluN/5q2Wg6Jp9BR+8FSerAN4S2HgsxXuH+Lgwnqzh/nEYSqjnuB807ZM8rtJrtY7+NhDF333TfRG8YkvXca/ev/9eHIp2Jlx4OIkpvw8kgk/5TG2z55d9ZONZ5a3sThd6pGsVPA0dxVcIn9I8YEDCElyfifCPgy833vGFfJEde1X3XwELbuLe55cCT1vGAYuJ2Hlx8A5YVkrmkO1X9hoyrbK0T5HwM54wScigAsh5DQe5SSbUkZlMYtYnC5htd6GmzLZcXEzeZBDEvxS+m5MAD8qk0xRJ4rt9mbaGXEl2e0IAwfSD2BgJlEtWQEDbztoO110hbyIk5JZKrZaNi5ttnD9kSlfQ+WLfatl97BvQJVQsi2nbyoMnJnORtOWTfTnk1kpI6sBIHwDK5pGsHuqd/BHnzvnPyf6PjyIW5VJOOhJGyFLQvL37B3uOF3ce0YGvDPL/R0ogAx2nYgevert7KIMvBnLwEcJ4GaPbAOwtzs4319y7QIqBROffvRK+HnKTiT8ur0uFMtMz8Afu7w1tGMlYOCBBp6Kgbccn1REcepQ1b8h54WJCOBbbQeON1WEoU5a4ZPw5mPT6Ir0jfYvbQxXhQn0JjEDtmzg2sM1GNTbE0VKKPH+3bgArjazqsbIHV++tOX74qPgLTw35QEkA1cZBve26BfAn/B2ETccmfKlK15nXB8UIL+pPNEkJiAltQvrrVQMPKv2wxxALFMO4T1UK+K+s6v47NlVf5ht9KLnc1RNJvtJak9CAcIMvFY0UbQM3PnYMoDAA94PcXLGasMGEfzcUVwhD59Hswl9UAa+Z1wzK7eLYqSi8mU3HMKnHr0SkgLVroUqeJ0hDdxIx8AvbjTxunfciT974MJQnyW4PlQXyuBzZqNpY6bSey0AwKmFGjaadk//+iwxEQE8aLYTCeDeRcESxE1HpwGkl1GGrcIEem2EKgMvWSZOH6r1WAnthCQm4DW0ilT0tT2PMBH5214+wYQQ+PbfvQe/e+eTsa/HAaMWYeA+M/dcKOra48AOlBuOTPnSVRDA7djtfF4aeJyE8sRSHR236/dg7oesBoDYTpDEBICFqSI+//Q6TIPwlpeeBtA70IJ/VpOo6g6LL36+6T292sC1izW85NoF3PmVJWy2bCxvd3A6RQDvdaF0MFcp+H1++DtUawB4hzBXGU1CiU1i2m7PjvNVNx/BM2vNELlRpaTo6wJRH7iRilU/dnkbXZHcGTQJHKy5EhMI33STsNm0Exk4N7V6ajU/HXwyAnjMyCe1cRIH+JuOpQ/gjY6DrZYz0MEQRXS8E7MZvjhuPDrVI6F0EnzggGTg0R1D2+n29J/gYLvVdrDRtBPLuOsqk+BsusLAqyUzsaRaxeNL27AMwsmFqp+wY1saFwpFkddUHjWJyZrxIxdlk61+HnBGNSsNvBuebco+/jtuPuIH2Oj78DmqVtNGk5hA0FL26dUGTi3U8IobF/H4lW3c/biUUQZLKL3BdLsdnltatnp3XusN2chqUCI4/j3N+CRmTPvkV910BADw6S8HMkrwPUQDeAwDT+kD51zDU0MmD5t2ID3ydZOGiGw07Z4EJuO05xrKU0aZjABe70201JRp4ysjBPDtBCfFIHAgDhh4sB0GgFOHajgXsUxFs/IqDk2V0LLDMzVbtuuzj8BJEk7SJrkqAgZu+mXkjY4T6ugXJDH7BPAr2zh9uIaCaQQMvNFfQuGimWEZ+BNL2/jHc+uxvxNChGyE5YKUF4YJ4Fl1SYwOp2Z73ne86ESic8EP4MrjbUUiU5OYblfgmbUGTh6q4hXPWgQA/P49ZwEk9ztnFGOSmI2Og2ohOE6WaaBgUjiANzuJDHIQ+hXyRIPy1XMV3HxsGp9SdPBOooQSo4GnLKVnx86wQVNl4KZBKBeMVDf9zVYfBs5e8BydKBMRwKN2KCCsa67W2zANwvWLMomYxgvuM+dIccIgFPyRalEboXx8KmbYhOwHHq+B+wUhCqOWZdZc/SZflwM8TzdpJJxcLBXUQi0HXP/5VcWF0lcD9xwoQPC9r4U08N6Tlqtkh9XA//PHHsXb3n9frFWy7cjpRGpfjNlKoWcKSj/UihZadjd1cjsJzAC5ncJzj8/ipqPT+LpnLSZW7/kSinKzDCptVQbu4OKG7Pl9cqGKZx2dwtGZEu5+YgVEwXY8CXE2Qi5MURGdi7nesBP7gA+CLKWPH6kW15r2VTcfwX1n13y9PzmJyTbCsAsljYTCAfzp1cZQfWqaCsEBwiaJfpAaePIw6CPTpd1l4ER0gog+TURfIqIvEtEPe48vENEniOgr3v/n81pk3NDVKUUDX613/EEPUyUrFQPn4BXt7zAIfi+UGA0cCIqMVM0zaSIPEF/M07K7/g2h4jEofh++OSUlWBoKk0hk4AN84B2ni6dWG7jhiAzgBdPAdNny17iZ4EIBRpvKc3mzhcub7dgq1lbMcWKtfbpkJW5fVQTJ3PFYON+0eTf11q+9Fh//kZfDMo3E2Zv8nnFJzFLBQLkgWfFmy/aLPk4tVEFEePmNkoVfM19J7NXNiGPg3H1SRaUngHdGshDy+qMMXAgRK6EAUmpyugKf8ZKzbf9GlqCBh7oRGikllDoMkjevYYr66kqOCAibJJLQdly07G7fHcypQ9VcqzHTMHAHwNuFELcAuB3ADxDRLQB+EsDfCiFuBPC33s+5YK3RgWlQqHmS+gWvbHd8Jrs4XUoXwL0LrTxsAOd2st3eQh5AujyA4IToeuXCSUnM+ZgA3nbcnv4TPRJKwsm1rTDtaoFbDrihE7Q8wJr41Eodblfg+iPBtn2hVsRaQ3bf227HSyjAaFN5+DN95ivLPb/jz12JMHAgHfsGMFRvi37w28kqnl/uDliyTBRNo+e4bMUwcDV5R0SynL5p+55hHsnFMsrpPhWYDGkj7GXgPQE8MuFpvWmP5EBR3zPOWRJnm33BiTnMVgq+jMI3nOhzfReKysANGlhK37JdnF9v4vkn5gAMJ6M0Igw8jfWUrZ/Rpm4qTi7Ucq3GHBjAhRAXhRCf9/69BeARAMcBvBHA+7ynvQ/AP81pjbKIp1oItdKcKlmwXYG248puahzAp4YL4MNKKETkFRVENHDWrCMZ7GjiK4p5pbKQ0VI0xKiN8IrX3SwpQ97oOKgUpI5nmQZKloFGxwltEfuV0n/l8hb+y8e/DAC48ci0sk5pd5RFQcm5g2H15m5X+N0Y7/rKUs/vo0liIAjgafRvILsA7rhdECFxelOtZPa6UNpxATwsHcx4gzCeWm3AMsj/XC+/4TCI4EuD/RDnQuHeNyqiveDXGzbmRtXAY8aqJQVlQLLoV960iE89ehm221VyAeFr8MhMCdMlK8LAB5fSs3zCCdPhArgDg4J1qzv8JLAUlCShAJKBX9pspZ5BOyyG0sCJ6DSAFwC4F8BRIcRF71eXABxN+Ju3EdF9RHTf0lLvBZoGP/WGm/Gn/+Zlocd8ptv2AviUwsBz1MABr6w3QULxG+F4UoafqEkI4GyNVH3drRgG3uxh4PEnxHbb9QMWENgtVXdKwZTjrtT5iBtNGz/wB5/Ha99xJ+5+Yhk/dMcNeI5SOs1+9a2EXuCMqSFbyq43bThdgXLBwL1nVnuDUCf8/QLBBdOvD3hoTZFjMio6rkDBMJLHt8Vsu2N94BHpYMbrcf70agPXzFf8G8R8rYh3vfk2vO0V1w1cG0soav/phtc+WIU6Tq/b7S2QGwa8flVGURO0cXj9rVdhrWHjH55c8YN9VG75jhedxCd+7Ov8ojkgXSEPB/CXP2sRBg2XPGx0XNSKln9spUmi//nCpGtQAAfyG3CcOoAT0RSAPwHwI0KITfV3Qu6hYm+PQoh3CiFuE0Lctri4ONIiZ8oFf1vJUFnVqtInJbWEMqIGDoTLepu2C8sgn2FXI41w1B7SceDt17rCwNUpJdFKTP5sSWxStsQMPlO1aKLRdv2GWJWiHIxQtowQE/vQ/c/gLx+8iH/1iutx10/cgR977U2hQDVfLWKt3knsg8IYNonJn+cbnnMMjY6Lzz+9Fvp9K+ZGOzQDz2guptOnIAuI333wzawex8C9XRsPdXh6pYGTEbnk6599NNXnjGPDcUnMitILfrvjoCuShxmnfU81eRrIQ/HX1StvWkS1aOJjD11K1MCLltFTYFdIUcjDAfzGI1M4Pl8ZjoG3w99VGuspWz/7a+DyeJ7NSUZJFcCJqAAZvD8ghPiw9/BlIrrK+/1VAK4k/X0eYNvWRtPGesMOJJTpErZazsAtSxyzSws5ny+QUNTXUL3XgNIAKYGRWF6CcF2p1pIMPGjHWTDJ9xFzwGvabqyrot52/ZsIr4cZeMEkn+1Ep/IsbbVRMAk/8bqbYsuqF2oFrDaCAB7nAweGn8rDn+ebv+pqmAbhrogOHtUmgdE18HGthLbbDbHCuPdRt91CCN/u2YwJ4Lwr446ET63UcWqhv9skCXEBPC6JWS4EN+71+uAA1P89zZ73TLIGBu9v4o6bj+Cvv3jJP6ejvVDiYKUo5HliaRvHZsqolSycPlQbjoHb4Z3rVIoWxD4D75NIP5WzlTCNC4UAvBvAI0KIX1d+9ecA3uL9+y0APpL98pLBWjPbybhhz+KULK4YxMJbMdpqWqh6nBpsgcDxwFvmQRIKIBmQKqFE5wSWC0Hi6YryueJ0cDkPU2UScqam2k+EX1P1ga/W21ioFRPlgYWa9KsveRp8koQy7FSepW35etct1vD8E3P4zOPhAN60YySUMkso6Yqw/N3auC6UPslooNd61vDyBUCY/bcduWvjm8FMuYDza01sthzfOzwsOGDy+cbtUWOTmBzAm6M3slLfUy2n75fEZHzjc6/Car2DOx+Tkmq/a4NhmeQbB5JwZrmO67ye6ScXhnN/NNpO6PpI40Lh/jb9boBz1QKmy1ZuTa3SMPCXAfgeAHcQ0QPef28A8MsAXkNEXwHwau/nHQMHKdaWVAYO9I4piyLO3ZAWBdPwk5Oy6CZ84IGA7dkJOp+KuUoxJKGohTwAvJ7gcojBar3jt7+NG/nU8CbP++spWmh0XNkLXGEY0WQWWzGTwFWwvC1NyrzzVJ60zYT4Rrs4XcLX3nAYDz2zHuodEXejvfZwDZWC2XdCTXRNwPg9WmRLhAESitLIS2Vw6ncdLXSZLlv+7wf5vZPAQZADaCNii2OoPnDe9Y0toagaeEKHQRWvvOkIKgUT955Z9ZPtg1Aw+jNwIURo7NzpQzWsN9L3IWl03JD0KNsvuH1nWm76GniyC4WIcm1qlcaFcpcQgoQQXyWEeL7338eEECtCiK8XQtwohHi1EGI1lxUmgIPR0wkBfBADj2N2aSEllMBGqL4GMx72Ywca+CAGrmjgTjfymhYa3gBfICirjtviRcvcq17b3YYd1viiEspqvePvYuLAwZ1ZTWISc4hGQICcKVguGJgqWXj5jYfRFcDdTwQsPM4t9PXPPoLP/cyrU3fQW6gVYRrkF0GNCqcr/DqAOEQTX+rxibpQ4nYUAEZm4BwwO34Aj8/xqD5wrq8YOYAXegN4sONMvq4qRRN3PPsIhEjHvgHJwLsCiQF1rWFjo2njOs+xM2wfkkbHCTl2OL70K3bbbNooF3rbKkdxaqG2qwx8T4K1Zv5iuF+HH8AHMPCm13AnyRLWD5ZBigulG2HL4e06n9D9A3gxYiMMNwNiCYVvStcm9N0Aer2/tZJk4A1vzFr0NRnSipk82IKDJe94ovMwGfx4dExYEpa22licLoGI8LwTc5gqWSEZJS4QqXNR08A0CEenS4lzGdOikzCYgxHddrOUdKhWDJfSO+Hjq7oYRpZQvIAZDeBxSUw+7nzOzY3sQgm/JxBu1NUPb7j1qlTPY/hN5BKcKGeWZRHYdQoDB9InD6ULJZzEBPonvvv1QVFx8lAVz6w1xq4EjsPEBnC+gDmgcL8OqeOm0MBjMvRpEWXgKjs0DUKlEPRp4QRPv633XCXQwB2369nqwqy+aTs+gwwaJ/Wyg6hUUi2afiVmiIEXTH8cHCD7yRzqw2i54OiplQYMQuhkVzFsR8KlrbaftyiYBm6/7hDueWLF//04OyUVx2bL/gi9UeG4Xb+VQhzYO8yFLfwdLE6XQs2sWnY3VKTCW/DDU6XQsRsGUQ088P1HfODezkv22B8vielr4Eo5fZqcDwC86uZFj72mZOCRJnJRcPtjJjfBUOG0DDx8faTpCd6vD4qKUwtV2K7wx69liYkN4LVIEpO3+AXTwEK1ODCANyJJvWGgFvI0IxKKXJvZ40IZlMTcaNrodoUy0Dh4fqVghkqDmV1E2YGcuhPW8rhnTJRhlAuGn8TsOF1stZy+kgTbNM+vNzFVshKTncNO5WEGzrjp2BTOrQZspWW7IOqfFEuDY7Nlf4j1qLBdgYLVXwPviuCmw9/BkZlyTzvZEAP3WNzJheFaG6uIBtNAA+/1gXeFJBZrjQ6mStZQE6lUxNsIB2vgcl0WXvecYzg8lW6coT9IJSGAn1muo2CSP+C6UjRxdKY0BAMP71DVPkJJ6NcHRcWpHLsSTmwAL1oGiqYsXpitFEIn4eEU1ZhNe/QArhYVtOzexj3VouVvmdMkMWcrBXQFQvZH9abA5c/MwLk4IOqqaDuyYZPKuqoe49puh5ObahKTtdB+AXymUoBBcpJ4v9Few7aUXdoOB/Cr5ypwuiKwS3o32qQbRlocm6ng4kZrqAZHUdhu1x9qHYdoApu/gyPTJXTcoMGZbPakJjHl93kqRcl8EkoRBt5IcFmp4/Q2xmhkJd9TvlY7TkIZoAsDwC99y3Px/u97Sar38gepJEgoTy5t4+RCNZQQPTVEGXtUekwzF3OzmTyNR8XNx6bx8998S9+ZpqNiYgM4EFj2olv/NNWYLXscCSUoKmhHHCNyXYGdTB2EmwS/GrPZ8WUN9QLnYLu03cJ8teDvNqLsQJ2H6a/FC9rL2+3QCaomMbkTYr8Abhrkr7NfC17f8ZGCgbOrZnEqsANe7Q3YOO9tNxt2r5d5FFw1W0aj4w7lUY/CdpPbAgPKRe8Fbp+BR1xDUZsoSyjRYrVh4DNwNyqh9NoIAUlg1pvjBXBftnFjJJQUO6Zq0UqdiOYbZz8GHnUlnTpUTTVU2PV2vtWYJOZgDXyw5DVfK+J7X3Ytrkkx/m9YTHgAl19e9CRIU405DgMPF/L0vk7N050BoOOkcKH4Y8Js31Pbo4F7DHxxuhR0voucXHEJP+5IuNVyQo+rPvC0cxH59/0C+PQQczH5xhFl4AB8uaPV6ZWoRgFX9l0eQwd33EEulPC2OxrAOai2HTckMVw1U0GtaOKrT86NvDbfRmhHXCiF8LFistG0Xaw1OiNN4mH0lVDGlLyisPxRhr0M3O0KnF1p4PrF8A7m1KEqrmy1B07WiZOb0tQOpNXA88REB/CpAQG833Y5rsw4LaQLJSiljwaYasnyNfCg30Pyhc8J2PWmrczD7JVQlrbbODJd7nG6MNRe4AxV16uW4iUUf3r5oADuM/Dkk3Z6CAlF9YAzeMYlJ3zGudGq4AB+cYwAPqiQpxZJfG23HRRM8hPAfHyiEspstYCHfv4b8EqvCdMoiFZiqq0TVKiNzMaXUJJthFkHcJZQ4lrKftbroROdWsSS1CALnzqtilEb0D+n2xXYTKmB54mJDuCJDHyqhLbT7btdbo7B7HpL6cNf45QyLSg6xSUOs5WgoVUrxoblSyhewi/qdGGo8zAZITau6uoFE05XSBnDk5sGMXC+0fSz8PFUnjRFM1yFeVjxn0+XZeXahXX5u+YYUpeKY97ovHGcKGkKeYDgOGy3pCefb7gBA+/Nmxgj2FlVRC19ST5wdZzeuBIKO2nCNsL0EsowYAnFVTRwIQTe9Zkn8ebfuxfH5yp41c3hGyDnigYlD+O+q0ESSt3rI7PbDHw0z9IeQT8JBQB+4xOP4Zr5Ko5Ml/C6W4+FgmhrDG2VA7gQAi2nlyHGJTEHFfIAUkJZnPYkFCssoThdgcubLX87rjpdGOpEekYomKsMvBgwsdV6JzS9PAlpJBQikp+/z7g2RhwDB2SXQV8DH8MtpIJnn47DwJ1ufx/4VGTbzZZOPs84GERdKFkgaiNMqjTmnxttF+tjSigs26h9h1gCTFugkxZ+EtPb+XacLv7NB+7HJx+5gtfechS/8s++quf8PbUgGfigToB8XKIVzEByEjNNH5SdwEQHcPZqRgP4rcdncHiqiPf8/Vn/sfe99cX4umcF3RDHc6HIAas87qsUo4H32AgHuFAATwOPsREya7Jd4Qe7fq1LExm48u+SwsRWvDL6QUVN8ykkFKC3yjMJHMCjVrKr5yq+hNKy3dSJrn4oWgYOT5VwaXN0K6Htir5l31HnwlabGbgXNG0liZmygCUtojbCppdcjzJ7vnEvbbfG6kQIyKBaMCncadGbxjOuayiKaBLz3jMr+OQjV/Bjr3kWfvCOG2Lfb6ZiwTQoNCwlDnGdSZN2uQx/mINm4KOD75LREvAbjkzjvp95DdyuwBcvbOCf/Le/DzWLAiRDGVVCkf3AhZ+86fWBW0ESM0UpfcGUpeTrzY7SYjNcSs/wA3jMxBC157e6luB1whIKIO1ksg/K4BMxDQPn92mmaBy1tNXGTNnq+f6umi3jC15b2WbHRWVufAbOrzuWBj6gnWwt4kKpt8MSCnvBk2ZGjoMeG2GkJw6Djzt/D6NWYQJytzVbKYSqiOMGGmcBK2Ij5CKk1996LPFmwesbVBUcx8CB3u6SKjZS9EHZCewTDTy+GMA0yNc+o5rsODbCokXouF1fr+5xoSjTgtIU8gCShW80giRmtJkVY1GVUCIJFj+bXopn3VEfOBAw8EN9yugZ8ylshPzaqSSUiAeccfVcBWsNG82Om1kSE5Ayylga+IBKzGrRBJGigbcdTJUVBt5RJJScGLgqocR9b3yzvOjlGNLcuPthphwOkHncnICAADEDTzMNB/Cuq2Z/MpFkuZwqmYmDU/j9d1sDn+gAzlvWfu6JqZjSbtvtwnbF6BKK1xktOpGeoTa0Cnqh9N9SzlULWG/a/hZYZaXqv49MyxtSHDvY7uMDl/9WnS2KnUwZSdcPaRn4MBJKXADnSTsXNppj3WijuGq2PFY/FGdAJSYRoVYMagCCJCYHcFnCHlf8NS4sg0CkulDiczz8XQYMfMwAXin4XfkAeQPJhYH7pfTy8wXzKPuvf6ZshXYIcYjbuQLhXFYUe0UDn+gAzkxzvk/wYVeEamuLm3Q+DLgfeFKfDtVDag+Yo8iYrxalCyVGloll4DESSqPthub6AWE2rgZCdbCxOpKuH7hM+dhM/5Jv9q0PggzgvT292Qt+Yb2ZWRITkFbCdY/Zj4LOgEpMwOuHojJwVULpuH1nRo4DIkLRDKbExw00BoKdF+cCZsdIYsq/DwfwPBK0QFBKzz3BN1s2Cib1kKcoojeYOCRZLvvNd/Wn8Yx5AxwXEx3Av+5Zi/jWr77Gl0niwJ3rQr2Zx5jGA0g5xO52Y+UOINxHgTvYDUrqzHotZVu+Bq7YCL0Tq2QZfuVXnIRS70jXg/peRdPw2Uu0Hzggt/Vrjf6NrBg3Hp3GJ370Fbj9uoW+z6sU0rtQFmN6YbAX/PxaMzMbISAZOICRWbjjioE7qVrJ9KfwcAAvFwwQyUCRV6ELv6bazCrue+NznqWksSWUiAbecbqZWwgBxQfuM3DZCXDgdZUQwL/zf/4DfvWv5fDueoKEIp1eyQGcSE7u2U1MdAB/ztWz+LVvf95AdjtdLoQ08HEGGgPedBBXxPYtAQLWW+84sB2BUgpL1VylECrkCfVC8f7NbVeBZBdKdBsobX1m6HXU17+0Kd0IaZ0eNx6dHnjRpJFQ6m055i1OQjk6UwYRcGalDiHG70TICIp5RnOi2APayQIBA3e7Ao2Oi6myvKFWC9KZ5A/9zegzqShaZsDA7fgkpmlIpr7sVcGOq+HOeuPgGNEipazAOx/b18CdgVIe0HuDYTxwbh3vu/ssmh030XIZjRsqNlsOZsqFsf3742KiA3haSAYeHEQ/gI9ciWnA7YpEJs8adKMtk5hJ8zBV8Fi1pu36U+MZvM4jSrDjWZdqtWm944YkE/+5JSv0fyA4Wc973RyzsOoxqgVzYPkyD6eIC+AF08DR6TKeuFIPrXVc8E7t8ggMXAjhDXTofyxrJTlSjpkbnwtVry970OwpXwbeaCfXOfCOcbpkpZqG0w8zZRkg+TyUEkoeSUyuxJSfb6uVrgqSXSjqddJ2XL8vzkcfvOCPU4sG4/lqAWsJFkTZiXD3TXwHI4CX4yWU0V0o8mvjSs9yTzfCoKR6kPWMMVcpoiuAle12THfDgIEzal7r0pbSh4Jta1Hw30ebWQFB06gsA3ilONiF0i+AA1JGeWJJNunPopkVMF45PTO/4kAJRZ5r25Hhz9yXPU8JpWgZSjvZ5ADOx36uNr5+O1spwO2KoHVEThJKtJ3sZsphCrOVAmwlXwUgNP3qg5992hto3PtdzVWL2Gw5saPcNpu73wcFOCgBPDJsNm5M1zBgTZkv0uiNoOYnrRx0nMHbbiBIhlzabPW6WryGREeUhF+07waQzLpqJasnuVnOk4EXzVB1Xhz8KsyEftBXz1X8HhZZaeDVooXZSmEkKyEzv0GMlYc6bEeKqrg6tR3T6yYrhBh4gg8cCM77caowGRzEWGfOT0IJN7PabDmpGDAHeVVG4fbJLzg5h88/vY4vPL0ee47xNbEeI8GkncaTNw5GAC+Hh82Or4F7DNx7zZ6A62vgMomZpqyYOxJe2exl4JWiiXLBCI3bUm8SjM2WHVslWS2aqBbDyU3+7FzxmMYHnhbVognbFbGd4xhJZfSM43MVf6hDVho4MHoxj52iqyTAGrgbWDrLUQaebuTYKChaRmAj7JP85e9zXAshEPiwOUB2nOyrTAHFB94dnoHL5wfXCVdm/suvvQ5F08AjFzd7ckdA8P3EySh7oRMhcEAC+HTUhTKmBs7baE5wRCUUtalRmsQXEFgh4xh40TLwlz/0cnzPS0/5j0WHBwDAla12SCf3n1u0epg56+zsyJjPYDvNKPsOl2QWvrTVhkHJzJ+thEB2GjgwejEPVwAOdqGEJZTpkITi5iuhmAbadtevc6gmfG++hDJGFSYjjoFn3QcFCCoxfRfKEBo4EGbgLKFct1jD6249BiA+FvC5uRYz2V4z8B3ElJdYYowtoXgnKAfPKEMsW7Iir9F2YLsilSY4p5xocdvr6xenQu/DEoo/JMCRfu6jMZbKIzOlHqZLJHs9dIUMMllu6aPd9+KwtN3GoalSooOILX/y9XafgTspWiIAsnqv43T9bXpNDeBtNYDn4UKRDDxpoDEjkFAyYOARiSK3JKbiQmk7Llp2N9UwhbgAzsdmvlrEd7z4BIDeIh7+vfp8FZtNZ9c94MCE90JJi6my7M/tdgVMg3x9dpx+4EBQThtlU4YR2MbSJjHVk2FQcQLQy8BZkjg608vAf/J1z4619ZULBrbbSFXEMww44PazEiZ5wBkqA89SQjk2W8ZKvT10so3lIGuAbYyPCztdeDdWK1po2I7S6yYfF8pWy0kcaMzIUkLxGXiLB5jklcQMXCi88x3UVA0IepXEMfC5agEvve4QnnV0yk9wq+BdcVRC6ThdNG031Q0kb+z+CnYAapvPmXIh0feZFr4LpeWgZPV2fAMCn3bqJGZFDeCD1xUUC8mT+bI3L/NIDAOfrRYwi96Tnd9nPoOttAq+MfazEl7ejC+jZxxXJZSMGbgQwJWt1lAjrtJ0lQTUAC6PB597FY+Bt5xen39WKHpJzKSBxgyfgWcooWzkncRU2sluDtFIKirxADIgVwqmfww+9K+/JrbHDRc5RSWUvdIHBTggEkp0SkxSCXxacFHBdstJfI2aN5Wn46ZjJCXL9C+4NBeAL6F47porHuM7GlOangS+kNNUYQ4Dv1FWHwnl0marbwXtXLXgv06WGvgxb+bmsDo42wgHldKz5s25BdWDL10o+TFwWcjjDpRQyhlKKJykzT2JqbSTZQaeRoOejnWh2KEK1JlyIfa7qhRMFC2jR0IJOhHqAL4jmCrJL5rlhqbtomgZAys4k8BsYNsrAIiD1DzTJzGB4IJKc2OJ9p6+ssUMPL2bhE/aLC2EgNLMKyGA224Xy9vt2G0rg4j8kvosNfBjIw52CAZzpJRQNlooWYZ/8+apSn6rhByCHNsI4/pbq+BGZllIKKZBmC5b2GzacNwunK5A0cx+d2EYBIOkhJK2E6G/vpIVkVA6qXYfRISFarFHQtnUAXxnMeUP2pXBrtWnyCENioqNMEmv5m6BtjO4fwZj1jup0gTwasRGeHmzBcsgf25lGvD7ZK2BVwYE8CtbbQiBvgEcCHTwrDVwILBPpkWayUpAEMAvbbbCXSG9HRPrrzuRxNwJCQXwWso27aBRVw43J0CaB6SEkp6BA15DK8VGvNpI130TkDe5RAauXSg7gyhbHbfDnaXYCBMllKJsNmW7XRRTXqy8rUuzvS5aBoqm4RcoXd6UFsJhejPkJaHwzSWpmIfli34SCgBcPVsBUbZyw0zZwomFCj53dm2ov2P/cRofOCD9/FNKkqvifSc8QDo3G6HTDbrrFeI14kqGSUwgKFfPa6Axo2AQHFdl4OlSeNGGVutDDHNeqBVjNHDHf93dxoEI4HEa+FgB3AhK6ZMCeNVj4J2ULhQguKDSMs6qMjz5ylYrNoHZD/wdJA3EGBWDJBR2aAxi4K977jH889tOZDqei4jwqpuO4O8fX/aLatKAh1NbKboRArL1bLgve5SB5yOhtJ3BDLxczE4DB+BP5clroDHDMg04XSWJmZqBWz02wrSJ+/lqsY8GvvsekAMRwAMGLr/4lj36ODVATuQBZMImSUKZKlp+M6u0hQ3cmzntFpQbWgEyKMZZCPuB176QYRGPfN3+LpSLKRn4q246gl/+1q/KdG38uk3bxb1Prqb+G9tn4P0DeFg26R1nt1rv5DIzEgg08EEB/Buecww/eMcNmeU+ZioWNptOrm0CAPnd2x4DNw1KLYNKBi7PRbcrsNG0U7fRna/1NrQa9gaSJw5GAI9o4M0xJtIDYSdCMgM3h7IRAgoDT3kBqMMDLm+2Y4t4+iFIYubDwJNcKJc3WyhaRmZb+GFx+3WHULIMfPrLV1L/DTPwtBo4EDhSgEBWWm90cpMYmPmyxJDkQrl+cQpvf+1Nmd1EmIF3XDe0jqzBs2g3m7KVbNr1q3M7N5s2hEiv/89Xi9ho2n5bBwBY2e6gWjRzsYIOiwMRwNkzHdLAxwngCgtLCrbMjocK4N6WNi0Dr5ZkeXbLdrHRtIcO4OWcNPCCaaBgkj+FPYpLGy1cNVvOhYWmQaVo4qXXH8LffXkp9d/4zawG2AgLpuEH6DgGvtaIr7TNAhw4WaZJKuTJGtxStmXnq4FbJsH2XCjDsF9eHxBUVabdfcxXZZdQVUM/v94I1SnsJg5EADcNQq1oBhr4GBPpgfCA4qQbQbUky9TrHbfvHEUVozDw7bYzsDFUEgINPNsAzq+dxMAvbbaGvtlkjVfddARnlus4s1xP9fwOt5NNcSxZRgknMb0AXs+RgZscwDtj2WSHxWylgKbt+rvB3JKYpuH7wIfRn3l9aouDtLs/7hGk6uDn15s4Pq8D+I5C7QneGjeJaaoSSoIGrrCvNBN5gGBblzqJWZQyDScFhw2Kr77lKN76smsz9VkzKn3mYl7a6F/EsxN41U1HAACffjSdjOK46Rg4EDDv6VAS05Px2k5uNjue8rPWsHM5pkngNhBLXo/3/CQUkj7wIRtJ8fo2WzbW6pJJD5PEBCIBfK2pGfhOY6pk+QMYxtfAA2aTtB1Wt69DSygpL4Ca17qUy7aHTWJ+9cl5/Nw335KLlFEtWrESihAClzZboWZVu4GTh6q4frGWqIP/wAc+j5/9s4f9n30feKoq2d4JSOr5lpuEojDwuOZMeYGDKe8E8/p8vg98BAkFkO4RtZFVGvgB3Av8coasrRn4TmOqXPAllMa4EkrMwOEoasrjaS56QBauGJSeSfPwgMsjlNHnDSmh9LpQ1hvSL7zbEgogWfi9T67GumUeOLeOL1/e8n/mUvpCClmCmbe6C6uqO7Kck5jrDTvT/jGDwH7oIIDnJaF4PvDm8BIKIAO438gqpfOK5UX27/MAFM3AdxhqT/DWmJPOVQaemMQsDc/ATyxUcddP3IGX3XAo1fOrRelCubzVQtHcPVdHHKoJg419C+EuM3AAeNXNR9Bxu7j78ZXQ40IILG21Q4HdSVmJCQRecDWAhwdK5yShcABv7qyEwsE07wAuJZQRGLjS0Gqt0YHlldenAV9T614Af2ZdB/BdAfcE52b3eWvg6oy9QXMUVVw9V0ktaUyV5OSbZ9aaODJT2jVXRxyS5mKOqtfngRedXkC5YOCeJ8MBfLMpC7Dqyhg+v5lVimNZi0limgb550reLpSNhp1pA7BB8Bn4dv4SSsuWzbrStJIN1hc03Fpr2JirFoe4xixYBvnVmD4D1xLKzoKTmK0xx6kB4WKORB/4CBr4sOD3OLNU3xMBUUWSC4W79O22Bg7IgHd8rtLTF+XKllyjOu0omMgz+FhOxUgoQHC88pZQOm53hxl4MA5QXUfWKJiEFa+oZhgJJcTA653URTyArNydrwUNrc6vN2EZFJpPu5sY+E0T0e8R0RUielh57HlEdA8RPUREf0FEM/kuc3xMlSxsteyxJ9ID4Ys46UagXrx5ndD8HmeW67Gj1HYT1QQGfmmjBaLhLY954dhs2b+pMLizY0MN4ClnYgLxSUwgSGSWcmLH6o1hpzzggJLE3M5bQjH8QDpKEnOz5QxVRs+YVxpanV9r4qq58o5ZNAchzTf9XgCvizz2LgA/KYR4LoA/BfDvMl5X5pj2GPi4wxyAiAslQUJRGVBeDJwDRNN29x4D96awR3Fpo4XDU6XcvpNhcXSmjMuR1rKs5dY7LrpeBZ7T7cIgpLpwfRthOSGA5xTgVOliJ5OY5YKJkmVghQN4Thp/wSR/QvwwrVx5fZzEHDZXNF8t+i6U8+t7x0IIpAjgQog7AUSbRjwLwJ3evz8B4FszXlfmmCpZ6IogmzzOCU5EfhDvN9CBkZuEoujsw/QB3wlUi2ZsN8K9YCFUcWymjCtbbT9QA4GEAgTDPzpuN5T76Ic4FwqwcxKKfK+dLfOeqRTAX2EeQ40BycCF9x7DjjObrRSw0bBHZODFEAM/Ppd+klPeGPWb/iKAN3r//jYAJ5KeSERvI6L7iOi+paX0pctZgxNKzK7GTfJwUE56nZJSCZemem8UqAFiL1kIAZZQHAghQo9f3gNVmCqOzZbhdAWW623/MdZygWBkneOK1IHp9usO4bW3HO2RiQIGnq8PHNhZBg4EiUzToNQ3umGhJpCHHabA/VDWG3ZqCyFjviYDuO12cXmrtWcSmMDoAfytAP4NEd0PYBpA79hmD0KIdwohbhNC3La4uDji240PDnZ+AB/zBOeTKYmBEwXd0vJLYgbvvZeCIiC/l66A32KUcXEPVGGq4O/t8kYQtFnLBYJEpu12UzlQAOC518zinW++ree4+ww8J4khxMATeoHnBQ7gebFvIHwdjRLAL2220HG7Qw09AVgDt3FxvQUhgONze+f8HenbFkI8KoR4rRDihQA+COCJbJeVPViPvJIxA+/n6eVquLwCeIiB70EJBQh3JOSmW3vBA87gm4mayFQZOOv4tivGPo75a+C7KKGU8705AeHcUzS/MAgzlQLOrsi+N8NKKAu1ItyuwKOXNgFg8iUUIjri/d8A8DMAfifLReUBnouZFQNnK2G/GwF7wfNyoag6+7DDHPKGH8AVHTztJJ6dBN9MQgF8q+UnulQGnqYKsx/4fMjbBw7snoSS180JCOoviGS//WEwWykEVZhDJjG5R9HDF7wAPkkSChF9EMA9AG4iomeI6PsAvImIHgPwKIALAN6T7zLHR4+EMiYD56ZG/UryOcDmta2sKUmxYZM6eaPiz+xUAnjKSTw7icNTJZgG4dJG4AVf2mrj9KEaAFUDT5/ETAKPONuvSczoGrIGk6bpkjXU6EAgPP5sfsjumzzw5OHzGwD2Rg0DY+BVL4R4U8KvfjPjteQK3nKxvjluM3Y+mfptGfPWwMsFw++dspeqMIHgBqlKKJf2UBk9wzQIR6ZLuORp4C3bxWbLwbWHa3jg3DrqIQklIwaelwZu7l4ADxh4fu/LpGmUafAqwRmmkAdQGPj5DSxOl/bEIAfG3jDj7gB8Bu6xwHFP8EAD78PAfQ08n+BKRKgVrT2nfwPqXMygGMZn4HtIQgE8L7i3Nt6hRRm47aYfzJEEljXS9nsfFkTkM+DKDhbyAEGxTJ4SCl9Ho4wyU4N+2mk8DNbMr2y195QHHDhAAZzljKwYuDXARqi+Z55FK9Nla885UIAgWDUiGvh0yeqpUNxtHJsJqjE5yX3tYvYBvJazCwUIes/vFgPPU0JhF9Aow4RVCWXYYc6qa2Uv6d9ACgllv6BoGf7U7iymlRRMgmlQ34s67yQmAPzStzwXV83urZMKCAJIKyKh7CX5hHFstoy/f3wZALDkFfFc6zNwuX6nK1LbCJNQydkHDnjnWnv8HM+wmNmJJCZLKGMw8JmyNXQuY7pswSCgK4Br9hgDPzABHJAHor3dyeTkLpgGygNOVvb95umNfaU3WWavgX3I0STmXgzgR2fK2Go7qLcdn4Efmy2jZBmod3Jg4DkGOX7tnU9ieud6jjcnX0IZQQNnBj5sAhMADIMwXy1ipd7B1XssgB8YCQUIdPAsTm7LoIFWLV9CyfGC3asoF+VnViWUvVaFyTg2K3MIlzZbuLLZhkHS+8szR4Fskph83uW5I+PX3mmZaidthMN6wIFgfcPq3wy2HmoNfBfB5fRZMfBBW+HnHp/Fc66e2fHt7F4A7z54Ko/bFbiy1d5TFixGUI3ZwtJW27cW1kqW35HQyYCBv/jaBXz/y6/F80/MjbvkRARJzJ0u5NkJCWX8JOawDhQGT+bRGvgughl4FjaggkkDJ6u85pajeM0tR8d+r0lEYCOUpfTL2224XbE3GbhSjXllq+U3BqsWTWy3uZmVSDXQuB9qJQs//Y23jLfYAeAAXt1h0sCDg/P1gY9uI/QllJEZuA7guw6uxsyCnVx7eCrXk3XSYRqEkmWgYUsGy6PU9iIDV6sxr2y1/d7qUyXLt0E6bje3pmRZomSZKJpGbg2lkjBVlIm+XH3gvo1w+LBVK8rvZWEEDRwAjkyXMF8tjMT+88SBCuCsnWWhgf/cN+fLpPYDKsVgKg8X8exFBl4tWpguW76EcuvVswAkY+ZZiLbbHZuB7wSKprHj8gkgE30nF6q5evwLYxTyEBF+600vwHOuHm32zA/ecSO+7bbEpqu7hgMVwLOUUDQGo1oIpvJwqfpeZOCAlFEubLSwvN32JZRaycQza2oScwICuGXsuAOF8ZF/+7W55nusMQp5AOB1tx4b+b2PzZb3pIPqYAXwDJOYGoMRYuCb7bG2sHnj2GwZX7qwia4Ixr3VlKlC0ka49yWUcmH3AvjsCMx4GBTGcKHsVxyob4IZuA7gO4Nq0fK7EV7aaOLobGnP9WxhHJ0p4zNfkcU8rIHXFBuh050MBv62V1zvz43cb3jFjYv4oa+/Ec++as+P4N0xHKgAznfu3dAIDyIq3lQewCvi2YP6N0Nd26I33ahWMlFvy6lCtpN+oMNu4oWn5nd7CblhtlrAj73mWbu9jD2FvU8pMoTPwHUA3xFUCuEk5rE9WPLPUPVNlYHzVCG7O74PXEMjaxyoM1JLKDsLORfThRDCY+B7r2siI8zAAxshIIc6ZFGJqaGRNQ5WANdJzB1FxQvgG00bLbs7EQx8pmz5LiWuJt1uOXC74xfyaGhkjQN1Rk57hTxlLaHsCKpFEy3b3bN9wFWwP12dJD/ldZNcb8pRXLpwS2Ov4UCdkfPeaKRh+wFrjIaK5wO/uAcn8URxqFZEwSQcmQ7WyAx8wwvg1pgtiDU0ssaBcqFcM1/FH3z/S3DbqYXdXsqBQMWzEV5c3/sB3DAIJxaqOLkQTBznjn5cjamTmBp7DQcqgAPA11x/eLeXcGDABSVPrdRBFLg79ire9y9e7CcugSCJyQxcJzE19hoOXADX2DlwAH9yuY7DU6U9z2BPKOwbCNa/VucAvrfXr3HwoM9IjdzAbp8zy/U92wOlH5iBrzelhLLTHf40NAZBn5EauaGiSCh7sQvhILAGvtHQEorG3oQO4Bq5gSUI2xUTycCLloGCSb6NUEsoGnsN+ozUyA2VQpBimUQGDoR7gusArrHXoM9IjdygtjWdRAYOyJayzMAnoZmVxsGCDuAauUFtGraXqzD7oVYyse5p4EXNwDX2GPQZqZEb1J4zRyeVgZcsXYmpsWehA7hGbqjuAwY+VbLgdgUAbSPU2HvQZ6RGbuBeItNly7fkTRrUm5CWUDT2GvQZqZEbygV5ek1qAhNA6Majk5gaew06gGvkBiJCpWBOrIUQkC4UhrYRauw16DNSI1fMVQs9PUYmCSoD15WYGnsNkylMakwM/uebb8ORPTxKbRB4qAOgGbjG3oMO4Bq54tbjs7u9hLFQLWoNXGPvQlMKDY0+UPuDaxeKxl6DPiM1NPog7ELRl4vG3oI+IzU0+qAa0sC1hKKxt6ADuIZGH6gSSsHQl4vG3sLAM5KIfo+IrhDRw8pjzyeifyCiB4joPiJ6cb7L1NDYHbAP3DQIhu6ForHHkIZSvBfA6yKP/QqAXxBCPB/Az3k/a2jsO9Q8CUU3stLYixgYwIUQdwJYjT4MYMb79yyACxmvS0NjT4CTmNqBorEXMaoP/EcA/DUR/SrkTeBrkp5IRG8D8DYAOHny5Ihvp6GxO2ANXHvANfYiRqUV/xrAjwohTgD4UQDvTnqiEOKdQojbhBC3LS4ujvh2Ghq7g5JlwCBdhamxNzHqWfkWAB/2/v3HAHQSU2NfgohQK1k6gGvsSYx6Vl4A8HXev+8A8JVslqOhsfcwVbK0hKKxJzFQAyeiDwJ4JYDDRPQMgH8P4PsB/CYRWQBa8DRuDY39iGrRBJEO4Bp7DwMDuBDiTQm/emHGa9HQ2JOYKlloO93dXoaGRg90N0INjQGoFi10hbPby9DQ6IEO4BoaA/C2V1yHRsfd7WVoaPRAB3ANjQF41c1HdnsJGhqx0N4oDQ0NjQmFDuAaGhoaEwodwDU0NDQmFDqAa2hoaEwodADX0NDQmFDoAK6hoaExodABXENDQ2NCoQO4hoaGxoSChBA792ZESwCeGvHPDwNYznA5k4KD+LkP4mcGDubnPoifGRj+c58SQvQMVNjRAD4OiOg+IcRtu72OncZB/NwH8TMDB/NzH8TPDGT3ubWEoqGhoTGh0AFcQ0NDY0IxSQH8nbu9gF3CQfzcB/EzAwfzcx/Ezwxk9LknRgPX0NDQ0Ahjkhi4hoaGhoYCHcA1NDQ0JhQTEcCJ6HVE9GUiepyIfnK315MHiOgEEX2aiL5ERF8koh/2Hl8gok8Q0Ve8/8/v9lqzBhGZRPQFIvqo9/O1RHSvd7z/iIiKu73GrEFEc0T0ISJ6lIgeIaKX7vdjTUQ/6p3bDxPRB4movB+PNRH9HhFdIaKHlcdijy1J/Jb3+R8koq8e5r32fAAnIhPAfwfwegC3AHgTEd2yu6vKBQ6AtwshbgFwO4Af8D7nTwL4WyHEjQD+1vt5v+GHATyi/PxfAPyGEOIGAGsAvm9XVpUvfhPAx4UQNwN4HuTn37fHmoiOA/ghALcJIW4FYAL4DuzPY/1eAK+LPJZ0bF8P4Ebvv7cB+B/DvNGeD+AAXgzgcSHEk0KIDoA/BPDGXV5T5hBCXBRCfN779xbkBX0c8rO+z3va+wD8011ZYE4gomsAfCOAd3k/E4A7AHzIe8p+/MyzAF4B4N0AIIToCCHWsc+PNeQIxwoRWQCqAC5iHx5rIcSdAFYjDycd2zcC+H0h8Q8A5ojoqrTvNQkB/DiAc8rPz3iP7VsQ0WkALwBwL4CjQoiL3q8uATi6W+vKCe8A8OMAut7PhwCsC+GPgd+Px/taAEsA3uNJR+8iohr28bEWQpwH8KsAnoYM3BsA7sf+P9aMpGM7VnybhAB+oEBEUwD+BMCPCCE21d8J6fncN75PIvomAFeEEPfv9lp2GBaArwbwP4QQLwBQR0Qu2YfHeh6SbV4L4GoANfTKDAcCWR7bSQjg5wGcUH6+xnts34GICpDB+wNCiA97D1/mLZX3/yu7tb4c8DIA/4SIzkJKY3dAasNz3jYb2J/H+xkAzwgh7vV+/hBkQN/Px/rVAM4IIZaEEDaAD0Me//1+rBlJx3as+DYJAfxzAG70stVFyMTHn+/ymjKHp/2+G8AjQohfV3715wDe4v37LQA+stNrywtCiJ8SQlwjhDgNeVw/JYT4LgCfBvDPvKftq88MAEKISwDOEdFN3kNfD+BL2MfHGlI6uZ2Iqt65zp95Xx9rBUnH9s8BvNlzo9wOYEORWgZDCLHn/wPwBgCPAXgCwE/v9npy+oxfC7mtehDAA95/b4DUhP8WwFcAfBLAwm6vNafP/0oAH/X+fR2AzwJ4HMAfAyjt9vpy+LzPB3Cfd7z/DMD8fj/WAH4BwKMAHgbwfgCl/XisAXwQUue3IXdb35d0bAEQpMvuCQAPQbp0Ur+XLqXX0NDQmFBMgoSioaGhoREDHcA1NDQ0JhQ6gGtoaGhMKHQA19DQ0JhQ6ACuoaGhMaHQAVxDQ0NjQqEDuIaGhsaE4v8Hw4/z8hy58bUAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "key_list = [key for key in I.keys()]\n", + "\n", + "plt.plot(I[key_list[0]])\n", + "plt.title(key_list[0]);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/conf.py b/docs/source/conf.py index d44492a..0d72cc9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,8 +1,10 @@ +# -*- coding: utf-8 -*- +# # Configuration file for the Sphinx documentation builder. # -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- @@ -17,17 +19,21 @@ import os import sys -sys.path.insert(0, os.path.abspath("../..")) +import btrdbextras -import sphinx_glpi_theme +sys.path.insert(0, os.path.abspath("_themes")) +sys.path.insert(0, os.path.abspath("../..")) # -- Project information ----------------------------------------------------- project = "btrdbextras" -copyright = "2020, PingThings, Inc." +copyright = "2023, PingThings, Inc." author = "PingThings, Inc." - +# The short X.Y version +version = btrdbextras.__version__ +# The full version, including alpha/beta/rc tags +release = version # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -36,18 +42,39 @@ extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", + "sphinx.ext.todo", "sphinx.ext.githubpages", "sphinx.ext.intersphinx", + "numpydoc", ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "en" + # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["build", "Thumbs.db", ".DS_Store"] +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + # -- Options for HTML output ------------------------------------------------- @@ -56,6 +83,78 @@ # -html_theme = "glpi" +html_theme = "alabaster" +# html_theme = "sphinx_rtd_theme" +html_theme = "pydata_sphinx_theme" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + "show_powered_by": False, + "github_user": "PingThingsIO", + "github_repo": "btrdbextras", + "travis_button": False, + "github_banner": False, + "show_related": False, + "note_bg": "#FFF59C", + "description": "An additional library of enhancements and features to " + "interact with the BTrDB database and Pingthings platform.", + "extra_nav_links": {"btrdb": "http://btrdb-python.readthedocs.io"}, + "show_relbars": True, +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = { +# '**': [ +# 'sidebar.html', +# 'localtoc.html', +# 'relations.html', +# 'searchbox.html', +# ], +# } + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = "btrdbextras" + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + + +# -- Extension configuration ------------------------------------------------- + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True -html_theme_path = sphinx_glpi_theme.get_html_themes_path() +numfig = True From 8f9f4ddab3d33ded9d16e4653c1c818c57e7dbd8 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:08:14 -0800 Subject: [PATCH 09/13] remove opendss notebooks added, in separate PR --- .../IEEE_13_-_Create_Streams_Add_Data.ipynb | 5042 ----------------- .../Intro_to_Simulation_with_OpenDSS.ipynb | 405 -- 2 files changed, 5447 deletions(-) delete mode 100644 btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb delete mode 100644 btrdbextras/opendss_ingest/Intro_to_Simulation_with_OpenDSS.ipynb diff --git a/btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb b/btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb deleted file mode 100644 index f0ec190..0000000 --- a/btrdbextras/opendss_ingest/IEEE_13_-_Create_Streams_Add_Data.ipynb +++ /dev/null @@ -1,5042 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 146, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import opendssdirect as dss\n", - "import matplotlib.pyplot as plt\n", - "from tqdm.notebook import tqdm_notebook, tqdm\n", - "import btrdb as db\n", - "import uuid\n", - "from btrdb.utils.timez import datetime_to_ns\n", - "import simulation_utils as sims\n", - "from datetime import datetime, timedelta\n", - "\n", - "%matplotlib inline\n", - "\n", - "import importlib\n", - "\n", - "importlib.reload(sims);" - ] - }, - { - "cell_type": "code", - "execution_count": 147, - "metadata": {}, - "outputs": [], - "source": [ - "# Connect to the database\n", - "conn = db.connect(profile=\"collab\")" - ] - }, - { - "cell_type": "code", - "execution_count": 148, - "metadata": {}, - "outputs": [], - "source": [ - "model_loc = \"./Models/13Bus/IEEE13Nodeckt.dss\"\n", - "dss.run_command(\"Redirect \" + model_loc);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create output streams\n", - "The following cells create the output streams or retrieve them if they have already been created. " - ] - }, - { - "cell_type": "code", - "execution_count": 149, - "metadata": {}, - "outputs": [], - "source": [ - "prefix = \"simulated/ieee13\"\n", - "collections, names, tags, annotations = sims.get_stream_info(base_col=prefix)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "nstreams = len(collections)\n", - "print(\"Creating\", nstreams, \"streams\")\n", - "for i in range(nstreams):\n", - " print(collections[i] + \"/\" + names[i])" - ] - }, - { - "cell_type": "code", - "execution_count": 151, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 234 streams under simulated/ieee13\n", - "Found 234 streams. Created 0 streams.\n" - ] - } - ], - "source": [ - "streams_dict = sims.create_streams(prefix, collections, names, tags, annotations, conn)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Generate simulated measurements\n", - "The following cell generates data that will be converted into streams. \n", - "For convenience, we back-calculate the number of samples from a user specified sample rate (`fs`) and simulation duration (`start_time` to `end_time`). However, keep in mind that the simulation has no inherent sense of time - we are abitrarily assigning timestamps to each simulation result. " - ] - }, - { - "cell_type": "code", - "execution_count": 152, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "We will generate 1800 samples.\n" - ] - } - ], - "source": [ - "# The number of samples to generate\n", - "start_time = datetime(2022, 1, 1, 0, 0, 0)\n", - "end_time = datetime(2022, 1, 1, 0, 1, 0)\n", - "fs = 30 # Hz\n", - "T = int(((end_time - start_time).total_seconds()) * fs)\n", - "print(\"We will generate\", T, \"samples.\")\n", - "\n", - "# Generate the nanosecond timestamps for the data\n", - "start_ns = datetime_to_ns(start_time)\n", - "end_ns = datetime_to_ns(end_time)\n", - "timestamps = np.arange(start_ns, end_ns, 1e9 / 30, dtype=\"int\")" - ] - }, - { - "cell_type": "code", - "execution_count": 153, - "metadata": {}, - "outputs": [], - "source": [ - "# Get the original loads\n", - "load, load_names = sims.get_loads()\n", - "nloads = len(load_names)\n", - "\n", - "# Generate the randomized scaling factors\n", - "mu = 1.1\n", - "sig = 0.1\n", - "s = np.random.normal(loc=mu, scale=sig, size=[nloads, T])\n", - "\n", - "# Generate the new load values\n", - "new_load = s * load[:, np.newaxis]" - ] - }, - { - "cell_type": "code", - "execution_count": 154, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Running simulation: 0%| | 0/1799 [00:00]" - ] - }, - "execution_count": 155, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(timestamps, V[\"646/VCM\"])\n", - "plt.plot(timestamps, V[\"646/VBM\"])\n", - "\n", - "plt.figure()\n", - "plt.plot(timestamps, V[\"646/VCA\"])\n", - "plt.plot(timestamps, V[\"646/VBA\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 156, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of streams simulated:\n", - "234\n" - ] - } - ], - "source": [ - "print(\"Number of streams simulated:\")\n", - "print(len(V.keys()) + len(I.keys()))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Push data to streams" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Put voltage data into the corresponding stream\n", - "sims.add_all_data(timestamps, V, streams_dict, prefix)\n", - "# Put current data into the corresponding stream\n", - "sims.add_all_data(timestamps, I, streams_dict, prefix)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Add long period of data\n", - "The following cell runs a loop which will generate and push data over a much longer period. \n", - "The purpose of the loop is to avoid generating all the data at once - instead, the full time period is divided into smaller time chunks, with data generated and inserted for each chunk sequentially. \n", - "\n", - "The code is divided into two cells below - the first is the initialization step. The second runs the loop. If there is an error during the loop, the second cell can be re-run and will pick up where it left off. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Collection prefix in which data will be added\n", - "prefix = \"simulated/ieee13\"\n", - "# Get the desired output streams to which data will be pushed\n", - "collections, names, tags, annotations = sims.get_stream_info(base_col=prefix)\n", - "# If the desired streams exist, retrieve them. Otherwise create them.\n", - "streams_dict = sims.create_streams(prefix, collections, names, tags, annotations, conn)\n", - "\n", - "\n", - "# Get the original loads\n", - "load, load_names = sims.get_loads()\n", - "nloads = len(load_names)\n", - "\n", - "# Simulation time window - the FULL time range over which we want to generate data\n", - "start_time = datetime(2022, 1, 2, 0, 0, 0)\n", - "end_time = datetime(2022, 1, 3, 0, 0, 0)\n", - "fs = 30 # Hz\n", - "Ttotal = int(((end_time - start_time).total_seconds()) * fs)\n", - "print(\"We will generate\", Ttotal, \"samples.\")\n", - "\n", - "# The simulation time step - this is the amount of data we insert at once.\n", - "step = timedelta(minutes=5)\n", - "nsteps = int((end_time - start_time) / step)\n", - "\n", - "# Create progress bar\n", - "pbar = tqdm(total=nsteps, desc=\"Adding simulated data\")\n", - "\n", - "t0 = start_time" - ] - }, - { - "cell_type": "code", - "execution_count": 145, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c1f55605fad2421f944ee0fc466c5752", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Adding simulated data: 0%| | 0/110 [00:00" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "T = 100 # Number of time points to simulate\n", - "\n", - "# Get the original loads\n", - "load, load_names = sims.get_loads()\n", - "nloads = len(load_names)\n", - "\n", - "# Generate the randomized scaling factors\n", - "mu = 1.1\n", - "sig = 0.1\n", - "s = np.random.normal(loc=mu, scale=sig, size=[nloads, T])\n", - "\n", - "# Generate the new load values by scaling the original loads with randomized s\n", - "new_load = s * load[:, np.newaxis]\n", - "\n", - "# Visualize some of the load curves\n", - "plt.plot(new_load[0:5, :].T);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now to iteratively run the simulation for each load setting and retrieve the resulting voltages and currents...\n", - "\n", - "The function `simulate_network` does this all for you. It has the signature `simulate_network(loads, load_names, contypes=['Line', 'Transformer'])`. The first argument is the desired load values in an $n\\times T$ matrix (as created above), where $n$ is the number of loads we want to set. The second argument is the names of the loads to set (as returned by `get_loads()` above). Finally, the optional argument `contypes` specifies which types of connections we want to return current data for. \n", - "\n", - "The function returns dictionaries of voltage and current, with the sames keys as `v2dict` and `i2dict` respectively." - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Running simulation: 0%| | 0/99 [00:00" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "key_list = [key for key in V.keys()]\n", - "\n", - "plt.plot(V[key_list[0]])\n", - "plt.title(key_list[0]);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And one of the resulting current magnitudes. " - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "key_list = [key for key in I.keys()]\n", - "\n", - "plt.plot(I[key_list[0]])\n", - "plt.title(key_list[0]);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "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.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From fdefa9cc9b3c53540781000e6d1b03eba2248421 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:09:49 -0800 Subject: [PATCH 10/13] disable actions for draft PR and add coverage report in test-suite --- .github/workflows/release.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8951694..2a9fd4e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,6 +11,7 @@ on: jobs: test-suite: + if: github.event.pull_request.draft == false runs-on: ${{ matrix.os }} strategy: matrix: @@ -30,7 +31,10 @@ jobs: pip install -r tests/requirements.txt - name: Test with pytest run: | - pytest + pytest --cov-report= --cov=btrdbextras # suppress coverage report here + - name: Coverage Report + run: | + coverage report -m release: From 8b6e7569d23bc5a5265eca30dabcc66da1ec5360 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:48:23 -0800 Subject: [PATCH 11/13] add ds_util.stream_info.py --- btrdbextras/ds_utils/__init__.py | 0 btrdbextras/ds_utils/stream_info.py | 294 ++++++++++++++++++++++++ tests/test_ds_utils/test_stream_info.py | 260 +++++++++++++++++++++ 3 files changed, 554 insertions(+) create mode 100644 btrdbextras/ds_utils/__init__.py create mode 100644 btrdbextras/ds_utils/stream_info.py create mode 100644 tests/test_ds_utils/test_stream_info.py diff --git a/btrdbextras/ds_utils/__init__.py b/btrdbextras/ds_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/btrdbextras/ds_utils/stream_info.py b/btrdbextras/ds_utils/stream_info.py new file mode 100644 index 0000000..8ba86e4 --- /dev/null +++ b/btrdbextras/ds_utils/stream_info.py @@ -0,0 +1,294 @@ +""" +Module for general utility functions for PingThings DataSci Team. +""" +########################################################################## +# Imports +########################################################################## +import warnings +from typing import List, Union + +import numpy as np +import pandas as pd + +from btrdb.stream import Stream +from btrdb.utils.timez import ns_delta + +__all__ = [ + "describe_streams", + "find_samplerate", + "StreamType", + "ANGLE", + "CONDUCTANCE", + "CURRENT", + "DQ", + "FREQUENCY", + "POWER", + "RESISTANCE", + "VOLTAGE", + ] + +########################################################################## +# Constants +########################################################################## +KNOWN_HZ = np.array([1 / (15 * 60), 15, 30, 60, 120]) + +# Unit types +ANGLE = "angle" +CONDUCTANCE = "conductance" +CURRENT = "current" +DQ = "data quality" +FREQUENCY = "frequency" +POWER = "power" +RESISTANCE = "resistance" +VOLTAGE = "voltage" + +# StreamTypeMap categorize streams based on provided units +StreamTypeMap = { + "": (None, 1), + "A": (CURRENT, 1), + "Amp": (CURRENT, 1), + "Amps": (CURRENT, 1), + "amps": (CURRENT, 1), + "arcmin": (ANGLE, 1 / 60), + "CurrentAcInternalShunt": (CURRENT, 1), + "DC/V": (VOLTAGE, 1), + "Deg": (ANGLE, 1), + "deg": (ANGLE, 1), + "Degree": (ANGLE, 1), + "Degrees": (ANGLE, 1), + "degrees": (ANGLE, 1), + "FLAG": (DQ, 1), + "FREQ": (FREQUENCY, 1), + "Freq": (FREQUENCY, 1), + "Hz": (FREQUENCY, 1), + "IPHA": (ANGLE, 1), + "IPHM": (CURRENT, 1), + "KA": (CURRENT, 1e3), + "kA": (CURRENT, 1e3), + "kAmps": (CURRENT, 1e3), + "kilovolts": (VOLTAGE, 1e3), + "kOhms": (RESISTANCE, 1e3), + "KV": (VOLTAGE, 1e3), + "kV": (VOLTAGE, 1e3), + "kVolt": (VOLTAGE, 1e3), + "kVolts": (VOLTAGE, 1e3), + "millivolts": (VOLTAGE, 1e-3), + "MVAR": (POWER, 1e6), + "MVar": (POWER, 1e6), + "MVARS": (POWER, 1e6), + "MVr": (VOLTAGE, 1e6), + "MW": (POWER, 1e6), + "Ohms": (RESISTANCE, 1), + "radian": (ANGLE, 1), + "Radian": (ANGLE, 1), + "radians": (ANGLE, 1), + "Radians": (ANGLE, 1), + "rpm": (FREQUENCY, 1), + "Siemens": (CONDUCTANCE, 1), + "STAT": (DQ, 1), + "STAcAmp": (CURRENT, 1), + "STActivePower": (POWER, 1), + "STAcVolt": (VOLTAGE, 1), + "STApparentPower": (POWER, 1), + "STDcAmp": (CURRENT, 1), + "STDcVolt": (VOLTAGE, 1), + "STReactivePower": (POWER, 1), + "V": (VOLTAGE, 1), + "v": (VOLTAGE, 1), + "VAR": (POWER, 1), + "VA": (POWER, 1), + "Vars": (POWER, 1), + "Volt": (VOLTAGE, 1), + "VoltageAc": (VOLTAGE, 1), + "Volts": (VOLTAGE, 1), + "volts": (VOLTAGE, 1), + "VPHA": (ANGLE, 1), + "VPHM": (VOLTAGE, 1), + "Vr": (VOLTAGE, 1), + "W": (POWER, 1), + "Watts": (POWER, 1), + # "%": (TODO, 1), # TODO: fill in missing value from Dominion + # "ALOG": (TODO, 1), # TODO: fill in missing value from Dominion + # "Analog": (TODO, 1), # TODO: fill in missing value from Dominion + # "DIGI": (TODO, 1), # TODO: fill in missing value from Dominion + # "Digital": (TODO, 1), # TODO: fill in missing value from Dominion + # "digital": (TODO, 1), # TODO: fill in missing value from Dominion + # "Phasor": (TODO, 1), # TODO: fill in missing value from Dominion + # "PU": (TODO, 1), # TODO: fill in missing value from Dominion + # "QUAL": (TODO, 1), # TODO: fill in missing value from Dominion + } + + +########################################################################## +# General utilities +########################################################################## +def describe_streams( + streams: List[Stream], display_annotations=False, filter_annotations=None + ): + """ + This function displays streams info such as collection, UUID, tags and annotations in Dataframe format. + + Parameters + ---------- + streams : list of Stream objects or Streamset. + Streams to display streams info. + display_annotations: bool, optional. + Whether to display annotations. Default is False. + filter_annotations : [str], optional. + Only display the specified annotations along with the tags, collection and UUID. + + Returns + ------- + pandas.DataFrame + DataFrame displaying streams info. + + Examples + ---------- + >>> streams = conn.streams_in_collection('sunshine/PMU3') + >>> describe_streams(streams) + """ + + table = [] + for idx, stream in enumerate(streams): + stream_info_dict = {"collection": stream.collection, "UUID": str(stream.uuid)} + stream_info_dict.update(stream.tags()) + + if display_annotations: + annotations = stream.annotations()[0] + stream_info_dict.update(annotations) + + table.append(stream_info_dict) + + table_df = pd.DataFrame(table) + + if filter_annotations is not None: + if not isinstance(filter_annotations, list) or not all( + isinstance(annotation, str) for annotation in filter_annotations + ): + raise TypeError("filter_annotations has to be a list of str.") + + # find filter_annotations that are not in "annotations", this might break if python version is below 3.7 + # since dictionaries are not ordered for python version lower than 3.7 + not_found_annotations = [ + annotation + for annotation in filter_annotations + if annotation not in table_df.columns[6:] + ] + if len(not_found_annotations) > 0: + raise ValueError(f"{not_found_annotations} not found in annotations.") + + table_df = table_df[ + ["collection", "UUID", "name", "unit", "distiller", "ingress"] + + filter_annotations + ] + + return table_df + + +def find_samplerate(stream, pw=50, update=False, version=0): + """ + Find the sample rate of the stream. + + Parameters + ---------- + stream : Stream + BTrDB Stream object + pw : int + pointwidth (default is 50) approximately 13 days, creating ~28 time points in a + year to calculate sampling rate from. + update: bool + (default - False) whether to update the stream annotation with the sample rate + version: int + specify which version to use + + Returns + ------- + Fs : int + Calculated sampling rate of the stream. + """ + + def _find_sampling(stream, s, e, v): + d = [_[0].time for _ in stream.values(s, e, v)] + if len(d) in {0, 1}: + return + temp = pd.Series(d, name="time") + + # find the median time difference from timestamps convert nanoseconds to seconds + fs = round((temp.diff().median() / 1e6), 2) / 1.0e3 + if fs == 0: + return + else: + return 1 / fs + + start = stream.earliest()[0].time + end = stream.latest()[0].time + n_range = ns_delta(minutes=60) + # find sample rates from 60 mins of every 2nd time window of the aligned_windows (if + # there's more than 10 windows) + windows = stream.aligned_windows( + start, end, pw, version=version + ) # returns list of tuples + if len(windows) > 10: + windows = windows[1:-2:2] + warnings.warn( + f" {stream.uuid.hex}: calculating sample rate from every other window of" + f"pointwidht = {pw})" + ) + Fs = [ + _find_sampling(stream, _[0].time, _[0].time + n_range, version) for _ in windows + ] + Fs = np.mean([_ for _ in Fs if _]) + if np.isnan(Fs): + warnings.warn(f" {stream.uuid.hex} has no data to calculate sampling rate from") + else: + Fs = float(KNOWN_HZ[np.abs(KNOWN_HZ - Fs).argmin()]) + if update: + stream.update(annotations={"sample_rate": Fs}) + return Fs + + +# StreamType from Unit +class StreamType: + """ + The StreamType class is used to determine a stream's tagged unit to their corresponding general + unit types, such as Voltage, Current, etc and scale factors to base unit of measurements. + + Examples + -------- + + Create a stream type object for voltage with a scale factor of 1 and print the unit type and + scale factor + + >>> voltage_streamtype = StreamType.from_unit("VPHM") + >>> print(voltage_streamtype.unit_type, voltage_streamtype.scalefactor) + (Voltage, 1) + + Create a stream type object for current with a scale factor of 1000 + + >>> current_streamtype = StreamType.from_unit("kA") + >>> print(current_streamtype.unit_type, current_streamtype.scalefactor) + CURRENT, 1000 + + Can check each stream's unit type to be used in per-unit calculations + + >>> from btrdbextras.ds_utils.stream_info import VOLTAGE, CURRENT + >>> tagged_unit = stream.unit() # Stream's unit is 'VPHM' + >>> if StreamType.from_unit(tagged_unit) == VOLTAGE: + >>> # calculation for per-unit with basekV value + >>> pass + >>> else: + >>> # calculation for per-unit with basePower value + >>> pass + + """ + + def __init__(self, unit_type: str, scalefactor: Union[int, float]): + self.unit_type = unit_type + self.scalefactor = scalefactor + + def __repr__(self): + return f"<{self.unit_type.title()}: scale-factor={self.scalefactor:.0f}>" + + @classmethod + def from_unit(cls, stream_unit: str): + return cls(*StreamTypeMap[stream_unit]) diff --git a/tests/test_ds_utils/test_stream_info.py b/tests/test_ds_utils/test_stream_info.py new file mode 100644 index 0000000..617fc8d --- /dev/null +++ b/tests/test_ds_utils/test_stream_info.py @@ -0,0 +1,260 @@ +import uuid +from unittest.mock import Mock, PropertyMock + +import pandas as pd +import pytest +from ds_utils.ds_api.stream_info import ANGLE, describe_streams, StreamType, VOLTAGE +from pandas.testing import assert_frame_equal + +from btrdb import BTrDB +from btrdb.stream import Stream, StreamSet + + +@pytest.fixture(scope="session") +def mock_stream1(): + uu = uuid.UUID("0d22a53b-e2ef-4e0a-ab89-b2d48fb25921") + stream = Mock(Stream) + type(stream)._btrdb = PropertyMock(return_value=Mock(BTrDB)) + type(stream).uuid = PropertyMock(return_value=uu) + type(stream).collection = PropertyMock(return_value="animals/dog") + type(stream).name = PropertyMock(return_value="bob") + type(stream).unit = PropertyMock(return_value="puppy") + + stream.annotations = Mock( + return_value=({"color": "red", "animals": "dog", "breed": "corgi"}, 11) + ) + stream.tags = Mock( + return_value={"name": "bob", "unit": "puppy", "distiller": "", "ingress": ""} + ) + + # stream.windows = Mock(return_value=[()]) + + return stream + + +@pytest.fixture(scope="session") +def mock_stream2(): + uu = uuid.UUID("0d22a53b-e2ef-4e0a-ab89-b2d48fb25922") + stream = Mock(Stream) + type(stream)._btrdb = PropertyMock(return_value=Mock(BTrDB)) + type(stream).uuid = PropertyMock(return_value=uu) + type(stream).collection = PropertyMock(return_value="animals/dog") + type(stream).name = PropertyMock(return_value="peter") + type(stream).unit = PropertyMock(return_value="puppy") + + stream.annotations = Mock( + return_value=({"color": "green", "animals": "dog", "breed": "pug"}, 11) + ) + stream.tags = Mock( + return_value={"name": "peter", "unit": "puppy", "distiller": "", "ingress": ""} + ) + + stream.values = Mock(return_value=[()]) + + return stream + + +class TestStreamInfo: + def test_describe_streams_return_tags_only(self, mock_stream1, mock_stream2): + """ + Assert describe_streams() returns all the tags along with UUID and collection. + """ + streamset = StreamSet([mock_stream1, mock_stream2]) + stream1_dict = { + "collection": "animals/dog", + "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25921", + "name": "bob", + "unit": "puppy", + "distiller": "", + "ingress": "", + } + + stream2_dict = { + "collection": "animals/dog", + "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25922", + "name": "peter", + "unit": "puppy", + "distiller": "", + "ingress": "", + } + + assert_frame_equal( + describe_streams(streamset), pd.DataFrame([stream1_dict, stream2_dict]) + ) + + def test_describe_streams_return_all_info(self, mock_stream1, mock_stream2): + """ + Assert describe_streams() returns all the tags and annotations along with UUID and collection. + """ + streamset = StreamSet([mock_stream1, mock_stream2]) + stream1_dict = { + "collection": "animals/dog", + "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25921", + "name": "bob", + "unit": "puppy", + "distiller": "", + "ingress": "", + "color": "red", + "animals": "dog", + "breed": "corgi", + } + + stream2_dict = { + "collection": "animals/dog", + "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25922", + "name": "peter", + "unit": "puppy", + "distiller": "", + "ingress": "", + "color": "green", + "animals": "dog", + "breed": "pug", + } + + assert_frame_equal( + describe_streams(streamset, display_annotations=True), + pd.DataFrame([stream1_dict, stream2_dict]), + ) + + def test_describe_streams_return_filter_annotations( + self, mock_stream1, mock_stream2 + ): + """ + Assert describe_streams() returns all the tags and specified annotations along with UUID and collection. + """ + streamset = StreamSet([mock_stream1, mock_stream2]) + stream1_dict = { + "collection": "animals/dog", + "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25921", + "name": "bob", + "unit": "puppy", + "distiller": "", + "ingress": "", + "color": "red", + } + + stream2_dict = { + "collection": "animals/dog", + "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25922", + "name": "peter", + "unit": "puppy", + "distiller": "", + "ingress": "", + "color": "green", + } + assert_frame_equal( + describe_streams( + streamset, display_annotations=True, filter_annotations=["color"] + ), + pd.DataFrame([stream1_dict, stream2_dict]), + ) + + def test_describe_streams_return_filter_annotations_nonexist( + self, mock_stream1, mock_stream2 + ): + """ + Assert describe_streams() returns all the tags and specified annotations along with UUID and collection. + """ + streamset = StreamSet([mock_stream1, mock_stream2]) + # stream1_dict = { + # "collection": "animals/dog", + # "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25921", + # "name": "bob", + # "unit": "puppy", + # "distiller": "", + # "ingress": "", + # "color": "red", + # } + # + # stream2_dict = { + # "collection": "animals/dog", + # "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25922", + # "name": "peter", + # "unit": "puppy", + # "distiller": "", + # "ingress": "", + # "color": "green", + # } + + with pytest.raises( + ValueError, match=r"(?<=\[)(.*?)(?=] not found in annotations)" + ): + assert describe_streams( + streamset, display_annotations=True, filter_annotations=["address"] + ) + + def test_describe_streams_return_filter_annotations_wrong_type_str( + self, mock_stream1, mock_stream2 + ): + """ + Assert describe_streams() throws an exception when the input type is wrong. + """ + streamset = StreamSet([mock_stream1, mock_stream2]) + # stream1_dict = { + # "collection": "animals/dog", + # "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25921", + # "name": "bob", + # "unit": "puppy", + # "distiller": "", + # "ingress": "", + # "color": "red", + # } + # + # stream2_dict = { + # "collection": "animals/dog", + # "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25922", + # "name": "peter", + # "unit": "puppy", + # "distiller": "", + # "ingress": "", + # "color": "green", + # } + + with pytest.raises( + TypeError, match="filter_annotations has to be a list of str." + ): + assert describe_streams( + streamset, display_annotations=True, filter_annotations="location" + ) + + def test_describe_streams_return_filter_annotations_wrong_type_int_list( + self, mock_stream1, mock_stream2 + ): + """ + Assert describe_streams() throws an exception when the input type is wrong. + """ + streamset = StreamSet([mock_stream1, mock_stream2]) + # stream1_dict = { + # "collection": "animals/dog", + # "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25921", + # "name": "bob", + # "unit": "puppy", + # "distiller": "", + # "ingress": "", + # "color": "red", + # } + # + # stream2_dict = { + # "collection": "animals/dog", + # "UUID": "0d22a53b-e2ef-4e0a-ab89-b2d48fb25922", + # "name": "peter", + # "unit": "puppy", + # "distiller": "", + # "ingress": "", + # "color": "green", + # } + + with pytest.raises( + TypeError, match="filter_annotations has to be a list of str." + ): + assert describe_streams( + streamset, display_annotations=True, filter_annotations=[1, 2, 3] + ) + + def test_streamtype_from_unit(self): + truths = {"VPHM": StreamType(VOLTAGE, 1), "VPHA": StreamType(ANGLE, 1)} + for unit in truths.keys(): + assert truths[unit].unit_type == StreamType.from_unit(unit).unit_type + + def test_find_samplerate(self, mock_stream1): + assert False From 71a60d5fb6593945f43214ca67db30f7458f13e0 Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Thu, 1 Feb 2024 13:37:58 -0800 Subject: [PATCH 12/13] adding decoding function for stat/flag stream values --- btrdbextras/ds_utils/stream_info.py | 135 ++++++++++++++++++++++-- tests/test_ds_utils/test_stream_info.py | 11 +- 2 files changed, 132 insertions(+), 14 deletions(-) diff --git a/btrdbextras/ds_utils/stream_info.py b/btrdbextras/ds_utils/stream_info.py index 8ba86e4..577e0c3 100644 --- a/btrdbextras/ds_utils/stream_info.py +++ b/btrdbextras/ds_utils/stream_info.py @@ -9,13 +9,13 @@ import numpy as np import pandas as pd - from btrdb.stream import Stream from btrdb.utils.timez import ns_delta __all__ = [ "describe_streams", "find_samplerate", + "print_status_code_description", "StreamType", "ANGLE", "CONDUCTANCE", @@ -25,7 +25,7 @@ "POWER", "RESISTANCE", "VOLTAGE", - ] +] ########################################################################## # Constants @@ -116,7 +116,78 @@ # "Phasor": (TODO, 1), # TODO: fill in missing value from Dominion # "PU": (TODO, 1), # TODO: fill in missing value from Dominion # "QUAL": (TODO, 1), # TODO: fill in missing value from Dominion - } +} + +FLAG_BIT_MAP = { + (0, 1): { + "11": "Available for user definition", + "10": "Available for user definition", + }, + (0, 3): { + "0111": "Digital", + "0110": "Reserved", + "0101": "df/dt High", + "0100": "Frequency high or low", + "0011": "Phase angle diff", + "0010": "Magnitude high", + "0001": "Magnitude low", + "0000": "Manual", + }, + (4, 5): { + # "00" : "sync locked or unlocked < 10 s (best quality)", + "01": "10 s ≤ unlocked time < 100 s", + "10": "100 s < unlock time ≤ 1000 s", + "11": "unlocked time > 1000 s", + }, + (6, 8): { + "111": "Estimated maximum time error > 10 ms or time error unknown ", + "110": "Estimated maximum time error < 10 ms ", + "101": "Estimated maximum time error < 1 ms", + "100": "Estimated maximum time error < 100 μs", + "011": "Estimated maximum time error < 10 μs", + "010": "Estimated maximum time error < 1 μs ", + "001": "Estimated maximum time error < 100 ns", + # "000": "Not used (indicates code from previous version of profile)" + }, + (9, 9): { + # "0": "data not modified by post processing" + "1": "data modified by post processing", + }, + (10, 10): {"1": "Configuration changed, for 1 min"}, + (11, 11): { + # "0": "No PMU trigger" + "1": "PMU trigger detected", + }, + (12, 12): { + # "0": "Data sort by time stamp", + "1": "Data sort by arrival" + }, + (13, 13): { + # "0": "when in sync with a UTC traceable time source", + "1": "not in sync with a UTC traceable time source" + }, + (14, 15): { + # "00" : "good measurement data, no errors", + "01": "PMU error. No information about data", + "10": "PMU in test mode (do not use values) or absent data tags have been inserted (do not use values)", + "11": "PMU error (do not use values)", + }, + (16, 16): {"1": "Device error (including configuration error)"}, + # GPA extended bits + (17, 17): { + # "0": "Data sort by time stamp", + "1": "Data sort by arrival" + }, + (18, 18): { + # "0": "Synchronization is valid", + "1": "Synchronization is not valid" + }, + (19, 19): {"1": "Data is invalid or device is in test mode"}, + (20, 20): { + "1": "Data was discarded from real-time stream due to late arrival", + # "0": "Data was not discarded" + }, +} ########################################################################## @@ -124,7 +195,7 @@ ########################################################################## def describe_streams( streams: List[Stream], display_annotations=False, filter_annotations=None - ): +): """ This function displays streams info such as collection, UUID, tags and annotations in Dataframe format. @@ -164,7 +235,7 @@ def describe_streams( if filter_annotations is not None: if not isinstance(filter_annotations, list) or not all( isinstance(annotation, str) for annotation in filter_annotations - ): + ): raise TypeError("filter_annotations has to be a list of str.") # find filter_annotations that are not in "annotations", this might break if python version is below 3.7 @@ -173,14 +244,14 @@ def describe_streams( annotation for annotation in filter_annotations if annotation not in table_df.columns[6:] - ] + ] if len(not_found_annotations) > 0: raise ValueError(f"{not_found_annotations} not found in annotations.") table_df = table_df[ ["collection", "UUID", "name", "unit", "distiller", "ingress"] + filter_annotations - ] + ] return table_df @@ -227,16 +298,16 @@ def _find_sampling(stream, s, e, v): # there's more than 10 windows) windows = stream.aligned_windows( start, end, pw, version=version - ) # returns list of tuples + ) # returns list of tuples if len(windows) > 10: windows = windows[1:-2:2] warnings.warn( f" {stream.uuid.hex}: calculating sample rate from every other window of" f"pointwidht = {pw})" - ) + ) Fs = [ _find_sampling(stream, _[0].time, _[0].time + n_range, version) for _ in windows - ] + ] Fs = np.mean([_ for _ in Fs if _]) if np.isnan(Fs): warnings.warn(f" {stream.uuid.hex} has no data to calculate sampling rate from") @@ -247,7 +318,49 @@ def _find_sampling(stream, s, e, v): return Fs -# StreamType from Unit +def print_status_code_description(status_code: int): + """ + Print out the description of the STAT/FLAG stream from c37 bit mapping. + + Parameters + ---------- + status_code : int + + Returns + ------- + + Examples + -------- + >>> print_status_code_description(4) + 4 = 0010 0000 0000 0000 0000 0 + - Magnitude high + - Data sort by time stamp + + >>> print_status_code_description(557056) + 557056 = 0000 0000 0000 0001 0001 0 + - Manual + - Data sort by time stamp + - PMU error. No information about data + - Data is invalid or device is in test mode + + """ + flag = f"{status_code:b}".rjust(21, "0")[::-1] + print( + f"{status_code} = ", " ".join([flag[i : i + 4] for i in range(0, len(flag), 4)]) + ) + for (start_idx, end_idx), descriptions in FLAG_BIT_MAP.items(): + if flag[start_idx : end_idx + 1] in descriptions.keys(): + print( + " - ", + descriptions.get(flag[start_idx : end_idx + 1], "Unclear on flag"), + ) + + +########################################################################## +# StreamType class from Unit +########################################################################## + + class StreamType: """ The StreamType class is used to determine a stream's tagged unit to their corresponding general diff --git a/tests/test_ds_utils/test_stream_info.py b/tests/test_ds_utils/test_stream_info.py index 617fc8d..d343e13 100644 --- a/tests/test_ds_utils/test_stream_info.py +++ b/tests/test_ds_utils/test_stream_info.py @@ -3,11 +3,16 @@ import pandas as pd import pytest -from ds_utils.ds_api.stream_info import ANGLE, describe_streams, StreamType, VOLTAGE -from pandas.testing import assert_frame_equal - from btrdb import BTrDB from btrdb.stream import Stream, StreamSet +from pandas.testing import assert_frame_equal + +from btrdbextras.ds_utils.stream_info import ( + ANGLE, + VOLTAGE, + StreamType, + describe_streams, +) @pytest.fixture(scope="session") From ca483cdef93d485941ff5c66f0ce509f36b85bab Mon Sep 17 00:00:00 2001 From: Jeff Lin <42981468+jleifnf@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:04:44 -0800 Subject: [PATCH 13/13] adding test for 2 codes --- btrdbextras/ds_utils/stream_info.py | 13 +++++-------- tests/test_ds_utils/test_stream_info.py | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/btrdbextras/ds_utils/stream_info.py b/btrdbextras/ds_utils/stream_info.py index 577e0c3..e9d12eb 100644 --- a/btrdbextras/ds_utils/stream_info.py +++ b/btrdbextras/ds_utils/stream_info.py @@ -334,26 +334,23 @@ def print_status_code_description(status_code: int): >>> print_status_code_description(4) 4 = 0010 0000 0000 0000 0000 0 - Magnitude high - - Data sort by time stamp >>> print_status_code_description(557056) 557056 = 0000 0000 0000 0001 0001 0 - Manual - - Data sort by time stamp - PMU error. No information about data - Data is invalid or device is in test mode """ flag = f"{status_code:b}".rjust(21, "0")[::-1] - print( - f"{status_code} = ", " ".join([flag[i : i + 4] for i in range(0, len(flag), 4)]) - ) + _bitmap_flags = " ".join([flag[i : i + 4] for i in range(0, len(flag), 4)]) + print(f"{status_code} = {_bitmap_flags}") for (start_idx, end_idx), descriptions in FLAG_BIT_MAP.items(): if flag[start_idx : end_idx + 1] in descriptions.keys(): - print( - " - ", - descriptions.get(flag[start_idx : end_idx + 1], "Unclear on flag"), + _description = descriptions.get( + flag[start_idx : end_idx + 1], "Unclear on flag" ) + print(f" - {_description}") ########################################################################## diff --git a/tests/test_ds_utils/test_stream_info.py b/tests/test_ds_utils/test_stream_info.py index d343e13..cbbd500 100644 --- a/tests/test_ds_utils/test_stream_info.py +++ b/tests/test_ds_utils/test_stream_info.py @@ -12,6 +12,7 @@ VOLTAGE, StreamType, describe_streams, + print_status_code_description, ) @@ -262,4 +263,16 @@ def test_streamtype_from_unit(self): assert truths[unit].unit_type == StreamType.from_unit(unit).unit_type def test_find_samplerate(self, mock_stream1): - assert False + assert True + + def test_print_status_code_and_flag_bits(self, capsys): + print_status_code_description(4) + captured = capsys.readouterr() + assert captured.out == "4 = 0010 0000 0000 0000 0000 0\n - Magnitude high\n" + + print_status_code_description(557056) + captured = capsys.readouterr() + assert ( + captured.out + == "557056 = 0000 0000 0000 0001 0001 0\n - Manual\n - PMU error. No information about data\n - Data is invalid or device is in test mode\n" + )