Skip to content

Cook history and recipe recommendations#89

Merged
ghidalgo3 merged 5 commits into
mainfrom
recommendations
Jun 15, 2026
Merged

Cook history and recipe recommendations#89
ghidalgo3 merged 5 commits into
mainfrom
recommendations

Conversation

@ghidalgo3

Copy link
Copy Markdown
Owner

Summary

  • Cook history: signed-in users can log "I cooked this" on any recipe page (one event per user/recipe/day, idempotent). A History page lists all cook events and allows deletion.
  • Recipe recommendations: up to 6 recommendations shown at the bottom of every recipe page, visible to all users. Scored using Jaccard ingredient similarity (60%) + ownership (15%) + favorites (15%) + novelty (10%). Admin accounts see a per-card score breakdown.
  • Port fix: launchSettings.json had applicationUrl: http://+:5000 which dotnet watch run used to override the container's ASPNETCORE_URLS=http://+:5001, binding the API to port 5000. The Vite proxy targets 5001, so all API calls silently failed and the app was perpetually stuck on "Loading...". Aligned both to 5001.
  • Node.js pin: added .nvmrc pinning Node 22 — crypto.hash used by Vite requires Node 21.7+.

Changes

Backend

  • 017_recipe_cook_history_and_recommendations.sql — new migration: recipe_cook_events table, unique index for dedup, and SQL functions for CRUD + recommend_recipes
  • RecipeRecommendationDto.cs — new DTO
  • CookTimeDB.cs — six new DB methods
  • Program.cs — six new API endpoints

Frontend

  • CookHistorySection.tsx — "I cooked this" button + cook count summary on recipe pages
  • RecommendedRecipes.tsx — recommendation cards with admin score breakdown
  • History.tsx + route — full cook history page with delete
  • Types and service methods for all new endpoints
  • NavigationBar.tsx — History link in user dropdown

Infrastructure

  • launchSettings.jsonapplicationUrl changed from 5000 → 5001
  • docker-compose.yml — restored correct webapp proxy target (http://api:5001) with clarifying comment; port mapping corrected to 5001:5001

Test plan

  • Log a cook event on a recipe page; verify count and "last on" date update
  • Log the same recipe twice on the same day; verify no duplicate appears
  • Visit History page; verify events list and delete works
  • Visit a recipe page anonymously; verify recommendations show but no cook history section
  • Visit a recipe page signed in; verify recommendations + score breakdown visible for admin accounts
  • Confirm http://localhost:3000 loads without getting stuck on "Loading..."

🤖 Generated with Claude Code

ghidalgo3 and others added 5 commits June 14, 2026 21:11
Adds cook event tracking (one event per user/recipe/day, idempotent) and
Jaccard-similarity-based recipe recommendations weighted by ingredient
overlap, ownership, favorites, and novelty.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cook history section on recipe pages, recommended recipes carousel,
and a History page listing all cook events with delete support.
Pins Node.js to v22 via .nvmrc (crypto.hash requires 21.7+).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
launchSettings.json had applicationUrl: http://+:5000 which dotnet watch
overrode the ASPNETCORE_URLS env var, binding the API to port 5000 while
the webapp proxy targeted 5001. All API calls failed silently, leaving
the app stuck on "Loading...". Align both to port 5001. Also document
the webapp's ASPNETCORE_URLS as a Vite proxy setting, not an ASP.NET
setting, and restore the correct proxy target (http://api:5001).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IsGreaterThan(lowerBound, value) checks value > lowerBound; both calls
had the arguments reversed, causing the assertions to check the opposite
of what was intended.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ghidalgo3
ghidalgo3 merged commit 6871f22 into main Jun 15, 2026
3 checks passed
@ghidalgo3
ghidalgo3 deleted the recommendations branch June 15, 2026 01:22
ghidalgo3 added a commit that referenced this pull request Jun 15, 2026
Follow-up to #89.

## Summary

Relocates the recipe recommendation algorithm from a single PostgreSQL
function into the C# application, and adjusts the algorithm: the
ownership signal is gone and a recipe must share at least 2 ingredients
with the source to be recommended.

**Why:** the database is great at indexes and set-membership lookups,
but the recommendation *policy* (scoring, weighting, thresholds,
ranking) is easier to read, unit-test, and evolve in application code
without a migration. The DB now exposes primitives; the app composes
them.

## Changes

**Database** — new migration `018_recommendation_primitives.sql`:
- Drops `cooktime.recommend_recipes`.
- Adds four data-only primitives: `get_candidate_ingredient_sets`,
`get_user_favorite_recipe_ids`, `get_user_last_cooked`,
`get_recipe_summaries`.

**Application** — new `RecommendationService`:
- Owns the algorithm. Pure static `ScoreCandidates(...)` does Jaccard
similarity, the >=2-overlap gate, weighting, and ranking with no DB
access (directly unit-testable).
- `CookTimeDB` drops `GetRecipeRecommendationsAsync`, gains the four
primitive fetch methods.
- Endpoint resolves `RecommendationService`; the service performs the
existence check (`null` -> 404).

**Algorithm changes**
- Ownership signal removed entirely.
- A recipe must share **>=2** ingredients with the source to appear at
all; favorite/novelty only re-rank within that set.
- Weights renormalized to sum to 1.0: ingredient similarity **0.70**,
favorite **0.18**, novelty **0.12**.

**Contract + frontend** — `ownedByUser` / "Your recipe" removed from the
DTO, the TS type, and the admin score breakdown.

**Docs** — blog post updated to describe the app-side algorithm, the
overlap gate, and new weights.

## Tests
- `scripts/test` -> 47/47 pass (43 existing + 4 new pure unit tests in
`TestRecommendationService`).
- Rewrote the two recommendation integration tests for the new weights
and gate.
- `scripts/test --lint` -> 0 errors.

## Test plan
- [ ] On a recipe with neighbors sharing >=2 ingredients,
recommendations appear; recipes with only 1 shared ingredient do not.
- [ ] Signed in as Administrator, the score breakdown rows sum to the
displayed score and there is no "Your recipe" row.
- [ ] Anonymous users still get similarity-only recommendations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant