Source code for ferrosoft.apps.emiflow.views.settings

#  Copyright (c) 2024 Ferrosoft GmbH. All rights reserved.
from dataclasses import dataclass
from decimal import Decimal

from django import forms
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import (
    TemplateView,
)

from ferrosoft.apps.emiflow.filter import EmitterFilter, EnergyCarrierFilter
from ferrosoft.apps.emiflow.forms.catalog import (
    EmitterForm,
    EnergyCarrierForm,
    EmissionFactorForm,
)
from ferrosoft.apps.emiflow.forms.settings import (
    DefaultsSearchForm,
    DefaultsEditForm,
    BusinessCentralSettingsForm,
)
from ferrosoft.apps.emiflow.models import (
    EmitterEpitype,
    Settings,
    VehicleType,
    Emitter,
    IntensityCapableEntity,
    TreatmentOperation,
    TransportOperation,
    BusinessCentralSettings,
    EnergyCarrier,
    EmissionFactor,
)
from ferrosoft.apps.emiflow.tables import (
    EmitterTable,
    EnergyCarrierTable,
    EmissionFactorTable,
)
from ferrosoft.apps.emiflow.views import intensity_create_url
from ferrosoft.apps.ferrobase.auth.roles import Permissions, Verb
from ferrosoft.apps.ferrobase.forms.copy import copy_form_factory, copy_fields
from ferrosoft.apps.ferrobase.views import (
    PlatformActivityMixin,
    CreateView,
    TableView,
    UpdateView,
    model_crud_routes,
    DeleteView,
    DependentCreateView,
)
from ferrosoft.apps.ferrobase.views.generic import CopyView
from ferrosoft.decimal import zero


def _careful_int(value):
    try:
        return int(value)
    except Exception:
        return 0


