Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: drinking water test display #512

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion api-node/src/aggregators/drinking_water.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
type ShortPrelevementResult,
type HubEAUResultsParameters,
type ExtendedShortPrelevementResult,
type PrelevementResult,
type HubEAUCompleteResponse,
ConformityEnum,
} from '~/types/api/drinking_water';
import {
Expand Down Expand Up @@ -294,7 +296,7 @@ async function fetchDrinkingWaterData(udi: User['udi']) {
}
}
if (hubeauUdiResponse.next) {
getHubeauDataRecursive(hubeauUdiResponse.next);
await getHubeauDataRecursive(hubeauUdiResponse.next);
} else {
if (currentPrelevementConclusions && currentPrelevementParametersCount) {
prelevements.push({
Expand Down Expand Up @@ -452,8 +454,60 @@ async function fetchDrinkingWaterDataCascade(udi: User['udi']) {
};
}

async function fetchDrinkingWaterPrelevement(code_prelevement: string) {
const hubeEauEndpoint =
'https://hubeau.eaufrance.fr/api/v1/qualite_eau_potable/resultats_dis';

// there is new data to fetch ! let's do it
const hubEauURL = new URL(hubeEauEndpoint);

const hubEauQuery: HubEAUResultsParameters = {
size: 1000,
code_prelevement,
};

Object.keys(hubEauQuery).forEach((key) => {
const value = hubEauQuery[key as keyof typeof hubEauQuery];
if (value) {
hubEauURL.searchParams.append(
key,
Array.isArray(value) ? value.join(',') : `${value}`,
);
}
});

const hubeau_first_url = hubEauURL.toString();
let hubeauUdiFirstResponse: HubEAUCompleteResponse | null = null;

const parametersTested: Array<PrelevementResult> = [];

async function getHubeauDataRecursive(hubeau_url: string) {
const hubeauUdiResponse: HubEAUCompleteResponse = await fetch(hubeau_url, {
retryDelay: 1000,
retries: 3,
}).then(async (res) => res.json());
if (hubeau_url === hubeau_first_url) {
hubeauUdiFirstResponse = hubeauUdiResponse;
}
if (hubeauUdiResponse.data?.length > 0) {
parametersTested.push(...hubeauUdiResponse.data);
}
if (hubeauUdiResponse.next) {
await getHubeauDataRecursive(hubeauUdiResponse.next);
}
}

await getHubeauDataRecursive(hubeau_first_url);

return {
...(hubeauUdiFirstResponse as unknown as HubEAUCompleteResponse),
data: parametersTested,
};
}

export {
getDrinkingWaterIndicator,
fetchDrinkingWaterData,
fetchDrinkingWaterDataCascade,
fetchDrinkingWaterPrelevement,
};
138 changes: 138 additions & 0 deletions api-node/src/controllers/hubeau-prelevement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import express from 'express';
import helmet from 'helmet';
import { z } from 'zod';
import fs from 'fs';
import { catchErrors } from '../middlewares/errors';
import { type CustomError } from '~/types/error';

Check failure on line 6 in api-node/src/controllers/hubeau-prelevement.ts

View workflow job for this annotation

GitHub Actions / lint

'CustomError' is defined but never used
import { type udis as UdiType } from '@prisma/client';

Check failure on line 7 in api-node/src/controllers/hubeau-prelevement.ts

View workflow job for this annotation

GitHub Actions / lint

'UdiType' is defined but never used
import { fetchDrinkingWaterPrelevement } from '~/aggregators/drinking_water';
import dayjs from 'dayjs';

const router = express.Router();
const drinkingWater403 = fs.readFileSync(
'./src/templates/drinking-water-403-no-code-provided.html',
'utf8',
);

router.get(
'/',
helmet.contentSecurityPolicy({
useDefaults: true,
directives: {
'default-src': ["'self'"],
'script-src': ["'self'", 'https://cdn.tailwindcss.com'],
'img-src': [
"'self'",
'https://j.gifs.com/VPXDoz.gif',
'https://recosante.beta.gouv.fr/favicon.ico',
],
'object-src': ["'none'"],
'upgrade-insecure-requests': [],
},
}),
catchErrors(
async (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) => {
try {
z.object({
code_prelevement: z.string(),
}).parse(req.query);
} catch (zodError) {
res.status(403).send(drinkingWater403);
return;
}
const { code_prelevement } = req.query;

if (!code_prelevement) {
res.status(403).send(drinkingWater403);
return;
}
const hubeauResponse = await fetchDrinkingWaterPrelevement(
code_prelevement as string,
);
const prelevement = hubeauResponse.data;
const metadata = prelevement[0];
const drinkingWater = fs
.readFileSync('./src/templates/drinking-water-test-result.html', 'utf8')
.replace('{{CODE_PRELEVEMENT}}', metadata.code_prelevement)
.replace(
'{{PLACE_PRELEVEMENT}}',
`${metadata.nom_commune} (${metadata.code_departement})`,
)
.replace('{{NOM_DISTRIBUTEUR}}', metadata.nom_distributeur)
.replace(
'{{CONCLUSION_CONFORMITE_PRELEVEMENT}}',
metadata.conclusion_conformite_prelevement,
)
.replace(
'{{HUBEAU_LINK}}',
`https://hubeau.eaufrance.fr/api/v1/qualite_eau_potable/resultats_dis?code_prelevement=${metadata.code_prelevement}&size=1000`,
)
.replace(
'{{DATE_PRELEVEMENT}}',
dayjs(metadata.date_prelevement).format('DD/MM/YYYY HH:mm'),
)
.replace('{{COUNT_PARAMETERS}}', prelevement.length.toString())
.replace(
'{{UDIS}}',
metadata.reseaux
?.map(
(reseau) => `${reseau.code} - ${reseau.nom} (${reseau.debit})`,
)
.join('<br />') || '',

Check failure on line 85 in api-node/src/controllers/hubeau-prelevement.ts

View workflow job for this annotation

GitHub Actions / lint

Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator
)
.replace(
'{{RESULTS}}',
prelevement
.map((paramTested) => {
return `
<tr>
<td class="w-full max-w-0 py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:w-auto sm:max-w-none sm:pl-0">
${
paramTested.libelle_parametre
} <span class="text-xs text-gray-400">(${
paramTested.code_parametre_se
})</span>
<dl class="font-normal lg:hidden">
<dt class="sr-only">Résultat</dt>
<dd class="mt-1 truncate text-gray-700">${
paramTested.resultat_alphanumerique
} ${
paramTested.libelle_unite !== 'SANS OBJET'
? paramTested.libelle_unite
: ''
}</dd>
<dt class="sr-only sm:hidden">Limite</dt>
<dd class="mt-1 truncate text-gray-500 sm:hidden">${
paramTested.limite_qualite_parametre
}</dd>
<dt class="sr-only sm:hidden">Référence</dt>
<dd class="mt-1 truncate text-gray-500 sm:hidden">${
paramTested.reference_qualite_parametre
}</dd>
</dl>
</td>
<td class="hidden px-3 py-4 text-sm text-gray-500 lg:table-cell">${
paramTested.resultat_alphanumerique
} (${paramTested.libelle_unite})</td>
<td class="hidden px-3 py-4 text-sm text-gray-500 sm:table-cell">${
paramTested.limite_qualite_parametre ?? 'N/A'
}</td>
<td class="hidden px-3 py-4 text-sm text-gray-500 sm:table-cell">${
paramTested.reference_qualite_parametre ?? 'N/A'
}</td>
</tr>
`;
})
.join('\n') || '',
);

res.status(200).send(drinkingWater);
},
),
);

export default router;
2 changes: 2 additions & 0 deletions api-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import mailRouter from './controllers/mail.ts';
import feedbackRouter from './controllers/feedback.ts';
import notificationRouter from './controllers/notification.ts';
import udiRouter from './controllers/udi.ts';
import hubeauPrelevementRouter from './controllers/hubeau-prelevement.ts';
import callToActionRouter from './controllers/call-to-action.ts';

import packageJson from '../package.json';
Expand Down Expand Up @@ -113,6 +114,7 @@ app.use('/feedback', versionCheck, feedbackRouter);
app.use('/notification', versionCheck, notificationRouter);
app.use('/call-to-action', versionCheck, callToActionRouter);
app.use('/udi', udiRouter);
app.use('/hubeau-prelevement', hubeauPrelevementRouter);

app.use(Sentry.Handlers.errorHandler());
app.use(sendError);
Expand Down
69 changes: 69 additions & 0 deletions api-node/src/templates/drinking-water-403-no-code-provided.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html class=""h-full w-full">
<head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://recosante.beta.gouv.fr/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
</head>
</head>
<body class=""h-full w-full">
<div
class="grid min-h-full grid-cols-1 grid-rows-[1fr,auto,1fr] bg-white lg:grid-cols-[max(50%,36rem),1fr]"
>
<!-- <header
class="mx-auto w-full max-w-7xl px-6 pt-6 sm:pt-10 lg:col-span-2 lg:col-start-1 lg:row-start-1 lg:px-8"
>
<a href="#">
<span class="sr-only">Recosanté</span>
<img
class="h-10 w-auto sm:h-12"
src=""
alt="Logo Recosanté"
/>
</a>
</header> -->
<main
class="mx-auto w-full max-w-7xl px-6 py-24 sm:py-32 lg:col-span-2 lg:col-start-1 lg:row-start-2 lg:px-8"
>
<div class="max-w-lg">
<p class="text-base font-semibold leading-8 text-indigo-600">403</p>
<h1
class="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"
>
Test non trouvé
</h1>
<p class="mt-6 text-base leading-7 text-gray-600">
Vous devez fournir un `code_prelevement` en paramètre d'URL
</p>
</div>
</main>
<footer class="self-end lg:col-span-2 lg:col-start-1 lg:row-start-3">
<div class="border-t border-gray-100 bg-gray-50 py-10">
<nav
class="mx-auto flex w-full max-w-7xl items-center gap-x-4 px-6 text-sm leading-7 text-gray-600 lg:px-8"
>
<a href="https://recosante.beta.gouv.fr">Retour sur Recosanté</a>
<svg
viewBox="0 0 2 2"
aria-hidden="true"
class="h-0.5 w-0.5 fill-gray-300"
>
<circle cx="1" cy="1" r="1" />
</svg>
</nav>
</div>
</footer>
<div
class="hidden lg:relative lg:col-start-2 lg:row-start-1 lg:row-end-4 lg:block"
>
<img
src="https://j.gifs.com/VPXDoz.gif"
alt=""
class="absolute inset-0 h-full w-full object-cover"
/>
</div>
</div>
</body>
</html>
42 changes: 42 additions & 0 deletions api-node/src/templates/drinking-water-test-result.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html class=""h-full w-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://recosante.beta.gouv.fr/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class=""h-full w-full">
<div class="px-4 sm:px-6 lg:px-8 mt-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-base font-semibold leading-6 text-gray-900">Test {{CODE_PRELEVEMENT}}</h1>
<p class="mt-2 text-sm text-gray-700"><b>Date de prélèvement&nbsp;:</b> {{DATE_PRELEVEMENT}}</p>
<p class="mt-2 text-sm text-gray-700"><b>Lieu de prélèvement&nbsp;:</b> {{PLACE_PRELEVEMENT}}</p>
<p class="mt-2 text-sm text-gray-700"><b>Nombre de paramètres testés&nbsp;:</b> {{COUNT_PARAMETERS}} (<a href="{{HUBEAU_LINK}}"
Fixed Show fixed Hide fixed
target="_blank" class="inline-flex items-center text-indigo-600 hover:text-indigo-900">lien vers les données brutes <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-3 h-3 inline ml-2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
</svg></a>)</p>
<p class="mt-2 text-sm text-gray-700"><b>Distributeur&nbsp;:</b> {{NOM_DISTRIBUTEUR}}</p>
<p class="mt-2 text-sm text-gray-700"><b>Réseaux concernés&nbsp;:</b>{{UDIS}}</p>
<p class="mt-2 text-sm text-gray-700"><b>Conclusion conformité prélèvement&nbsp;:</b> {{CONCLUSION_CONFORMITE_PRELEVEMENT}}</p>
</div>
</div>
<div class="-mx-4 mt-8 sm:-mx-0">
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0" data-field="libelle_parametre,code_parametre_se">Paramètre</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell" data-field="resultat_alphanumerique,libelle_unite">Résultat</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell" data-field="limite_qualite_parametre">Limite(s) de qualité </th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900" data-field="reference_qualite_parametre">Référence(s) de qualité</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{RESULTS}}
</tbody>
</table>
</div>
</div>
</body>
</html>
Loading