Rewrite variety score and suggestions with configurable scoring
- Add VarietyScoreConfig entity, repository, and V020 migration for per-household scoring weights and configurable tag types - Rewrite getVarietyScore: tag-type repeats on consecutive days, non-staple ingredient overlaps, cooking log history, plan duplicates - Rewrite getSuggestions: simulate variety score for each candidate, add tag filter (AND, case-insensitive) and configurable topN param - Update SuggestionResponse to return simulatedScore instead of fitReasons/warnings, update VarietyScoreResponse to new shape - Seed default VarietyScoreConfig on household creation - Extend test suite across all domains (+270 tests, all passing) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package com.recipeapp.planning.entity;
|
||||
|
||||
import com.recipeapp.household.entity.Household;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "variety_score_config")
|
||||
public class VarietyScoreConfig {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "household_id", nullable = false, unique = true)
|
||||
private Household household;
|
||||
|
||||
@Column(name = "repeat_tag_types", nullable = false, columnDefinition = "text[]")
|
||||
private String[] repeatTagTypes;
|
||||
|
||||
@Column(name = "w_tag_repeat", nullable = false, precision = 3, scale = 1)
|
||||
private BigDecimal wTagRepeat;
|
||||
|
||||
@Column(name = "w_ingredient_overlap", nullable = false, precision = 3, scale = 1)
|
||||
private BigDecimal wIngredientOverlap;
|
||||
|
||||
@Column(name = "w_recent_repeat", nullable = false, precision = 3, scale = 1)
|
||||
private BigDecimal wRecentRepeat;
|
||||
|
||||
@Column(name = "w_plan_duplicate", nullable = false, precision = 3, scale = 1)
|
||||
private BigDecimal wPlanDuplicate;
|
||||
|
||||
@Column(name = "history_days", nullable = false)
|
||||
private int historyDays;
|
||||
|
||||
protected VarietyScoreConfig() {}
|
||||
|
||||
public VarietyScoreConfig(Household household, String[] repeatTagTypes,
|
||||
BigDecimal wTagRepeat, BigDecimal wIngredientOverlap,
|
||||
BigDecimal wRecentRepeat, BigDecimal wPlanDuplicate,
|
||||
int historyDays) {
|
||||
this.household = household;
|
||||
this.repeatTagTypes = repeatTagTypes;
|
||||
this.wTagRepeat = wTagRepeat;
|
||||
this.wIngredientOverlap = wIngredientOverlap;
|
||||
this.wRecentRepeat = wRecentRepeat;
|
||||
this.wPlanDuplicate = wPlanDuplicate;
|
||||
this.historyDays = historyDays;
|
||||
}
|
||||
|
||||
public static VarietyScoreConfig defaults(Household household) {
|
||||
return new VarietyScoreConfig(
|
||||
household,
|
||||
new String[]{"protein", "cuisine"},
|
||||
new BigDecimal("1.5"),
|
||||
new BigDecimal("0.3"),
|
||||
new BigDecimal("1.0"),
|
||||
new BigDecimal("2.0"),
|
||||
14
|
||||
);
|
||||
}
|
||||
|
||||
public UUID getId() { return id; }
|
||||
public Household getHousehold() { return household; }
|
||||
public List<String> getRepeatTagTypes() { return Arrays.asList(repeatTagTypes); }
|
||||
public BigDecimal getWTagRepeat() { return wTagRepeat; }
|
||||
public BigDecimal getWIngredientOverlap() { return wIngredientOverlap; }
|
||||
public BigDecimal getWRecentRepeat() { return wRecentRepeat; }
|
||||
public BigDecimal getWPlanDuplicate() { return wPlanDuplicate; }
|
||||
public int getHistoryDays() { return historyDays; }
|
||||
}
|
||||
Reference in New Issue
Block a user