#!/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"