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

Feature/dei 24 tests for parser #7

Merged
merged 7 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,4 @@ dmypy.json
# test files
input_file.yaml
**/*.nc
!tests/**/*.nc
3 changes: 2 additions & 1 deletion decoimpact/business/entities/rules/multiply_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ def __init__(
input_variable_names: List[str],
multipliers: List[float],
output_variable_name: str = "output",
description: str = "",
):
super().__init__(name, input_variable_names, output_variable_name)
super().__init__(name, input_variable_names, output_variable_name, description)
self._multipliers = multipliers

@property
Expand Down
3 changes: 2 additions & 1 deletion decoimpact/business/entities/rules/rule_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ def __init__(
name: str,
input_variable_names: List[str],
output_variable_name: str = "output",
description: str = ""
):

self._name = name
self._description = ""
self._description = description
self._input_variable_names = input_variable_names
self._output_variable_name = output_variable_name

Expand Down
5 changes: 5 additions & 0 deletions decoimpact/data/api/i_rule_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ class IRuleData(ABC):
def name(self) -> str:
"""Name of the rule"""

@property
@abstractmethod
def description(self) -> str:
"""Description of the rule"""

@property
@abstractmethod
def output_variable(self) -> str:
Expand Down
9 changes: 4 additions & 5 deletions decoimpact/data/entities/dataset_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, dataset: dict[str, Any]):
"""Create DatasetData based on provided info dictionary

Args:
info (dict[str, Any]):
dataset (dict[str, Any]):
"""
super()
self._path = Path(get_dict_element("filename", dataset)).resolve()
Expand Down Expand Up @@ -54,13 +54,12 @@ def _get_original_dataset(self) -> _xr.Dataset:
raise NotImplementedError(message)

try:
dataset: _xr.Dataset = _xr.open_dataset(self._path,
mask_and_scale=True)
dataset: _xr.Dataset = _xr.open_dataset(self._path, mask_and_scale=True)
# mask_and_scale argument is needed to prevent inclusion of NaN's
# in dataset for missing values. This inclusion converts integers
# to floats
except OSError as exc:
except ValueError as exc:
msg = "ERROR: Cannot open input .nc file -- " + str(self._path)
raise OSError(msg) from exc
raise ValueError(msg) from exc

return dataset
10 changes: 3 additions & 7 deletions decoimpact/data/entities/model_data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@ class ModelDataBuilder:
read from the input file to Rule and DatasetData objects)"""

def __init__(self) -> None:
""""""
"""Create ModelDataBuilder"""
self._rule_parsers = list(rule_parsers())

def parse_yaml_data(self, contents: dict[Any, Any]) -> IModelData:
"""_summary_
Args:
contents (dict[Any, Any]): _description_
Returns:
IModelData: _description_
"""
"""Parse the Yaml input file into a data object"""
print('contennts', contents)
Copy link
Contributor

Choose a reason for hiding this comment

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

print statement can be removed

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to add a description for the parameter ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

check, added it. Have now on multiple locations a default for the description.
print statement removed


datasets = list(self._parse_datasets(contents))
rules = list(self._parse_rules(contents))
Expand Down
5 changes: 3 additions & 2 deletions decoimpact/data/entities/multiply_rule_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ def __init__(
name: str,
multipliers: List[float],
input_variable: str,
output_variable: str,
output_variable: str = "output",
description: str = ""
):
super().__init__(name, output_variable)
super().__init__(name, output_variable, description)
self._input_variable = input_variable
self._multipliers = multipliers

Expand Down
11 changes: 10 additions & 1 deletion decoimpact/data/entities/rule_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,31 @@
class RuleData(IRuleData):
"""Class for storing rule information"""

def __init__(self, name: str, output_variable: str):
def __init__(
self, name: str, output_variable: str = "output", description: str = ""
):
"""Create RuleData based on provided info dictionary

Args:
info (dict[str, Any]):
"""
super()
self._name = name
self._description = description
self._output_variable = output_variable

@property
def name(self) -> str:
"""Name to the rule"""
return self._name

@property
def description(self) -> str:
"""Description of the rule"""
return self._description

@property
def output_variable(self) -> str:
"""Data of the rule data"""
return self._output_variable

7 changes: 6 additions & 1 deletion decoimpact/data/parsers/parser_multiply_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Module for ParserMultiplyRule class

Classes:
MultiplyRuleParser
ParserMultiplyRule
"""
from typing import Any, Dict

Expand Down Expand Up @@ -32,6 +32,11 @@ def parse_dict(self, dictionary: Dict[str, Any]) -> IRuleData:
name = get_dict_element("name", dictionary)
input_variable_name = get_dict_element("input_variable", dictionary)
multipliers = get_dict_element("multipliers", dictionary)

if not all(isinstance(m, (int, float)) for m in multipliers):
message = f"""Multipliers should be a list of floats, \
received: {multipliers}"""
raise ValueError(message)
output_variable_name = get_dict_element("output_variable", dictionary)

