Forge Heat Cycles
Forge heat-cycle registry API: B-tree indexes, chained risk UDFs, and parallel shop-floor writers
One app.py with 1 table, 4 computed columns, 8 HTTP routes. Needs pixeltable[serve]>=0.7.16. Updated 2026-10-10.
app.py
"""Forge Heat Cycle Registry API built with Pixeltable.Log a forge heat with forge id, alloy, peak temperature, hold time and quench.Pixeltable derives a heat band, risk score, combined heat tag and priority lanevia a UDF chain, and B-tree indexes on forge, alloy, status and band back theshop-floor lookup queries. The client demo exercises parallel writers.Layout: UDFs live in udfs.py; this file holds table models, @pxt.query functions and routes.Run locally:pxt schema update app.py forgepxt service run app.py forge"""import pixeltable as pxtimport pixeltable.functions as pxtffrom pixeltable.serving import FastAPIRouterfrom udfs import heat_band, heat_tag, priority_lane, risk_score# ---- tables ----TableModel = pxt.model_base()class Heats(TableModel, name='heats', has_default_idxs=False):heat_id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)forge_id: pxt.Stringalloy: pxt.Stringstatus: pxt.String # queued / firing / quench / cool / done / hold / scrappedpeak_c: pxt.Floathold_min: pxt.Floatquench: pxt.String # air / oil / water / brineoperator: pxt.String | Nonenote: pxt.String | Noneband = heat_band(peak_c)risk = risk_score(peak_c, hold_min, quench)tag = heat_tag(band, risk, alloy)lane = priority_lane(risk, status)__indexes__ = [pxt.BtreeIndex(forge_id),pxt.BtreeIndex(alloy),pxt.BtreeIndex(status),pxt.BtreeIndex(band),]# ---- queries ----@pxt.querydef by_forge(forge_id: str):"""Heats on one forge (forge_id index), highest risk first."""return (Heats.where(Heats.forge_id == forge_id).select(Heats.heat_id, Heats.alloy, Heats.status, Heats.peak_c, Heats.band, Heats.risk, Heats.tag, Heats.lane).order_by(Heats.risk, asc=False))@pxt.querydef by_band_status(band: str, status: str):"""Heats in a heat band and status (band + status indexes)."""return (Heats.where((Heats.band == band) & (Heats.status == status)).select(Heats.heat_id, Heats.forge_id, Heats.alloy, Heats.peak_c, Heats.risk, Heats.tag, Heats.lane).order_by(Heats.peak_c, asc=False))@pxt.querydef by_alloy(alloy: str):"""All heats for an alloy (alloy index)."""return (Heats.where(Heats.alloy == alloy).select(Heats.heat_id, Heats.forge_id, Heats.status, Heats.band, Heats.risk, Heats.tag).order_by(Heats.forge_id))@pxt.querydef hot_lane():"""Shop board: heats currently in the hot priority lane."""return (Heats.where(Heats.lane == 'hot').select(Heats.heat_id, Heats.forge_id, Heats.alloy, Heats.status, Heats.risk, Heats.tag).order_by(Heats.risk, asc=False))# ---- routes ----forge_api = FastAPIRouter(name='forge_api')forge_api.add_insert_route(Heats,path='/heats',inputs=[Heats.forge_id, Heats.alloy, Heats.status, Heats.peak_c, Heats.hold_min, Heats.quench, Heats.operator, Heats.note],outputs=[Heats.heat_id, Heats.band, Heats.risk, Heats.tag, Heats.lane],)forge_api.add_update_route(Heats,path='/heats/status',inputs=[Heats.status, Heats.note],outputs=[Heats.heat_id, Heats.status, Heats.lane, Heats.tag],)forge_api.add_compute_route(Heats,path='/score',inputs=[Heats.peak_c, Heats.hold_min, Heats.quench, Heats.alloy, Heats.status],outputs=[Heats.band, Heats.risk, Heats.tag, Heats.lane],)forge_api.add_delete_route(Heats, path='/heats/delete')forge_api.add_query_route(path='/heats/by-forge', query=by_forge, method='get')forge_api.add_query_route(path='/heats/by-band', query=by_band_status, method='get')forge_api.add_query_route(path='/heats/by-alloy', query=by_alloy, method='get')forge_api.add_query_route(path='/heats/hot-lane', query=hot_lane, method='get')
What app.py declares
Each row links to its line.
- runs on insert
- stored
- index
- route
- heat_idforge_idalloystatuspeak_chold_minquenchoperatornote
- band = heat_band(peak_c)L35
- risk = risk_score(peak_c, hold_min, quench)L36
- tag = heat_tag(band, risk, alloy)L37
- lane = priority_lane(risk, status)L38
- B-tree index on forge_idL41
- B-tree index on alloyL42
- B-tree index on statusL43
- B-tree index on bandL44
serves forge_api
- POST/heatsinsert, heatstakes forge_id, alloy, status, peak_c, hold_min, quench, operator, note; returns heat_id, band, risk, tag, laneL92
- POST/heats/statusupdate, heatstakes status, note; returns heat_id, status, lane, tagL98
- POST/scorecompute, heatstakes peak_c, hold_min, quench, alloy, status; returns band, risk, tag, laneL104
- POST/heats/deletedelete, heatsL110
- GET/heats/by-forgequery, by_forgetakes forge_idL111
- GET/heats/by-bandquery, by_band_statustakes band, statusL112
- GET/heats/by-alloyquery, by_alloytakes alloyL113
- GET/heats/hot-lanequery, hot_laneL114
Run it
Needs uv. The routes then answer on port 8000.
terminal
git clone https://github.com/pierrebrunelle/pixeltable-forge-heatscd pixeltable-forge-heatsuv syncuv run pxt schema update app.py forgeuv run python seed.py forgeuv run pxt service run app.py forge --port 8000
Related apps
- Climbing Route ToposClimbing-route topo registry API: photo uploads with thumbnails, grayscale copies, grade bands and index-backed crag lookupsShares: B-tree indexes, UDFs
- Warehouse Pick ListsWarehouse pick-list REST API: units, pick zones, waves and tote labels as incremental computed columns over Json order linesShares: B-tree indexes, UDFs
- Ferry BookingFerry booking REST API: fares and deck classes as incremental computed columns, served with FastAPIShares: B-tree indexes, UDFs