Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding validation of the number of options in the ApplyUpgrade measure #330

Merged
merged 7 commits into from Nov 30, 2022

Conversation

nmerket
Copy link
Member

@nmerket nmerket commented Nov 29, 2022

Fixes #329.

Pull Request Description

This checks the ApplyUpgrade measure and throws an error message with a link explaining how to fix it if there are more options than the measure allows.

Checklist

Not all may apply

  • Code changes (must work)
  • Tests exercising your feature/bug fix (check coverage report on Checks -> BuildStockBatch Tests -> Artifacts)
  • Coverage has increased or at least not decreased. Update minimum_coverage in .github/workflows/ci.yml as necessary.
  • All other unit tests passing
  • Update validation for project config yaml file changes
  • Update existing documentation
  • Run a small batch run to make sure it all works (local is fine, unless an Eagle specific feature)
  • Add to the changelog_dev.rst file and propose migration text in the pull request

Comment on lines +719 to +721
measure_xml_filename = os.path.join(
cfg["buildstock_directory"], "measures", "ApplyUpgrade", "measure.xml"
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the measure.xml because it exists in the same place in both ResStock and ComStock, and it's more straightforward and less error prone to parse xml from python than ruby code.

Comment on lines +722 to +735
if os.path.exists(measure_xml_filename):
measure_xml_tree = objectify.parse(measure_xml_filename)
measure_xml = measure_xml_tree.getroot()
n_options_in_measure = 0
n_costs_per_option_in_measure = 0
for argument in measure_xml.arguments.argument:
m_option = re.match(r"^option_(\d+)$", str(argument.name))
if m_option:
option_number = int(m_option.group(1))
n_options_in_measure = max(option_number, n_options_in_measure)
m_costs = re.match(r"^option_(\d+)_cost_(\d+)_value", str(argument.name))
if m_costs:
cost_number = int(m_costs.group(2))
n_costs_per_option_in_measure = max(cost_number, n_costs_per_option_in_measure)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm counting up the number of options and costs per option in the ApplyUpgrade measure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it speed things up to not check every option's number of costs (since we know it's the same number for every option)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already runs pretty fast enough. It seems like an unnecessary optimization to me. Since we have to loop over all the arguments anyway, it's just as easy and simpler to check on each one.

Comment on lines +736 to +743
n_options_in_cfg = 0
n_costs_in_cfg = 0
for upgrade in cfg.get("upgrades", []):
options = upgrade.get("options", [])
n_options = len(options)
n_costs = max(len(option.get("costs", [])) for option in options)
n_options_in_cfg = max(n_options, n_options_in_cfg)
n_costs_in_cfg = max(n_costs, n_costs_in_cfg)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I count up the number of options and costs per option required in the project file.

Comment on lines +744 to +754
err_msgs = []
if n_options_in_cfg > n_options_in_measure:
err_msgs.append(
f"There are {n_options_in_cfg} options in an upgrade in your project file and only "
f"{n_options_in_measure} in the ApplyUpgrade measure."
)
if n_costs_in_cfg > n_costs_per_option_in_measure:
err_msgs.append(
f"There are {n_costs_in_cfg} costs on an option in your project file and only "
f"{n_costs_per_option_in_measure} in the ApplyUpgrade measure."
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write some errors that tell them what's going on if there's going to be a problem.

Comment on lines +755 to +759
if err_msgs:
err_msgs.append(
"See https://github.com/NREL/buildstockbatch/wiki/Adding-Options-to-the-ApplyUpgrade-measure"
)
raise ValidationError("\n".join(err_msgs))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Send them to a page on the wiki explaining how to fix the problem.

Comment on lines +93 to +95
cfg = get_project_configuration(str(proj_filename))
cfg["upgrades"][-1]["options"] = cfg["upgrades"][-1]["options"] * 10
cfg["upgrades"][0]["options"][0]["costs"] = cfg["upgrades"][0]["options"][0]["costs"] * 5
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the test I add a lot more options so we bump into the validation error.

@@ -68,6 +68,7 @@ def run_tests(self):
'yamale',
'ruamel.yaml',
'awsretry',
'lxml'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need lxml to parse the xml. If adding yet another kind of heavy dependency gives anybody heartburn, I could begrudgingly rewrite this to just use element tree parser in the standard library.

@github-actions
Copy link

github-actions bot commented Nov 29, 2022

File Coverage
All files 86%
base.py 88%
eagle.py 74%
exc.py 57%
localdocker.py 63%
postprocessing.py 84%
utils.py 96%
sampler/base.py 79%
sampler/downselect.py 33%
sampler/precomputed.py 93%
sampler/residential_quota.py 83%
test/test_validation.py 96%
workflow_generator/base.py 90%
workflow_generator/commercial.py 53%
workflow_generator/residential.py 96%
workflow_generator/residential_hpxml.py 70%

Minimum allowed coverage is 33%

Generated by 🐒 cobertura-action against bbaeb14

Comment on lines +722 to +735
if os.path.exists(measure_xml_filename):
measure_xml_tree = objectify.parse(measure_xml_filename)
measure_xml = measure_xml_tree.getroot()
n_options_in_measure = 0
n_costs_per_option_in_measure = 0
for argument in measure_xml.arguments.argument:
m_option = re.match(r"^option_(\d+)$", str(argument.name))
if m_option:
option_number = int(m_option.group(1))
n_options_in_measure = max(option_number, n_options_in_measure)
m_costs = re.match(r"^option_(\d+)_cost_(\d+)_value", str(argument.name))
if m_costs:
cost_number = int(m_costs.group(2))
n_costs_per_option_in_measure = max(cost_number, n_costs_per_option_in_measure)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it speed things up to not check every option's number of costs (since we know it's the same number for every option)?

@@ -79,4 +79,4 @@ jobs:
if: ${{ matrix.python-version == '3.10' }}
with:
name: documentation
path: docs/_build/html/
path: buildstockbatch/docs/_build/html/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, unrelated. I missed it on a previous PR.


.. change::
:tags: validaton
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Ha. Thanks for catching all my hasty typos.

Adds some integration tests with the lastest ResStock.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

latest

@nmerket nmerket merged commit bab2b8c into develop Nov 30, 2022
@nmerket nmerket deleted the too-many-options branch November 30, 2022 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Validator request: catch too many options relative to constants.rb
2 participants