Source code for ferrosoft.apps.emiflow.models.signals
# Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
"""Cross-model lifecycle hooks for emiflow.
Two concerns are wired up here:
* Whenever a treatment-operation output line is inserted or removed, the
parent operation's allocation is recomputed (no-op under manual
allocation).
* Whenever an entity that owns an :class:`EmissionIntensity` (transport
operation, treatment operation, or emitter) is deleted, the matching
intensity row is removed. The intensity table uses a weak
``(owner_type, owner_id)`` reference rather than a real foreign key, so
this cascade must be driven explicitly from signals.
The module is loaded via the wildcard import in
:mod:`ferrosoft.apps.emiflow.models.__init__`, which is what registers
these receivers at app startup.
"""
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from ferrosoft.apps.emiflow.models.transport import TransportOperation
from ferrosoft.apps.emiflow.models.catalog import Emitter
from ferrosoft.apps.emiflow.models.intensity import (
EmissionIntensity,
IntensityCapableEntity,
)
from ferrosoft.apps.emiflow.models.treatment import (
TreatmentOperationOutputLine,
TreatmentOperation,
)
[docs]
@receiver(post_save, sender=TreatmentOperationOutputLine)
def allocate_treatment_emissions_after_insertion(
instance: TreatmentOperationOutputLine, **kwargs
):
"""Recompute parent operation's allocation after an output line is saved."""
instance.operation.trigger_allocation()
[docs]
@receiver(post_delete, sender=TreatmentOperationOutputLine)
def allocate_treatment_emissions_after_deletion(
instance: TreatmentOperationOutputLine, **kwargs
):
"""Recompute parent operation's allocation after an output line is deleted."""
instance.operation.trigger_allocation()
[docs]
@receiver(post_delete, sender=TransportOperation)
def delete_transport_operation_emission_intensity(
instance: TransportOperation, **kwargs
):
"""Remove the :class:`EmissionIntensity` owned by a deleted transport operation."""
EmissionIntensity.objects.delete_of_owner(
IntensityCapableEntity.TRANSPORT_OPERATION, instance.pk
)
[docs]
@receiver(post_delete, sender=TreatmentOperation)
def delete_treatment_operation_emission_intensity(
instance: TreatmentOperation, **kwargs
):
"""Remove the :class:`EmissionIntensity` owned by a deleted treatment operation."""
EmissionIntensity.objects.delete_of_owner(
IntensityCapableEntity.TREATMENT_OPERATION, instance.pk
)
[docs]
@receiver(post_delete, sender=Emitter)
def delete_emitter_emission_intensity(instance: Emitter, **kwargs):
"""Remove the :class:`EmissionIntensity` owned by a deleted emitter."""
EmissionIntensity.objects.delete_of_owner(
IntensityCapableEntity.EMITTER, instance.pk
)