Creating a Regular now enqueues a ground_dinner job and returns at once
with its grounding_job_id. The job grounds the dinner's recipe_url with
GroundURL, else its name with Ground, and sets grounding_status grounded
(recipe, photo, method, minutes, difficulty) or failed with a
grounding_error reason code (migration 0009). It writes only a dinner
still pending, so a fix made meanwhile wins.
GET /api/v1/dinners/{id}/recipe-candidates returns up to 3 ranked
candidates {candidate_id, title, site_name, photo_url, minutes} without
downloading photos or running the model. POST /api/v1/dinners/{id}/ground
takes exactly one of {candidate_id}, {recipe_url} or {manual: {...}} and
sets grounded or manual.
PhotoCache takes an ImageFetcher interface and recipestest provides fakes
so other packages can test grounding with fake search, fetch and model.
main wires the Grounder with normalize_recipe over the AI settings.
Maestro task: 0cbaa5bf-8be4-4cbd-aab2-891e7f1bc2b5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
557 lines
13 KiB
Go
557 lines
13 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: dinners.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
)
|
|
|
|
const archiveDinner = `-- name: ArchiveDinner :one
|
|
UPDATE dinners
|
|
SET status = 'archived',
|
|
updated_by = ?,
|
|
updated_at = ?
|
|
WHERE id = ?
|
|
RETURNING id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error
|
|
`
|
|
|
|
type ArchiveDinnerParams struct {
|
|
UpdatedBy sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// An unknown id returns sql.ErrNoRows.
|
|
func (q *Queries) ArchiveDinner(ctx context.Context, arg ArchiveDinnerParams) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, archiveDinner, arg.UpdatedBy, arg.UpdatedAt, arg.ID)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createDinner = `-- name: CreateDinner :one
|
|
INSERT INTO dinners (
|
|
id, name, normalized_name, status, origin, method, minutes_total,
|
|
difficulty, cuisine, is_favorite, recipe_id, recipe_url, photo_id,
|
|
grounding_status, updated_by, created_at, updated_at
|
|
) VALUES (
|
|
?, ?, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?
|
|
)
|
|
RETURNING id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error
|
|
`
|
|
|
|
type CreateDinnerParams struct {
|
|
ID string
|
|
Name string
|
|
NormalizedName string
|
|
Status string
|
|
Origin string
|
|
Method sql.NullString
|
|
MinutesTotal sql.NullInt64
|
|
Difficulty sql.NullString
|
|
Cuisine sql.NullString
|
|
IsFavorite int64
|
|
RecipeID sql.NullString
|
|
RecipeUrl sql.NullString
|
|
PhotoID sql.NullString
|
|
GroundingStatus string
|
|
UpdatedBy sql.NullString
|
|
CreatedAt string
|
|
UpdatedAt string
|
|
}
|
|
|
|
func (q *Queries) CreateDinner(ctx context.Context, arg CreateDinnerParams) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, createDinner,
|
|
arg.ID,
|
|
arg.Name,
|
|
arg.NormalizedName,
|
|
arg.Status,
|
|
arg.Origin,
|
|
arg.Method,
|
|
arg.MinutesTotal,
|
|
arg.Difficulty,
|
|
arg.Cuisine,
|
|
arg.IsFavorite,
|
|
arg.RecipeID,
|
|
arg.RecipeUrl,
|
|
arg.PhotoID,
|
|
arg.GroundingStatus,
|
|
arg.UpdatedBy,
|
|
arg.CreatedAt,
|
|
arg.UpdatedAt,
|
|
)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const failPendingDinnerGrounding = `-- name: FailPendingDinnerGrounding :execrows
|
|
UPDATE dinners
|
|
SET grounding_status = 'failed',
|
|
grounding_error = ?,
|
|
updated_at = ?
|
|
WHERE id = ? AND grounding_status = 'pending'
|
|
`
|
|
|
|
type FailPendingDinnerGroundingParams struct {
|
|
GroundingError sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// Records why the background grounding of a dinner still pending failed; a
|
|
// dinner grounded some other way meanwhile is left as it is (0 rows).
|
|
func (q *Queries) FailPendingDinnerGrounding(ctx context.Context, arg FailPendingDinnerGroundingParams) (int64, error) {
|
|
result, err := q.db.ExecContext(ctx, failPendingDinnerGrounding, arg.GroundingError, arg.UpdatedAt, arg.ID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected()
|
|
}
|
|
|
|
const getDinner = `-- name: GetDinner :one
|
|
SELECT id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error FROM dinners
|
|
WHERE id = ?
|
|
`
|
|
|
|
func (q *Queries) GetDinner(ctx context.Context, id string) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, getDinner, id)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const groundDinner = `-- name: GroundDinner :one
|
|
UPDATE dinners
|
|
SET recipe_id = ?,
|
|
photo_id = ?,
|
|
method = ?,
|
|
minutes_total = ?,
|
|
difficulty = ?,
|
|
grounding_status = ?,
|
|
grounding_error = NULL,
|
|
updated_by = ?,
|
|
updated_at = ?
|
|
WHERE id = ?
|
|
RETURNING id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error
|
|
`
|
|
|
|
type GroundDinnerParams struct {
|
|
RecipeID sql.NullString
|
|
PhotoID sql.NullString
|
|
Method sql.NullString
|
|
MinutesTotal sql.NullInt64
|
|
Difficulty sql.NullString
|
|
GroundingStatus string
|
|
UpdatedBy sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// Records a dinner's grounding (grounded or manual) and clears any
|
|
// grounding_error. An unknown id returns sql.ErrNoRows.
|
|
func (q *Queries) GroundDinner(ctx context.Context, arg GroundDinnerParams) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, groundDinner,
|
|
arg.RecipeID,
|
|
arg.PhotoID,
|
|
arg.Method,
|
|
arg.MinutesTotal,
|
|
arg.Difficulty,
|
|
arg.GroundingStatus,
|
|
arg.UpdatedBy,
|
|
arg.UpdatedAt,
|
|
arg.ID,
|
|
)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const groundPendingDinner = `-- name: GroundPendingDinner :execrows
|
|
UPDATE dinners
|
|
SET recipe_id = ?,
|
|
photo_id = ?,
|
|
method = ?,
|
|
minutes_total = ?,
|
|
difficulty = ?,
|
|
grounding_status = 'grounded',
|
|
grounding_error = NULL,
|
|
updated_at = ?
|
|
WHERE id = ? AND grounding_status = 'pending'
|
|
`
|
|
|
|
type GroundPendingDinnerParams struct {
|
|
RecipeID sql.NullString
|
|
PhotoID sql.NullString
|
|
Method sql.NullString
|
|
MinutesTotal sql.NullInt64
|
|
Difficulty sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// Records the background grounding of a dinner still pending; a dinner
|
|
// grounded some other way meanwhile is left as it is (0 rows).
|
|
func (q *Queries) GroundPendingDinner(ctx context.Context, arg GroundPendingDinnerParams) (int64, error) {
|
|
result, err := q.db.ExecContext(ctx, groundPendingDinner,
|
|
arg.RecipeID,
|
|
arg.PhotoID,
|
|
arg.Method,
|
|
arg.MinutesTotal,
|
|
arg.Difficulty,
|
|
arg.UpdatedAt,
|
|
arg.ID,
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected()
|
|
}
|
|
|
|
const listDinnersByStatus = `-- name: ListDinnersByStatus :many
|
|
SELECT id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error FROM dinners
|
|
WHERE status = ?
|
|
ORDER BY normalized_name, id
|
|
`
|
|
|
|
func (q *Queries) ListDinnersByStatus(ctx context.Context, status string) ([]Dinner, error) {
|
|
rows, err := q.db.QueryContext(ctx, listDinnersByStatus, status)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Dinner
|
|
for rows.Next() {
|
|
var i Dinner
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listUnarchivedDinners = `-- name: ListUnarchivedDinners :many
|
|
SELECT id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error FROM dinners
|
|
WHERE status <> 'archived'
|
|
ORDER BY id
|
|
`
|
|
|
|
// Every dinner a new or renamed dinner must not repeat.
|
|
func (q *Queries) ListUnarchivedDinners(ctx context.Context) ([]Dinner, error) {
|
|
rows, err := q.db.QueryContext(ctx, listUnarchivedDinners)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Dinner
|
|
for rows.Next() {
|
|
var i Dinner
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const setDinnerFavorite = `-- name: SetDinnerFavorite :one
|
|
UPDATE dinners
|
|
SET is_favorite = ?,
|
|
updated_by = ?,
|
|
updated_at = ?
|
|
WHERE id = ?
|
|
RETURNING id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error
|
|
`
|
|
|
|
type SetDinnerFavoriteParams struct {
|
|
IsFavorite int64
|
|
UpdatedBy sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// An unknown id returns sql.ErrNoRows.
|
|
func (q *Queries) SetDinnerFavorite(ctx context.Context, arg SetDinnerFavoriteParams) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, setDinnerFavorite,
|
|
arg.IsFavorite,
|
|
arg.UpdatedBy,
|
|
arg.UpdatedAt,
|
|
arg.ID,
|
|
)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const toggleDinnerFavorite = `-- name: ToggleDinnerFavorite :one
|
|
UPDATE dinners
|
|
SET is_favorite = 1 - is_favorite,
|
|
updated_by = ?,
|
|
updated_at = ?
|
|
WHERE id = ?
|
|
RETURNING id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error
|
|
`
|
|
|
|
type ToggleDinnerFavoriteParams struct {
|
|
UpdatedBy sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// An unknown id returns sql.ErrNoRows.
|
|
func (q *Queries) ToggleDinnerFavorite(ctx context.Context, arg ToggleDinnerFavoriteParams) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, toggleDinnerFavorite, arg.UpdatedBy, arg.UpdatedAt, arg.ID)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateDinner = `-- name: UpdateDinner :one
|
|
UPDATE dinners
|
|
SET name = ?,
|
|
normalized_name = ?,
|
|
status = ?,
|
|
origin = ?,
|
|
method = ?,
|
|
minutes_total = ?,
|
|
difficulty = ?,
|
|
cuisine = ?,
|
|
recipe_id = ?,
|
|
photo_id = ?,
|
|
grounding_status = ?,
|
|
updated_by = ?,
|
|
updated_at = ?
|
|
WHERE id = ?
|
|
RETURNING id, name, normalized_name, status, origin, method, minutes_total, difficulty, cuisine, is_favorite, recipe_id, photo_id, grounding_status, updated_by, created_at, updated_at, recipe_url, grounding_error
|
|
`
|
|
|
|
type UpdateDinnerParams struct {
|
|
Name string
|
|
NormalizedName string
|
|
Status string
|
|
Origin string
|
|
Method sql.NullString
|
|
MinutesTotal sql.NullInt64
|
|
Difficulty sql.NullString
|
|
Cuisine sql.NullString
|
|
RecipeID sql.NullString
|
|
PhotoID sql.NullString
|
|
GroundingStatus string
|
|
UpdatedBy sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// An unknown id returns sql.ErrNoRows.
|
|
func (q *Queries) UpdateDinner(ctx context.Context, arg UpdateDinnerParams) (Dinner, error) {
|
|
row := q.db.QueryRowContext(ctx, updateDinner,
|
|
arg.Name,
|
|
arg.NormalizedName,
|
|
arg.Status,
|
|
arg.Origin,
|
|
arg.Method,
|
|
arg.MinutesTotal,
|
|
arg.Difficulty,
|
|
arg.Cuisine,
|
|
arg.RecipeID,
|
|
arg.PhotoID,
|
|
arg.GroundingStatus,
|
|
arg.UpdatedBy,
|
|
arg.UpdatedAt,
|
|
arg.ID,
|
|
)
|
|
var i Dinner
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.NormalizedName,
|
|
&i.Status,
|
|
&i.Origin,
|
|
&i.Method,
|
|
&i.MinutesTotal,
|
|
&i.Difficulty,
|
|
&i.Cuisine,
|
|
&i.IsFavorite,
|
|
&i.RecipeID,
|
|
&i.PhotoID,
|
|
&i.GroundingStatus,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.RecipeUrl,
|
|
&i.GroundingError,
|
|
)
|
|
return i, err
|
|
}
|