Skip to content

Commit

Permalink
Update CLI and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
lymereJ committed Oct 27, 2023
1 parent 3f9deff commit f7fe8e2
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 60 deletions.
1 change: 1 addition & 0 deletions copper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from copper.chiller import *
from copper.schema import *
from copper.constants import LOGGING_FORMAT
import sys

Expand Down
22 changes: 14 additions & 8 deletions copper/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import click, json, inspect

# import copper.Chiller as chiller
from copper.chiller import Chiller
import copper.schema


@click.group()
Expand All @@ -23,18 +23,23 @@ def cli():
@click.argument("input_file", type=click.File("rb"), required=True)
def run(input_file):
"""Run a set of Copper instructions through a JSON input file. See 'Using Copper's command line interface in the Quickstart Guide section of the documenation for more information."""

try:
f = json.load(input_file)

# Validate input file
assert copper.Schema(f).validate() == True
except:
raise ValueError("Could not read the input file. A JSON file is expected.")
for eqp, eqp_props in f.items():
for action in f["actions"]:
eqp_props = action["equipment"]
# Make sure that the equipment is supported by Copper
assert eqp_props["eqp_type"].lower() in [
assert eqp_props["type"].lower() in [
"chiller"
], "Equipment type not currently supported by Copper."

# Get properties for equipment type
eqp_type_props = inspect.getfullargspec(eval(eqp_props["eqp_type"]).__init__)[0]
eqp_type_props = inspect.getfullargspec(eval(eqp_props["type"]).__init__)[0]

# Set the equipment properties from input file
obj_args = {}
Expand All @@ -43,12 +48,13 @@ def run(input_file):
obj_args[p] = eqp_props[p]

# Create instance of the equipment
obj = eval(eqp_props["eqp_type"])(**obj_args)
obj = eval(eqp_props["type"])(**obj_args)

# Perform actions defined in input file
if "do" in list(eqp_props.keys()):
for action in eqp_props["do"]:
getattr(obj, action)(**eqp_props["do"][action])
func = action["function_call"]["function"]
del action["function_call"]["function"]
args = action["function_call"]
getattr(obj, func)(**args)


if __name__ == "__main__":
Expand Down
11 changes: 11 additions & 0 deletions copper/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,15 @@
====================================
Holds all the constants referenced in copper.
"""

import os

LOGGING_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
CHILLER_SCHEMA_PATH = os.path.join("./schema/", "copper.chiller.schema.json")
CHILLER_GENE_SCHEMA_PATH = os.path.join(
"./schema/", "copper.chiller.generate_set_of_curves.schema.json"
)
CHILLER_ACTION_SCHEMA_PATH = os.path.join(
"./schema/", "copper.chiller.action.schema.json"
)
SCHEMA_PATH = os.path.join("./schema/", "copper.schema.json")
43 changes: 43 additions & 0 deletions copper/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
schema.py
====================================
Validation of the CLI input files.
"""

import json, jsonschema, logging
from copper.constants import SCHEMA_PATH
from copper.constants import CHILLER_SCHEMA_PATH
from copper.constants import CHILLER_GENE_SCHEMA_PATH
from copper.constants import CHILLER_ACTION_SCHEMA_PATH

# Load schemas
schema_chiller = json.load(open(CHILLER_SCHEMA_PATH, "r"))
schema_chiller_gene = json.load(open(CHILLER_GENE_SCHEMA_PATH, "r"))
schema_chiller_action = json.load(open(CHILLER_ACTION_SCHEMA_PATH, "r"))
schema = json.load(open(SCHEMA_PATH, "r"))

# Define schema store for the validator
schema_store = {
"copper.chiller.schema.json": schema_chiller,
"copper.chiller.generate_set_of_curves.schema.json": schema_chiller_gene,
"copper.chiller.action.schema.json": schema_chiller_action,
"copper.schema.json": schema,
}


class Schema:
def __init__(self, input):
self.input = input
self.schema = schema
self.schema_store = schema_store
self.resolver = jsonschema.RefResolver.from_schema(schema, store=schema_store)
Validator = jsonschema.validators.validator_for(schema)
self.validator = Validator(self.schema, resolver=self.resolver)

def validate(self):
try:
self.validator.validate(self.input)
return True
except:
logging.critical("Input file is not valid.")
return
3 changes: 2 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ def test_cli_incorrect_file():
runner = CliRunner()
result = runner.invoke(run, ["test"])
assert "'test': No such file or directory" in result.output
assert result.exit_code == 2


def test_cli_correct(caplog):
caplog.set_level(logging.INFO)
runner = CliRunner()
result = runner.invoke(run, ["./tests/data/cli_input_file.json"])
assert "Target met after 13 generations."
assert result.exit_code == 0
62 changes: 11 additions & 51 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,16 @@
import json, jsonschema, os
import copper as cp
from unittest import TestCase


input_file = json.load(open("./tests/data/cli_input_file.json", "r"))
CHILLER_SCHEMA_PATH = os.path.join("./schema/", "copper.chiller.schema.json")
CHILLER_GENE_SCHEMA_PATH = os.path.join(
"./schema/", "copper.chiller.generate_set_of_curves.schema.json"
)
CHILLER_ACTION_SCHEMA_PATH = os.path.join(
"./schema/", "copper.chiller.action.schema.json"
)
SCHEMA_PATH = os.path.join("./schema/", "copper.schema.json")
schema_chiller = json.load(open(CHILLER_SCHEMA_PATH, "r"))
schema_chiller_gene = json.load(open(CHILLER_GENE_SCHEMA_PATH, "r"))
schema_chiller_action = json.load(open(CHILLER_ACTION_SCHEMA_PATH, "r"))
schema = json.load(open(SCHEMA_PATH, "r"))

schema_store = {
"copper.chiller.schema.json": schema_chiller,
"copper.chiller.generate_set_of_curves.schema.json": schema_chiller_gene,
"copper.chiller.action.schema.json": schema_chiller_action,
"copper.schema.json": schema,
}


class TestCurves(TestCase):
def test_chiller_schema(self):
resolver = jsonschema.RefResolver.from_schema(
schema_chiller, store=schema_store
)
Validator = jsonschema.validators.validator_for(schema_chiller)
validator = Validator(schema_chiller, resolver=resolver)
validator.validate(input_file["actions"][0]["equipment"])

def test_chiller_generate_set_of_curves(self):
resolver = jsonschema.RefResolver.from_schema(
schema_chiller_gene, store=schema_store
)
Validator = jsonschema.validators.validator_for(schema_chiller_gene)
validator = Validator(schema_chiller_gene, resolver=resolver)
validator.validate(input_file["actions"][0]["function_call"])

def test_chiller_action(self):
resolver = jsonschema.RefResolver.from_schema(
schema_chiller_action, store=schema_store
)
Validator = jsonschema.validators.validator_for(schema_chiller_action)
validator = Validator(schema_chiller_action, resolver=resolver)
validator.validate(input_file["actions"][0])

def test_copper(self):
resolver = jsonschema.RefResolver.from_schema(schema, store=schema_store)
Validator = jsonschema.validators.validator_for(schema)
validator = Validator(schema, resolver=resolver)
validator.validate(input_file)
def test_good_schema(self):
input_file = json.load(open("./tests/data/cli_input_file.json", "r"))
assert cp.Schema(input=input_file).validate()

def test_bad_schema(self):
input_file = json.load(open("./tests/data/cli_input_file.json", "r"))
input_file["actions"][0]["function_call"]["vars"] = 42.0
with self.assertLogs() as captured:
assert cp.Schema(input=input_file).validate() is None
self.assertTrue(captured[0][0].msg == "Input file is not valid.")

0 comments on commit f7fe8e2

Please sign in to comment.