diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfbc17c..8ad24bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,9 @@ name: CI on: pull_request: push: + branches: + - "develop" + - "main" jobs: test: diff --git a/README.rst b/README.rst index 5cb3e6b..ff7844c 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,6 @@ Within Python you can use the client like this: .. code-block:: python - import pyseed from pathlib import Path from pyseed.seed_client import SeedClient diff --git a/pyseed/seed_client.py b/pyseed/seed_client.py index 5b66f4c..fba1dfe 100644 --- a/pyseed/seed_client.py +++ b/pyseed/seed_client.py @@ -89,16 +89,17 @@ def __init__( ) # favor the connection params over the config file + self.payload = {} if connection_params: # the connetion params are simply squashed on SEEDReadWriteClient init - payload = connection_params + self.payload = connection_params elif connection_config_filepath: - payload = SeedClientWrapper.read_connection_config_file( + self.payload = SeedClientWrapper.read_connection_config_file( connection_config_filepath ) # read in from config file - self.client = SEEDReadWriteClient(organization_id, **payload) + self.client = SEEDReadWriteClient(organization_id, **self.payload) @classmethod def read_connection_config_file(cls, filepath: Path) -> dict: @@ -114,7 +115,8 @@ def read_connection_config_file(cls, filepath: Path) -> dict: "username": "user@somedomain.com", "api_key": "1b5ea1ee220c8628789c61d66253d90398e6ad03", "port": 8000, - "use_ssl": false + "use_ssl": false, + "seed_org_name: "test-org" } Args: @@ -139,6 +141,35 @@ def __init__( ) -> None: super().__init__(organization_id, connection_params, connection_config_filepath) + # set org if you can + if self.payload and self.payload.get('seed_org_name', None): + self.get_org_by_name(self.payload['seed_org_name'], set_org_id=True) + + def get_org_id(self) -> int: + """Return the org ID that is set""" + return self.client.org_id + + def get_org_by_name(self, org_name: str, set_org_id: bool = False) -> dict: + """Set the current organization by name. + + Args: + org_name (str): name of the organization to set + set_org_id (bool): set the org_id on the object for later use. Defaults to None. + + Returns: + dict: { + org data + } + """ + orgs = self.get_organizations() + for org in orgs: + if org["name"] == org_name: + if set_org_id: + self.client.org_id = org["id"] + return org + + raise ValueError(f"Organization '{org_name}' not found") + def instance_information(self) -> dict: """Return the instance information. @@ -179,21 +210,25 @@ def get_organizations(self, brief: bool = True) -> Dict: ) return orgs - def get_buildings(self) -> list: - self.client.list(endpoint="properties", data_name="pagination", per_page=1)[ - "total" - ] - buildings = self.client.list( - endpoint="properties", - data_name="results", - per_page=100, - cycle=self.cycle_id, - ) + def get_buildings(self) -> List[dict]: + total_qry = self.client.list(endpoint="properties", data_name="pagination", per_page=100) + + # print(f" total: {total_qry}") + # step through each page of the results + buildings: List[dict] = [] + for i in range(1, total_qry['num_pages'] + 1): + buildings = buildings + self.client.list( + endpoint="properties", + data_name="results", + per_page=100, + page=i, + cycle=self.cycle_id, + ) + # print(f"number of buildings retrieved: {len(buildings)}") - # TODO: what to do with this if paginated? return buildings - def get_property(self, property_view_id: int) -> dict: + def get_property_view(self, property_view_id: int) -> dict: """Return a single property (view and state) by the property view id. Args: @@ -213,6 +248,29 @@ def get_property(self, property_view_id: int) -> dict: property_view_id, endpoint="property_views", data_name="property_views" ) + def get_property(self, property_id: int) -> dict: + """Return a single property by the property id. + + Args: + property__id (int): ID of the property to return. This is the ID that is in the URL http://SEED_URL/app/#/properties/{property_view_id} + + Returns: + dict: { + 'state': { + 'extra_data': {}, + }, + 'cycle': {...}, + 'property': {...}, + 'labels': {...}, + 'measures': {...} + ... + } + """ + # NOTE: this seems to be the call that OEP uses (returns property and labels dictionaries) + return self.client.get( + property_id, endpoint="properties", data_name="properties" + ) + def search_buildings( self, identifier_filter: str = None, identifier_exact: str = None ) -> dict: @@ -584,27 +642,6 @@ def get_cycle_by_name(self, cycle_name: str, set_cycle_id: bool = None) -> dict: raise ValueError(f"cycle '{cycle_name}' not found") - def get_org_by_name(self, org_name: str, set_org_id: bool = False) -> dict: - """Set the current organization by name. - - Args: - org_name (str): name of the organization to set - set_org_id (bool): set the org_id on the object for later use. Defaults to None. - - Returns: - dict: { - org data - } - """ - orgs = self.get_organizations() - for org in orgs: - if org["name"] == org_name: - if set_org_id: - self.client.org_id = org["id"] - return org - - raise ValueError(f"Organization '{org_name}' not found") - def delete_cycle(self, cycle_id: str) -> dict: """Delete the cycle. This will only work if there are no properties or tax lots in the cycle diff --git a/pyseed/seed_client_base.py b/pyseed/seed_client_base.py index eb747a7..08f6daa 100644 --- a/pyseed/seed_client_base.py +++ b/pyseed/seed_client_base.py @@ -233,7 +233,7 @@ def _check_response(self, response, *args, **kwargs): # this is a system matching response, which is okay. return the success flag of this status_flag = response.json()['progress_data'].get('status', None) error = status_flag not in ['not-started', 'success', 'parsing'] - elif not any(key in ['results', 'readings', 'data', 'status', 'id', 'organizations'] for key in response.json().keys()): + elif not any(key in ['results', 'readings', 'data', 'status', 'id', 'organizations', 'sha'] for key in response.json().keys()): # In some cases there is not a 'status' field, so check if there are # any other keys in the response that depict a success: # readings - this comes from meters @@ -241,6 +241,7 @@ def _check_response(self, response, *args, **kwargs): # status - sometimes the status comes back as complete # id - For some object creates, the response is simply the object back in JSON format with an ID field. # organizations - this is the only key when returning the list of orgs + # sha - When parsing the version of SEED error = True elif not isinstance(response.json(), list): diff --git a/setup.cfg b/setup.cfg index 8deb353..384d4a4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,6 +15,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 [options] packages = find: diff --git a/tests/test_seed_client.py b/tests/test_seed_client.py index 21022b4..5291265 100644 --- a/tests/test_seed_client.py +++ b/tests/test_seed_client.py @@ -87,9 +87,9 @@ def test_seed_orgs(self): orgs = self.seed_client.get_organizations() assert len(orgs) > 0 - # need to figure out what org to look for - # org = self.seed_client.get_org_by_name('nrel', set_org_id=False) - # assert org['name'] == 'nrel' + def test_seed_client_info(self): + info = self.seed_client.instance_information() + assert set(("version", "sha")).issubset(info.keys()) def test_seed_buildings(self): buildings = self.seed_client.get_buildings() @@ -100,10 +100,16 @@ def test_search_buildings(self): assert len(properties) == 1 prop = self.seed_client.get_property(properties[0]["id"]) + assert prop["state"]["address_line_1"] == "111 Street Lane, Chicago, IL" + assert prop["state"]["extra_data"]["EUI Target"] == 120 + + # test the property view (same as previous, just less data) + prop = self.seed_client.get_property_view(properties[0]["id"]) assert prop["id"] == properties[0]["id"] assert prop["state"]["address_line_1"] == "111 Street Lane, Chicago, IL" assert prop["state"]["extra_data"]["EUI Target"] == 120 + # There are 2 if filtering, because B-1 and B-10 properties = self.seed_client.search_buildings(identifier_filter="B-1") assert len(properties) == 2