Skip to content

Commit

Permalink
Merge pull request #13 from Myoldmopar/Polishing
Browse files Browse the repository at this point in the history
Update so many things, major renovation, added cli, etc.
  • Loading branch information
Myoldmopar authored Nov 21, 2022
2 parents b96feb9 + 6cfc24b commit 34039c6
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 61 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ omit =
setup.py
*distutils*
*.pyenv*
*pyiddidf/cli.py*
132 changes: 132 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# EnergyPlus Python IDD/IDF Utilities

Python library of EnergyPlus IDD/IDF manipulation utilities.

## Documentation

[![Documentation Status](https://readthedocs.org/projects/energyplus-idd-idf/badge/?version=latest)](https://energyplus-idd-idf.readthedocs.io/en/latest/?badge=latest)

Documentation is hosted on ReadTheDocs at https://energyplus-idd-idf.readthedocs.io/en/latest/.
To build the documentation, enter the docs/ subdirectory and execute `make html`; then open
`/docs/_build/html/index.html` to see the documentation.

## Installation

This package is deployed to PyPi at https://badge.fury.io/py/energyplus-idd-idf-utilities.
To install, simply `pip install energyplus-idd-idf-utilities`.

## Basic Usage

Once installed, the utilities are available for use as a library of functionality to call from Python, or with a very limited (for now) CLI called `energyplus_idd_idf`.
Some example CLI calls:

Get the CLI form:

```shell
$ ./some_python_venv/bin/energyplus_idd_idf --help
usage: energyplus_idd_idf [-h] [--idd_check] [--idd_obj_matches IDD_OBJ_MATCHES] [--summarize_idd_object SUMMARIZE_IDD_OBJECT] filename

EnergyPlus IDD/IDF Utility Command Line

positional arguments:
filename Path to IDD/IDF file to be operated upon

optional arguments:
-h, --help show this help message and exit
--idd_check Process the given IDD file and report statistics and issues
--idd_obj_matches IDD_OBJ_MATCHES
Find IDD objects that match the given basic pattern
--summarize_idd_object SUMMARIZE_IDD_OBJECT
Print a summary of a single IDD object by name

This CLI is in infancy and will probably have features added over time

```

Check an existing IDD file and get basic information:

```shell
$ ./some_python_venv/bin/energyplus_idd_idf --idd_check /path/to/EnergyPlus-22-2-0/Energy+.idd
{
"message": "Everything looks OK",
"content": {
"idd_version": "22.2.0",
"idd_build_id": "c249759bad",
"num_groups": 59,
"num_objects": 881
}
}

```

Find all objects which match a certain name pattern:

```shell
$ ./some_python_venv/bin/energyplus_idd_idf --idd_obj_matches 'Coil:Cooling*' /path/to/EnergyPlus-22-2-0/Energy+.idd
{
"message": "Everything looks OK",
"content": {
"pattern": "Coil:Cooling*",
"matching_objects": [
"Coil:Cooling:Water",
"Coil:Cooling:Water:DetailedGeometry",
"Coil:Cooling:DX",
"Coil:Cooling:DX:CurveFit:Performance",
"Coil:Cooling:DX:CurveFit:OperatingMode",
"Coil:Cooling:DX:CurveFit:Speed",
"Coil:Cooling:DX:SingleSpeed",
"Coil:Cooling:DX:TwoSpeed",
"Coil:Cooling:DX:MultiSpeed",
"Coil:Cooling:DX:VariableSpeed",
"Coil:Cooling:DX:TwoStageWithHumidityControlMode",
"Coil:Cooling:DX:VariableRefrigerantFlow",
"Coil:Cooling:DX:VariableRefrigerantFlow:FluidTemperatureControl",
"Coil:Cooling:WaterToAirHeatPump:ParameterEstimation",
"Coil:Cooling:WaterToAirHeatPump:EquationFit",
"Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit",
"Coil:Cooling:DX:SingleSpeed:ThermalStorage"
]
}
}

```

Get specific details about a single object by name:

```shell
$ ./some_python_venv/bin/energyplus_idd_idf /path/to/EnergyPlus-22-2-0/Energy+.idd --summarize_idd_object "Coil:Cooling:DX"
{
"message": "Everything looks OK",
"content": {
"searched_object_name": "COIL:COOLING:DX",
"field": [
"A1 : Name",
"A2 : Evaporator Inlet Node Name",
"A3 : Evaporator Outlet Node Name",
"A4 : Availability Schedule Name",
"A5 : Condenser Zone Name",
"A6 : Condenser Inlet Node Name",
"A7 : Condenser Outlet Node Name",
"A8 : Performance Object Name",
"A9 : Condensate Collection Water Storage Tank Name",
"A10 : Evaporative Condenser Supply Water Storage Tank Name"
]
}
}
```

## Testing

[![Flake8](https://github.com/Myoldmopar/py-idd-idf/actions/workflows/flake8.yml/badge.svg)](https://github.com/Myoldmopar/py-idd-idf/actions/workflows/flake8.yml)
[![Run Tests](https://github.com/Myoldmopar/py-idd-idf/actions/workflows/test.yml/badge.svg)](https://github.com/Myoldmopar/py-idd-idf/actions/workflows/test.yml)

The source is tested using the python unittest framework.
To execute all the unit tests, simply run `nosetests` from the project root.
The tests are also executed by GitHub Actions for each commit.

## Test Coverage

[![Coverage Status](https://coveralls.io/repos/github/Myoldmopar/py-idd-idf/badge.svg?branch=master)](https://coveralls.io/github/Myoldmopar/py-idd-idf?branch=master)

Coverage of the code from unit testing is reported to Coveralls at https://coveralls.io/github/Myoldmopar/py-idd-idf.
Anything less than 100% coverage will be frowned upon. :)
42 changes: 0 additions & 42 deletions README.rst

This file was deleted.

2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand Down
2 changes: 1 addition & 1 deletion docs/idd_objects.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
IDD Object Module Documentation
===============================

.. automodule:: pyiddidf.idd.objects
.. automodule:: pyiddidf.idd_objects
:members:
:undoc-members:
:show-inheritance:
Expand Down
2 changes: 1 addition & 1 deletion docs/idd_processor.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
IDD Processor Module Documentation
==================================

.. automodule:: pyiddidf.idd.processor
.. automodule:: pyiddidf.idd_processor
:members:
:undoc-members:
:show-inheritance:
Expand Down
2 changes: 1 addition & 1 deletion docs/idf_objects.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
IDF Object Module Documentation
===============================

.. automodule:: pyiddidf.idf.objects
.. automodule:: pyiddidf.idf_objects
:members:
:undoc-members:
:show-inheritance:
Expand Down
2 changes: 1 addition & 1 deletion docs/idf_processor.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
IDF Processor Module Documentation
==================================

.. automodule:: pyiddidf.idf.processor
.. automodule:: pyiddidf.idf_processor
:members:
:undoc-members:
:show-inheritance:
Expand Down
119 changes: 119 additions & 0 deletions pyiddidf/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from argparse import ArgumentParser
from fnmatch import fnmatch
from json import dumps
from functools import reduce
from pathlib import Path
from sys import exit
from typing import Optional

from pyiddidf.exceptions import ProcessingException
from pyiddidf.idd_objects import IDDObject
from pyiddidf.idd_processor import IDDProcessor


class Actions:
IDDCheck = 'idd_check'
FindIDDObjectsMatching = 'find_idd_objects_matching'
SummarizeIDDObject = 'summarize_idd_object'


class ExitCodes:
OK = 0
ProcessingError = 1
BadArguments = 2


# Eventually this could become a more feature rich CLI
def main_cli() -> int:
parser = ArgumentParser(
prog="energyplus_idd_idf",
description="EnergyPlus IDD/IDF Utility Command Line",
epilog="This CLI is in infancy and will probably have features added over time"
)
parser.add_argument('filename', help="Path to IDD/IDF file to be operated upon") # positional argument
parser.add_argument(
'--idd_check', action='store_const', const=Actions.IDDCheck,
help="Process the given IDD file and report statistics and issues"
)
parser.add_argument(
'--idd_obj_matches', type=str, help="Find IDD objects that match the given basic pattern"
)
parser.add_argument(
'--summarize_idd_object', type=str, help="Print a summary of a single IDD object by name"
)
args = parser.parse_args()
all_options = [
args.idd_check, args.idd_obj_matches, args.summarize_idd_object
]
if all([x is None for x in all_options]):
print(dumps({'message': "Nothing to do...use command line switches to perform operations"}, indent=2))
return ExitCodes.OK
p = Path(args.filename)
if not p.exists():
print(dumps({'message': "Supplied file does not appear to exist, check paths and retry!"}, indent=2))
return ExitCodes.BadArguments
# for now assume it's always the IDD so we don't have to repeat this code
processor = IDDProcessor()
try:
processor.process_file_given_file_path(str(p))
except ProcessingException:
print("Issues occurred during processing")
return ExitCodes.ProcessingError
if args.idd_check:
num_groups = len(processor.idd.groups)
num_objects = reduce(
lambda x, y: x + y,
[len(g.objects) for g in processor.idd.groups],
0
)
print(dumps({
'message': 'Everything looks OK',
'content': {
'idd_version': processor.idd.version_string,
'idd_build_id': processor.idd.build_string,
'num_groups': num_groups,
'num_objects': num_objects
}
}, indent=2))
elif args.idd_obj_matches:
pattern = args.iddobjmatches
matching_objects = []
for g in processor.idd.groups:
for o in g.objects:
obj_name = o.name
if fnmatch(obj_name, pattern):
matching_objects.append(o)
print(dumps({
'message': 'Everything looks OK',
'content': {
'pattern': pattern,
'matching_objects': [
o.name for o in matching_objects
]
}
}, indent=2))
elif args.summarize_idd_object:
object_name = args.summarize_idd_object.upper()
matching_object: Optional[IDDObject] = None
for g in processor.idd.groups:
for o in g.objects:
obj_name = o.name.upper()
if fnmatch(obj_name, object_name):
matching_object = o
if matching_object is None:
print(dumps({'message': f"Could not find matching object by name {object_name}"}, indent=2))
return ExitCodes.BadArguments
print(dumps({
'message': 'Everything looks OK',
'content': {
'searched_object_name': object_name,
'field': [
f"{f.field_an_index} : {f.field_name}" for f in matching_object.fields
]
}
}, indent=2))
return ExitCodes.OK


if __name__ == "__main__": # pragma: no cover
exit(main_cli())
Loading

0 comments on commit 34039c6

Please sign in to comment.