ferrosoft.apps.emiflow.services package¶
Service layer for the emiflow application.
This package holds the fat services / thin views business logic for emission calculation, reporting, integration, and supporting concerns:
builder— fluent builders that composeTransportChains and their related rows.businesscentral— adapters for the Microsoft Dynamics 365 Business Central connector.emissions— transport-chain emission calculator and booking service.geographic— strategy for resolving activity distance by distance type.pcf— product carbon footprint roll-up across all chains carrying a material.reporting— document generators that turn booking records into renderable reports.session— per-request storage of the current transport chain in the user session.tonnage— per-tenant / per-organisation tonnage queries used for subscription accounting.treatment— treatment emission calculator and booking service.update— periodic update hooks that seed predefined lifecycle categories per tenant.vehicle— heuristic that guesses aVehicleTypefrom freight weight.visitor— visitor implementations executed againstTransportChainwalks (debug printer, geocoding/routing).
Subpackages¶
- ferrosoft.apps.emiflow.services.businesscentral package
- Submodules
- ferrosoft.apps.emiflow.services.businesscentral.connector module
- ferrosoft.apps.emiflow.services.businesscentral.rapidstart module
CatalogSerializerClassAttributesElement()EmitterSerializerEnergyCarrierSerializerMeasurementUnitSerializerModelSerializerModelSerializerMetaRapidstartExporterSerializerSwitchTransportOperationSerializerTreatmentOperationInputLineSerializerTreatmentOperationInputLineSerializer.get_queryset()TreatmentOperationInputLineSerializer.modelTreatmentOperationInputLineSerializer.page_idTreatmentOperationInputLineSerializer.primary_keyTreatmentOperationInputLineSerializer.serialize_fields()TreatmentOperationInputLineSerializer.table_idTreatmentOperationInputLineSerializer.table_name
TreatmentOperationOutputLineSerializerTreatmentOperationSerializerderive_bc_number()
Submodules¶
ferrosoft.apps.emiflow.services.builder module¶
Fluent builders for assembling transport chains and related rows.
Each ModelBuilder subclass wraps a Django model instance and
exposes chainable setter methods so that complex transport chains can
be constructed declaratively. Builders compose via
BuilderOfBuilders (a chain holds builders for its elements
and freight items), and can either save() rows one-by-one or
defer everything into a BulkCreator for one bulk upsert per
model class.
- class ferrosoft.apps.emiflow.services.builder.BuilderOfBuilders[source]¶
Bases:
ModelBuilderAggregate of child builders that fans
save/add_bulkout to each.
- class ferrosoft.apps.emiflow.services.builder.BulkCreator(create_order)[source]¶
Bases:
objectCollects unsaved model instances and upserts them in declared order.
The model classes are grouped by name; on
upsert()the creator iterates throughcreate_orderso foreign keys are satisfied (e.g.TransportChainbeforeTransportChainElement) and each model’s manager-levelupsertis invoked once with all instances of that class.- upsert()[source]¶
Run
Manager.upsert(...)for each pending model class in declared order.- Returns:
A mapping of class name to the list of upserted instances. Classes with no pending rows are omitted.
- Return type:
- Raises:
RuntimeError – When a model class’s manager has no
upsertmethod.
- class ferrosoft.apps.emiflow.services.builder.FreightItemBuilder(item)[source]¶
Bases:
ModelBuilderFluent builder for one
FreightItem.- add_bulk(bulk)[source]¶
Register the built instance with
bulkfor deferred persistence.- Return type:
- class ferrosoft.apps.emiflow.services.builder.GeoPointBuilder(point)[source]¶
Bases:
ModelBuilderFluent builder around a
TCEGeoPoint(origin or destination).- add_bulk(bulk)[source]¶
Register the built instance with
bulkfor deferred persistence.- Return type:
- class ferrosoft.apps.emiflow.services.builder.MaterialBuilder(material)[source]¶
Bases:
ModelBuilderFluent builder around a
Material.- add_bulk(bulk)[source]¶
Register the built instance with
bulkfor deferred persistence.- Return type:
- class ferrosoft.apps.emiflow.services.builder.ModelBuilder[source]¶
Bases:
ABCAbstract fluent builder around a single Django model instance.
Subclasses expose chainable setter methods returning
selfand must implementsave()to persist the instance individually oradd_bulk()to defer persistence into aBulkCreator.
- class ferrosoft.apps.emiflow.services.builder.TransportChainBuilder(chain)[source]¶
Bases:
ModelBuilderTop-level fluent builder that composes the whole chain.
Owns nested builders for chain elements (auto-numbered by insertion order) and freight items.
savecascades to all children so the entire object graph can be persisted from a single call.- add_bulk(bulk)[source]¶
Register the built instance with
bulkfor deferred persistence.- Return type:
- add_element()[source]¶
Append a new chain element (auto-numbered by insertion order) and return its builder.
- Return type:
- add_freight_item()[source]¶
Append a new freight item to the chain and return its builder.
- Return type:
- class ferrosoft.apps.emiflow.services.builder.TransportChainElementBuilder(element)[source]¶
Bases:
ModelBuilderFluent builder for one
TransportChainElement(with optional geo-points).- add_bulk(bulk)[source]¶
Register the built instance with
bulkfor deferred persistence.- Return type:
- add_geo_destination()[source]¶
Attach a new destination geo-point and return its builder for chaining.
- Return type:
- add_geo_origin()[source]¶
Attach a new origin geo-point and return its builder for chaining.
- Return type:
- classmethod new()[source]¶
Start a fresh builder around an empty
TransportChainElement.- Return type:
- ferrosoft.apps.emiflow.services.builder.resolve_model(needle, model_type)[source]¶
Resolve
needleto an instance ofmodel_type.- Parameters:
needle –
strto look up byname,UUIDto look up by primary key, an instance ofmodel_type(returned unchanged), orNone(returned asNone).model_type – Django model class.
- Returns:
The resolved model instance, or
NonewhenneedleisNone.- Raises:
ValueError – When
needleis none of the accepted types.model_type.DoesNotExist – When the lookup finds no row.
ferrosoft.apps.emiflow.services.emissions module¶
Transport-chain emission calculation and booking.
This module is the heart of the transport-side emiflow pipeline:
TransportChainCalculatorwalks a chain’s elements, looks up each operation’s emission intensity, and produces per-element and per-chain results (transport activity, distance, emissions for the operation and energy provision categories).EmissionIntensityCalculatorderives a new emission intensity from raw inputs (factor, fuel quantity, mass, distance), performing unit normalisation so callers can supply values in any supported unit.TransportBookingServiceposts a chain: it geocodes points, computes emissions, denormalises everything intoEmissionBookingRecordrows (one per chain + one perTransportLCCValue), and flips the chain status toPOSTED.
ISO 14083 terminology is used throughout: TCE = Transport Chain
Element, transport activity = mass × distance (tkm), intensity =
emission / activity.
- class ferrosoft.apps.emiflow.services.emissions.EmissionIntensityCalculator[source]¶
Bases:
objectCompute emission intensities from input variables.
Either pass an existing
EmissionIntensityrow whose input fields are populated (the calculator pulls the energy carrier’s density from the related factor), or callcalculate()directly with anEmissionIntensityInputsvalue.- calculate(inputs)[source]¶
Calculate one emission intensity from the supplied inputs.
Steps: normalise distance (treat zero as a neutral multiplier for hub operations), normalise the factor and verify its divider matches the quantity unit, normalise the quantity (volume → mass via density when needed), compute activity distance (mass × distance), and finally
emission / activity.- Raises:
IntensityCalculationError – When factor and quantity units are incompatible, or when activity distance resolves to zero (which would divide by zero).
- Return type:
- class ferrosoft.apps.emiflow.services.emissions.EmissionIntensityInputs(emission_factor, quantity, mass, distance, energy_carrier_density)[source]¶
Bases:
objectRaw inputs to
EmissionIntensityCalculator.calculate().- distance: DecimalWithUnit¶
Distance in km or m unit.
Distance may be None if calculating emission intensity for HUB operations.
- emission_factor: DecimalWithUnit¶
Factor with gCO2e/MJ or kgCO2e/kg unit.
The divider of the unit has to align with the unit of the quantity.
- energy_carrier_density: Decimal | None¶
Density of energy carrier (L/kg). Only required if quantity unit is L.
- mass: DecimalWithUnit¶
Material mass with kg or t unit.
- quantity: DecimalWithUnit¶
Quantity of energy carrier (fuel or electricity) with kg, L or kWh unit.
Kilogram and Liter is converted to Metric Ton. Kilowatt hours is converted to Megajoule.
- class ferrosoft.apps.emiflow.services.emissions.EmissionResult(emission_category, operation_epitype, emission)[source]¶
Bases:
objectOne emission value tagged by category and operation epitype.
- emission: DecimalWithUnit¶
- emission_category: EmissionCategory¶
- operation_epitype: OperationEpitype¶
- class ferrosoft.apps.emiflow.services.emissions.EmissionResultMixin[source]¶
Bases:
objectMemoised summation helpers for classes that own a list of
EmissionResult.- emission(emission_category, operation_epitype)[source]¶
Return the single emission matching both category and epitype, or
None.- Return type:
- emission_of_category(emission_category)[source]¶
Sum emissions in
emission_category(zero when none match).- Return type:
- property emission_total: DecimalWithUnit[source]¶
Sum of all emissions;
0 gCO2ewhen the list is empty.
- exception ferrosoft.apps.emiflow.services.emissions.EmissionServiceError[source]¶
Bases:
RuntimeErrorBase class for emiflow emission-service errors.
- exception ferrosoft.apps.emiflow.services.emissions.IntensityCalculationError[source]¶
Bases:
EmissionServiceErrorRaised when emission intensity cannot be computed (bad units, zero activity).
- class ferrosoft.apps.emiflow.services.emissions.TransportActivityGroups(groups=<factory>)[source]¶
Bases:
objectHelper container that buckets transport activity by
DistanceType.- groups: dict[DistanceType, DecimalWithUnit]¶
- class ferrosoft.apps.emiflow.services.emissions.TransportBookingService[source]¶
Bases:
objectPost a transport chain’s calculated emissions to the booking journal.
Refuses to double-post (already-
POSTEDchains are skipped to keep the subscription tonnage counter accurate), enforces that each vehicle type within the chain uses a single distance strategy, geocodes geo-points, runs theTransportChainCalculator, and writes oneEmissionBookingRecordper chain plus one perTransportLCCValue. Finally moves the chain toPOSTED.- book(transport)[source]¶
Calculate and persist booking records, then mark the chain
POSTED.Returns the upserted records, or an empty list when the chain is not in
OPENstatus.- Raises:
BookingError – When chain elements within one vehicle type mix distance strategies (an ISO 14083 reporting violation).
- Return type:
- class ferrosoft.apps.emiflow.services.emissions.TransportChainCalculator[source]¶
Bases:
objectCompute per-element and per-chain emissions for one or more transport chains.
The calculator carries running totals across multiple chains so it can be reused via
add_transport_chain()for set-level reporting; for a single-chain calculation usecalculate_for()on a freshly constructed calculator.- calculate_for(chain)[source]¶
Compute the
TransportChainResultforchain.For each chain element the operation’s emission intensity is looked up and combined with the transport activity (mass × distance for transport TCEs, mass alone for hub TCEs) and the distance-adjustment factor to yield emissions in the operation and energy provision categories.
Elements whose operation lacks an intensity contribute an empty result so the chain can still be reported on, instead of failing the whole calculation.
- Raises:
RuntimeError – When the chain’s summed freight weight has no measurement unit attached.
- Return type:
- tc_results: List[TransportChainResult]¶
- tce_results: List[TransportChainElementResult]¶
- class ferrosoft.apps.emiflow.services.emissions.TransportChainElementResult(distance_type, element, transport_activity, emissions=<factory>)[source]¶
Bases:
EmissionResultMixinCalculation result for one
TransportChainElement.- distance_type: DistanceType | None¶
- element: TransportChainElement¶
- emissions: List[EmissionResult]¶
- transport_activity: DecimalWithUnit¶
- class ferrosoft.apps.emiflow.services.emissions.TransportChainResult(chain, emissions=<factory>, tce_results=<factory>, transport_activity=<factory>, distance=<factory>, weight=<factory>)[source]¶
Bases:
EmissionResultMixinAggregated calculation result for one
TransportChain.Mirrors the ISO 14083 reporting layout: emissions are accumulated across all elements, distance is summed, and transport activity is kept grouped by
DistanceTypeso it can be reported per strategy.- add_activity_distance(dtype, activity_distance)[source]¶
Accumulate
activity_distanceinto the bucket fordtype.
- chain: TransportChain¶
- distance: DecimalWithUnit¶
- emission_intensity(category)[source]¶
Return the chain’s emission intensity for
category(emission / activity).Returns a zero
DecimalWithUnitwhen there is no transport activity (a chain made entirely of hub elements).- Return type:
- emissions: List[EmissionResult]¶
- tce_results: List[TransportChainElementResult]¶
- transport_activity: dict[DistanceType, DecimalWithUnit]¶
- transport_activity_sum()[source]¶
Total transport activity across every distance-type bucket.
- Return type:
- weight: DecimalWithUnit¶
- class ferrosoft.apps.emiflow.services.emissions.TransportChainSetResult(emission_total, emission_operation, transport_activity, emission_intensity_total, emission_intensity_operation, distance, weight)[source]¶
Bases:
objectAggregated result over a set of transport chains (cross-chain reporting).
Supports
set_result += chain_resultso callers can fold a chain result into the running totals.- distance: DecimalWithUnit¶
- emission_intensity_operation: DecimalWithUnit¶
- emission_intensity_total: DecimalWithUnit¶
- emission_operation: DecimalWithUnit¶
- emission_total: DecimalWithUnit¶
- transport_activity: dict[DistanceType, DecimalWithUnit]¶
- weight: DecimalWithUnit¶
- class ferrosoft.apps.emiflow.services.emissions.TransportChainVisitorImportCallback(visitor)[source]¶
Bases:
ImportCallbackImport callback that runs a
TransportChainVisitorafter import finishes.Collects every
TransportChaininstance the import emits; onceon_finishfires (all rows committed), each chain accepts the wrapped visitor. Used to defer geocoding/routing until imported rows are queryable.
ferrosoft.apps.emiflow.services.geographic module¶
Strategy that resolves activity distance from a TCE’s distance type.
For actual distance the user-entered value is treated as authoritative. For great-circle (GCD) and shortest-feasible (SFD) distances the configured router computes the distance between origin and destination geo-points. Results are normalised to kilometres.
- class ferrosoft.apps.emiflow.services.geographic.DistanceStrategyCalculator(gcd_router, sfd_router)[source]¶
Bases:
objectResolve a TCE’s activity distance according to its
DistanceType.- distance(activity, force_calculation=False)[source]¶
Return the activity distance for
activity.- Parameters:
activity (
ActivityDistanceParameters) – Bundles distance type, the already-entered distance, the DAF, and origin/destination geo-points.force_calculation – When
True, ignore any pre-existingactivity.distancevalue and re-route from origin/destination.
- Returns:
The resolved distance, or
Nonefor the actual distance strategy (the caller should keep the user-entered value).- Return type:
- Raises:
GeographicError – When the inputs are inconsistent with the strategy (zero actual distance, missing origin or destination, unrouted route, or unknown distance type).
ferrosoft.apps.emiflow.services.pcf module¶
Product Carbon Footprint (PCF) calculation across all chains for a material.
The ProductCarbonFootprintService aggregates transport
emissions for every TransportChain that carries a given
Material, normalises units across chains, and reports the
emission-per-tonne intensity in kgCO2e.
- class ferrosoft.apps.emiflow.services.pcf.LifecycleResult(label, value)[source]¶
Bases:
objectOne row of a PCF table: a labelled lifecycle contribution.
- value: DecimalWithUnit¶
- class ferrosoft.apps.emiflow.services.pcf.ProductCarbonFootprintService[source]¶
Bases:
objectCalculate the Product Carbon Footprint of one material across all transports.
Uses a raw SQL query to find the transport chains carrying the material, then runs the chain calculator against each one and divides the summed emissions by the summed weight to express the result as
kgCO2eper weight unit.- calculate_for(material)[source]¶
Compute the PCF for
materialand return one transport row.Returns an empty list when no chains carry the material. Raises
RuntimeErrorwhen the source chains mix weight or emission units, since combining mismatched units would silently falsify the result.- Return type:
ferrosoft.apps.emiflow.services.reporting module¶
Document generators that turn booking records into renderable reports.
Each DocumentGenerator subclass binds a Report (and
its filtered queryset of booking records) to a list of
DocumentOutput objects. The base generate() renders each
output’s template into HTML, and prepare_report_models() persists
the rendered HTML as short-lived StoredFile-backed reports
that can later be turned into PDF by the upstream reporting service.
DocumentGeneratorRegistry maps ReportType values to
the matching generator class.
- class ferrosoft.apps.emiflow.services.reporting.DocumentGenerator(report, booking_records=[], template=None)[source]¶
Bases:
ABCGenerate HTML document for Emiflow reports.
- booking_records: QuerySet¶
Reports center around booking records.
They can be used in get_context_data to make queries.
- generate(template=None)[source]¶
Generate document outputs.
Outputs are retrieved via get_document_outputs, which must be implemented in child classes. For each output, the template defined in the output is rendered and the resulting HTML is stored in output content field.
- Parameters:
template (
ReportTemplate|None) – Content customization parameters overriding default parameters.- Return type:
Returns: List of outputs to be saved as StoredFile associated with a Report model.
- get_context_data()[source]¶
Get context for report template rendering.
This provides the base context for all document outputs. DocumentOutput itself also provides a get_contex_data method, which adds to the base context.
- Return type:
- abstractmethod get_document_outputs()[source]¶
Return which outputs to create.
A generator can return one or more outputs. Each non-empty output is saved into a StoredFile associated with a Report model. The user can select which output to view or to export to PDF.
See also generate method on how the outputs are used.
- Return type:
- prepare_report_models(outputs, owner)[source]¶
Create Report models (of the reporting app) with the content of given outputs.
- template: ReportTemplate | None¶
Content customization parameters
- class ferrosoft.apps.emiflow.services.reporting.DocumentGeneratorRegistry[source]¶
Bases:
objectMaps
ReportTypeto the matchingDocumentGeneratorclass.- generator_classes = {ReportType.PCF: <class 'ferrosoft.apps.emiflow.services.reporting.PCFDocumentGenerator'>, ReportType.TRANSPORT: <class 'ferrosoft.apps.emiflow.services.reporting.TransportEmissionsDocumentGenerator'>, ReportType.WAREHOUSE: <class 'ferrosoft.apps.emiflow.services.reporting.WarehouseOverviewGenerator'>}¶
- class ferrosoft.apps.emiflow.services.reporting.DocumentOutput(template_name, title='Report', content='')[source]¶
Bases:
objectDocument outputs are produced by report generators.
- class ferrosoft.apps.emiflow.services.reporting.PCFDocumentGenerator(report, booking_records=[], template=None)[source]¶
Bases:
DocumentGeneratorGenerator that emits one PCF document per distinct material.
- get_document_outputs()[source]¶
Return one
PCFDocumentOutputper material in the booking records.- Return type:
- class ferrosoft.apps.emiflow.services.reporting.PCFDocumentOutput(template_name, title='Report', content='', material_name='')[source]¶
Bases:
DocumentOutputOutput for one PCF report — one document per material.
- class ferrosoft.apps.emiflow.services.reporting.TransportEmissionsDocumentGenerator(report, booking_records=[], template=None)[source]¶
Bases:
DocumentGeneratorGenerator for ISO 14083 transport-emissions reports (summary + listing).
- get_context_data()[source]¶
Aggregate transport bookings (distance, intensity, totals) into the template context.
Restricts the records to the three transport-related lifecycle categories (incoming, outgoing, drop shipment) and computes distance- and intensity-based aggregates from the denormalised
extraJSON fields. Unit symbols are hard-coded because booking records always store transport distances and weights in those units.- Return type:
- class ferrosoft.apps.emiflow.services.reporting.WarehouseOverviewGenerator(report, booking_records=[], template=None)[source]¶
Bases:
DocumentGeneratorGenerator for the warehouse overview report (one table grouped by material).
- get_context_data()[source]¶
Provide the warehouse overview table, sourced from the PCF aggregate query.
- Return type:
- ferrosoft.apps.emiflow.services.reporting.format_html(template, context=None, title='', timestamp=None, params=None)[source]¶
Format report template as HTML.
- Parameters:
template (
str|Template) – Django template. It should extend reporting/report.html.title (
str) – Default document title. It is overridden by ReportTemplate.document_title if not empty.timestamp (
datetime|None) – Timestamp to show in document. If None, current time is used.params (
ReportTemplate|None) – Override visual appearance and document title.
- Return type:
- Returns:
HTML content
ferrosoft.apps.emiflow.services.session module¶
Per-user-session storage of the current transport chain.
When the user is mid-way through editing a transport chain across multiple views, the chain’s primary key is stashed in the Django session so subsequent views can resume without it being passed through every URL. All helpers degrade silently when the request has no session (e.g. during management commands or unit tests).
- ferrosoft.apps.emiflow.services.session.clear_session(request)[source]¶
Forget the current chain. No-op when nothing is stored.
ferrosoft.apps.emiflow.services.tonnage module¶
Tonnage accounting for subscription billing.
Customers are billed on the cumulative weight of (non-simulation) emission bookings. This module exposes two query paths:
A direct database aggregate per tenant (
TonnageService.get_summary()) used at startup to seed the Prometheus counter and on demand for reconciliation. Expensive — it opens a connection to every active tenant database.A cheap VictoriaMetrics query (
TonnageServiceVictoria.get_org_stats()) that returns the rolling 4-week tonnage and configured limit per organisation from the metrics store.
- class ferrosoft.apps.emiflow.services.tonnage.TonnageService[source]¶
Bases:
ABCAbstract tonnage service. The concrete singleton is
TonnageServiceVictoria.- abstractmethod get_org_stats(org)[source]¶
Return the billable tonnage and limit for
org(implementation-specific).- Return type:
- get_summary()[source]¶
Return per-tenant booked tonnage across every active tenant.
Iterates over every organisation and active tenant, opening each tenant database to read its booking-record aggregate. Misconfigured databases (
ImproperlyConfigured) yield a zero-tonnage row so the summary remains complete. Costly; preferget_org_stats()whenever per-organisation totals are sufficient.- Return type:
- class ferrosoft.apps.emiflow.services.tonnage.TonnageServiceVictoria(config)[source]¶
Bases:
TonnageServiceTonnage service backed by VictoriaMetrics Prometheus-compatible queries.
Uses a single PromQL
unionover theemiflow_tonnage_limitgauge and the 4-week increase of theemiflow_tonnage_totalcounter to return both values in one HTTP call. Returns zero-stats on query failure so callers can render a safe fallback.- class Config(base_url, timeout=10)[source]¶
Bases:
objectVictoriaMetrics endpoint configuration.
- classmethod get_instance()[source]¶
Instantiate the service with config loaded from Django settings.
- class ferrosoft.apps.emiflow.services.tonnage.TonnageStats(billable_tonnage, tonnage_limit)[source]¶
Bases:
objectBilling snapshot for an organisation: current usage versus configured limit.
- billable_tonnage: DecimalWithUnit¶
- tonnage_limit: DecimalWithUnit¶
ferrosoft.apps.emiflow.services.treatment module¶
Emission calculation and booking for treatments.
TreatmentEmissionCalculator multiplies the treatment’s output
mass by the operation’s emission intensity (for both operation and
energy provision categories) to derive the total emission.
TreatmentBookingService then splits that total across output
lines using each line’s allocated_fraction and creates one
EmissionBookingRecord per output and per attached
TreatmentLCCValue.
- class ferrosoft.apps.emiflow.services.treatment.EmissionResult(emission, intensity)[source]¶
Bases:
objectOne category’s calculated emission paired with the intensity that produced it.
- emission: DecimalWithUnit¶
- intensity: DecimalWithUnit¶
- class ferrosoft.apps.emiflow.services.treatment.TreatmentBookingService[source]¶
Bases:
objectPersist
EmissionBookingRecordrows for a treatment.For every output line, the treatment’s total emission is multiplied by the line’s
allocated_fractionto produce one booking record; each attachedTreatmentLCCValueis similarly split across output lines.
- class ferrosoft.apps.emiflow.services.treatment.TreatmentEmissionCalculator[source]¶
Bases:
objectCompute operation and energy-provision emissions for a treatment.
- calculate_for(treatment)[source]¶
Calculate emissions for
treatment.- Raises:
NormalizationError – When the operation has no emission intensity, or the intensity unit cannot be normalised to
kgCO2e/t.- Return type:
- class ferrosoft.apps.emiflow.services.treatment.TreatmentResult(activity, emissions)[source]¶
Bases:
objectPer-treatment activity (output mass) and per-category emission results.
- activity: DecimalWithUnit¶
- emissions: Mapping[EmissionCategory, EmissionResult]¶
ferrosoft.apps.emiflow.services.update module¶
Emiflow hooks into the ferrobase update pipeline.
The ferrobase ferrobase_update management command iterates over
registered global and per-tenant updaters at deploy time. This module
registers an updater that ensures the four predefined
LifecycleCategory rows exist in every database — both the
shared catalog database (global pass) and each tenant database (tenant
pass).
- ferrosoft.apps.emiflow.services.update.create_lifecycle_categories(writer, tenant=None)[source]¶
Upsert the predefined lifecycle categories in the appropriate database.
- Parameters:
writer (
StatusWriter) – Status writer used to report progress back to the update pipeline.tenant (
Tenant|None) – When provided, the upsert is routed to that tenant’s database; whenNone, the default (catalog) database is used so the same function serves both the global and the per-tenant pass.
- ferrosoft.apps.emiflow.services.update.init()[source]¶
Register
create_lifecycle_categories()with the global and tenant updater registries.Called from
EmiflowConfig.ready()so the updaters are wired up at Django app initialisation.
ferrosoft.apps.emiflow.services.vehicle module¶
Heuristic mapping from freight weight to a likely VehicleType.
Used during bulk import when the source data does not specify the vehicle type explicitly. The thresholds are hard-coded approximations and will likely become tenant-configurable.
ferrosoft.apps.emiflow.services.visitor module¶
Visitor implementations driven by TransportChain.accept().
Visitors here perform actions over an entire chain’s structure
(elements, freight items, geo-points). DebuggerVisitor dumps
every field to stdout for development; GeocodingRoutingVisitor
populates missing coordinates and activity distances before emission
calculation.
- class ferrosoft.apps.emiflow.services.visitor.DebuggerVisitor[source]¶
Bases:
TransportChainVisitorVisitor that prints every field of every visited model to stdout.
Intended for ad-hoc debugging only — production code should not depend on it.
- exception ferrosoft.apps.emiflow.services.visitor.GeocodingRoutingError[source]¶
Bases:
RuntimeErrorRaised when geocoding or routing fails for a chain element.
- class ferrosoft.apps.emiflow.services.visitor.GeocodingRoutingVisitor(geocoder, distance_calc, force_geocoding=False, force_distance_calculation=False)[source]¶
Bases:
TransportChainVisitorFill in missing coordinates and activity distances on each TCE.
For every transport (non-hub) element with origin or destination geo-points whose coordinates are still
(0, 0), the address is geocoded and the coordinates are persisted. Then, when the activity distance has not been entered (orforce_distance_calculationis set), the configuredDistanceStrategyCalculatoris used to compute and store it. Run before booking to make sure emissions can actually be calculated.- classmethod get_instance(**kwargs)[source]¶
Build a visitor wired up with the default address-router-backed services.
- on_element(element)[source]¶
Geocode geo-points and resolve activity distance for
element.Hub elements are skipped — distance is irrelevant there. For transport elements an origin and destination are required for any distance type other than actual distance. Geocoding and routing failures are re-raised as
GeocodingRoutingErrorwith a user-facing message.