Skip to main content
Sign in
Snippets Groups Projects
Commit 73846fd8 authored by Toufic Batache's avatar Toufic Batache
Browse files

Automatiquement détecter si une simulation publique est disponible

parent 1ac4d1a9
Branches
Tags
2 merge requests!158Bouton et modale simulation budget et autre évolution UI du budget,!157Ajout partage simulation budgétaire
Pipeline #10263 passed
...@@ -6,19 +6,43 @@ ...@@ -6,19 +6,43 @@
Transition, Transition,
TransitionChild, TransitionChild,
} from "@rgossiaux/svelte-headlessui" } from "@rgossiaux/svelte-headlessui"
import { getContext } from "svelte"
import type { Writable } from "svelte/types/runtime/store"
import { browser } from "$app/environment" import { browser } from "$app/environment"
import { page } from "$app/stores" import { page } from "$app/stores"
import { hashObject } from "$lib/hash"
import type { ParametricReform } from "$lib/reforms"
import type { CachedSimulation } from "$lib/simulations" import type { CachedSimulation } from "$lib/simulations"
import { budgetEditableParametersName } from "$lib/variables"
export let isOpen = false export let isOpen = false
let cachedSimulations: CachedSimulation[] = [] let cachedSimulations: CachedSimulation[] = []
let currentSimulationCache: CachedSimulation | undefined
const parametricReform = getContext(
"parametricReform",
) as Writable<ParametricReform>
$: if (browser) { $: if (browser && isOpen) {
fetchCachedSimulations() fetchCachedSimulations()
} }
$: currentSimulationCache = cachedSimulations.find(
(simulation) => simulation.parametersHash === parametricReformDigest,
)
$: parametricReformDigest = hashObject(
Object.entries($parametricReform)
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
.reduce((filtered: ParametricReform, [parameterName, value]) => {
if (budgetEditableParametersName.has(parameterName)) {
filtered[parameterName] = value
}
return filtered
}, {}),
)
async function fetchCachedSimulations() { async function fetchCachedSimulations() {
const res = await fetch("/simulations_budget/index", { const res = await fetch("/simulations_budget/index", {
method: "POST", method: "POST",
...@@ -125,15 +149,40 @@ ...@@ -125,15 +149,40 @@
>.</span >.</span
> >
</div> </div>
<div class="bg-gray-100 px-5 pb-10 pt-5 border-t mt-10">
{#if currentSimulationCache !== undefined}
<div
class="flex justify-between items-center bg-le-jaune-light bg-opacity-50 px-5 py-3 mt-10"
>
<h2 class="text-2xl font-bold">
🎉&nbsp;&nbsp;&nbsp;Votre réforme est déjà calculée !
</h2>
<a
class="flex items-center gap-2 p-2 hover:bg-gray-500 hover:bg-opacity-10 active:bg-gray-500 active:bg-opacity-20 rounded-md underline text-gray-600 text-sm font-bold tracking-[0.085em] uppercase"
data-sveltekit-reload
href="/simulations_budget/{currentSimulationCache.hash}"
title="Voir la simulation"
>
Voir la simulation<iconify-icon
class="align-[-0.25rem] text-xl"
icon="ri-arrow-right-line"
/>
</a>
</div>
{/if}
<div class="flex flex-col gap-10 bg-gray-100 p-5 border-t mt-10">
{#if currentSimulationCache === undefined}
<div>
<h2 class="w-full text-left text-2xl font-bold"> <h2 class="w-full text-left text-2xl font-bold">
Demandez le calcul de votre réforme au service LexImpact&nbsp;: Demandez le calcul de votre réforme au service
LexImpact&nbsp;:
</h2> </h2>
<p class="w-full text-base font-normal leading-6 mt-1.5 mb-5"> <p class="w-full text-base font-normal leading-6 mt-1.5 mb-5">
Après vérification par nos services, si elle est calculable avec Après vérification par nos services, si elle est calculable
les données dont nous disposons et répond au secret statistique, avec les données dont nous disposons et répond au secret
la simulation sera rendue publique. Vous serez alors informé par statistique, la simulation sera rendue publique. Vous serez
e-mail : alors informé par e-mail :
</p> </p>
<span class="font-bold text-sm py-2 pl-10" <span class="font-bold text-sm py-2 pl-10"
>Votre adresse e-mail :</span >Votre adresse e-mail :</span
...@@ -168,8 +217,10 @@ ...@@ -168,8 +217,10 @@
</a> </a>
</div> </div>
</div> </div>
</div>
{/if}
{#if cachedSimulations !== undefined && cachedSimulations.length > 0} {#if cachedSimulations !== undefined && cachedSimulations.length > 0}
<div class="mt-10 flex flex-col items-center"> <div class="flex flex-col items-center">
<h2 class="w-full text-left text-xl font-bold"> <h2 class="w-full text-left text-xl font-bold">
Consultez la liste des simulations budgétaires déjà Consultez la liste des simulations budgétaires déjà
disponibles&nbsp;: disponibles&nbsp;:
...@@ -196,13 +247,13 @@ ...@@ -196,13 +247,13 @@
{/each} {/each}
</table> </table>
<a <a
class="mt-2 flex items-center gap-2 py-2 px-5 hover:bg-gray-200 active:bg-gray-300 rounded-md underline text-gray-600 text-sm font-bold tracking-[0.085em] uppercase" class="mt-2 flex items-center gap-3 py-2 px-5 hover:bg-gray-200 active:bg-gray-300 rounded-md underline text-gray-600 text-sm font-bold tracking-[0.085em] uppercase"
data-sveltekit-reload data-sveltekit-reload
href="/" href="/"
title="Voir toutes les simulations" title="Voir toutes les simulations"
> >
Voir toutes les simulations<iconify-icon Voir toutes les simulations<iconify-icon
class="ml-2 align-[-0.25rem] text-xl" class="align-[-0.25rem] text-xl"
icon="ri-arrow-right-line" icon="ri-arrow-right-line"
/> />
</a> </a>
... ...
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
import { page } from "$app/stores" import { page } from "$app/stores"
import type { BudgetSimulation } from "$lib/budgets" import type { BudgetSimulation } from "$lib/budgets"
import type { DisplayMode } from "$lib/displays"
import CopyClipboard from "$lib/components/CopyClipboard.svelte" import CopyClipboard from "$lib/components/CopyClipboard.svelte"
import type { DisplayMode } from "$lib/displays"
import type { ParametricReform } from "$lib/reforms" import type { ParametricReform } from "$lib/reforms"
import { budgetEditableParametersName } from "$lib/variables" import { budgetEditableParametersName } from "$lib/variables"
...@@ -68,15 +68,14 @@ ...@@ -68,15 +68,14 @@
body: JSON.stringify({ body: JSON.stringify({
budgetSimulation: $budgetSimulation?.result, budgetSimulation: $budgetSimulation?.result,
displayMode, displayMode,
parametricReform: Object.entries($parametricReform).reduce( parametricReform: Object.entries($parametricReform)
(filtered, [parameterName, value]) => { .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
.reduce((filtered: ParametricReform, [parameterName, value]) => {
if (budgetEditableParametersName.has(parameterName)) { if (budgetEditableParametersName.has(parameterName)) {
filtered[parameterName] = value filtered[parameterName] = value
} }
return filtered return filtered
}, }, {}),
{},
),
}), }),
headers: { headers: {
Accept: "application/json", Accept: "application/json",
... ...
......
import { XXH64 } from "xxh3-ts"
// Sometimes, the magnitude of the BigInt value generated by XXH64()
// is not large enough to require 16 characters when represented in base 16.
// As a result, the base 16 representation has leading zeros that are not necessary,
// and JavaScript omits them when converting the BigInt to a string. To fix this,
// we add zeros in the beginning until we reach a length of 16.
export function hashString(str: string): string {
return XXH64(Buffer.from(str)).toString(16).padStart(16, "0")
}
export function hashObject(obj: object): string {
return XXH64(Buffer.from(JSON.stringify(obj)))
.toString(16)
.padStart(16, "0")
}
...@@ -2,6 +2,7 @@ export interface CachedSimulation { ...@@ -2,6 +2,7 @@ export interface CachedSimulation {
date: string date: string
hash: string hash: string
parameters: string[] parameters: string[]
parametersHash: string
title: string title: string
} }
... ...
......
...@@ -8,8 +8,7 @@ import { error, json } from "@sveltejs/kit" ...@@ -8,8 +8,7 @@ import { error, json } from "@sveltejs/kit"
import fs from "fs-extra" import fs from "fs-extra"
import path from "path" import path from "path"
import { XXH64 } from "xxh3-ts" import { hashString } from "$lib/hash"
import config from "$lib/server/config" import config from "$lib/server/config"
import type { RequestHandler } from "./$types" import type { RequestHandler } from "./$types"
...@@ -83,12 +82,7 @@ export const POST: RequestHandler = async ({ request, url }) => { ...@@ -83,12 +82,7 @@ export const POST: RequestHandler = async ({ request, url }) => {
} }
const bodyJson = JSON.stringify(body, null, 2) const bodyJson = JSON.stringify(body, null, 2)
// Sometimes, the magnitude of the BigInt value generated by XXH64() const digest = hashString(bodyJson)
// is not large enough to require 16 characters when represented in base 16.
// As a result, the base 16 representation has leading zeros that are not necessary,
// and JavaScript omits them when converting the BigInt to a string. To fix this,
// we add zeros in the beginning until we reach a length of 16.
const digest = XXH64(Buffer.from(bodyJson)).toString(16).padStart(16, "0")
const simulationDir = path.join(simulationsDir, digest.substring(0, 2)) const simulationDir = path.join(simulationsDir, digest.substring(0, 2))
const simulationFilePath = path.join(simulationDir, `${digest}.json`) const simulationFilePath = path.join(simulationDir, `${digest}.json`)
... ...
......
...@@ -2,7 +2,6 @@ import { auditRequire, cleanAudit, type Audit } from "@auditors/core" ...@@ -2,7 +2,6 @@ import { auditRequire, cleanAudit, type Audit } from "@auditors/core"
import { error, json } from "@sveltejs/kit" import { error, json } from "@sveltejs/kit"
import fs from "fs-extra" import fs from "fs-extra"
import path from "path" import path from "path"
import { XXH64 } from "xxh3-ts"
import { getParameter, rootParameter } from "$lib/parameters" import { getParameter, rootParameter } from "$lib/parameters"
import type { ParametricReform } from "$lib/reforms" import type { ParametricReform } from "$lib/reforms"
...@@ -10,6 +9,7 @@ import config from "$lib/server/config" ...@@ -10,6 +9,7 @@ import config from "$lib/server/config"
import type { CachedSimulation } from "$lib/simulations" import type { CachedSimulation } from "$lib/simulations"
import type { RequestHandler } from "./$types" import type { RequestHandler } from "./$types"
import { hashObject, hashString } from "$lib/hash"
const { simulationsBudgetDir } = config const { simulationsBudgetDir } = config
...@@ -81,12 +81,11 @@ export const POST: RequestHandler = async ({ request, url }) => { ...@@ -81,12 +81,11 @@ export const POST: RequestHandler = async ({ request, url }) => {
const bodyJson = JSON.stringify(body, null, 2) const bodyJson = JSON.stringify(body, null, 2)
// Sometimes, the magnitude of the BigInt value generated by XXH64() const digest = hashString(bodyJson)
// is not large enough to require 16 characters when represented in base 16.
// As a result, the base 16 representation has leading zeros that are not necessary, const parametricReformDigest = hashObject(
// and JavaScript omits them when converting the BigInt to a string. To fix this, (body as { parametricReform: ParametricReform }).parametricReform,
// we add zeros in the beginning until we reach a length of 16. )
const digest = XXH64(Buffer.from(bodyJson)).toString(16).padStart(16, "0")
const simulationDir = path.join(simulationsBudgetDir, digest.substring(0, 2)) const simulationDir = path.join(simulationsBudgetDir, digest.substring(0, 2))
const simulationFilePath = path.join(simulationDir, `${digest}.json`) const simulationFilePath = path.join(simulationDir, `${digest}.json`)
...@@ -105,11 +104,12 @@ export const POST: RequestHandler = async ({ request, url }) => { ...@@ -105,11 +104,12 @@ export const POST: RequestHandler = async ({ request, url }) => {
date: new Intl.DateTimeFormat("fr-FR").format(new Date()), date: new Intl.DateTimeFormat("fr-FR").format(new Date()),
hash: digest, hash: digest,
parameters: modifiedParametersNames, parameters: modifiedParametersNames,
parametersHash: parametricReformDigest,
title: modifiedParametersTitles.join(" | "), title: modifiedParametersTitles.join(" | "),
} as CachedSimulation, } as CachedSimulation,
].filter( ].filter(
(value, index, self) => (value, index, self) =>
index === self.findIndex((el) => el.hash === value.hash), index === self.findLastIndex((el) => el.hash === value.hash),
) )
await fs.writeFile(indexDir, JSON.stringify(contents)) await fs.writeFile(indexDir, JSON.stringify(contents))
... ...
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment