From 59109433d90fec557e5d3048e5a72265a02a39e0 Mon Sep 17 00:00:00 2001 From: Tor-Einar Skog <tor-einar.skog@nibio.no> Date: Tue, 31 Jan 2023 14:44:21 +0100 Subject: [PATCH] Refactored reference_model out of this module --- src/vipscore_common/reference_model.py | 151 ------------------------- tests/test_reference_model.py | 96 ---------------- 2 files changed, 247 deletions(-) delete mode 100755 src/vipscore_common/reference_model.py delete mode 100644 tests/test_reference_model.py diff --git a/src/vipscore_common/reference_model.py b/src/vipscore_common/reference_model.py deleted file mode 100755 index 1fd3bf6..0000000 --- a/src/vipscore_common/reference_model.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/python3 - -LICENSE = """ -Copyright (c) 2023 NIBIO <http://www.nibio.no/>. - -This file is part of VIPSCore-Python-Common. -VIPSCore-Python-Common is free software: you can redistribute it and/or modify -it under the terms of the NIBIO Open Source License as published by -NIBIO, either version 1 of the License, or (at your option) any -later version. - -VIPSCore-Python-Common is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -NIBIO Open Source License for more details. - -You should have received a copy of the NIBIO Open Source License -along with VIPSCore-Python-Common. If not, see <http://www.nibio.no/licenses/>. -""" - -from .vips_model import VIPSModel -from .entities import Result, ModelConfiguration, WeatherObservation -from .data_utils import * -import numpy as np - -class ReferenceModel(VIPSModel): - """ - A reference implementation of the VIPSModel abstract class. Showcasing core functionality - and best practices - """ - - MODEL_ID = "REFERENCEM" - COPYRIGHT = "(c) 2023 NIBIO" - - THRESHOLD_LOW = 100.0 - THRESHOLD_MEDIUM = 300.0 - THRESHOLD_HIGH = 500.0 - - def set_configuration(self, model_configuration: ModelConfiguration): - if not isinstance(model_configuration, ModelConfiguration): - raise ValueError("%s is not a ModelConfiguration object" % model_configuration) - if model_configuration.model_id != ReferenceModel.MODEL_ID: - raise ValueError("%s is not the correct model ID!" % model_configuration.model_id) - - # Input data check - self.sowing_date = model_configuration.get_config_parameter_as_date("sowingDate") - self.timezone = model_configuration.get_config_parameter_as_timezone("timeZone") - # Weather data is turned into Pandas dataframe - self.df = get_dataframe_from_weather_observations( - get_weather_observations_from_json_list( - model_configuration.config_parameters["observations"] - ), - self.timezone - ) - - - - def determine_warning_status(self, TMDD: float) -> int: - """ - Used in get_result as a dataframe operation. Determines the warning status - based on the model thresholds - """ - if TMDD < ReferenceModel.THRESHOLD_MEDIUM: - return Result.WARNING_STATUS_NO_WARNING - if TMDD < ReferenceModel.THRESHOLD_HIGH: - return Result.WARNING_STATUS_MINOR_RISK - else: - return Result.WARNING_STATUS_HIGH_RISK - - - - def get_result(self) -> list[Result]: - """Get the results as a list of Result objects (TODO ref)""" - - # Calculate day degrees from sowingDate and as far as weather data goes - # Adjusting for base temperature - self.df["TMContrib"] = self.df["TM"].apply(get_temp_adjusted_for_base, args=(5,)) - # Aggregating the day degrees - self.df["TMDD"] = self.df["TMContrib"].cumsum() - # Adding the thresholds to the data frame - self.df["THRESHOLD_LOW"] = ReferenceModel.THRESHOLD_LOW - self.df["THRESHOLD_MEDIUM"] = ReferenceModel.THRESHOLD_MEDIUM - self.df["THRESHOLD_HIGH"] = ReferenceModel.THRESHOLD_HIGH - # For each day: check accumulated day-degrees and decide warning status - self.df["WARNING_STATUS"] = self.df["TMDD"].apply(self.determine_warning_status) - #print(self.df) - result = get_result_list_from_dataframe(self.df) - #print(result) - return result - - - @property - def model_id(self) -> str: - """10-character ID of the model. Must be unique (at least in the current system)""" - return ReferenceModel.MODEL_ID - - - @property - def license(self) -> str: - """Returns the license for this piece of software""" - return LICENSE - - @property - def copyright(self) -> str: - """Name of person/organization that holds the copyright, and contact information""" - return ReferenceModel.COPYRIGHT - - @property - def sample_config(self) -> dict: - """A sample configuration in JSON format (TODO check relation with Dict)""" - return """ - { - model_id:'REFERENCEM', - config_parameters: { - 'sowingDate': '2022-03-01', - 'observations': [ - {'timeMeasured': '2015-03-01T00:00:00+01:00', 'elementMeasurementTypeId': 'TM', 'logIntervalId': '2', 'value': '1.41025'}, - {'timeMeasured': '2015-03-02T00:00:00+01:00', 'elementMeasurementTypeId': 'TM', 'logIntervalId': '2', 'value': '2.87608333333333'}, - {'timeMeasured': '2015-03-03T00:00:00+01:00', 'elementMeasurementTypeId': 'TM', 'logIntervalId': '2', 'value': '1.00854166666667'}, - {'timeMeasured': '2015-03-04T00:00:00+01:00', 'elementMeasurementTypeId': 'TM', 'logIntervalId': '2', 'value': '-1.44675'} - ] - } -} - """ - - def get_model_name(self, language = VIPSModel.default_language) -> str: - """Returns the model name in the specified language (<a href="http://www.loc.gov/standards/iso639-2/php/English_list.php">ISO-639-2</a>)""" - return "Reference Model" - - def get_model_description(self, language = VIPSModel.default_language) -> str: - """Returns the model description in the specified language (<a href="http://www.loc.gov/standards/iso639-2/php/English_list.php">ISO-639-2</a>)""" - return """ - The model is a reference model for developers, showcasing best practices and functionalities of a model. - It's a simple day degree model for an imagined pest, and when thresholds are passed, the warning status progresses. - """ - - def get_warning_status_interpretation(self, language = VIPSModel.default_language) -> str: - """How to interpret the warning status (red-yellow-green, what does it mean?) in the specified language (<a href="http://www.loc.gov/standards/iso639-2/php/English_list.php">ISO-639-2</a>)""" - return """ - Gray status (warning status == 0): Warning not applicable - Blue status (warning status == 1): Missing data - Green status (warning status == 2): No risk. Sleep well - Yellow status (warning status == 3): The day-degree hreshold for medium risk has been passed. Be on the alert, inspect your field - Red status (warning status == 4): The day-degree threshold for high risk has been passed. When the going gets tough, the tough get going - """ - - def get_model_usage(self, language = VIPSModel.default_language) -> str: - """Technical manual for this model, in the specified language (<a href="http://www.loc.gov/standards/iso639-2/php/English_list.php">ISO-639-2</a>)""" - return "TODO" - - \ No newline at end of file diff --git a/tests/test_reference_model.py b/tests/test_reference_model.py deleted file mode 100644 index 5d6ba90..0000000 --- a/tests/test_reference_model.py +++ /dev/null @@ -1,96 +0,0 @@ -import unittest - -import json - -from src.vipscore_common.reference_model import * -from src.vipscore_common.entities import * - -def get_model_configuration(): - with open ("tests/weather_data_2015_NO_aas_TMD.json") as f: - weather_observations = json.load(f) - return ModelConfiguration( - model_id=ReferenceModel.MODEL_ID, - config_parameters={ - "sowingDate": "2022-03-01", - "timeZone" : "Europe/Oslo", - "observations": weather_observations - } - ) - -class TestReferenceModel(unittest.TestCase): - def test_set_configuration(self): - """ - Passing a configuration object does not fail - """ - #print(get_model_configuration()) - instance = ReferenceModel() - instance.set_configuration(get_model_configuration()) - - def test_get_result(self): - """ - We get a series of results from the calculation, - and the TMDD is as expected - """ - instance = ReferenceModel() - instance.set_configuration(get_model_configuration()) - result_list = instance.get_result() - self.assertIsNotNone(result_list) - last_result = result_list[len(result_list)-1] - self.assertEqual(555.8507083333333, last_result.all_values["TMDD"]) - - def test_get_model_id(self): - """ - The model returns the correct ID - """ - instance = ReferenceModel() - self.assertEqual(instance.model_id, ReferenceModel.MODEL_ID) - - def test_get_license(self): - """ - The model returns its license - """ - instance = ReferenceModel() - self.assertIsNotNone(instance.license) - - def test_get_copyright(self): - """ - The model returns its copyright notice - """ - instance = ReferenceModel() - self.assertEqual(instance.copyright, ReferenceModel.COPYRIGHT) - - def test_get_sample_config(self): - """ - The model returns its sample configuration - """ - instance = ReferenceModel() - self.assertIsNotNone(instance.sample_config) - - def test_get_model_name(self): - """ - The model returns its name in the default language - """ - instance = ReferenceModel() - self.assertEqual(instance.get_model_name(), "Reference Model") - - def test_get_model_description(self): - """ - The model returns a description - """ - instance = ReferenceModel() - self.assertIsNotNone(instance.get_model_description()) - - def test_get_warning_status_interpretation(self): - """ - The model returns a warning status interpretation - """ - instance = ReferenceModel() - self.assertIsNotNone(instance.get_warning_status_interpretation()) - - def test_get_model_usage(self): - """ - The model returns a description of usage - """ - -if __name__ == '__main__': - unittest.main() \ No newline at end of file -- GitLab