R-01: a Rotation check decided drop now records the dinner's name at the decision in rotation_reviews.name (migration 0014, a table rebuild as 0012 did for suggestions; checks dropped before it take their dinner's name). ListPassedNames and ListRecentPassedNames read that name instead of the mutable dinners.name, so renaming the archived dinner no longer lets the dropped dish back into a Side quest batch. A CHECK keeps a name on drops only. R-02: deciding on a dinner that has never had a Rotation check is now ErrReviewNotFound, 404 not_found; a dinner whose checks are all decided, such as a second decision, stays 409 no_pending_review. Tests: TestLaterBatchNeverResuggestsRenamedDrop (drop, rename, next batch still refuses the original name), TestMigrateRotationReviewName (0013 to 0014 upgrade and constraints), TestPostRotationReviewNeverChecked (404), and TestDecideReview/TestDecideReviewRefused updated. Maestro: a1887d1c-c405-478c-ac7d-8cb8b40d280d Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
258 lines
7.0 KiB
Go
258 lines
7.0 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: rotation_reviews.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
)
|
|
|
|
const countRotationReviews = `-- name: CountRotationReviews :one
|
|
SELECT COUNT(*) FROM rotation_reviews WHERE dinner_id = ?
|
|
`
|
|
|
|
// How many Rotation checks dinner_id has had, pending or decided.
|
|
func (q *Queries) CountRotationReviews(ctx context.Context, dinnerID string) (int64, error) {
|
|
row := q.db.QueryRowContext(ctx, countRotationReviews, dinnerID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const createRotationReview = `-- name: CreateRotationReview :exec
|
|
INSERT INTO rotation_reviews (id, dinner_id, plan_date, status, created_at, updated_at)
|
|
VALUES (?, ?, ?, 'pending', ?, ?)
|
|
`
|
|
|
|
type CreateRotationReviewParams struct {
|
|
ID string
|
|
DinnerID string
|
|
PlanDate string
|
|
CreatedAt string
|
|
UpdatedAt string
|
|
}
|
|
|
|
func (q *Queries) CreateRotationReview(ctx context.Context, arg CreateRotationReviewParams) error {
|
|
_, err := q.db.ExecContext(ctx, createRotationReview,
|
|
arg.ID,
|
|
arg.DinnerID,
|
|
arg.PlanDate,
|
|
arg.CreatedAt,
|
|
arg.UpdatedAt,
|
|
)
|
|
return err
|
|
}
|
|
|
|
const decideRotationReview = `-- name: DecideRotationReview :execrows
|
|
UPDATE rotation_reviews
|
|
SET status = 'decided',
|
|
decision = ?1,
|
|
name = ?2,
|
|
decided_at = ?3,
|
|
updated_by = ?4,
|
|
updated_at = ?3
|
|
WHERE id = ?5 AND status = 'pending'
|
|
`
|
|
|
|
type DecideRotationReviewParams struct {
|
|
Decision sql.NullString
|
|
Name sql.NullString
|
|
Now sql.NullString
|
|
UpdatedBy sql.NullString
|
|
ID string
|
|
}
|
|
|
|
// Records the household's decision on a pending Rotation check, with the
|
|
// dinner's name for a drop and NULL otherwise; one already decided is left
|
|
// as it is (0 rows).
|
|
func (q *Queries) DecideRotationReview(ctx context.Context, arg DecideRotationReviewParams) (int64, error) {
|
|
result, err := q.db.ExecContext(ctx, decideRotationReview,
|
|
arg.Decision,
|
|
arg.Name,
|
|
arg.Now,
|
|
arg.UpdatedBy,
|
|
arg.ID,
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected()
|
|
}
|
|
|
|
const getPendingRotationReview = `-- name: GetPendingRotationReview :one
|
|
SELECT id, dinner_id, plan_date, status, decision, name, decided_at, updated_by, created_at, updated_at FROM rotation_reviews
|
|
WHERE dinner_id = ? AND status = 'pending'
|
|
`
|
|
|
|
// The pending Rotation check of dinner_id. MarkTried makes one only for a
|
|
// dinner in the Quest log, which it moves to tried, so a dinner has at
|
|
// most one. Returns sql.ErrNoRows when there is none.
|
|
func (q *Queries) GetPendingRotationReview(ctx context.Context, dinnerID string) (RotationReview, error) {
|
|
row := q.db.QueryRowContext(ctx, getPendingRotationReview, dinnerID)
|
|
var i RotationReview
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.DinnerID,
|
|
&i.PlanDate,
|
|
&i.Status,
|
|
&i.Decision,
|
|
&i.Name,
|
|
&i.DecidedAt,
|
|
&i.UpdatedBy,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listPendingRotationReviews = `-- name: ListPendingRotationReviews :many
|
|
SELECT rotation_reviews.plan_date, dinners.id, dinners.name, dinners.normalized_name, dinners.status, dinners.origin, dinners.method, dinners.minutes_total, dinners.difficulty, dinners.cuisine, dinners.is_favorite, dinners.recipe_id, dinners.photo_id, dinners.grounding_status, dinners.updated_by, dinners.created_at, dinners.updated_at, dinners.recipe_url, dinners.grounding_error, dinners.grounding_revision
|
|
FROM rotation_reviews
|
|
JOIN dinners ON dinners.id = rotation_reviews.dinner_id
|
|
WHERE rotation_reviews.status = 'pending' AND dinners.status = 'tried'
|
|
ORDER BY rotation_reviews.plan_date, rotation_reviews.id
|
|
`
|
|
|
|
type ListPendingRotationReviewsRow struct {
|
|
PlanDate string
|
|
Dinner Dinner
|
|
}
|
|
|
|
// The pending Rotation checks whose dinner is still tried, oldest plan
|
|
// date first, with their dinners.
|
|
func (q *Queries) ListPendingRotationReviews(ctx context.Context) ([]ListPendingRotationReviewsRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, listPendingRotationReviews)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListPendingRotationReviewsRow
|
|
for rows.Next() {
|
|
var i ListPendingRotationReviewsRow
|
|
if err := rows.Scan(
|
|
&i.PlanDate,
|
|
&i.Dinner.ID,
|
|
&i.Dinner.Name,
|
|
&i.Dinner.NormalizedName,
|
|
&i.Dinner.Status,
|
|
&i.Dinner.Origin,
|
|
&i.Dinner.Method,
|
|
&i.Dinner.MinutesTotal,
|
|
&i.Dinner.Difficulty,
|
|
&i.Dinner.Cuisine,
|
|
&i.Dinner.IsFavorite,
|
|
&i.Dinner.RecipeID,
|
|
&i.Dinner.PhotoID,
|
|
&i.Dinner.GroundingStatus,
|
|
&i.Dinner.UpdatedBy,
|
|
&i.Dinner.CreatedAt,
|
|
&i.Dinner.UpdatedAt,
|
|
&i.Dinner.RecipeUrl,
|
|
&i.Dinner.GroundingError,
|
|
&i.Dinner.GroundingRevision,
|
|
); 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 listTriedCandidates = `-- name: ListTriedCandidates :many
|
|
SELECT DISTINCT dinners.id
|
|
FROM plan_days
|
|
JOIN dinners ON dinners.id = plan_days.dinner_id
|
|
WHERE plan_days.date = ?1
|
|
AND dinners.status = 'quest_log'
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM rotation_reviews
|
|
WHERE rotation_reviews.dinner_id = dinners.id
|
|
AND rotation_reviews.plan_date = plan_days.date
|
|
)
|
|
ORDER BY dinners.id
|
|
`
|
|
|
|
// The ids of the dinners with status quest_log planned on date, on a plan
|
|
// of any status, that have no Rotation check for date yet.
|
|
func (q *Queries) ListTriedCandidates(ctx context.Context, date string) ([]string, error) {
|
|
rows, err := q.db.QueryContext(ctx, listTriedCandidates, date)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []string
|
|
for rows.Next() {
|
|
var id string
|
|
if err := rows.Scan(&id); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, id)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const markDinnerTried = `-- name: MarkDinnerTried :execrows
|
|
UPDATE dinners
|
|
SET status = 'tried', updated_at = ?
|
|
WHERE id = ? AND status = 'quest_log'
|
|
`
|
|
|
|
type MarkDinnerTriedParams struct {
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// Moves a dinner still in the Quest log to tried; a dinner with any other
|
|
// status is left as it is (0 rows).
|
|
func (q *Queries) MarkDinnerTried(ctx context.Context, arg MarkDinnerTriedParams) (int64, error) {
|
|
result, err := q.db.ExecContext(ctx, markDinnerTried, arg.UpdatedAt, arg.ID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected()
|
|
}
|
|
|
|
const moveTriedDinner = `-- name: MoveTriedDinner :execrows
|
|
UPDATE dinners
|
|
SET status = ?1, updated_by = ?2, updated_at = ?3
|
|
WHERE id = ?4 AND status = 'tried'
|
|
`
|
|
|
|
type MoveTriedDinnerParams struct {
|
|
Status string
|
|
UpdatedBy sql.NullString
|
|
UpdatedAt string
|
|
ID string
|
|
}
|
|
|
|
// Moves a dinner still tried to status (regular, archived or quest_log)
|
|
// by a Rotation check decision, recording updated_by; its origin is kept.
|
|
// A dinner no longer tried is left as it is (0 rows).
|
|
func (q *Queries) MoveTriedDinner(ctx context.Context, arg MoveTriedDinnerParams) (int64, error) {
|
|
result, err := q.db.ExecContext(ctx, moveTriedDinner,
|
|
arg.Status,
|
|
arg.UpdatedBy,
|
|
arg.UpdatedAt,
|
|
arg.ID,
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected()
|
|
}
|