health score
This commit is contained in:
@@ -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 {
|
||||
|
||||
59
scratch/nutritionproof/main.go
Normal file
59
scratch/nutritionproof/main.go
Normal 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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user