part_tracking
part_tracking ¤
Production tracking by part number.
Simple, practical module for daily production reporting: - How many parts were produced? - Production by part number and time window - Daily summaries
Counters are assumed to be monotonically increasing during normal
operation. Many real-world counters reset back to zero (or to a lower
value) - for example at a shift change, a part change, or a controller
restart. A naive last - first calculation silently loses all of the
production that happened in a window where a reset occurred. Pass
handle_resets=True to account for these resets, and use
:meth:PartProductionEvents.detect_resets to inspect when they happened.
PartProductionEvents ¤
PartProductionEvents(
dataframe: DataFrame,
*,
time_column: str = "systime",
event_uuid: str = "prod:part_tracking"
)
Bases: Base
Track production quantities by part number.
Each UUID represents one signal: - part_id_uuid: string signal with current part number - counter_uuid: monotonic counter for production count
The counter is expected to increase as parts are produced. If the
counter is reset during operation (back to zero or a lower value),
enable handle_resets so the reset is treated as new production
instead of being discarded.
Example
import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.production_by_part("part", "counter")[["start", "part_number", "quantity"]] start part_number quantity 0 2025-01-01 00:00:00 A 59 1 2025-01-01 01:00:00 B 60
Initialize part production tracker.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataframe
|
DataFrame
|
Input DataFrame with timeseries data |
required |
time_column
|
str
|
Name of timestamp column (default: 'systime') |
'systime'
|
event_uuid
|
str
|
UUID tagged on derived point events (the |
'prod:part_tracking'
|
production_by_part ¤
production_by_part(
part_id_uuid: str,
counter_uuid: str,
*,
window: str = "1h",
value_column_part: str = "value_string",
value_column_counter: str = "value_integer",
handle_resets: bool = True
) -> pd.DataFrame
Calculate production quantity per part number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
part_id_uuid
|
str
|
UUID for part number signal |
required |
counter_uuid
|
str
|
UUID for production counter |
required |
window
|
str
|
Time window for aggregation (e.g., '1h', '8h', '1d') |
'1h'
|
value_column_part
|
str
|
Column containing part numbers |
'value_string'
|
value_column_counter
|
str
|
Column containing counter values |
'value_integer'
|
handle_resets
|
bool
|
When True (default), treat a decreasing counter as a reset
and count the production after the reset instead of losing
it. The result gains a |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with columns: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
Example
import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.production_by_part("part", "counter")[["part_number", "quantity"]] part_number quantity 0 A 59 1 B 60
With handle_resets=True (the default), a window where the counter
drops from 432 to 61 reports a quantity of 61 (and resets=1)
instead of 0.
detect_resets ¤
detect_resets(
part_id_uuid: str,
counter_uuid: str,
*,
value_column_part: str = "value_string",
value_column_counter: str = "value_integer"
) -> pd.DataFrame
Find the points in time where the counter was reset.
A reset is any reading where the counter value is lower than the previous reading.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
part_id_uuid
|
str
|
UUID for part number signal |
required |
counter_uuid
|
str
|
UUID for production counter |
required |
value_column_part
|
str
|
Column containing part numbers |
'value_string'
|
value_column_counter
|
str
|
Column containing counter values |
'value_integer'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per reset and columns: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
Example
import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A"], hold=6, freq="1min") counter = parts.assign(uuid="counter", value_string=pd.NA, ... value_integer=[430, 431, 432, 61, 62, 63]) pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.detect_resets("part", "counter")[["part_number", "count_before", "count_after"]] part_number count_before count_after 0 A 432.0 61.0
daily_production_summary ¤
daily_production_summary(
part_id_uuid: str,
counter_uuid: str,
*,
value_column_part: str = "value_string",
value_column_counter: str = "value_integer",
handle_resets: bool = True
) -> pd.DataFrame
Daily production summary by part number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
part_id_uuid
|
str
|
UUID for part number signal |
required |
counter_uuid
|
str
|
UUID for production counter |
required |
value_column_part
|
str
|
Column containing part numbers |
'value_string'
|
value_column_counter
|
str
|
Column containing counter values |
'value_integer'
|
handle_resets
|
bool
|
Account for counter resets (see
:meth: |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with columns: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
Example
import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) daily = pp.daily_production_summary("part", "counter") daily[["date", "part_number", "total_quantity", "hours_active"]] date part_number total_quantity hours_active 0 2025-01-01 A 59 1 1 2025-01-01 B 60 1
production_totals ¤
production_totals(
part_id_uuid: str,
counter_uuid: str,
*,
start_date: str | None = None,
end_date: str | None = None,
value_column_part: str = "value_string",
value_column_counter: str = "value_integer",
handle_resets: bool = True
) -> pd.DataFrame
Total production by part number for a date range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
part_id_uuid
|
str
|
UUID for part number signal |
required |
counter_uuid
|
str
|
UUID for production counter |
required |
start_date
|
str | None
|
Start date 'YYYY-MM-DD' (optional) |
None
|
end_date
|
str | None
|
End date 'YYYY-MM-DD' (optional) |
None
|
value_column_part
|
str
|
Column containing part numbers |
'value_string'
|
value_column_counter
|
str
|
Column containing counter values |
'value_integer'
|
handle_resets
|
bool
|
Account for counter resets (see
:meth: |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with total production per part |
Example
import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.production_totals("part", "counter", start_date="2025-01-01") part_number total_quantity days_produced resets 0 A 59 1 0 1 B 60 1 0