mineproductivity.kpis¶
Auto-generated API reference for the Kpis package, rendered from the package's own docstrings. For the narrative overview, dependency rules, and extension guide, see Packages -> Kpis.
kpis ¶
mineproductivity.kpis -- the metric backbone of MineProductivity.
Makes every performance indicator a discoverable, versioned, self -describing object rather than a formula buried in a script or spreadsheet cell (KPI-as-object, the root README's engineering philosophy), guaranteeing the platform's central promise: two engineers, two sites, or two AI agents each compute "availability" must get the same number from the same events.
Implements docs/architecture/05_KPI_Engine_Design_Specification.md
exactly. kpis depends on core, ontology, events, and
registry -- and MUST NEVER import connectors (the single most
load-bearing rule in the governing specification) -- see README.md
for the full set of architectural rules this package must satisfy.
Everything documented here is part of the public API and can be imported
directly from mineproductivity.kpis, e.g.::
from mineproductivity.kpis import REGISTRY, KPIEngine
BaseKPI ¶
Bases: ABC
The root of every KPI. A concrete leaf declares meta:
ClassVar[KPIMetadata] and implements :meth:_compute -- everything
else (validation, dependency declaration, result wrapping) is
inherited.
This mirrors Cookbook Part I, Ch. 6 exactly: "The category base... and
the metadata do most of the work; _compute is the only logic you
write." Instances are stateless across :meth:compute calls and
therefore safe to share and invoke concurrently from multiple
threads.
compute ¶
Validate rows against :meth:_required_columns, then call
:meth:_compute, then wrap in a :class:KPIResult.
This method is not overridden by leaf KPIs -- it is the one place the "qualify, don't coerce" stance (Cookbook Part I Ch. 6) is enforced uniformly: a missing required column becomes a warning-carrying result, never an exception.
ResultCache ¶
Memoizes :class:~mineproductivity.kpis.result.KPIResult per
(code, window, scope, fingerprint).
fingerprint is a caller-supplied, cheap proxy for "has anything
relevant changed" -- :class:~mineproductivity.kpis.engine.KPIEngine
passes the count of envelopes matching the query backing a given
computation, so the cache key naturally changes the moment a new
matching event lands (design spec §22's "invalidated automatically...
never silently stale" guarantee), without requiring a new versioning
API on the locked EventStore contract.
Thread-safe: a single lock serializes reads-that-may-write, so
concurrent :meth:get_or_compute calls for the same key never
duplicate work or corrupt the cache -- the public contract only
guarantees a consistent result, not this specific mechanism,
mirroring registry.DiscoveryCache's concurrency contract.
DelayKPI ¶
Bases: BaseKPI, ABC
DISP namespace: delay hours by category, dispatch metrics,
consuming :class:~mineproductivity.ontology.DelayCategory's
six-value taxonomy.
EnergyKPI ¶
Bases: BaseKPI, ABC
ENERGY/CARBON/WATER namespaces: consumption, emissions,
and water-use indicators.
ProductionKPI ¶
Bases: BaseKPI, ABC
PROD namespace: throughput, cycle time, payload -- read
directly from CycleEvent streams (Developer & Cookbook Guide
Part III).
UtilizationKPI ¶
Bases: BaseKPI, ABC
UTIL namespace: PA, UA, EU, OEE -- built on the canonical time
model (design spec §19): calendar >= scheduled >= available >=
operating.
CompositeKPI ¶
Bases: BaseKPI, ABC
A KPI whose value is DERIVED from other KPIs' already-computed
results, not directly from raw event rows -- e.g. UTIL.OEE =
UTIL.PA x UTIL.Performance x UTIL.Quality (Part III, Canonical
Semantics). Kept a distinct base class from leaf KPIs (design spec
AD-KP-04) so "reads raw rows" and "reads other KPIs' results" are
never conflated in one method signature.
combine ¶
The composite counterpart of :meth:BaseKPI.compute: if any
declared dependency's result has a None value, propagate
None with a warning rather than calling :meth:_combine
(which would otherwise have to special-case every missing
dependency itself).
DependencyGraph ¶
A DAG over KPI codes, built from KPIMetadata.dependencies.
Topologically sorts so dependencies compute before dependents (Part
III, Introduction: "the engine resolves them as a directed acyclic
graph, computing dependencies before dependents").
Topological order per code is memoized (design spec §9's
kpis.dependency_graph._topo_cache), invalidated only by an
explicit :meth:invalidate call -- mirrors
registry.DiscoveryCache's explicit-invalidation-only rule.
topological_order ¶
Dependency-first order of every KPI code code transitively
depends on, ending with code itself.
Raises:
| Type | Description |
|---|---|
KPICircularDependencyError
|
If resolving |
detect_cycle ¶
Non-raising cycle detection across every code currently in the
registry. KPIEngine/the @register decorator converts a
Some result into :class:KPICircularDependencyError at
registration time, not at first execution.
invalidate ¶
Explicitly drop every memoized topological order -- call after the registry changes (a new KPI registered).
KPIEngine ¶
Orchestration ONLY -- holds no metric-specific logic (design spec
§3.2, AD-KP-01). Resolves a requested KPI's dependency graph,
assembles exactly the rows each node needs, executes leaves before
composites, and returns the final :class:KPIResult.
shifts is an optional, injectable lookup of known
:class:~mineproductivity.ontology.Shift instances -- when
scope["shift"] names a known shift, its
start_utc/end_utc resolve the query window directly (a
genuine integration with ontology, not a placeholder). Without a
matching shift, scope["since"]/scope["until"] (ISO-8601
strings) are used as a direct escape hatch; resolving a
"day"/"week"/"month" window label against a site's
calendar is Future Work (this reference engine's ontology
integration is scoped to shift-level resolution for v0.7.0).
execute ¶
The .by(grouping) execution path (Cookbook Part I, Ch. 4).
summary ¶
Batched multi-KPI execution (the mine.summary(...)
equivalent): scans the event store once per distinct
required_events set shared across the requested KPIs and
their transitive dependencies, rather than once per KPI (design
spec §22).
rows_for ¶
Assembles exactly the rows kpi needs: an :class:EventQuery
scoped to kpi.meta.required_events and the window/scope, with
every envelope flattened into a row dict and round-tripped
through the active :class:~mineproductivity.kpis.backends.base_backend.ExecutionBackend
(assemble then to_rows) -- this is the mechanical proof
that BaseKPI._compute implementations are truly
backend-independent (design spec §29's backend parity tests):
swapping the backend never changes the row data, only how it
was vectorized in transit.
Returns (rows, fingerprint) -- fingerprint is the
matching envelope count, the cheap "has anything changed" proxy
:class:~mineproductivity.kpis.caching.ResultCache keys on
(design spec §22).
KPIAggregationError ¶
Bases: MineProductivityError
An attempt was made to aggregate a RATIO/AVERAGE-kind KPI's already-computed sub-period results as if they were ADDITIVE (the "averaging shift TPHs" mistake, made structurally impossible by the engine rather than merely discouraged by documentation).
KPICircularDependencyError ¶
Bases: MineProductivityError
DependencyGraph.detect_cycle() found a cycle -- raised at
registration time (registering the KPI that completes the cycle),
never deferred to first execution.
KPINotFoundError ¶
KPIValidationError ¶
Bases: ValidationError
KPIMetadata failed validation -- e.g. malformed identifier,
missing mandatory field, aggregation/time-model violation.
KPIVersionConflictError ¶
Bases: RegistrationError
A plugin attempted to re-register an existing, Active KPI
code with metadata that changes its formula/units/semantics without
a MAJOR version bump.
KPIStatus ¶
Bases: Enum
Proposed -> Active -> Deprecated -> Retired. A Deprecated
KPI remains resolvable via meta.aliases/deprecated_successor;
a Retired identifier is never reused.
Aggregation ¶
Bases: Enum
Governs how a KPI MAY be aggregated across periods/groups -- directly enforces the RATIO-not-averaged rule (Cookbook Part I Ch. 6).
DigitalMaturity ¶
Bases: Enum
The site digital-maturity level a KPI requires to be computable.
Direction ¶
Bases: Enum
Which way "better" points for a KPI's value.
KPIMetadata
dataclass
¶
KPIMetadata(name, code, *, description='', tags=frozenset(), attributes=dict(), official_name='', business_purpose='', operational_question='', business_meaning='', formula='', unit='', dimensions=(), required_events=(), required_ontology=(), dependencies=(), aggregation=Aggregation.ADDITIVE, visualization_hint='', benchmark_bands=dict(), edge_cases=(), leading_or_lagging='lagging', operational_or_strategic='operational', related_kpis=(), references=(), direction=Direction.HIGHER_IS_BETTER, status=(lambda: KPIStatus.PROPOSED)(), version='1.0.0', min_maturity=DigitalMaturity.L1_MANUAL, method_applicability=('open_pit', 'underground'), commodity_applicability=(), aliases=(), deprecated_successor=None)
Bases: BaseMetadata
The complete, governed metadata for one KPI -- fields 1-15 and
18-29 of the Standard Library's mandatory template are typed engine
fields; fields 16-17 (worked example, sample dataset) are
documentation artifacts carried in attributes since the engine
does not execute prose (design spec §34).
No field is optional -- a blank field is a specification gap (Standard Library governance rule, enforced here at construction).
KPIIdentifier
dataclass
¶
Bases: BaseValueObject
A parsed NAMESPACE.Name[.Specialization...] KPI code.
Examples:
>>> identifier = parse_identifier("PROD.TPH.Ore")
>>> identifier.namespace, identifier.name, identifier.specialization
('PROD', 'TPH', ('Ore',))
>>> str(identifier)
'PROD.TPH.Ore'
KPIResult
dataclass
¶
Bases: BaseValueObject
The outcome of one :class:~mineproductivity.kpis.base_kpi.BaseKPI
computation -- a scalar value (or None if legitimately
uncomputable), plus enough provenance (unit, source-row count,
scope) to trace it back to its inputs.
Examples:
>>> result = KPIResult(code="PROD.TPH", value=1212.1, unit="t/h", n=48)
>>> result.value
1212.1
>>> KPIResult(code="PROD.TPH", value=None, unit="t/h", warnings=("zero operating hours",)).warnings
('zero operating hours',)
to_frame ¶
Export to a DataFrame via the process's active
:class:~mineproductivity.kpis.backends.base_backend.ExecutionBackend
(design spec §9's kpis.backends._active_backend) -- "Familiar
by design": .kpi(code).by(grouping).to_frame() mirrors
df.groupby(...).agg(...).
CumulativeWindow
dataclass
¶
RollingWindow
dataclass
¶
Bases: Window
A moving window of fixed span, re-evaluated at each step (e.g. a 7-day rolling TPH trend).
Window
dataclass
¶
Bases: BaseValueObject
A time-bounded scope for KPI computation -- "shift", "day",
"week", "month", or a custom [since_utc, until_utc).
Examples:
>>> from datetime import timezone
>>> window = Window(
... kind="custom",
... since_utc=datetime(2026, 6, 25, tzinfo=timezone.utc),
... until_utc=datetime(2026, 6, 26, tzinfo=timezone.utc),
... )
>>> window.kind
'custom'
register ¶
Register cls into :data:REGISTRY, keyed by cls.meta.code.
Raises:
| Type | Description |
|---|---|
KPIValidationError
|
If |
KPIVersionConflictError
|
If |
KPICircularDependencyError
|
If registering |
parse_identifier ¶
Parse and validate code against the NAMESPACE.Name naming
standard: a controlled uppercase namespace, a PascalCase name, and
zero or more PascalCase dotted specializations (PROD.TPH.Ore).
Raises:
| Type | Description |
|---|---|
KPIValidationError
|
If |