Skip to content

Commit

Permalink
update nutrition game
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfauquette committed Dec 8, 2024
1 parent 49195fd commit a7df72d
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 92 deletions.
60 changes: 60 additions & 0 deletions src/pages/nutrition/PictureSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from "react";
import { InsightType } from "./insight.types";
import { ProductType } from "./useRobotoffPredictions";
import { getImageId } from "./utils";
import {
ReactZoomPanPinchRef,
TransformComponent,
TransformWrapper,
} from "react-zoom-pan-pinch";
import { OFF_IMAGE_URL } from "../../const";

interface PictureSectionProps {
isLoading?: boolean;
insight?: InsightType;
product?: ProductType;
apiRef: React.Ref<ReactZoomPanPinchRef>;
}

export default function PictureSection(props: PictureSectionProps) {
const { isLoading, insight, product, apiRef } = props;

if (isLoading) {
return <p>Loading ....</p>;
}
if (!insight) {
return <p>No predicition found</p>;
}

const imageId = getImageId(insight.source_image);

const imageTimestamp = product?.images?.[imageId]?.uploaded_t;

return (
<React.Fragment>
<p>
Photo upload:{" "}
{imageTimestamp
? new Date(imageTimestamp * 1000).toLocaleDateString(undefined, {
day: "2-digit",
month: "long",
year: "numeric",
})
: "..."}
</p>
<TransformWrapper limitToBounds={false} ref={apiRef}>
<TransformComponent>
<img
key={insight.source_image}
src={`${OFF_IMAGE_URL}${insight.source_image}`}
alt=""
style={{
width: "100%",
maxHeight: "200vh",
}}
/>
</TransformComponent>
</TransformWrapper>
</React.Fragment>
);
}
57 changes: 11 additions & 46 deletions src/pages/nutrition/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import * as React from "react";
import { useRobotoffPredicitions } from "./useRobotoffPredicitions";
import { OFF_IMAGE_URL } from "../../const";
import {
ReactZoomPanPinchRef,
TransformComponent,
TransformWrapper,
} from "react-zoom-pan-pinch";
import { useRobotoffPredictions } from "./useRobotoffPredictions";
import { ReactZoomPanPinchRef } from "react-zoom-pan-pinch";
import { Box, Button, Checkbox, FormControlLabel } from "@mui/material";
import Stack from "@mui/material/Stack";
import {
deleteRobotoff,
getImageId,
NUTRIMENTS,
postRobotoff,
skipRobotoff,
Expand All @@ -20,15 +14,17 @@ import { NutrimentPrediction } from "./insight.types";
import { ErrorBoundary } from "../taxonomyWalk/Error";
import LinksToProduct from "./LinksToProduct";
import { NutrimentCell } from "./NutrimentCell";
import PictureSection from "./PictureSection";

export default function Nutrition() {
const [partiallyFilled, setPartiallyFilled] = React.useState(false);
const [displayOFFValue, setDisplayOFFValue] = React.useState(false);
const handlePartiallyFilled = (_, checked) => setPartiallyFilled(checked);
const handleDisplayOFFValue = (_, checked) => setDisplayOFFValue(checked);

console.log({ partiallyFilled });
const { isLoading, insight, nextItem, count, product } =
useRobotoffPredicitions(partiallyFilled);
useRobotoffPredictions(partiallyFilled);

const [values, setValues] = React.useState<
Record<string, Pick<NutrimentPrediction, "value" | "unit">>
Expand Down Expand Up @@ -66,50 +62,19 @@ export default function Nutrition() {
}));
}, [insight]);

if (isLoading) {
return <p>Loading ....</p>;
}
if (!insight) {
return <p>No predicition found</p>;
}

const nutrimentsDetected = structurePredictions(values);

const imageId = getImageId(insight.source_image);

const imageTimestamp = product?.images?.[imageId]?.uploaded_t;

