updates on the ai agents and time series prediction and updates on the api to
This commit is contained in:
53
models/demand_forecast.go
Normal file
53
models/demand_forecast.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// DemandForecast is one zone-day the engine expects.
|
||||
//
|
||||
// ─── Why this is stored rather than computed on read ───────────────────────
|
||||
//
|
||||
// The forecast is produced in Python (AI_engine/prediction), because the model
|
||||
// is: Prophet where it beats a seasonal baseline, the baseline otherwise. The
|
||||
// console and any staffing decision need it in Go. So the engine writes here
|
||||
// and the backend serves it — the same split the agent registry and the
|
||||
// decision log already use, and the reason the /internal/* surface exists.
|
||||
//
|
||||
// It is also the honest shape: a forecast is a thing produced at a moment by a
|
||||
// model, not a function of the current table. Recomputing it on every read
|
||||
// would make yesterday's number unrecoverable, which is exactly what you want
|
||||
// when asking "was the forecast any good".
|
||||
type DemandForecast struct {
|
||||
Forecastid int `json:"forecastid" gorm:"primaryKey;column:forecastid;autoIncrement"`
|
||||
|
||||
// Zone is the first three digits of a pickup pincode — the same grain the
|
||||
// hub console scopes on (pickuppincode LIKE '641%') and the same grain
|
||||
// internal/prediction calibrates ETA at. Keeping one definition of "zone"
|
||||
// across both is deliberate.
|
||||
Zone string `json:"zone" gorm:"column:zone;size:8;not null;uniqueIndex:uq_demandforecast_zone_day,priority:1"`
|
||||
|
||||
// Forday is the day being predicted, not the day it was predicted on.
|
||||
Forday time.Time `json:"forday" gorm:"column:forday;not null;uniqueIndex:uq_demandforecast_zone_day,priority:2;index"`
|
||||
|
||||
Expectedbookings int `json:"expectedbookings" gorm:"column:expectedbookings;not null"`
|
||||
|
||||
// Model and Reason record WHICH model produced this and why it was chosen —
|
||||
// "prophet beat the weekly baseline over 12 folds (18% lower MAE)", or
|
||||
// "prophet is not installed in this image". Stored because the choice is
|
||||
// made per zone from that zone's own history, so without it nobody can tell
|
||||
// whether a bad forecast came from a bad model or from a thin series.
|
||||
Model string `json:"model" gorm:"column:model;size:32;not null"`
|
||||
Reason string `json:"reason" gorm:"column:reason"`
|
||||
|
||||
// Observations is how many days of history the forecast was fitted on, and
|
||||
// Baselinemae/Modelmae are the backtest scores. A forecast with 31
|
||||
// observations and a model barely beating the baseline deserves less trust
|
||||
// than one with 400, and this is what lets a reader see that rather than
|
||||
// taking the number at face value.
|
||||
Observations int `json:"observations" gorm:"column:observations;default:0"`
|
||||
Baselinemae *float64 `json:"baselinemae" gorm:"column:baselinemae"`
|
||||
Modelmae *float64 `json:"modelmae" gorm:"column:modelmae"`
|
||||
|
||||
Generatedat time.Time `json:"generatedat" gorm:"column:generatedat;not null"`
|
||||
}
|
||||
|
||||
func (DemandForecast) TableName() string { return "demandforecast" }
|
||||
Reference in New Issue
Block a user