Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
94a52c1
Fix CLI (#290)
haneslinger Oct 25, 2023
9b041dd
Fix docker (#288)
haneslinger Oct 25, 2023
323fb48
Squish (#289)
haneslinger Oct 26, 2023
0c5983e
Exclude python 3.12 from support (#293)
TShapinsky Nov 1, 2023
c1bf14d
Refactor Table Connection (#291)
TShapinsky Nov 2, 2023
7ce4036
Documenting shape-to-template process and adding some QoL improvement…
gtfierro Nov 27, 2023
2115d17
Add Application Explorer (#292)
haneslinger Dec 5, 2023
ea5eeac
Generate SPARQL Queries from SHACL shapes (#273)
gtfierro Feb 14, 2024
87e2cbd
Fixing template inlining (#298)
gtfierro Feb 19, 2024
e374bc0
Allow Library to find template definitions from dependent libraries (…
gtfierro Mar 15, 2024
59fa3a4
Add ability to use TopQuadrant SHACL implementation as engine (#308)
gtfierro Apr 10, 2024
9a6d1aa
install rtd deps
MatthewSteen May 8, 2024
bc12f99
jupyter-book needs to be installed pre-build
MatthewSteen May 8, 2024
2dedaa6
cleanup and update to default config comments
MatthewSteen May 8, 2024
4794fd5
change from ubuntu:latest to python:3.9 for bacnet testing (#317)
TShapinsky May 9, 2024
69bbe68
Fix tutorials (#310)
gtfierro May 13, 2024
ab73790
Merge branch 'develop' into fix-rtd-deps
MatthewSteen May 15, 2024
a0811f5
Merge pull request #316 from NREL/fix-rtd-deps
MatthewSteen May 20, 2024
a200093
Add new transitive_parameters method to accelerate library loading (#…
gtfierro May 20, 2024
88f7daf
BMS naming convention parsing (#286)
gtfierro May 22, 2024
f1e5890
New model builder (#301)
gtfierro May 29, 2024
a5ed29d
Add graph hashing method to utils (#296)
TShapinsky May 30, 2024
af3f2fa
Create generic serializer deserializer for parsers (#322)
TShapinsky Jun 12, 2024
5633c0b
Strip param inlining (#325)
gtfierro Jun 26, 2024
1f14497
Changes to ingresses to support external work (#312)
gtfierro Jun 26, 2024
0b4c683
Fix autogeneration of templates (#329)
gtfierro Jul 3, 2024
82f6329
Update 223P to public advisory review version (#309)
gtfierro Jul 9, 2024
b1924b8
Create parser UI (#319)
haneslinger Jul 24, 2024
034b1ce
rename 5.16
MatthewSteen Aug 22, 2024
ff32526
cleanup 5.16 prefixes
MatthewSteen Aug 22, 2024
b3957ba
add 5.16 missing fault conditions 1 and 9-15
MatthewSteen Aug 22, 2024
e2c83ee
cleanup 5.16 fault conditions 2-8
MatthewSteen Aug 22, 2024
b9ee02e
typos
MatthewSteen Aug 22, 2024
bc4cbb9
add 5.17 fault conditions
MatthewSteen Aug 23, 2024
1ab3773
add 5.18 fault conditions
MatthewSteen Aug 23, 2024
11ca781
remove unused components prefix
MatthewSteen Aug 23, 2024
7eab13c
add 5.22 fault conditions
MatthewSteen Aug 23, 2024
d9b495e
update Operating State points
MatthewSteen Aug 23, 2024
d8a952b
changing the model of outside air fraction
gtfierro Aug 27, 2024
0263b78
remove internal variables (constants)
MatthewSteen Aug 30, 2024
dedbf77
Updating setuptool to use the newest version, bumping other deps too …
gtfierro Sep 2, 2024
64f58b7
Update NREL 223P Templates (#343)
gtfierro Sep 3, 2024
5c8df42
add %OAmin
MatthewSteen Sep 3, 2024
e0277b8
remove %OA
MatthewSteen Sep 3, 2024
df942c9
update test_model.py
MatthewSteen Sep 3, 2024
37f4a46
fix typo in FCU prefix
MatthewSteen Sep 3, 2024
6751fb4
Skip ipynb checkpoint directory (#330)
gtfierro Sep 4, 2024
ed725ff
Merge pull request #341 from NREL/guideline36-section5-afdd-air-side
MatthewSteen Sep 9, 2024
4e69315
Service clean up (#335)
haneslinger Sep 10, 2024
43e5b3e
Add test and fix for #326 (#327)
gtfierro Sep 12, 2024
06d969e
fix tests for windows (#318)
dllliu Sep 12, 2024
471ef84
Add explanation of point label parsing (#344)
gtfierro Sep 12, 2024
e24d741
Fixing runtime issues in Jupyter notebook (#348)
gtfierro Sep 16, 2024
39087f9
Update Brick to 1.4.1 (#346)
gtfierro Sep 18, 2024
849e672
Add parser vis (#334)
haneslinger Sep 18, 2024
fb17085
update pyproject.toml for release
MatthewSteen Sep 20, 2024
124cac3
Merge pull request #352 from NREL/0.3.0-version
MatthewSteen Sep 20, 2024
947483c
Merge branch 'main' into develop
MatthewSteen Sep 20, 2024
370da4b
fix CD action to publish to pypi once per release (#356)
MatthewSteen Oct 16, 2024
b1275d5
remove needs: deploy-docs from CD (#357)
MatthewSteen Oct 16, 2024
dd5b048
Improve validation report output (#320)
gtfierro Nov 19, 2024
7c09149
Add custom error classes (#360)
gtfierro Jan 30, 2025
7cb9a55
Add new model creation methods (#365)
gtfierro Feb 7, 2025
393d290
Add infer_templates method on shape collection (#361)
gtfierro Feb 7, 2025
fd1d17a
Add automatic development releases to pypi (#368)
TShapinsky Feb 19, 2025
891e498
Web API changes to support demo features (#362)
gtfierro Feb 26, 2025
5759706
Add CompiledModel class which can generate DataFrames and Tables (#359)
gtfierro Apr 16, 2025
2f80d77
Cascade DELETEs in the database (#363)
gtfierro Jun 5, 2025
06cd44f
Optimizations to Improve testing speed (#373)
TShapinsky Jun 16, 2025
8a001c9
0.4.0 docs (#378)
MatthewSteen Sep 19, 2025
64ae7a0
Merge branch 'main' into develop
MatthewSteen Sep 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
name: continuous deployment

on:
pull_request:
branches:
- develop
- main
push:
branches:
- develop
- main
release:
types:
- published

jobs:

# deploy docs for develop and main branches
# deploy docs if not release
deploy-docs:
if: github.event_name != 'release'
runs-on: ubuntu-latest
steps:
# setup, checkout pull_request.head.ref for repo-vis
Expand Down Expand Up @@ -56,10 +55,9 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/_build/html

# deploy distribution if a new release and tag are created
# deploy distribution if release
deploy-dist:
needs: deploy-docs
if: startsWith(github.ref, 'refs/tags')
if: github.event_name == 'release'
runs-on: ubuntu-latest
steps:
# setup
Expand Down Expand Up @@ -88,3 +86,41 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}

# deploy development distribution with changes to develop
deploy-dev-dist:
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: install-poetry
uses: snok/install-poetry@v1
with:
version: 1.4.0
virtualenvs-in-project: false
virtualenvs-path: ~/.virtualenvs
- name: install dependencies
run: poetry install --no-root --with=dev
- name: increment dev version
env:
PYPI_URL: https://pypi.org
run: poetry run python scripts/bump_dev_version.py
- name: build dist
run: poetry build
- name: publish dev distribution to Test PyPI
id: test-pypi
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
skip_existing: true
- name: publish distribution to PyPI
if: steps.test-pypi.outcome == 'success'
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
16 changes: 11 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.10', '3.11', '3.12']
steps:
- name: checkout
uses: actions/checkout@v4
Expand All @@ -62,12 +62,14 @@ jobs:
virtualenvs-path: ~/.virtualenvs
- name: poetry install
run: poetry install --all-extras
- name: install pytest-xdist for parallel tests
run: poetry run pip install pytest-xdist
- name: lint
run: poetry run flake8 buildingmotif
- name: type check
run: poetry run mypy --ignore-missing-imports
- name: unit tests
run: poetry run pytest tests/unit --cov=./ --cov-report=xml
run: poetry run pytest -n auto tests/unit --cov=./ --cov-report=xml
- name: build tests
run: poetry build

Expand Down Expand Up @@ -101,8 +103,10 @@ jobs:
virtualenvs-path: ~/.virtualenvs
- name: poetry install
run: poetry install --all-extras
- name: install pytest-xdist for parallel tests
run: poetry run pip install pytest-xdist
- name: integration tests
run: poetry run pytest tests/integration
run: poetry run pytest -n auto tests/integration
- name: bacnet tests
run: |
cd tests/integration/fixtures/bacnet
Expand Down Expand Up @@ -139,8 +143,10 @@ jobs:
virtualenvs-path: ~/.virtualenvs
- name: poetry install
run: poetry install --all-extras
- name: install pytest-xdist for parallel tests
run: poetry run pip install pytest-xdist
- name: library tests
run: poetry run pytest tests/library
run: poetry run pytest -n auto tests/library

coverage:
needs: testing
Expand All @@ -149,4 +155,4 @@ jobs:
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
files: ./coverage.xml
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- id: black
entry: poetry run black
- repo: https://github.com/pycqa/flake8
rev: 5.0.0
rev: 7.0.0
hooks:
- id: flake8
entry: poetry run flake8 buildingmotif
Expand Down
8 changes: 3 additions & 5 deletions buildingmotif/api/views/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from flask import Blueprint, current_app, jsonify
from flask_api import status
from rdflib import URIRef
from sqlalchemy.orm.exc import NoResultFound

from buildingmotif.api.serializers.library import serialize
from buildingmotif.database.errors import LibraryNotFound
from buildingmotif.dataclasses.shape_collection import ShapeCollection

blueprint = Blueprint("libraries", __name__)
Expand Down Expand Up @@ -81,9 +81,7 @@ def get_library(library_id: int) -> flask.Response:
"""
try:
db_lib = current_app.building_motif.table_connection.get_db_library(library_id)
except NoResultFound:
return {
"message": f"No library with id {library_id}"
}, status.HTTP_404_NOT_FOUND
except LibraryNotFound:
return {"message": f"ID: {library_id}"}, status.HTTP_404_NOT_FOUND

return jsonify(serialize(db_lib)), status.HTTP_200_OK
52 changes: 30 additions & 22 deletions buildingmotif/api/views/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
from flask_api import status
from rdflib import Graph, URIRef
from rdflib.plugins.parsers.notation3 import BadSyntax
from sqlalchemy.orm.exc import NoResultFound

from buildingmotif.api.serializers.model import serialize
from buildingmotif.database.errors import (
LibraryNotFound,
ModelNotFound,
ShapeCollectionNotFound,
)
from buildingmotif.dataclasses import Library, Model, ShapeCollection

blueprint = Blueprint("models", __name__)
Expand Down Expand Up @@ -34,8 +38,8 @@ def get_model(models_id: int) -> flask.Response:
"""
try:
model = current_app.building_motif.table_connection.get_db_model(models_id)
except NoResultFound:
return {"message": f"No model with id {models_id}"}, status.HTTP_404_NOT_FOUND
except ModelNotFound:
return {"message": f"ID: {models_id}"}, status.HTTP_404_NOT_FOUND

return jsonify(serialize(model)), status.HTTP_200_OK

Expand All @@ -51,8 +55,8 @@ def get_model_graph(models_id: int) -> Graph:
"""
try:
model = Model.load(models_id)
except NoResultFound:
return {"message": f"No model with id {models_id}"}, status.HTTP_404_NOT_FOUND
except ModelNotFound:
return {"message": f"ID: {models_id}"}, status.HTTP_404_NOT_FOUND

g = Graph() + model.graph

Expand All @@ -70,8 +74,8 @@ def get_target_nodes(models_id: int) -> Graph:
"""
try:
model = Model.load(models_id)
except NoResultFound:
return {"message": f"No model with id {models_id}"}, status.HTTP_404_NOT_FOUND
except ModelNotFound:
return {"message": f"ID: {models_id}"}, status.HTTP_404_NOT_FOUND

result = model.graph.query(
"""
Expand Down Expand Up @@ -132,8 +136,8 @@ def update_model_graph(models_id: int) -> flask.Response:
"""
try:
model = Model.load(models_id)
except NoResultFound:
return {"message": f"No model with id {models_id}"}, status.HTTP_404_NOT_FOUND
except ModelNotFound:
return {"message": f"ID: {models_id}"}, status.HTTP_404_NOT_FOUND

if request.content_type != "application/xml":
return {
Expand All @@ -160,12 +164,16 @@ def validate_model(models_id: int) -> flask.Response:
# get model
try:
model = Model.load(models_id)
except NoResultFound:
return {"message": f"No model with id {models_id}"}, status.HTTP_404_NOT_FOUND
except ModelNotFound:
return {"message": f"ID: {models_id}"}, status.HTTP_404_NOT_FOUND

# we will read the shape collections from the input
shape_collections = []

# no body provided -- default to model manifest and default SHACL engine
# get shacl_engine from the query params, defaults to the current engine
shacl_engine = request.args.get("shacl_engine", None)

# no body provided -- default to model manifest
if request.content_length is None:
shape_collections = [model.get_manifest()]
else:
Expand All @@ -182,23 +190,23 @@ def validate_model(models_id: int) -> flask.Response:

if body is not None and not isinstance(body, dict):
return {"message": "body is not dict"}, status.HTTP_400_BAD_REQUEST
shape_collections = []
body = body if body is not None else {}
nonexistent_libraries = []
for library_id in body.get("library_ids", []):
try:
shape_collection = Library.load(library_id).get_shape_collection()
shape_collections.append(shape_collection)
except NoResultFound:
except LibraryNotFound:
nonexistent_libraries.append(library_id)
if len(nonexistent_libraries) > 0:
return {
"message": f"Libraries with ids {nonexistent_libraries} do not exist"
}, status.HTTP_400_BAD_REQUEST

# if shape_collections is empty, model.validate will default
# to the model's manifest
vaildation_context = model.validate(shape_collections)
# if shape_collections is empty, model.validate will default to the model's manifest
vaildation_context = model.validate(
shape_collections, error_on_missing_imports=False, shacl_engine=shacl_engine
)

return {
"message": vaildation_context.report_string,
Expand All @@ -215,8 +223,8 @@ def validate_shape(models_id: int) -> flask.Response:
# get model
try:
model = Model.load(models_id)
except NoResultFound:
return {"message": f"No model with id {models_id}"}, status.HTTP_404_NOT_FOUND
except ModelNotFound:
return {"message": f"ID: {models_id}"}, status.HTTP_404_NOT_FOUND

# get body
if request.content_type != "application/json":
Expand All @@ -239,7 +247,7 @@ def validate_shape(models_id: int) -> flask.Response:
try:
shape_collection = ShapeCollection.load(shape_collection_id)
shape_collections.append(shape_collection)
except NoResultFound:
except ShapeCollectionNotFound:
nonexistent_shape_collections.append(shape_collection_id)
if len(nonexistent_shape_collections) > 0:
return {
Expand All @@ -255,8 +263,8 @@ def validate_shape(models_id: int) -> flask.Response:
target_class = URIRef(body.get("target_class"))

# test
conformance = model.test_model_against_shapes(
shape_collections=shape_collections,
compiled = model.compile(shape_collections)
conformance = compiled.validate_model_against_shapes(
shapes_to_test=shape_uris,
target_class=target_class,
)
Expand Down
Loading
Loading