Skip to content

Commit

Permalink
Client cleanup (#68)
Browse files Browse the repository at this point in the history
* streamline tests

* remove unused files

* update readme

* update actions dependencies

* wait for web api to be ready before starting tests
  • Loading branch information
TShapinsky authored Jun 26, 2024
1 parent b0b4e09 commit 302b78b
Show file tree
Hide file tree
Showing 29 changed files with 140 additions and 573,169 deletions.
13 changes: 9 additions & 4 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ jobs:
python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Install Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install poetry
uses: abatilo/actions-poetry@v2.0.0
uses: abatilo/actions-poetry@v3
with:
poetry-version: "1.3.1"
poetry-version: 1.8.3

- name: Start Alfalfa
uses: NREL/alfalfa-action@v1
Expand All @@ -36,6 +36,11 @@ jobs:
compose-file-ref: "develop"
worker-scale: 2

- name: Wait for web API
uses: iFaxity/[email protected]
with:
resource: http://localhost

- name: Run tests with poetry and pytest
run: |
poetry install
Expand Down
42 changes: 19 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The purpose of this repository is to provide a standalone client for use with the Alfalfa application. It additionally includes a Historian to quickly/easily enable saving of results from Alfalfa simulations.

# Usage
## Usage

This repo is packaged and hosted on [PyPI here](https://pypi.org/project/alfalfa-client/).

Expand All @@ -11,45 +11,41 @@ pip install alfalfa-client
```

```python
import alfalfa_client.alfalfa_client as ac
import alfalfa_client.historian as ah
from alfalfa_client.alfalfa_client import AlfalfaClient

client = ac.AlfalfaClient
historian = ah.Historian
client = AlfalfaClient("http://localhost")
```

# Setup and Testing
Additional documentation for the functions of `alfalfa-client` can be found [here](https://nrel.github.io/alfalfa-client/).

This repository is setup to use:
## Development

Prerequisites:

- [pyenv](https://github.com/pyenv/pyenv#installation) for managing python versions
- [poetry](https://python-poetry.org/docs/#installation) for managing environment
- [pre-commit](https://pre-commit.com/#install) for managing code styling
- tox for running tests in isolated build environments. See the expected python versions in [tox.ini](./tox.ini)

Assuming poetry is installed and the necessary python versions are installed, the following should exit cleanly:
Cloning and Installing:

```bash
git clone https://github.com/NREL/alfalfa-client.git
cd alfalfa-client
poetry run tox
poetry install
```

This may take some time resolving on the initial run, but subsequent runs should be faster.

See [this gist](https://gist.github.com/corymosiman12/26fb682df2d36b5c9155f344eccbe404) for additional info.

# History
Running Tests:
All `alfalfa-client` tests currently require a running instance of [Alfalfa](https://github.com/NREL/alfalfa) with at least 2 workers.

- The implemented client is previously referred to as Boptest, from the alfalfa/client/boptest.py implementation. It has been ported as a standalone package for easier usage across projects.
```bash
poetry run pytest -m integration
```

# Releasing
## Releasing

1. Merge all branches into develop, make sure tests pass
1. Finish merging PRs into develop
1. Confirm all tests pass
1. Update the version (assume version is 0.1.2): `poetry version 0.1.2`
1. Update the version test file (i.e. my-repo/tests/test_version.py) to match the above version
1. Make sure tests pass: `poetry run tox`
1. Merge develop into main (previously, master), make sure tests pass
1. Create PR to merge version update
1. Rebase develop onto main, make sure tests pass
1. Create a tag: `git tag 0.1.2`
1. Build: `poetry build`
1. Publish `poetry publish` (this will push to pypi)
Expand Down
13 changes: 5 additions & 8 deletions alfalfa_client/alfalfa_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
from urllib.parse import urljoin

import requests
from requests.exceptions import HTTPError
from requests_toolbelt import MultipartEncoder

from alfalfa_client.lib import (
Expand Down Expand Up @@ -81,13 +80,9 @@ def _request(self, endpoint: str, method="POST", parameters=None) -> requests.Re
else:
response = requests.request(method=method, url=self.url + endpoint)

if response.status_code == 400 or response.status_code == 500:
if response.status_code >= 400:
try:
body = response.json()
exception = AlfalfaAPIException(body["message"])
if "payload" in body:
exception.add_payload(json.dumps(body["payload"]))
raise exception
raise AlfalfaAPIException(response)
except json.JSONDecodeError:
pass
response.raise_for_status()
Expand Down Expand Up @@ -129,7 +124,7 @@ def wait(self, run_id: Union[RunID, List[RunID]], desired_status: str, timeout:
while time() - timeout < start_time:
try:
current_status = self.status(run_id)
except HTTPError as e:
except AlfalfaAPIException as e:
if e.response.status_code != 404:
raise e

Expand Down Expand Up @@ -280,6 +275,8 @@ def set_inputs(self, run_id: str, inputs: dict) -> None:
id = self._get_point_translation(run_id, name)
if id:
point_writes[id] = value
else:
raise AlfalfaClientException(f"No Point exists with name {name}")
self._request(f"runs/{run_id}/points/values", method="PUT", parameters={'points': point_writes})

def get_outputs(self, run_id: str) -> dict:
Expand Down
11 changes: 9 additions & 2 deletions alfalfa_client/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
from pathlib import Path
from typing import List

from requests import Response


def parallelize(func):
"""Parallelize a function
Expand Down Expand Up @@ -121,8 +123,13 @@ class AlfalfaWorkerException(AlfalfaException):
class AlfalfaAPIException(AlfalfaException):
"""Wrapper for API errors"""

def add_payload(self, payload):
self.payload = payload
def __init__(self, response: Response, *args: object) -> None:
self.response = response
body = response.json()
super().__init__(body["message"], *args)

if "payload" in body:
self.payload = json.dumps(body["payload"])

def __str__(self) -> str:
if hasattr(self, "payload"):
Expand Down
6 changes: 0 additions & 6 deletions setup.cfg

This file was deleted.

2 changes: 1 addition & 1 deletion tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def pytest_generate_tests(metafunc):
model_dir = Path(os.path.dirname(__file__)) / 'models'
if "model_path" in metafunc.fixturenames:
model_paths = [
model_dir / 'refrig_case_osw'
model_dir / 'small_office'
]

metafunc.parametrize("model_path", model_paths)
Expand Down
4 changes: 0 additions & 4 deletions tests/integration/models/refrig_case_osw/empty.osm

This file was deleted.

Loading

0 comments on commit 302b78b

Please sign in to comment.