Source code for greenlang.calculations.ghg.base.forms

""" GHG base forms."""

import json
from django.utils import timezone
from django import forms
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Hidden, Row, Column, Field
from crispy_forms.bootstrap import AppendedText


from fingreen_web.models import (
    CollectionItem,
    GhgEmissionFactor,
    GhgEmissionFactorValue,
    GhgEmissionSourceComputationMethod,
)

class TaggedFormMixin:
    """ TaggedFormMixin """

    def __init__(self, *args, **kwargs):
        self.user = kwargs.get('user')
        kwargs.pop('user')
        super().__init__(*args, **kwargs)

    def clean_tags(self):
        """ Clean tags field """
        if "tags" in self.data:
            try:
                return json.loads(self.data['tags'])
            except json.JSONDecodeError:
                pass
        return []

    def save_tags(self, instance):
        """ Save tags """
        instance.tags.clear()
        instance.tags.add(*[tag['value'] for tag in self.cleaned_data['tags']], tag_kwargs={
            'creator': self.user,
            'organization': self.user.org_active
        })


[docs] class PredefinedFactorCalculationMethodForm(TaggedFormMixin, forms.ModelForm): """ This form provides the inputs for category using a predefined factor. It exposes the following fields: - description_user: a description of the item - ghg_factor: the GHG emission factor to use, prefilled with the factors related to selected calculation method - value_float: the amount value - ghg_unit: the GHG emission unit to use, prefilled with the units related to selected GHG emission factor As hidden fields, the form exposes: - method: the selected GHG emission source computation method - collection: the selected collection - item_type: the selected item type, must be 'ghg' - ghg_scope: the selected GHG scope """ # pylint: disable=line-too-long method = forms.ModelChoiceField( queryset=GhgEmissionSourceComputationMethod.objects.all(), required=True ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field_name in self.fields.keys(): if field_name != 'tags': self.fields[field_name].required = True self.helper = FormHelper() self.helper.form_tag = False self.helper.form_show_labels = False method = self.initial["method"] self.has_instance = 'instance' in kwargs and kwargs['instance'] if self.has_instance: instance = kwargs['instance'] if instance.widget_data: for key, value in instance.widget_data.items(): setattr(self.fields[key], 'initial', value) factor_ids = ( GhgEmissionFactorValue.objects.filter(factor__method=method) .filter(tot_co2_kg__isnull=False) .filter(tot_co2_kg__gt=0) .values_list('factor', flat=True) .distinct() ) factors = method.factors.filter(id__in=factor_ids).order_by( "factor_subtype", "name" ) self.fields["ghg_factor"].choices = [ ( factor.pk, f"{factor.factor_subtype_repr}{' > ' if factor.factor_subtype else ''}{factor.name}", ) # pylint: disable=line-too-long for factor in factors ] if not self.has_instance and len(factors) > 0: self.fields["ghg_factor"].initial = factors[0].id self.helper.layout = Layout( Hidden( "method", self.initial["method"].id ), # field used in the view to preset form Hidden("collection", self.initial["collection"].id), Hidden("item_type", self.initial["item_type"]), Hidden("ghg_scope", self.initial["ghg_scope"]), Row( Column( Field( "description_user", placeholder=_("Description"), type="text", autocomplete="off", css_class="form-control form-control-lg form-control-solid mb-3 mb-lg-0", ), css_class="col-md-6", ), Column( Field( "tags", css_class="form-control tags-input", ), css_class="col-md-6", ), ), Row( Column( Field( "ghg_factor", data_control="select2", data_placeholder=_("Choose a GHG emission factor"), hx_post=reverse("ghg_factor_units"), hx_swap="innerHTML", hx_target="#custom_units", hx_trigger="load, change", hx_include="[name='ghg_factor']", style="display: none;", css_class="form-control", ), css_class="col-5", ), Column( Field( "value_float", placeholder=_("Enter amount"), min=0, type="number", autocomplete="off", css_class="form-control form-control-lg form-control-solid mb-3 mb-lg-0", ), css_class="col-2", ), Column( Field( "ghg_unit", data_placeholder=_("Select an emission source 1st"), data_control="select2", style="display: none;", css_class="form-control", ), id="custom_units", css_class="col", ), *self.get_extra_fields(), Column( Submit( "submit", _("Add") if not self.has_instance else _("Update"), css_class="btn btn-light-primary", ), css_class="col", ), ), )
[docs] def get_extra_fields(self): """ Return extra fields to add to the form. This method is meant to be overriden by subclasses. Return: A list of extra fields to add to the form. Empty list by default. """ return []
[docs] def save(self, commit=True): """Save""" instance = super().save(commit=False) if commit: if self.user: instance.value_last_editor = self.user instance.value_update_date = timezone.now() instance.save() self.save_tags(instance) return instance
class Meta: model = CollectionItem fields = [ "collection", "ghg_scope", "item_type", "description_user", "tags", "ghg_factor", "value_float", "ghg_unit", ]
[docs] class CustomFactorCalculationMethodForm(TaggedFormMixin, forms.ModelForm): """CustomFactorCalculationMethodForm""" method = forms.ModelChoiceField( queryset=GhgEmissionSourceComputationMethod.objects.all(), required=True ) custom_factor_name = forms.CharField(required=True) custom_factor_value = forms.FloatField(required=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field_name in self.fields.keys(): if field_name != 'tags': self.fields[field_name].required = True has_instance = 'instance' in kwargs and kwargs['instance'] if has_instance: instance = kwargs['instance'] if instance.widget_data: for key, value in instance.widget_data.items(): setattr(self.fields[key], 'initial', value) self.factor_type = "custom" self.helper = FormHelper() self.helper.form_tag = False self.helper.form_show_labels = False self.fields["custom_factor_value"].widget.attrs["placeholder"] = _( "Emission factor value" ) self.fields["custom_factor_name"].widget.attrs["placeholder"] = _( _("Emission factor name") ) self.fields["custom_factor_value"].widget.attrs["min"] = 0 self.fields["custom_factor_value"].widget.attrs["step"] = 0.01 self.helper.layout = Layout( Hidden( "method", self.initial["method"].id ), # field used in the view to preset form Hidden("collection", self.initial["collection"].id), Hidden("item_type", self.initial["item_type"]), Hidden("ghg_scope", self.initial["ghg_scope"]), Row( Column( Field( "description_user", placeholder=_("Description"), type="text", autocomplete="off", css_class="form-control form-control-lg form-control-solid mb-3 mb-lg-0", ), css_class="col-md-6", ), Column( Field( "tags", css_class="form-control tags-input", ), css_class="col-md-6", ), ), Row( Column( Field( "custom_factor_name", # do not use placeholder here, it could be more difficult to override type="text", autocomplete="off", css_class="form-control form-control-lg form-control-solid mb-3 mb-lg-0", ), css_class="col-3", ), *self.get_value_float_columns(), Column( AppendedText("custom_factor_value", "kg CO2e/ unit"), css_class="col-3", ), *self.get_extra_fields(), Column( Submit( "submit", _("Add") if not has_instance else _("Update"), css_class="btn btn-light-primary w-100", ), css_class="col-1", ), ), )
[docs] def get_value_float_columns(self): """ Get value float columns """ return [ Column( Field( "value_float", placeholder=self.get_placeholder('value_float'), min=0, type="number", autocomplete="off", css_class="form-control form-control-lg form-control-solid mb-3 mb-lg-0", ), css_class="col-2", ), Column( Field( "ghg_unit", data_placeholder=_("Select an emission source 1st"), data_control="select2", style="display: none;", css_class="form-control", ), id="custom_units", css_class="col-2", ) ]
[docs] def get_extra_fields(self): """ Return extra fields to add to the form. This method is meant to be overriden by subclasses. Return: A list of extra fields to add to the form. Empty list by default. """ return []
[docs] def get_placeholder(self, field_name): """ Get placeholder """ if field_name == "value_float": return _("Amount") return ""
[docs] def save(self, commit=True): """Save""" instance = super().save(commit=False) custom_factor_name = self.cleaned_data["custom_factor_name"] custom_factor_value = self.cleaned_data["custom_factor_value"] method = self.cleaned_data["method"] instance.widget_data = { "custom_factor_name": custom_factor_name, "custom_factor_value": custom_factor_value, } if instance.item_type == "ghg": instance.ghg_factor = GhgEmissionFactor.objects.create( name=custom_factor_name, factor_type=self.factor_type, method=method, organization=self.user.org_active, ) GhgEmissionFactorValue.objects.update_or_create( factor=instance.ghg_factor, unit=instance.ghg_unit, defaults={ "tot_co2_kg": custom_factor_value, } ) if commit: if self.user: instance.last_editor = self.user instance.value_update_date = timezone.now() instance.save() self.save_tags(instance) return instance
class Meta: model = CollectionItem fields = [ "collection", "ghg_scope", "item_type", "description_user", "tags", "value_float", "ghg_unit", ]
[docs] class SupplierSpecificMethodForm(CustomFactorCalculationMethodForm): """Supplier specific method""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["custom_factor_name"].label = _("Emission Source Name") self.fields["custom_factor_name"].widget.attrs["placeholder"] = _("Emission Source Name") self.factor_type = "supplier" class Meta(CustomFactorCalculationMethodForm.Meta): pass
[docs] class CustomAverageDataMethodForm(CustomFactorCalculationMethodForm): """Custom Average data method form""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["custom_factor_name"].label = _("Name") self.fields["custom_factor_name"].widget.attrs["placeholder"] = _("Name") self.factor_type = "goods" class Meta(CustomFactorCalculationMethodForm.Meta): pass