feate: ajoute options de formattage des stas

wip-related-pages
sebastien.arod@gmail.com 2024-06-04 10:50:16 +02:00
parent e1acd45eb7
commit 39d1125681
10 changed files with 225 additions and 153 deletions

View File

@ -0,0 +1,32 @@
import { describe, expect, test } from "@jest/globals";
import { formatValue } from "./formatValue";
describe("formatValue", () => {
test("format with default options", () => {
expect(formatValue(42.42, { notionPropName: "whatever" })).toBe("42,4");
expect(formatValue(42, { notionPropName: "whatever" })).toBe("42");
});
test("format with valueMaxFractioDigits: 2", () => {
expect(
formatValue(42.4242, {
notionPropName: "whatever",
valueMaxFractioDigits: 2,
})
).toBe("42,42");
expect(
formatValue(42, {
notionPropName: "whatever",
valueMaxFractioDigits: 2,
})
).toBe("42");
});
test("format with unit", () => {
expect(
formatValue(42, {
notionPropName: "whatever",
unit: "%",
})
).toBe("42%");
});
});

View File

@ -0,0 +1,15 @@
import { StatPublishOptions } from "../../statPropsPublishOptions";
export function formatValue(value: number, publishOptions: StatPublishOptions) {
const valueStr = value.toLocaleString("fr-FR", {
useGrouping: false,
maximumFractionDigits:
publishOptions.valueMaxFractioDigits === undefined
? 1
: publishOptions.valueMaxFractioDigits,
});
const formattedValue = `${valueStr}${
publishOptions.unit === undefined ? "" : publishOptions.unit
}`;
return formattedValue;
}

View File

@ -0,0 +1,33 @@
import { ValueWithEvol } from "../../../statistiques/ELStats";
import { StatPublishOptions } from "../../statPropsPublishOptions";
import { formatValue } from "./formatValue";
export function formatValueWithEvol(
n: ValueWithEvol,
publishOptions: StatPublishOptions
): string {
const value = n.value;
const valueStr = formatValue(value, publishOptions);
if (isNaN(n.evol)) {
return valueStr;
} else {
const evolStr = n.evol.toLocaleString("fr-FR", {
useGrouping: false,
maximumFractionDigits:
publishOptions.evolMaxFractioDigits === undefined
? 1
: publishOptions.evolMaxFractioDigits,
signDisplay: "always",
});
const evolPercentStr = Math.round(n.evolPercent).toLocaleString("fr-FR", {
useGrouping: false,
signDisplay: "always",
maximumFractionDigits:
publishOptions.evolPctMaxFractioDigits === undefined
? 1
: publishOptions.evolPctMaxFractioDigits,
});
return `${valueStr} (${evolStr} | ${evolPercentStr}%)`;
}
}

View File