[docs] @dataclass class SettingsPage: title: str subtitle: str view: str
SETTINGS_PAGES = [ SettingsPage( title=_("Emitters"), subtitle=_("Vehicles and hub equipment"), view="emiflow:emitter_index", ), SettingsPage( title=_("Operations"), subtitle=_("Operations of vehicles and hub equipment"), view="emiflow:operation_index", ), SettingsPage( title=_("Operation Categories"), subtitle=_("Groups of operations that share similar characteristics"), view="emiflow:operationcategory_index", ), SettingsPage( title=_("Transport Defaults"), subtitle=_("Default options for transport imports"), view="emiflow:settings_transport_defaults", ), ]
[docs] class SettingsIndex(LoginRequiredMixin, PlatformActivityMixin, TemplateView): template_name = "emiflow/settings/index.html" activity_title = _("Settings")
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["pages"] = SETTINGS_PAGES return context
[docs] def settings_transport_defaults(request): instance = None search_form = DefaultsSearchForm(request.GET) if search_form.is_valid(): instance = Settings.get_instance().get_transport_chain_defaults( VehicleType( _careful_int(search_form.cleaned_data["vehicle_type"]) or VehicleType.ROAD ), ) if request.method == "POST" and instance is not None: edit_form = DefaultsEditForm(request.POST, instance=instance) if edit_form.is_valid(): edit_form.save() else: edit_form = DefaultsEditForm(instance=instance) return render( request, "emiflow/settings/transport_defaults.html", { "activity_title": _("Transport defaults"), "activity_app": "emiflow", "search_form": search_form, "edit_form": edit_form, }, )
[docs] class BusinessCentralSettingsUpdate(UpdateView): form_class = BusinessCentralSettingsForm model = BusinessCentralSettings
[docs] def get_object(self, queryset=None): return BusinessCentralSettings.get_instance()
[docs] class EmitterCopyView(CopyView):
[docs] @staticmethod def prepare_copy(source: Emitter, target: Emitter, **kwargs): def copy_emission_intensity(): # Copy emission intensity as-is and let user adjust it in update view. source_intensity = getattr(source, "emission_intensity", None) if source_intensity is not None: target_intensity = source_intensity.copy_with_owner( IntensityCapableEntity.EMITTER, target.pk ) target.emission_intensity = target_intensity target.save(update_fields=["emission_intensity"]) # All user-defined objects go into user catalog. target.catalog = Settings.get_instance().user_catalog return [copy_emission_intensity]
form_class = copy_form_factory( Emitter, fields=[ "name", "epitype", "vehicle_type", "energy_carrier", ], preparator=prepare_copy, ) model = Emitter
[docs] class EmitterCreateView(CreateView): form_class = EmitterForm help_topic = "emitters" model = Emitter
[docs] class EmitterTableView(TableView): filterset_class = EmitterFilter help_topic = "emitters" model = Emitter ordering = "name" table_class = EmitterTable
[docs] class EmitterUpdateView(UpdateView): add_copy_action = True form_class = EmitterForm help_topic = "emitters" model = Emitter template_name = "emiflow/emitter/update.html"
[docs] def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) | { "intensity_create_url": intensity_create_url( IntensityCapableEntity.EMITTER, self.object.pk, self.object.energy_carrier_id, EmitterEpitype(self.object.epitype), ), }
[docs] class EmitterDeleteView(DeleteView): help_topic = "emitters" model = Emitter
emitter_urls = model_crud_routes( Emitter, copy_view=EmitterCopyView, create_view=EmitterCreateView, read_view=EmitterTableView, update_view=EmitterUpdateView, delete_view=EmitterDeleteView, )
[docs] class EnergyCarrierTableView(TableView): filterset_class = EnergyCarrierFilter model = EnergyCarrier ordering = "name" table_class = EnergyCarrierTable
[docs] class EnergyCarrierCreateView(CreateView): form_class = EnergyCarrierForm model = EnergyCarrier
[docs] class EnergyCarrierCopyView(CopyView):
[docs] @staticmethod def prepare_copy(source: EnergyCarrier, target: EnergyCarrier, **kwargs): reduction = kwargs.pop("cleaned_data", {}).get("reduction", zero) reduction_factor = Decimal(1) - reduction / Decimal(100) def copy_emission_factors(): # Copy emission factors as-is and let user adjust them in update view. copies = [] for source_factor in source.emission_factors.all(): target_factor = EmissionFactor() copy_fields( source_factor, target_factor, fields=["unit", "category"], energy_carrier=target, value=source_factor.value * reduction_factor, ) copies.append(target_factor) EmissionFactor.objects.bulk_create(copies) # All user-defined objects go into user catalog. target.catalog = Settings.get_instance().user_catalog return [copy_emission_factors]
form_class = copy_form_factory( EnergyCarrier, additional_fields={ "reduction": forms.DecimalField( max_digits=5, decimal_places=2, initial=zero, label=_("Emission Factor Reduction"), help_text=_( "Copied emission factors are reduced according to this percentage." ), ) }, fields=[ "name", "density", "composition", "application", "lower_heating", ], preparator=prepare_copy, ) model = EnergyCarrier
[docs] class EnergyCarrierUpdateView(UpdateView): add_copy_action = True form_class = EnergyCarrierForm model = EnergyCarrier template_name = "emiflow/energy_carrier/update.html"
[docs] def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) | { "emission_factor_table": EmissionFactorTable( self.object.emission_factors.order_by("category"), create_url=reverse( "emiflow:emissionfactor_create", query={EmissionFactorCreateView.parent_query: str(self.object.pk)}, ), ) }
[docs] class EnergyCarrierDeleteView(DeleteView): model = EnergyCarrier
energy_carrier_urls = model_crud_routes( EnergyCarrier, create_view=EnergyCarrierCreateView, read_view=EnergyCarrierTableView, update_view=EnergyCarrierUpdateView, delete_view=EnergyCarrierDeleteView, copy_view=EnergyCarrierCopyView, )
[docs] class EmissionFactorCreateView(DependentCreateView): form_class = EmissionFactorForm model = EmissionFactor parent_model = EnergyCarrier
[docs] def cancel_view(self): return self.parent_object.get_absolute_url()
[docs] def get_form_kwargs(self): return super().get_form_kwargs() | { "initial": { "energy_carrier": self.parent_object, } }
[docs] class EmissionFactorUpdateView(UpdateView): form_class = EmissionFactorForm model = EmissionFactor related_links_default = False
[docs] def cancel_view(self): return self.object.energy_carrier.get_absolute_url()
[docs] def get_success_url(self): return self.object.energy_carrier.get_absolute_url()
[docs] class EmissionFactorDeleteView(DeleteView): model = EmissionFactor
[docs] def cancel_view(self): return self.object.energy_carrier.get_absolute_url()
[docs] def get_success_url(self): return self.object.energy_carrier.get_absolute_url()
emission_factor_urls = model_crud_routes( EmissionFactor, create_view=EmissionFactorCreateView, delete_view=EmissionFactorDeleteView, update_view=EmissionFactorUpdateView, )