ci: trigga på master + formatfix inför Gitea Actions
CI / Typecheck, test & build (push) Failing after 2s
CI / Typecheck, test & build (push) Failing after 2s
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
ProvenanceEntry,
|
||||
RecommendationCandidate,
|
||||
RecommendationContext,
|
||||
} from "./types.js";
|
||||
import type { ProvenanceEntry, RecommendationCandidate, RecommendationContext } from "./types.js";
|
||||
import { renderProvenance } from "./provenance-templates.js";
|
||||
|
||||
import type { LanguageTag } from "./provenance-templates.js";
|
||||
@@ -182,7 +178,8 @@ const WHY_TEMPLATES: Record<LanguageTag, WhyTemplates> = {
|
||||
nl: {
|
||||
coverage100: "Jullie hebben alle ingrediënten thuis.",
|
||||
coveragePct: "Jullie hebben {{pct}} % van de ingrediënten thuis.",
|
||||
coverageLow: "Jullie hebben {{pct}} % van de ingrediënten; de rest komt op het boodschappenlijstje.",
|
||||
coverageLow:
|
||||
"Jullie hebben {{pct}} % van de ingrediënten; de rest komt op het boodschappenlijstje.",
|
||||
expiringToday: "{{ingredient}} moet vandaag gebruikt worden.",
|
||||
expiringTomorrow: "{{ingredient}} moet uiterlijk morgen gebruikt worden.",
|
||||
expiringDays: "{{ingredient}} moet binnen {{days}} dagen gebruikt worden.",
|
||||
|
||||
@@ -7,23 +7,9 @@ import type { ProvenanceEntry } from "./types.js";
|
||||
*/
|
||||
|
||||
export type LanguageTag =
|
||||
| "sv"
|
||||
| "en"
|
||||
| "es"
|
||||
| "it"
|
||||
| "de"
|
||||
| "fr"
|
||||
| "da"
|
||||
| "nb"
|
||||
| "fi"
|
||||
| "nl"
|
||||
| "pl"
|
||||
| "pt";
|
||||
"sv" | "en" | "es" | "it" | "de" | "fr" | "da" | "nb" | "fi" | "nl" | "pl" | "pt";
|
||||
|
||||
const TEMPLATES: Record<
|
||||
string,
|
||||
Record<LanguageTag, string>
|
||||
> = {
|
||||
const TEMPLATES: Record<string, Record<LanguageTag, string>> = {
|
||||
favoriteCuisine: {
|
||||
sv: "För att du har berättat att du gillar {{cuisine}} mat.",
|
||||
en: "Because you said you like {{cuisine}} food.",
|
||||
@@ -124,10 +110,7 @@ const TEMPLATES: Record<
|
||||
},
|
||||
};
|
||||
|
||||
export function renderProvenance(
|
||||
entries: ProvenanceEntry[],
|
||||
languageTag: string,
|
||||
): string {
|
||||
export function renderProvenance(entries: ProvenanceEntry[], languageTag: string): string {
|
||||
const lang = (languageTag.split("-")[0] ?? "sv") as LanguageTag;
|
||||
const parts: string[] = [];
|
||||
for (const entry of entries) {
|
||||
@@ -142,10 +125,7 @@ export function renderProvenance(
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
export function renderProvenanceList(
|
||||
entries: ProvenanceEntry[],
|
||||
languageTag: string,
|
||||
): string[] {
|
||||
export function renderProvenanceList(entries: ProvenanceEntry[], languageTag: string): string[] {
|
||||
const lang = (languageTag.split("-")[0] ?? "sv") as LanguageTag;
|
||||
return entries
|
||||
.map((entry) => {
|
||||
|
||||
@@ -230,7 +230,12 @@ function memoryFit(candidate: RecommendationCandidate, ctx: RecommendationContex
|
||||
|
||||
// Favoritkök
|
||||
if (memory.kind === "structured_fact" && value.favoriteCuisine === candidate.cuisine) {
|
||||
const weight = memory.verifiedByUser || memory.origin === "user_stated" ? 1 : memory.origin === "observed" ? 0.7 : 0.4;
|
||||
const weight =
|
||||
memory.verifiedByUser || memory.origin === "user_stated"
|
||||
? 1
|
||||
: memory.origin === "observed"
|
||||
? 0.7
|
||||
: 0.4;
|
||||
score = Math.max(score, weight);
|
||||
if (weight >= 0.7) {
|
||||
provenance.push({
|
||||
|
||||
@@ -454,13 +454,29 @@ describe("S4 rekommendationsvyer", () => {
|
||||
});
|
||||
|
||||
// default utan samtycke = depersonalize(DEFAULT_WEIGHTS) == NON_PERSONALIZED_WEIGHTS
|
||||
const defaultRanked = rankAll([pantry, healthy, tasty], nonPersonalCtx, depersonalize(viewWeights("default")));
|
||||
const nonPersonalDefaultRanked = rankAll([pantry, healthy, tasty], nonPersonalCtx, NON_PERSONALIZED_WEIGHTS);
|
||||
expect(defaultRanked.map((r) => r.recipeId)).toEqual(nonPersonalDefaultRanked.map((r) => r.recipeId));
|
||||
const defaultRanked = rankAll(
|
||||
[pantry, healthy, tasty],
|
||||
nonPersonalCtx,
|
||||
depersonalize(viewWeights("default")),
|
||||
);
|
||||
const nonPersonalDefaultRanked = rankAll(
|
||||
[pantry, healthy, tasty],
|
||||
nonPersonalCtx,
|
||||
NON_PERSONALIZED_WEIGHTS,
|
||||
);
|
||||
expect(defaultRanked.map((r) => r.recipeId)).toEqual(
|
||||
nonPersonalDefaultRanked.map((r) => r.recipeId),
|
||||
);
|
||||
|
||||
// vyerna är fortfarande skilda från default även utan samtycke
|
||||
expect(rankAll([tasty, healthy, pantry], nonPersonalCtx, depersonalize(viewWeights("pantry")))[0]?.recipeId).toBe("pantry");
|
||||
expect(rankAll([tasty, pantry, healthy], nonPersonalCtx, depersonalize(viewWeights("health")))[0]?.recipeId).toBe("healthy");
|
||||
expect(
|
||||
rankAll([tasty, healthy, pantry], nonPersonalCtx, depersonalize(viewWeights("pantry")))[0]
|
||||
?.recipeId,
|
||||
).toBe("pantry");
|
||||
expect(
|
||||
rankAll([tasty, pantry, healthy], nonPersonalCtx, depersonalize(viewWeights("health")))[0]
|
||||
?.recipeId,
|
||||
).toBe("healthy");
|
||||
|
||||
// taste-vyn honoreras men personliga axlar är nollade
|
||||
const tasteWeights = depersonalize(viewWeights("taste"));
|
||||
|
||||
@@ -8,20 +8,7 @@ import {
|
||||
type ProvenanceEntry,
|
||||
} from "../src/index.js";
|
||||
|
||||
const EXPECTED_LANGS = [
|
||||
"sv",
|
||||
"en",
|
||||
"es",
|
||||
"it",
|
||||
"de",
|
||||
"fr",
|
||||
"da",
|
||||
"nb",
|
||||
"fi",
|
||||
"nl",
|
||||
"pl",
|
||||
"pt",
|
||||
];
|
||||
const EXPECTED_LANGS = ["sv", "en", "es", "it", "de", "fr", "da", "nb", "fi", "nl", "pl", "pt"];
|
||||
|
||||
const fullCoverage = { coverage: 1, matches: [], missing: [], expiringUsed: [] };
|
||||
const nutrition = {
|
||||
@@ -71,7 +58,9 @@ describe("S6 i18n-paritet", () => {
|
||||
const langsByTemplate = provenanceTemplateLanguages();
|
||||
expect(Object.keys(langsByTemplate).length).toBeGreaterThan(0);
|
||||
for (const [key, langs] of Object.entries(langsByTemplate)) {
|
||||
expect(langs.length, `mall ${key} har dubbletter eller saknar språk`).toBe(EXPECTED_LANGS.length);
|
||||
expect(langs.length, `mall ${key} har dubbletter eller saknar språk`).toBe(
|
||||
EXPECTED_LANGS.length,
|
||||
);
|
||||
expect(new Set(langs).size, `mall ${key} har dubbletter`).toBe(EXPECTED_LANGS.length);
|
||||
for (const lang of EXPECTED_LANGS) {
|
||||
expect(langs).toContain(lang);
|
||||
@@ -89,11 +78,31 @@ describe("S6 i18n-paritet", () => {
|
||||
});
|
||||
|
||||
it("buildWhy renderar på rätt språk för varje supporterat språk", () => {
|
||||
const provenance: ProvenanceEntry[] = [
|
||||
{ key: "favoriteCuisine", args: { cuisine: "svensk" } },
|
||||
];
|
||||
const provenance: ProvenanceEntry[] = [{ key: "favoriteCuisine", args: { cuisine: "svensk" } }];
|
||||
for (const lang of EXPECTED_LANGS) {
|
||||
const why = buildWhy(candidate(), { ...ctx, personalizationEnabled: true }, { coverage: 1, expiry: 0, nutritionFit: 0.9, taste: 0.5, rating: 0.5, season: 0, holiday: 0, time: 0, budget: 0, variety: 0.5, weather: 0.5, craving: 0.5, memoryFit: 1, tasteFit: 0, cookingAssumptionFit: 0 }, provenance, `${lang}-XX`);
|
||||
const why = buildWhy(
|
||||
candidate(),
|
||||
{ ...ctx, personalizationEnabled: true },
|
||||
{
|
||||
coverage: 1,
|
||||
expiry: 0,
|
||||
nutritionFit: 0.9,
|
||||
taste: 0.5,
|
||||
rating: 0.5,
|
||||
season: 0,
|
||||
holiday: 0,
|
||||
time: 0,
|
||||
budget: 0,
|
||||
variety: 0.5,
|
||||
weather: 0.5,
|
||||
craving: 0.5,
|
||||
memoryFit: 1,
|
||||
tasteFit: 0,
|
||||
cookingAssumptionFit: 0,
|
||||
},
|
||||
provenance,
|
||||
`${lang}-XX`,
|
||||
);
|
||||
expect(why.length, `tom why för ${lang}`).toBeGreaterThan(0);
|
||||
// Ingen why får falla tillbaka på svenska fallback om språket finns.
|
||||
if (lang !== "sv") {
|
||||
|
||||
Reference in New Issue
Block a user