diff --git a/forecastmanager/forecast_settings.py b/forecastmanager/forecast_settings.py index 43e2b1a..de57ab3 100644 --- a/forecastmanager/forecast_settings.py +++ b/forecastmanager/forecast_settings.py @@ -13,14 +13,14 @@ from wagtail.contrib.settings.registry import register_setting from wagtail.models import Orderable -from forecastmanager.constants import WEATHER_PARAMETER_CHOICES, WEATHER_PARAMETERS_AS_DICT -from forecastmanager.widgets import WeatherSymbolChooserWidget +from forecastmanager.constants import WEATHER_PARAMETERS_AS_DICT +from forecastmanager.widgets import WeatherSymbolChooserWidget, DataParameterWidget @register_setting class ForecastSetting(ClusterableModel, BaseSiteSetting): enable_auto_forecast = models.BooleanField(default=False, verbose_name=_('Enable automated forecasts')) - default_city = models.ForeignKey("City", blank=True, null=True, on_delete=models.CASCADE, + default_city = models.ForeignKey("City", blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_("Default City")) weather_detail_page = models.ForeignKey("wagtailcore.Page", blank=True, null=True, on_delete=models.SET_NULL, ) weather_reports_page = models.ForeignKey("wagtailcore.Page", blank=True, null=True, on_delete=models.SET_NULL, @@ -98,12 +98,11 @@ def save(self, *args, **kwargs): class ForecastDataParameters(Orderable): PARAMETER_TYPE_CHOICES = ( ("numeric", _("Number")), - ("time", _("Time")), ("text", _("Text")), ) parent = ParentalKey(ForecastSetting, on_delete=models.CASCADE, related_name="data_parameters") - parameter = models.CharField(max_length=100, choices=WEATHER_PARAMETER_CHOICES, unique=True, - verbose_name=_("Parameter")) + use_known_parameters = models.BooleanField(default=True, verbose_name=_("Use predefined parameters")) + parameter = models.CharField(max_length=100, unique=True, verbose_name=_("Parameter")) name = models.CharField(max_length=100, verbose_name=_("Parameter Label"), help_text=_("Parameter name as locally labelled")) parameter_type = models.CharField(max_length=100, choices=PARAMETER_TYPE_CHOICES, verbose_name=_("Parameter Type"), @@ -112,31 +111,32 @@ class ForecastDataParameters(Orderable): help_text="e.g °C, %, mm, hPa, etc ") panels = [ - FieldPanel('parameter'), + FieldPanel('use_known_parameters'), + FieldPanel('parameter', widget=DataParameterWidget), FieldPanel('name'), + FieldPanel('parameter_type'), + FieldPanel('parameter_unit'), ] def __str__(self): return self.name @property - def parameter_info(self): - return WEATHER_PARAMETERS_AS_DICT.get(self.parameter) + def units(self): + if self.parameter_unit: + return self.parameter_unit - def parse_value(self, value): - info = self.parameter_info + if self.parameter_info: + return self.parameter_info.get("units") - if not info.get("data_type"): - return value + return None - try: - if info.get("data_type") == "int": - return int(float(value)) - elif info.get("data_type") == "float": - return float(value) - except ValueError: - pass + @property + def parameter_info(self): + return WEATHER_PARAMETERS_AS_DICT.get(self.parameter) + def parse_value(self, value): + # TODO: Implement parsing for different parameter types return value diff --git a/forecastmanager/forms.py b/forecastmanager/forms.py index f676f0b..c7ccdd7 100644 --- a/forecastmanager/forms.py +++ b/forecastmanager/forms.py @@ -97,12 +97,13 @@ def clean(self): # check parameters for param, value in params_data.items(): - param = ForecastDataParameters.objects.filter(name=param).first() - if not param: - self.add_error(None, f"Unknown parameter found in table data: {param}") - return cleaned_data + if value: + param = ForecastDataParameters.objects.filter(name=param).first() + if not param: + self.add_error(None, f"Unknown parameter found in table data: {param}") + return cleaned_data - city_data["data_values"].append({"parameter": param, "value": value}) + city_data["data_values"].append({"parameter": param, "value": value}) forecast_data.append(city_data) added_cities.append(city.id) diff --git a/forecastmanager/migrations/0022_alter_forecastdataparameters_parameter_type_and_more.py b/forecastmanager/migrations/0022_alter_forecastdataparameters_parameter_type_and_more.py new file mode 100644 index 0000000..a29fc55 --- /dev/null +++ b/forecastmanager/migrations/0022_alter_forecastdataparameters_parameter_type_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.3 on 2024-04-15 06:55 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('forecastmanager', '0021_forecastsetting_weather_reports_page'), + ] + + operations = [ + migrations.AlterField( + model_name='forecastdataparameters', + name='parameter_type', + field=models.CharField(choices=[('numeric', 'Number'), ('text', 'Text')], default='numeric', max_length=100, verbose_name='Parameter Type'), + ), + migrations.AlterField( + model_name='forecastsetting', + name='default_city', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='forecastmanager.city', verbose_name='Default City'), + ), + ] diff --git a/forecastmanager/migrations/0023_forecastdataparameters_use_known_parameters.py b/forecastmanager/migrations/0023_forecastdataparameters_use_known_parameters.py new file mode 100644 index 0000000..947976e --- /dev/null +++ b/forecastmanager/migrations/0023_forecastdataparameters_use_known_parameters.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.3 on 2024-04-15 11:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('forecastmanager', '0022_alter_forecastdataparameters_parameter_type_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='forecastdataparameters', + name='use_known_parameters', + field=models.BooleanField(default=True, verbose_name='Use known parameters'), + ), + ] diff --git a/forecastmanager/models.py b/forecastmanager/models.py index b88ee6c..0af01e4 100644 --- a/forecastmanager/models.py +++ b/forecastmanager/models.py @@ -73,7 +73,7 @@ class Meta: unique_together = ("forecast_date", "effective_period") verbose_name = _("Forecast") verbose_name_plural = _("Forecasts") - ordering = ["forecast_date", "effective_period"] + ordering = ["-forecast_date", "effective_period"] panels = [ FieldPanel("forecast_date"), @@ -126,15 +126,22 @@ def effective_period(self): def data_values_dict(self): data_values = {} for data_value in self.data_values.all(): - data_values[data_value.parameter.parameter] = { + parameter_info = data_value.parameter.parameter_info + val = { "value": data_value.parsed_value, "name": data_value.parameter.name, - "label": data_value.parameter.parameter_info.get("label"), - "units": data_value.parameter.parameter_info.get("unit"), + "label": data_value.parameter.name, + "units": data_value.parameter.units, "value_with_units": data_value.value_with_units, - "icon": data_value.parameter.parameter_info.get("icon"), } + if parameter_info: + val.update({ + "icon": data_value.parameter.parameter_info.get("icon"), + }) + + data_values[data_value.parameter.parameter] = val + # Group temperature values temperature = {} if "air_temperature_max" in data_values: @@ -201,4 +208,10 @@ def parsed_value(self): @property def value_with_units(self): - return f"{self.parsed_value}{self.parameter.parameter_info.get('unit')}" + if not self.parsed_value: + return None + + if not self.parameter.units: + return self.parsed_value + + return f"{self.parsed_value} {self.parameter.units}" diff --git a/forecastmanager/static/forecastmanager/js/forecast-data-parameter-widget.js b/forecastmanager/static/forecastmanager/js/forecast-data-parameter-widget.js new file mode 100644 index 0000000..1f0b632 --- /dev/null +++ b/forecastmanager/static/forecastmanager/js/forecast-data-parameter-widget.js @@ -0,0 +1,52 @@ +function DataParameterWidget(id, initialValue) { + const inputId = '#' + id + this.input = $(inputId); + + const parameterListSelectId = id + "_parameter_list" + this.parameterListSelectInput = $("#" + parameterListSelectId); + + const useKnownParameterInputId = id.split("-parameter")[0] + "-use_known_parameters" + this.checkIsKnownParameterInput = $("#" + useKnownParameterInputId) + + const that = this + + this.checkIsKnownParameterInput.change(function () { + const checked = $(this).is(":checked") + + if (checked) { + that.input.hide() + that.parameterListSelectInput.show() + } else { + that.input.show() + that.parameterListSelectInput.hide() + } + + }) + + if (this.checkIsKnownParameterInput.is(":checked")) { + // clear any previous value + that.parameterListSelectInput.show() + } else { + that.input.show() + } + + this.parameterListSelectInput.change(function () { + const selectedVal = $(this).val() + that.setState(selectedVal) + }) +} + +DataParameterWidget.prototype.setState = function (newState) { + this.input.val(newState); +}; + +DataParameterWidget.prototype.getState = function () { + return this.input.val(); +}; + +DataParameterWidget.prototype.getValue = function () { + return this.input.val(); +}; + +DataParameterWidget.prototype.focus = function () { +} \ No newline at end of file diff --git a/forecastmanager/templates/forecastmanager/create_forecast.html b/forecastmanager/templates/forecastmanager/create_forecast.html index e1f60c6..dafa4c4 100644 --- a/forecastmanager/templates/forecastmanager/create_forecast.html +++ b/forecastmanager/templates/forecastmanager/create_forecast.html @@ -199,7 +199,7 @@