@ -1,17 +1,15 @@
import { Client, isFullPage } from "@notionhq/client"; import { Client, isFullPage } from "@notionhq/client";
import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints"; import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints";
import { ELPeriodStats, ValueWithEvol } from "../../statistiques/ELStats"; import { ELPeriodStats, ValueWithEvol } from "../../statistiques/ELStats";
import {
StatPublishOptions,
statPropsPublishOptions,
} from "../statPropsPublishOptions";
import { titlePropertyToText } from "../utils/properties/titlePropertyToText"; import { titlePropertyToText } from "../utils/properties/titlePropertyToText";
import { queryAllDbResults } from "../utils/queryAllDbResults"; import { queryAllDbResults } from "../utils/queryAllDbResults";
import { removeBlocks } from "../utils/removeBlocks"; import { removeBlocks } from "../utils/removeBlocks";
import { CreatePageProperties } from "../utils/types/CreatePageProperties"; import { CreatePageProperties } from "../utils/types/CreatePageProperties";
import { import { formatValueWithEvol } from "./format/formatValueWithEvol";
statNameDureeResistanceMediane,
statNameDureeResistanceMoyenne,
statsNameNbFamillesMisesEnDemeure,
statsNameNbFamillesResistantes,
statsNamePourcentageEntreeApresMisesEnDemeure,
} from "./statNames";
export async function publishPeriodStats( export async function publishPeriodStats(
notionClient: Client, notionClient: Client,
@ -58,59 +56,61 @@ export async function publishPeriodStats(
// Create rows to create // Create rows to create
for (const periodId of periodIdsToCreate) { for (const periodId of periodIdsToCreate) {
const stat = indexedPeriodStats[periodId]; const periodStats = indexedPeriodStats[periodId];
await notionClient.pages.create({ await notionClient.pages.create({
parent: { parent: {
database_id: periodStatsDbId, database_id: periodStatsDbId,
}, },
properties: buildRowPropertiesForUpsert(stat), properties: buildRowPropertiesForUpsert(periodId, periodStats.stats),
}); });
} }
// Update rows // Update rows
for (const periodId of periodIdsToUpdate) { for (const periodId of periodIdsToUpdate) {
const stat = indexedPeriodStats[periodId]; const periodStats = indexedPeriodStats[periodId];
const row = indexedPeriodRows[periodId]; const row = indexedPeriodRows[periodId];
await notionClient.pages.update({ await notionClient.pages.update({
page_id: row.id, page_id: row.id,
properties: buildRowPropertiesForUpsert(stat), properties: buildRowPropertiesForUpsert(periodId, periodStats.stats),
}); });
} }
} }
function buildRowPropertiesForUpsert( function buildRowPropertiesForUpsert(
stat: ELPeriodStats periodId: string,
stats: ELPeriodStats["stats"]
): CreatePageProperties { ): CreatePageProperties {
const statsNotionProps: CreatePageProperties = Object.fromEntries(
(Object.keys(stats) as Array<keyof ELPeriodStats["stats"]>).map(
(jsProp) => {
const value: ValueWithEvol = stats[jsProp];
const publishOptions = statPropsPublishOptions[jsProp];
return [
publishOptions.notionPropName,
valueWithEvolProp(value, publishOptions),
];
}
)
);
return { return {
Période: { Période: {
title: [ title: [
{ {
text: { text: {
content: stat.periodId, content: periodId,
}, },
}, },
], ],
}, },
[statsNameNbFamillesResistantes]: valueWithEvolProp( ...statsNotionProps,
stat.nbFamilleResistantes
),
[statNameDureeResistanceMediane]: valueWithEvolProp(
stat.dureeResistanceMediane
),
[statNameDureeResistanceMoyenne]: valueWithEvolProp(
stat.dureeResistanceMoyenne
),
[statsNameNbFamillesMisesEnDemeure]: valueWithEvolProp(
stat.nbFamillesMisesEnDemeure
),
[statsNamePourcentageEntreeApresMisesEnDemeure]: valueWithEvolProp(
stat.pourcentageEntreeApresMiseEnDemeure
),
}; };
} }
function valueWithEvolProp(n: ValueWithEvol) { function valueWithEvolProp(
const formatted = formatValueWithEvol(n); n: ValueWithEvol,
publishOptions: StatPublishOptions
) {
const formatted = formatValueWithEvol(n, publishOptions);
return { return {
rich_text: [ rich_text: [
{ {
@ -121,24 +121,3 @@ function valueWithEvolProp(n: ValueWithEvol) {
], ],
}; };
} }
function formatValueWithEvol(n: ValueWithEvol): string {
const value = n.value.toLocaleString("fr-FR", {
useGrouping: false,
maximumFractionDigits: 2,
});
if (isNaN(n.evol)) {
return value;
} else {
const evol = n.evol.toLocaleString("fr-FR", {
useGrouping: false,
maximumFractionDigits: 2,
signDisplay: "always",
});
const evolPercent = Math.round(n.evolPercent).toLocaleString("fr-FR", {
useGrouping: false,
signDisplay: "always",
});
return `${value} (${evol} | ${evolPercent}%)`;
}
}

View File

@ -1,23 +1,53 @@
import { Client, isFullBlock } from "@notionhq/client"; import { Client, isFullBlock } from "@notionhq/client";
import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints"; import { BlockObjectRequest } from "@notionhq/client/build/src/api-endpoints";
import { ELStatsActuelles } from "../../statistiques/ELStats"; import { ELStatsActuelles } from "../../statistiques/ELStats";
import {
StatPublishOptions,
statPropsPublishOptions,
} from "../statPropsPublishOptions";
import { listAllChildrenBlocks } from "../utils/listAllChildrenBlocks"; import { listAllChildrenBlocks } from "../utils/listAllChildrenBlocks";
import { removeBlocks } from "../utils/removeBlocks"; import { removeBlocks } from "../utils/removeBlocks";
import { richTextToPlainText } from "../utils/text/richTextToPlainText"; import { richTextToPlainText } from "../utils/text/richTextToPlainText";
import { formatValue } from "./format/formatValue";
import { currentStatsHeading, statsPageId } from "./publishStatisticsToNotion"; import { currentStatsHeading, statsPageId } from "./publishStatisticsToNotion";
import {
statNameDureeResistanceMediane,
statNameDureeResistanceMoyenne,
statsNameNbFamillesMisesEnDemeure,
statsNameNbFamillesResistantes,
statsNameNbFamillesResistantesOuEx,
statsNamePourcentageEntreeApresMisesEnDemeure,
statsNamePourcentageFamilleMisesEnDemeure,
} from "./statNames";
export async function publishStatsActuelles( export async function publishStatsActuelles(
notionClient: Client, notionClient: Client,
statsActuelles: ELStatsActuelles statsActuelles: ELStatsActuelles
) {
const newBlocks = (
Object.keys(statsActuelles) as Array<keyof ELStatsActuelles>
).map((jsProp) => {
const value: number = statsActuelles[jsProp];
const publishOptions = statPropsPublishOptions[jsProp];
return currentStatBlock(value, publishOptions);
});
await updateStatsActuellesBlocks(notionClient, newBlocks);
}
function currentStatBlock(
value: number,
publishOptions: StatPublishOptions
): BlockObjectRequest {
const formattedValue = formatValue(value, publishOptions);
const content = `${publishOptions.notionPropName} : ${formattedValue}`;
return {
paragraph: {
rich_text: [
{
text: {
content: content,
},
},
],
},
};
}
async function updateStatsActuellesBlocks(
notionClient: Client,
blocks: BlockObjectRequest[]
) { ) {
const childrenBlocks = ( const childrenBlocks = (
await listAllChildrenBlocks(notionClient, { await listAllChildrenBlocks(notionClient, {
@ -46,60 +76,9 @@ export async function publishStatsActuelles(
.map((b) => b.id); .map((b) => b.id);
await removeBlocks(notionClient, blocksIdsToRemove); await removeBlocks(notionClient, blocksIdsToRemove);
notionClient.blocks.children.append({ await notionClient.blocks.children.append({
block_id: statsPageId, block_id: statsPageId,
after: currentStatsHeadingBlock.id, after: currentStatsHeadingBlock.id,
children: [ children: [...blocks],
currentStatBlock(
statsNameNbFamillesResistantes,
statsActuelles.nbFamilleResistantes
),
currentStatBlock(
statsNameNbFamillesResistantesOuEx,
statsActuelles.nbFamilleResistantesOrEx
),
currentStatBlock(
statNameDureeResistanceMoyenne,
statsActuelles.dureeResistanceMoyenne
),
currentStatBlock(
statNameDureeResistanceMediane,
statsActuelles.dureeResistanceMediane
),
currentStatBlock(
statsNameNbFamillesMisesEnDemeure,
statsActuelles.nbFamillesMiseEnDemeure
),
currentStatBlock(
statsNamePourcentageFamilleMisesEnDemeure,
statsActuelles.pourcentageFamillesMisesEnDemeure
),
currentStatBlock(
statsNamePourcentageEntreeApresMisesEnDemeure,
statsActuelles.pourcentageEntreeApresMiseEnDemeure
),
],
}); });
} }
function currentStatBlock(
stateName: string,
value: number
): BlockObjectRequest {
return {
paragraph: {
rich_text: [
{
text: {
content:
stateName +
": " +
value.toLocaleString("us-FR", {
maximumFractionDigits: 0,
}),
},
},
],
},
};
}

View File

@ -1,10 +0,0 @@
export const statNameDureeResistanceMoyenne = "Durée Résistance Moyenne";
export const statNameDureeResistanceMediane = "Durée Résistance Médiane";
export const statsNameNbFamillesResistantes = "Nb Familles Résistantes";
export const statsNameNbFamillesResistantesOuEx =
"Nb Familles Résistante ou Ex-Résistantes";
export const statsNameNbFamillesMisesEnDemeure = "Nb Familles Mises en Demeure";
export const statsNamePourcentageFamilleMisesEnDemeure =
"Pourcentage de Familles Mises en Demeure";
export const statsNamePourcentageEntreeApresMisesEnDemeure =
"Pourcentage d'Entrees Après Mises en Demeure";

View File

@ -0,0 +1,41 @@
import { ELPeriodStats, ELStatsActuelles } from "../statistiques/ELStats";
export const statPropsPublishOptions: {
[jsPropName in statsPropNames]: StatPublishOptions;
} = {
nbFamilleResistantes: {
notionPropName: "Nb Familles Résistantes",
},
dureeResistanceMoyenne: {
notionPropName: "Durée Résistance Moyenne",
unit: "j",
},
dureeResistanceMediane: {
notionPropName: "Durée Résistance Médiane",
unit: "j",
},
nbFamilleResistantesOrEx: {
notionPropName: "Nb Familles Résistante ou Ex-Résistantes",
},
nbFamillesMisesEnDemeure: {
notionPropName: "Nb Familles Mises en Demeure",
},
pourcentageFamillesMisesEnDemeure: {
notionPropName: "Pourcentage de Familles Mises en Demeure",
unit: "%",
},
pourcentageEntreeApresMiseEnDemeure: {
notionPropName: "Pourcentage d'Entrees Après Mises en Demeure",
unit: "%",
},
};
export type statsPropNames =
| keyof ELPeriodStats["stats"]
| keyof ELStatsActuelles;
export type StatPublishOptions = {
notionPropName: string;
unit?: string;
valueMaxFractioDigits?: number;
evolMaxFractioDigits?: number;
evolPctMaxFractioDigits?: number;
};

View File

@ -8,21 +8,22 @@ export type ELStatsActuelles = {
nbFamilleResistantes: number; nbFamilleResistantes: number;
/** Includes Ancient resistants */ /** Includes Ancient resistants */
nbFamilleResistantesOrEx: number; nbFamilleResistantesOrEx: number;
dureeResistanceMoyenne: number; dureeResistanceMoyenne: number;
dureeResistanceMediane: number; dureeResistanceMediane: number;
nbFamillesMiseEnDemeure: number; nbFamillesMisesEnDemeure: number;
pourcentageFamillesMisesEnDemeure: number; pourcentageFamillesMisesEnDemeure: number;
pourcentageEntreeApresMiseEnDemeure: number; pourcentageEntreeApresMiseEnDemeure: number;
}; };
export type ELPeriodStats = { export type ELPeriodStats = {
periodId: string; periodId: string;
nbFamilleResistantes: ValueWithEvol; stats: {
dureeResistanceMoyenne: ValueWithEvol; nbFamilleResistantes: ValueWithEvol;
dureeResistanceMediane: ValueWithEvol; dureeResistanceMoyenne: ValueWithEvol;
nbFamillesMisesEnDemeure: ValueWithEvol; dureeResistanceMediane: ValueWithEvol;
pourcentageEntreeApresMiseEnDemeure: ValueWithEvol; nbFamillesMisesEnDemeure: ValueWithEvol;
pourcentageEntreeApresMiseEnDemeure: ValueWithEvol;
};
}; };
export type ValueWithEvol = { export type ValueWithEvol = {

View File

@ -15,7 +15,7 @@ export function computeELPeriodStats(
periods: IdentifiedPeriod[] periods: IdentifiedPeriod[]
): ELPeriodStats[] { ): ELPeriodStats[] {
const periodStats: ELPeriodStats[] = []; const periodStats: ELPeriodStats[] = [];
let previousELPeriodStats: ELPeriodStats | null = null; let previousELPeriodStats: ELPeriodStats["stats"] | null = null;
for (const period of periods) { for (const period of periods) {
const periodEndOrNow = const periodEndOrNow =
period.end.getTime() > Date.now() ? new Date(Date.now()) : period.end; period.end.getTime() > Date.now() ? new Date(Date.now()) : period.end;
@ -53,29 +53,31 @@ export function computeELPeriodStats(
const stats: ELPeriodStats = { const stats: ELPeriodStats = {
periodId: period.id, periodId: period.id,
nbFamilleResistantes: valueWithEvol( stats: {
nbFamilleResistantes, nbFamilleResistantes: valueWithEvol(
previousELPeriodStats?.nbFamilleResistantes.value nbFamilleResistantes,
), previousELPeriodStats?.nbFamilleResistantes.value
dureeResistanceMediane: valueWithEvol( ),
dureeResistanceMediane, dureeResistanceMediane: valueWithEvol(
previousELPeriodStats?.dureeResistanceMediane.value dureeResistanceMediane,
), previousELPeriodStats?.dureeResistanceMediane.value
dureeResistanceMoyenne: valueWithEvol( ),
dureeResistanceMoyenne, dureeResistanceMoyenne: valueWithEvol(
previousELPeriodStats?.dureeResistanceMoyenne.value dureeResistanceMoyenne,
), previousELPeriodStats?.dureeResistanceMoyenne.value
nbFamillesMisesEnDemeure: valueWithEvol( ),
nbFamillesMiseEnDemeure, nbFamillesMisesEnDemeure: valueWithEvol(
previousELPeriodStats?.nbFamillesMisesEnDemeure.value nbFamillesMiseEnDemeure,
), previousELPeriodStats?.nbFamillesMisesEnDemeure.value
pourcentageEntreeApresMiseEnDemeure: valueWithEvol( ),
pourcentageEntreeApresMiseEnDemeure, pourcentageEntreeApresMiseEnDemeure: valueWithEvol(
previousELPeriodStats?.pourcentageEntreeApresMiseEnDemeure.value pourcentageEntreeApresMiseEnDemeure,
), previousELPeriodStats?.pourcentageEntreeApresMiseEnDemeure.value
),
},
}; };
periodStats.push(stats); periodStats.push(stats);
previousELPeriodStats = stats; previousELPeriodStats = stats?.stats;
} }
return periodStats; return periodStats;
} }

View File

@ -35,7 +35,7 @@ export function computeStatsActuelles(familles: Famille[]): ELStatsActuelles {
nbFamilleResistantesOrEx: resistantsOrEx.length, nbFamilleResistantesOrEx: resistantsOrEx.length,
dureeResistanceMoyenne: dureeMoyenne, dureeResistanceMoyenne: dureeMoyenne,
dureeResistanceMediane: dureeMediane, dureeResistanceMediane: dureeMediane,
nbFamillesMiseEnDemeure: familleAvecMiseEnDemeure.length, nbFamillesMisesEnDemeure: familleAvecMiseEnDemeure.length,
pourcentageFamillesMisesEnDemeure: pourcentageFamillesMisesEnDemeure:
(100 * familleAvecMiseEnDemeure.length) / resistantsOrEx.length, (100 * familleAvecMiseEnDemeure.length) / resistantsOrEx.length,
pourcentageEntreeApresMiseEnDemeure: pourcentageEntreeApresMiseEnDemeure: