Skip to content

Commit

Permalink
Refine nutri game: limit nutriments to known list and add color (#1099)
Browse files Browse the repository at this point in the history
* only-known-nutriments

* Add color code + instructions
  • Loading branch information
alexfauquette authored Dec 12, 2024
1 parent dc9c601 commit d26cc5e
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 25 deletions.
99 changes: 99 additions & 0 deletions src/pages/nutrition/Instructions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* eslint-disable react/no-unescaped-entities */
import * as React from "react";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import Typography from "@mui/material/Typography";

export default function Instructions() {
const [open, setOpen] = React.useState(false);

const handleClickOpen = () => {
setOpen(true);
};

const handleClose = () => {
setOpen(false);
};

return (
<React.Fragment>
<Button
size="small"
variant="outlined"
onClick={handleClickOpen}
sx={{ margin: 2 }}
>
Instructions
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
Comment utiliser cette interface
</DialogTitle>
<DialogContent sx={{ "& p": { marginBottom: 2 } }}>
<DialogContentText component="div" id="alert-dialog-description">
<Typography>
Avec les checkbox du haut, vous pouvez choisir entre traiter des
tableux de produit sans tableau nutritionel. Ou traiter des
produits pour lequels robotoof à trouver des nutriments en plus.
</Typography>
<Typography>
La date de la photo est affichée au dessus de la photo.
</Typography>
<Typography>
Les nutriments déjà associé au produit sont affiché en petit sous
les inputs.
<ol>
<li>
En <span style={{ color: "green" }}>vert</span> pour ceux qui
matchent avec la valeur du champ
</li>
<li>
En <span style={{ color: "orange" }}>orange</span> si le champ
est vide
</li>
<li>
En <span style={{ color: "red" }}>rouge</span> si le champ est
différent de la valeur connue.
</li>
</ol>
La valeur "-" indique une valeur abscente du tableau nutritionel.
Particulierement utile pour les fibres qui sont souvent abscente.
</Typography>
<Typography>
Quand une colone a été vérifiée (celle pour 100g ou celle par
portion) il ne vous reste plus qu'à la valider pour passer à la
suite. Innutil de remplir les deux colones. Une seul des deux peut
être enregistrée par OFF.
</Typography>

<ul>
<li>
Le bouton "skip" passe à la suite. quelqu'un d'autre s'en
chargera.
</li>
<li>
Le bouton "invalid image" indique que la photo ne correspond pas
à un tableau nutritionel, et supprime la question pour tout le
monde.
</li>
</ul>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} autoFocus>
Fermer
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
53 changes: 49 additions & 4 deletions src/pages/nutrition/NutrimentCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,35 @@ interface NutrimentCellProps {
setValues: (object) => void;
}

/**
* Returns the string value of the input without any space.
*/
function clean(input: undefined | string | null | number): string {
if (input == undefined) {
return "";
}
return `${input}`.replaceAll(" ", "");
}
function getLegendColor(product, prediction, nutrimentId) {
const cleanProduct = clean(product);
const cleanPrediction = clean(prediction);

console.log({ nutrimentId, cleanProduct, cleanPrediction });

if (cleanProduct === cleanPrediction) {
return "green";
}

if (cleanProduct === "" || cleanPrediction === "") {
return "orange";
}

if (cleanProduct !== cleanPrediction) {
return "red";
}
return undefined;
}

export const NutrimentCell = (props: NutrimentCellProps) => {
const {
nutrimentId,
Expand Down Expand Up @@ -64,13 +93,29 @@ export const NutrimentCell = (props: NutrimentCellProps) => {
/>
<br />
{displayOFFValue && (
<legend style={{ fontSize: 13, textAlign: "end" }}>
{productValue}
{productUnit}
<legend
style={{
fontSize: 13,
textAlign: "end",
}}
>
<span
style={{
color: getLegendColor(productValue, value, nutrimentId),
}}
>
{productValue}
</span>
<span
style={{
color: getLegendColor(productUnit, unit, nutrimentId),
}}
>
{productUnit}
</span>
</legend>
)}
</div>

{isValidUnit(unit) ? (
<select
style={{ width: 55 }}
Expand Down
18 changes: 5 additions & 13 deletions src/pages/nutrition/config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
export const UNITS = ["", "g", "mg", "µg"];

export const OFF_NUTRIMENTS_TO_IGNORE = [
"serving",
"energy", // Already available with energy kj and kcal.
"fruits-vegetables-legumes-estimate-from-ingredients",
"fruits-vegetables-nuts-estimate-from-ingredients",
"nova-group",
"nutrition-score-fr",
];
export const NUTRIMENTS_ORDER = [
export const KNOWN_NUTRIMENTS = [
// Energy
"energy-kj",
"energy-kcal",
Expand Down Expand Up @@ -104,10 +96,10 @@ export const NUTRIMENTS_ORDER = [
"pantothenic-acid",
"silica",
"bicarbonate",
"Sulphate",
"Nitrate",
"Hydrogencarbonate",
"Nitrite",
"sulphate",
"nitrate",
"nydrogencarbonate",
"nitrite",
"potassium",
"chloride",
"calcium",
Expand Down
12 changes: 7 additions & 5 deletions src/pages/nutrition/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { ErrorBoundary } from "../taxonomyWalk/Error";
import LinksToProduct from "./LinksToProduct";
import { NutrimentCell } from "./NutrimentCell";
import PictureSection from "./PictureSection";
import { NUTRIMENTS_ORDER } from "./config";
import { KNOWN_NUTRIMENTS } from "./config";
import Instructions from "./Instructions";

export default function Nutrition() {
const [partiallyFilled, setPartiallyFilled] = React.useState(false);
Expand Down Expand Up @@ -68,18 +69,19 @@ export default function Nutrition() {
}));
}, [insight]);

const nutrimentsDetected = React.useMemo(
const nutrimentsDisplayed = React.useMemo(
() => structurePredictions(values, product, additionalIds),
[values, product, additionalIds],
);

const notUsedNutriments = React.useMemo(
() => NUTRIMENTS_ORDER.filter((id) => !nutrimentsDetected.includes(id)),
[nutrimentsDetected],
() => KNOWN_NUTRIMENTS.filter((id) => !nutrimentsDisplayed.includes(id)),
[nutrimentsDisplayed],
);
return (
<React.Suspense>
<ErrorBoundary>
<Instructions />
<Stack direction="row">
<Box sx={{ width: "50%" }}>
<PictureSection
Expand Down Expand Up @@ -160,7 +162,7 @@ export default function Nutrition() {
</tr>
</thead>
<tbody>
{nutrimentsDetected.map((nutrimentId) => {
{nutrimentsDisplayed.map((nutrimentId) => {
const key100g = `${nutrimentId}_100g`;
const { value: value100g, unit: unit100g } =
values[key100g] ?? {};
Expand Down
6 changes: 3 additions & 3 deletions src/pages/nutrition/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from "axios";
import { OFF_NUTRIMENTS_TO_IGNORE, UNITS } from "./config";
import { KNOWN_NUTRIMENTS, UNITS } from "./config";
import { NutrimentPrediction } from "./insight.types";
import { ROBOTOFF_API_URL } from "../../const";

Expand Down Expand Up @@ -32,7 +32,7 @@ export function structurePredictions(
Object.keys(predictions).forEach((key) => {
const id = key.split("_")[0]; // split 'energy-kj_100g' to only get 'energy-kj'

if (OFF_NUTRIMENTS_TO_IGNORE.includes(id)) {
if (!KNOWN_NUTRIMENTS.includes(id)) {
return;
}
if (!nurimentsIds.includes(id)) {
Expand All @@ -43,7 +43,7 @@ export function structurePredictions(
Object.keys(productValue?.nutriments ?? {}).forEach((key) => {
const id = key.split("_")[0]; // split 'energy-kj_100g' to only get 'energy-kj'

if (OFF_NUTRIMENTS_TO_IGNORE.includes(id)) {
if (!KNOWN_NUTRIMENTS.includes(id)) {
return;
}
if (!nurimentsIds.includes(id)) {
Expand Down

0 comments on commit d26cc5e

Please sign in to comment.