return (
<React.Suspense>
<ErrorBoundary>
<Stack direction="row">
<Box sx={{ width: "50%" }}>
<p>
Photo upload:{" "}
{imageTimestamp
? new Date(imageTimestamp * 1000).toLocaleDateString(
undefined,
{
day: "2-digit",
month: "long",
year: "numeric",
},
)
: "..."}
</p>
<TransformWrapper limitToBounds={false} ref={apiRef}>
<TransformComponent>
<img
key={insight.source_image}
src={`${OFF_IMAGE_URL}${insight.source_image}`}
alt=""
style={{
width: "100%",
maxHeight: "200vh",
}}
/>
</TransformComponent>
</TransformWrapper>
<PictureSection
isLoading={isLoading}
insight={insight}
product={product}
apiRef={apiRef}
/>
</Box>
<Stack direction="column" sx={{ width: "50%", p: 2 }}>
<Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
import * as React from "react";
import axios from "axios";
import robotoff from "../../robotoff";
import { InsightType } from "./insight.types";
import axios from "axios";
import { useCountry } from "../../contexts/CountryProvider";

export type ProductType = {
images: Record<string, any>;
serving_size?: any;
nutriments?: any;
};

export function useRobotoffPredicitions(partiallyFilled: boolean) {
export function useRobotoffPredictions(partiallyFilled: boolean) {
const [isLoading, setIsLoading] = React.useState(false);
const [count, setCount] = React.useState(0);
const [insights, setInsights] = React.useState<InsightType[]>([]);
const [offData, setOffData] = React.useState<{
[barecode: string]:
| "loading"
| { images: Record<string, any>; serving_size?: any; nutriments?: any };
}>({});
const [insightIndex, setInsightIndex] = React.useState(0);

const campaign = partiallyFilled
? "incomplete-nutrition"
: "missing-nutrition";

const [insights, setInsights] = React.useState<{
campaign: "incomplete-nutrition" | "missing-nutrition";
data: InsightType[];
}>({
campaign,
data: [],
});

const [offData, setOffData] = React.useState<{
[barecode: string]: "loading" | ProductType;
}>({});
const [insightIndex, setInsightIndex] = React.useState(0);
const [country] = useCountry();

React.useEffect(() => {
if (isLoading || insightIndex < insights.length - 1) {
if (
campaign === insights.campaign &&
(isLoading || insightIndex < insights.data.length - 1)
) {
return;
}
let valid = true;
Expand All @@ -34,56 +51,40 @@ export function useRobotoffPredicitions(partiallyFilled: boolean) {
1,
25,
campaign,
country,
)
.then(({ data }) => {
if (!valid) {
return;
}

setCount(data.count);
setInsights((prev) =>
data.insights.length === 0 ? prev : [...prev, ...data.insights],
);

setIsLoading(false);
});

return () => {
valid = false;
};
}, [insightIndex, insights]);

React.useEffect(() => {
let valid = true;
setIsLoading(true);

robotoff
.getInsights(
"",
"nutrient_extraction",
"",
"not_annotated",
1,
25,
campaign,
)
.then(({ data }) => {
if (!valid) {
return;
}
setInsights((prev) => {
if (campaign === prev.campaign) {
if (data.insights.length === 0) {
return prev;
}
return {
...prev,
data: [...prev.data, ...data.insights],
};
}
return {
campaign,
data: data.insights,
};
});

setCount(data.count);
setInsights(data.insights);
setIsLoading(false);
});

return () => {
valid = false;
};
}, [campaign]);
}, [insightIndex, insights, campaign]);

React.useEffect(() => {
const barecodeToImport = insights
const barecodeToImport = insights.data
.slice(insightIndex, insightIndex + 5)
.filter((insight) => offData[insight.barcode] === undefined)
.map((insight) => insight.barcode);
Expand All @@ -98,14 +99,14 @@ export function useRobotoffPredicitions(partiallyFilled: boolean) {
setOffData((prev) => ({ ...prev, [code]: product }));
});
});
}, [insightIndex, insights]);
}, [insightIndex, insights.data]);

const nextItem = React.useCallback(() => {
setInsightIndex((p) => p + 1);
setCount((p) => p - 1);
}, []);

const insight = insights[insightIndex];
const insight = insights.data[insightIndex];

const product = insight !== undefined ? offData[insight.barcode] : undefined;

Expand Down
2 changes: 2 additions & 0 deletions src/robotoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ const robotoff = {
page = 1,
count = 25,
campaigns = "",
country = "",
) {
let annotated;
if (annotation.length && annotation === "not_annotated") {
Expand All @@ -178,6 +179,7 @@ const robotoff = {
annotated,
count,
campaigns,
countries: country,
}),
});
},
Expand Down

0 comments on commit a7df72d

Please sign in to comment.