return MultiplyRuleData(
Expand Down
2 changes: 1 addition & 1 deletion tests/business/entities/rules/test_multiply_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_execute_value_array_multiplied_by_multipliers():
"""Test setting input_variable_names of a RuleBase"""

# Arrange & Act
rule = MultiplyRule("test", ["foo"], [0.5, 4.0])
rule = MultiplyRule("test", ["foo"], [0.5, 4.0], "description")
data = [1, 2, 3, 4]
value_array = _xr.DataArray(data)
multiplied_array = rule.execute(value_array)
Expand Down
13 changes: 13 additions & 0 deletions tests/business/entities/test_rule_based_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ def test_create_rule_based_model_with_defaults():
assert model.status == ModelStatus.CREATED


def test_status_setter():
# Arrange
rule = Mock(IRule)
dataset = Mock(IDatasetData)

# Act
model = RuleBasedModel([dataset], [rule])

assert model.status == ModelStatus.CREATED
model.status = ModelStatus.EXECUTED
assert model.status == ModelStatus.EXECUTED


def test_validation_of_rule_based_model():
"""Test if the model correctly validates for required
parameters (datasets, rules)
Expand Down
4 changes: 1 addition & 3 deletions tests/business/workflow/test_model_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,5 @@ def test_create_rule_based_model_with_non_supported_rule():
exception_raised = exc_info.value

# Assert
expected_message = "The rule type of rule 'test' is currently "\
"not implemented"
expected_message = "The rule type of rule 'test' is currently " "not implemented"
assert exception_raised.args[0] == expected_message

26 changes: 26 additions & 0 deletions tests/data/entities/test_dataset_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Tests for DatasetData class
"""

from pathlib import Path

import pytest
import xarray as _xr

Expand Down Expand Up @@ -86,3 +88,27 @@ def test_dataset_data_get_input_dataset_should_check_if_extension_is_correct():
assert exception_raised.args[0].endswith(
"Currently only UGrid (NetCDF) files are supported."
)


def test_dataset_data_get_input_dataset_should_not_read_incorrect_file():
"""When calling get_input_dataset on a dataset should
read the specified IDatasetData.path. If the file is not correct (not
readable), raise OSError.
"""

# Arrange
path = get_test_data_path() + "/FlowFM_net_incorrect.nc"
data_dict = {"filename": path, "variable_mapping": {"test": "test_new"}}
data = DatasetData(data_dict)

# Act
with pytest.raises(ValueError) as exc_info:
data.get_input_dataset()

exception_raised = exc_info.value
# Assert

path = Path(path).resolve()
assert exception_raised.args[0].endswith(
f"ERROR: Cannot open input .nc file -- " + str(path)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test incorrect nc
56 changes: 56 additions & 0 deletions tests/data/entities/test_model_data_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Tests for ModelDataBuilder class
"""

import pytest

from decoimpact.data.api.i_model_data import IModelData
from decoimpact.data.entities.model_data_builder import ModelDataBuilder

contents = dict(
{
"input-data": [
{"dataset": {"filename": "test", "variabel_mapping": {"foo": "bar"}}}
],
"rules": [
{
"multiply_rule": {
"name": "testrule",
"description": "testdescription",
"multipliers": [1, 2.0],
"input_variable": "testin",
"output_variable": "testout",
}
}
],
}
)


def test_model_data_builder_parse_dict_to_model_data():
"""The ModelDataBuilder should parse the provided dictionary
to a IModelData object"""

# Act
data = ModelDataBuilder()

parsed_data = data.parse_yaml_data(contents)
assert isinstance(parsed_data, IModelData)


def test_model_data_builder_gives_error_when_rule_not_defined():
"""The ModelDataBuilder should parse the provided dictionary
to a IModelData object"""

# Act
data = ModelDataBuilder()
contents["rules"] = [{"wrong_rule": "test"}]

with pytest.raises(KeyError) as exc_info:
data.parse_yaml_data(contents)

exception_raised = exc_info.value

# Assert
exc = exception_raised.args[0]
assert exc.endswith(f"No parser for wrong_rule")
20 changes: 20 additions & 0 deletions tests/data/entities/test_multiply_rule_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Tests for MultiplyRuleData class
"""

from decoimpact.data.api.i_rule_data import IRuleData
from decoimpact.data.entities.multiply_rule_data import MultiplyRuleData


def test_multiply_rule_data_creation_logic():
"""The MultiplyRuleData should parse the provided dictionary
to correctly initialize itself during creation"""

# Act
data = MultiplyRuleData("test_name", [1.0, 2.0], "input", "output", "description")

# Assert

assert isinstance(data, IRuleData)
assert data.input_variable == "input"
assert data.multipliers == [1.0, 2.0]
3 changes: 2 additions & 1 deletion tests/data/entities/test_rule_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ def test_rule_data_creation_logic():
to correctly initialize itself during creation"""

# Act
data = RuleData("test_name", "foo")
data = RuleData("test_name", output_variable="foo")

# Assert

assert isinstance(data, IRuleData)
assert data.name == "test_name"
assert data.description == ""
assert data.output_variable == "foo"
14 changes: 12 additions & 2 deletions tests/data/entities/test_yaml_model_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
"""


from unittest.mock import Mock

from decoimpact.data.api.i_dataset import IDatasetData
from decoimpact.data.api.i_model_data import IModelData
from decoimpact.data.api.i_rule_data import IRuleData
from decoimpact.data.entities.dataset_data import DatasetData
from decoimpact.data.entities.multiply_rule_data import MultiplyRuleData
from decoimpact.data.entities.yaml_model_data import YamlModelData


Expand All @@ -12,8 +18,8 @@ def test_yaml_model_data_default_settings_and_type():
interface and gives the right default settings"""

# Arrange
datasets = []
rules = []
datasets = [Mock(DatasetData)]
rules = [Mock(MultiplyRuleData)]

# Act
model_data = YamlModelData("Model 1", datasets, rules)
Expand All @@ -24,3 +30,7 @@ def test_yaml_model_data_default_settings_and_type():
assert isinstance(model_data, IModelData)

assert model_data.name == "Model 1"
assert model_data.datasets == datasets
assert isinstance(model_data.datasets[0], IDatasetData)
assert model_data.rules == rules
assert isinstance(model_data.rules[0], IRuleData)
Loading