health score

This commit is contained in:
2026-09-29 22:40:36 +05:30
parent f9fb405974
commit fb859ecda1
21 changed files with 1590 additions and 703 deletions

View File

@@ -64,17 +64,6 @@ var scalarFacts = []string{
var arrayFacts = []string{"providers", "highlights", "nutrients"}
// The structured nutrition panel: a jsonb column the agent team fills, which
// the customer app renders. Its own category because it is neither a scalar
// nor a text[] — stored as a string it would reach the app as an escaped blob
// inside the snapshot instead of an object, and every reader would have to
// decode it twice.
//
// Absent on every brand table today, which costs this fact and nothing else.
// The lines in `nutrients` above are parsed into a panel at read time, so a
// product backfilled before the column exists still shows figures.
var jsonFacts = []string{"nutrition"}
type product struct {
Productid int
Productbrand string
@@ -323,7 +312,7 @@ func factColumnsOf(db *gorm.DB, table string) []string {
}
var keep []string
for _, c := range append(append(append([]string{}, scalarFacts...), arrayFacts...), jsonFacts...) {
for _, c := range append(append([]string{}, scalarFacts...), arrayFacts...) {
if present[c] {
keep = append(keep, c)
}
@@ -343,9 +332,7 @@ func factsFor(db *gorm.DB, table string, cols []string, imageID string) (map[str
selects := make([]string, 0, len(cols))
for _, c := range cols {
// jsonb and text[] are both cast to text for the same reason: GORM's
// raw scan cannot land either one in a Go value directly.
if isArrayFact(c) || isJSONFact(c) {
if isArrayFact(c) {
selects = append(selects, c+"::text AS "+c)
continue
}
@@ -369,21 +356,6 @@ func factsFor(db *gorm.DB, table string, cols []string, imageID string) (map[str
if text == "" {
continue
}
if isJSONFact(c) {
// Embedded as an object, not as the text the column scanned to.
// Stored as a string it would reach the app as an escaped blob
// inside the snapshot, and every reader would decode it twice.
//
// Unreadable JSON is skipped rather than stored: an absent panel
// falls back to the nutrient lines at read time, a broken one does
// not.
var panel models.NutritionPanel
if err := json.Unmarshal([]byte(text), &panel); err != nil || !panel.HasValues() {
continue
}
facts[c] = panel
continue
}
if isArrayFact(c) {
values := models.ParsePGArray(text)
kept := make([]string, 0, len(values))
@@ -402,15 +374,6 @@ func factsFor(db *gorm.DB, table string, cols []string, imageID string) (map[str
return facts, true
}
func isJSONFact(name string) bool {
for _, c := range jsonFacts {
if c == name {
return true
}
}
return false
}
func isArrayFact(name string) bool {
for _, c := range arrayFacts {
if c == name {

View File

@@ -0,0 +1,59 @@
// Proves the nutrition panel and health score end to end against the LIVE
// catalogue-intelligence service — no database, no Fiesta, just the piece that
// was built.
//
// go run ./scratch/nutritionproof # known products
// go run ./scratch/nutritionproof <brand> <image_id> # any product
//
// Prints what `getproductbyvariant` will put on the product once deployed.
package main
import (
"encoding/json"
"fmt"
"os"
"nearle/models"
"nearle/services"
)
func main() {
base := os.Getenv("NUTRITION_BASE")
if base == "" {
base = "https://mcp.nearle.ai.in/api"
}
service := services.NewNutritionService(base)
// Real products from tenant 1147, checked against the live service on
// 29 Sep 2026: one the service has scored, one it has nothing for, and one
// that is not food — which must come back with no score whatever the
// service says, because that endpoint is not gated for edibility.
products := []models.Products{
{Productid: 7101, Productbrand: "balaji", Imageid: "balaji_balaji_wafers_simply_salted_135g",
Productname: "Balaji Wafers Simply Salted 135g (scored)"},
{Productid: 7093, Productbrand: "patanjali", Imageid: "patanjali_patanjali_cow_ghee_500ml",
Productname: "Patanjali Cow Ghee 500ml (no data)"},
{Productid: 7121, Productbrand: "godrej", Imageid: "godrej_godrej_no1_sandal_and_turmeric_soap_100g",
Productname: "Godrej No.1 Soap 100g (not food)"},
}
if len(os.Args) == 3 {
products = []models.Products{{
Productbrand: os.Args[1],
Imageid: os.Args[2],
Productname: os.Args[1] + "/" + os.Args[2],
}}
}
for i := range products {
found := service.ForProduct(products[i].Productbrand, products[i].Imageid)
products[i].Nutrition = found.Panel
products[i].Healthscore = found.Health
fmt.Printf("\n---- %s ----\n", products[i].Productname)
encoded, _ := json.MarshalIndent(map[string]any{
"nutrition": products[i].Nutrition,
"healthscore": products[i].Healthscore,
}, "", " ")
fmt.Println(string(encoded))
}
}