ai cooking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

drop out of stock and not available in store (#524)

Co-authored-by: paul miller <paul.miller>

authored by

Paul Miller
paul miller
and committed by
GitHub
c2e3c928 bfdc0090

+50 -65
+35 -52
cmd/ingredients/main.go
··· 20 20 func main() { 21 21 var searchTerm string 22 22 var location string 23 + var verbose bool 23 24 flag.StringVar(&searchTerm, "ingredient", "", "Search term for ingredient lookup") 24 25 flag.StringVar(&searchTerm, "i", "", "Search term for ingredient lookup") 25 26 flag.StringVar(&location, "location", "", "Location for recipe sourcing (e.g., 70100023)") 26 27 flag.StringVar(&location, "l", "", "Location for recipe sourcing (short form)") 28 + flag.BoolVar(&verbose, "verbose", false, "dump all ingredients and grades") 27 29 flag.Parse() 28 30 ctx := context.Background() 31 + 32 + if searchTerm != "" { 33 + verbose = true 34 + } 29 35 30 36 cfg, err := config.Load() 31 37 if err != nil { ··· 48 54 } 49 55 50 56 catMap := make(map[string]int) 51 - if cfg.IngredientGrading.Enable { 52 - log.Printf("Grading %d ingredients", len(ings)) 53 - cacheStore, err := cache.MakeCache() 54 - if err != nil { 55 - log.Fatalf("failed to create cache for ingredient grading: %s", err) 57 + 58 + log.Printf("Grading %d ingredients", len(ings)) 59 + cacheStore, err := cache.MakeCache() 60 + if err != nil { 61 + log.Fatalf("failed to create cache for ingredient grading: %s", err) 62 + } 63 + grader := ingredientgrading.NewManager(cfg, cacheStore) 64 + graded, err := grader.GradeIngredients(ctx, ings) 65 + if err != nil { 66 + log.Fatalf("failed to grade ingredients: %s", err) 67 + } 68 + slices.SortFunc(graded, func(a, b ai.InputIngredient) int { 69 + if a.Grade.Score != b.Grade.Score { 70 + return b.Grade.Score - a.Grade.Score 56 71 } 57 - grader := ingredientgrading.NewManager(cfg, cacheStore) 58 - graded, err := grader.GradeIngredients(ctx, ings) 59 - if err != nil { 60 - log.Fatalf("failed to grade ingredients: %s", err) 72 + return strings.Compare(strings.ToLower(a.Description), strings.ToLower(b.Description)) 73 + }) 74 + for _, result := range graded { 75 + for _, cat := range result.Categories { 76 + catMap[cat] += 1 61 77 } 62 - slices.SortFunc(graded, func(a, b ai.InputIngredient) int { 63 - if a.Grade.Score != b.Grade.Score { 64 - return b.Grade.Score - a.Grade.Score 65 - } 66 - return strings.Compare(strings.ToLower(a.Description), strings.ToLower(b.Description)) 67 - }) 68 - for _, result := range graded { 69 - for _, cat := range result.Categories { 70 - catMap[cat] += 1 71 - } 72 - 78 + if verbose { 73 79 fmt.Printf("%2d/10: %s - %s: size: %s: %s\n", result.Grade.Score, result.Brand, result.Description, result.Size, result.Grade.Reason) 74 80 } 75 - for cat, count := range catMap { 76 - fmt.Printf("Category: %s, Count: %d\n", cat, count) 77 - } 78 - 79 - counts := lo.Reduce(graded, func(counts map[int]int, ingredient ai.InputIngredient, _ int) map[int]int { 80 - counts[ingredient.Grade.Score] += 1 81 - return counts 82 - }, make(map[int]int)) 83 - fmt.Println("Grade distribution:") 84 - for score := 0; score <= 10; score++ { 85 - fmt.Printf("Score %2d: %d ingredients\n", score, counts[score]) 86 - } 87 - return 88 - } 89 - 90 - for _, i := range ings { 91 - for _, cat := range categories(i) { 92 - catMap[cat] += 1 93 - } 94 - fmt.Printf("%s: %s - %s:($%s) size: %s categories: %v\n", i.ProductID, i.Brand, i.Description, toFloat(i.PriceRegular), i.Size, i.Categories) 95 81 } 96 82 for cat, count := range catMap { 97 83 fmt.Printf("Category: %s, Count: %d\n", cat, count) 98 84 } 99 - } 100 85 101 - func toFloat(f *float32) string { 102 - if f == nil { 103 - return "" 86 + counts := lo.Reduce(graded, func(counts map[int]int, ingredient ai.InputIngredient, _ int) map[int]int { 87 + counts[ingredient.Grade.Score] += 1 88 + return counts 89 + }, make(map[int]int)) 90 + fmt.Println("Grade distribution:") 91 + for score := range 10 { 92 + fmt.Printf("Score %2d: %d ingredients\n", score, counts[score]) 104 93 } 105 - return fmt.Sprintf("%.2f", *f) 106 - } 107 - 108 - func categories(i ai.InputIngredient) []string { 109 - if i.Categories == nil { 110 - return nil 111 - } 112 - return i.Categories 94 + sumGrades := lo.SumBy(graded, func(ing ai.InputIngredient) int { return ing.Grade.Score }) 95 + fmt.Printf("Total count %d and score %d\n", len(graded), sumGrades) 113 96 }
+1 -5
internal/kroger/products/client.gen.go
··· 340 340 Retstrictions *ProductsRestrictionsModel `json:"retstrictions,omitempty"` 341 341 342 342 // SnapEligible Indicates if the product is eligible for SNAP benefits. 343 - SnapEligible *bool `json:"snapEligible,omitempty"` 344 - SweeteningMethods *struct { 345 - Code *string `json:"code,omitempty"` 346 - Name *string `json:"name,omitempty"` 347 - } `json:"sweeteningMethods,omitempty"` 343 + SnapEligible *bool `json:"snapEligible,omitempty"` 348 344 349 345 // Temperature Information about the item's temperature requirements. 350 346 Temperature *ProductsProductTemperatureModel `json:"temperature,omitempty"`
+3
internal/kroger/products/overlay.yaml
··· 6 6 - target: $.components.schemas["products.productModel"].properties.nutritionInformation 7 7 description: Drop field because Kroger returns both object and array shapes and staples does not use it. 8 8 remove: true 9 + - target: $.components.schemas["products.productModel"].properties.sweeteningMethods 10 + description: Drop field because Kroger returns both object and array shapes and staples does not use it. 11 + remove: true 9 12 - target: $.components.schemas["products.productItemPriceModel"].properties.expirationDate 10 13 description: Drop field because Kroger returns date-time strings while the spec declares date-only values and staples does not use it. 11 14 remove: true
+11 -8
internal/kroger/staples.go
··· 75 75 return searchIngredients(ctx, p.client, locationID, searchTerm, []string{"*"}, false, skip) 76 76 } 77 77 78 + var availableInStore = products.Ais 79 + 78 80 func searchIngredients(ctx context.Context, client *products.ClientWithResponses, locationID, term string, brands []string, frozen bool, skip int) ([]Ingredient, error) { 79 81 limit := 50 82 + 80 83 productResults, err := client.ProductGetWithResponse(ctx, &products.ProductGetParams{ 81 - FilterLocationId: &locationID, 82 - FilterTerm: &term, 83 - FilterLimit: &limit, 84 - FilterStart: &skip, 84 + FilterLocationId: &locationID, 85 + FilterTerm: &term, 86 + FilterLimit: &limit, 87 + FilterStart: &skip, 88 + FilterFulfillment: &availableInStore, 85 89 }) 86 90 if err != nil { 87 91 return nil, fmt.Errorf("kroger product search request failed: %w", err) ··· 106 110 continue 107 111 } 108 112 109 - /* comeback and do in seperate change 110 - if item.Inventory != nil && item.Inventory.StockLevel != nil && *item.Inventory.StockLevel == TEMPORARILYOUTOFSTOCK { 111 - slog.WarnContext(ctx, "OOS", "description", product.Description) 113 + if item.Inventory != nil && item.Inventory.StockLevel != nil && *item.Inventory.StockLevel == products.TEMPORARILYOUTOFSTOCK { 114 + // TODO pass along low stock levels to AI to use in recipe planning 115 + // slog.WarnContext(ctx, "OOS", "description", *product.Description) 112 116 continue 113 117 } 114 - */ 115 118 116 119 var aisle *string 117 120 if product.AisleLocations != nil && len(*product.AisleLocations) > 0 {