Source code for ferrosoft.apps.emiflow.forms.intensity

#  Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
from django import forms
from django.forms import ValidationError
from django.utils.translation import gettext_lazy as _

from ferrosoft.apps.emiflow.models import (
    EmissionIntensity,
    EnergyCarrier,
    EmissionFactor,
    EmissionCategory,
)
from ferrosoft.apps.emiflow.services.emissions import EmissionIntensityCalculator
from ferrosoft.apps.ferrobase.forms import UnitChoiceField
from ferrosoft.apps.ferrobase.models import MeasurementUnit, UnitCollection, UnitCursor


[docs] class EmissionIntensityForm(forms.ModelForm):
[docs] class Meta: model = EmissionIntensity fields = [ "enable_distance", "owner_type", "owner_id", "factor_operation_value", "factor_energy_provision_value", "quantity_value", "quantity_unit", "mass_value", "mass_unit", "distance_value", "distance_unit", ] widgets = { "enable_distance": forms.HiddenInput(), "owner_type": forms.HiddenInput(), "owner_id": forms.HiddenInput(), }
quantity_unit = UnitChoiceField( units=UnitCollection.NATIVE_CONSUMPTION, initial=UnitCursor("kg"), ) mass_unit = UnitChoiceField( units=UnitCollection.MASS, initial=UnitCursor("t"), ) distance_unit = UnitChoiceField( units=UnitCollection.DISTANCE, initial=UnitCursor("km"), ) def __init__(self, *args, **kwargs): self.energy_carrier: EnergyCarrier | None = kwargs.pop("energy_carrier") enable_distance: bool = kwargs.pop("enable_distance") super().__init__(*args, **kwargs) operation_factors = EmissionFactor.objects.filter( category=EmissionCategory.OPERATION ) energy_provision_factors = EmissionFactor.objects.filter( category=EmissionCategory.ENERGY_PROVISION ) if self.energy_carrier is not None: # Constrain factors to sensible set based on energy carrier. operation_factors = operation_factors.filter( energy_carrier_id=self.energy_carrier.pk ) energy_provision_factors = energy_provision_factors.filter( energy_carrier_id=self.energy_carrier.pk ) # Remove Liter unit if density is not set, as a Liter value # cannot be converted to Kilogram in this case. if self.energy_carrier.density is None: self.fields["quantity_unit"].choices = filter( lambda choice: choice[0] != "13e6e7ee-e33c-4f82-b824-09e7697aeef3", self.fields["quantity_unit"].choices, ) # Distance is hidden on request, which is useful for hub equipment. if not enable_distance: self.fields["distance_value"].widget = forms.HiddenInput() self.fields["distance_unit"].widget = forms.HiddenInput() self.fields["factor_energy_provision_value"].queryset = energy_provision_factors self.fields["factor_operation_value"].queryset = operation_factors
[docs] def clean_quantity_unit(self): # Volumetric quantities cannot be converted to kilogram with energy # carriers without density. value = self.cleaned_data["quantity_unit"] is_volumetric = value.cursor in UnitCollection.VOLUMETRIC if ( is_volumetric and self.energy_carrier is not None and self.energy_carrier.density is None ): raise ValidationError( _( "Volume unit cannot be used because the energy carrier has no density." ), code="energy_carrier_no_density", ) return value
[docs] def clean(self): cleaned_data = super().clean() if ( "factor_operation_value" not in cleaned_data or "factor_energy_provision_value" not in cleaned_data ): return # Units of factors must match each other. if ( cleaned_data["factor_operation_value"].unit.symbol != cleaned_data["factor_energy_provision_value"].unit.symbol ): err = _("Factor units must be the same") self.add_error("factor_operation_value", err) self.add_error("factor_energy_provision_value", err)
[docs] def save(self, commit=True): instance: EmissionIntensity = super().save(commit=False) # Fudge calculated numbers onto instance. int_energy_provision, int_operation = ( EmissionIntensityCalculator().calculateFor(instance) ) instance.operation_value = int_operation.value instance.energy_provision_value = int_energy_provision.value instance.unit = int_operation.unit if commit: instance.save() instance.update_foreign_key() return instance