Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
10 changes: 7 additions & 3 deletions packages/applications/cli/src/helpers/parse-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises';
import { z } from 'zod';
import { Flags } from '@oclif/core';

import { CsvValidationError, parseCsv, ParseOptions } from '@potentiel-libraries/csv';
import { CsvLineValidationError, parseCsv, ParseOptions } from '@potentiel-libraries/csv';

export const csvFlags = {
delimiter: Flags.string({
Expand All @@ -29,9 +29,13 @@ export const parseCsvFile = async <T extends z.ZodRawShape>(
controller.close();
},
});
return await parseCsv(readableStream, schema, options);
return await parseCsv({
fileStream: readableStream,
lineSchema: schema,
parseOptions: options,
});
} catch (error) {
if (error instanceof CsvValidationError) {
if (error instanceof CsvLineValidationError) {
console.log(error.errors);
}
throw error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ export type CorrigerCandidaturesParLotFormKeys = keyof zod.infer<typeof schema>;

const action: FormAction<FormState, typeof schema> = async (_, { fichierCorrectionCandidatures }) =>
withUtilisateur(async (utilisateur) => {
const { parsedData, rawData } = await parseCsv(
fichierCorrectionCandidatures.content,
candidatureCsvSchema,
{ encoding: 'win1252', delimiter: ';' },
);
const { parsedData, rawData } = await parseCsv({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je me demande s'il ne faudrait pas vérifier les colonnes aussi lors de la correction par lot. Parce que c'est ce qui est arrivé l'autre jour, Tiphany essayait d'importer un fichier de correction avec une mauvaise colonne.

fileStream: fichierCorrectionCandidatures.content,
lineSchema: candidatureCsvSchema,
parseOptions: {
encoding: 'win1252',
delimiter: ';',
},
});

if (parsedData.length === 0) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Routes } from '@potentiel-applications/routes';
import { ActionResult, FormAction, formAction, FormState } from '@/utils/formAction';
import { withUtilisateur } from '@/utils/withUtilisateur';
import { singleDocument } from '@/utils/zod/document/singleDocument';
import { candidatureCsvSchema } from '@/utils/candidature';
import { candidatureCsvSchema, candidatureCsvHeadersMapping } from '@/utils/candidature';
import { mapCsvRowToFournisseurs } from '@/utils/candidature/csv/fournisseurCsv';
import { removeEmptyValues } from '@/utils/candidature/removeEmptyValues';

Expand All @@ -32,14 +32,15 @@ const action: FormAction<FormState, typeof schema> = async (
{ fichierImportCandidature, appelOffre, periode, modeMultiple },
) =>
withUtilisateur(async (utilisateur) => {
const { parsedData, rawData } = await parseCsv(
fichierImportCandidature.content,
candidatureCsvSchema,
{
const { parsedData, rawData } = await parseCsv({
fileStream: fichierImportCandidature.content,
lineSchema: candidatureCsvSchema,
columnsToVerify: Object.values(candidatureCsvHeadersMapping),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il faudrait peut être une fonction intermédiaire pour filtrer les colonnes selon l'AO. Sinon on va avoir des erreurs pour les AOs qui n'ont pas les colonnes spécifiques à Petit PV. On peut se baser sur l'AO entity > Champs supplémentaires.

D'ailleurs ces colonnes ne devraient jamais être dans un csv de candidats. Sauf si on veut pouvoir corriger par lot. Sinon ça sert juste pour nos tests.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aussi toutes les colonnes relatives à la typologie ne sont pas demandées pour tous les AOs. Par exemple si les colonnes sont manquantes pour une période de PPE2 - Eolien, je ne suis pas sûre qu'il faille bloquer l'import. Le rapprochement entre ces colonnes et les AOs n'est pas encore matérialisé dans les l'entity AO il me semble, donc à discuter pour voir comment on met ça en place !

parseOptions: {
encoding: 'win1252',
delimiter: ';',
},
);
});

if (parsedData.length === 0) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ const action: FormAction<FormState, typeof schema> = async (
let success: number = 0;
const errors: ActionResult['errors'] = [];

const { parsedData: instructions } = await parseCsv(
fichierInstruction.content,
instructionCsvSchema,
{ delimiter: ';' },
);
const { parsedData: instructions } = await parseCsv({
fileStream: fichierInstruction.content,
lineSchema: instructionCsvSchema,
parseOptions: { delimiter: ';' },
});

if (instructions.length === 0) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ const action: FormAction<FormState, typeof schema> = async (
GestionnaireRéseau.IdentifiantGestionnaireRéseau.convertirEnValueType(
identifiantGestionnaireReseau,
);
const { parsedData: lines } = await parseCsv(fichierDatesMiseEnService.content, csvSchema);
const { parsedData: lines } = await parseCsv({
fileStream: fichierDatesMiseEnService.content,
lineSchema: csvSchema,
});

if (lines.length === 0) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ const csvSchema = zod.object({

const action: FormAction<FormState, typeof schema> = (_, { fichierCorrections }) =>
withUtilisateur(async (utilisateur) => {
const { parsedData: lines } = await parseCsv(fichierCorrections.content, csvSchema, {
// on conserve les espaces, car c'est potentiellement l'erreur à corriger
ltrim: false,
rtrim: false,
const { parsedData: lines } = await parseCsv({
fileStream: fichierCorrections.content,
lineSchema: csvSchema,
parseOptions: {
// on conserve les espaces, car c'est potentiellement l'erreur à corriger
ltrim: false,
rtrim: false,
},
});

if (lines.length === 0) {
Expand Down
8 changes: 5 additions & 3 deletions packages/applications/ssr/src/components/atoms/form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { formAction, ValidationErrors } from '@/utils/formAction';

import { Heading2 } from '../headings';

import { FormFeedback } from './FormFeedback';
import { FormFeedback } from './form-feedback/FormFeedback';
import { FormPendingModal, FormPendingModalProps } from './FormPendingModal';
import { FormFeedbackCsvErrors } from './FormFeedbackCsvErrors';
import { FormActionButtons, FormActionButtonsProps } from './FormActionButtons';
import { FormFeedbackCsvLineErrors } from './form-feedback/FormFeedbackCsvLineErrors';
import { FormFeedbackCsvColumnErrors } from './form-feedback/FormFeedbackCsvColumnErrors';

export type FormProps = {
id?: string;
Expand Down Expand Up @@ -99,7 +100,8 @@ export const Form: FC<FormProps> = ({
</div>
)}
</div>
<FormFeedbackCsvErrors formState={state} />
{state.status === 'csv-line-error' && <FormFeedbackCsvLineErrors formState={state} />}
{state.status === 'csv-column-error' && <FormFeedbackCsvColumnErrors formState={state} />}
</form>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { FC } from 'react';
import Alert from '@codegouvfr/react-dsfr/Alert';
import { useFormStatus } from 'react-dom';
import Link from 'next/link';
import { match, P } from 'ts-pattern';

import { FormState } from '@/utils/formAction';

import { FormAlertError } from './FormAlertError';
import { FormAlertError } from '../FormAlertError';

export type FormFeedbackProps = {
formState: FormState;
Expand All @@ -20,8 +21,8 @@ export const FormFeedback: FC<FormFeedbackProps> = ({ formState }) => {
return undefined;
}

switch (formState.status) {
case 'success':
return match(formState)
.with({ status: 'success' }, (formState) => {
if (formState.result) {
const {
result: { successMessage, errors, link },
Expand Down Expand Up @@ -70,20 +71,17 @@ export const FormFeedback: FC<FormFeedbackProps> = ({ formState }) => {
}

return <Alert closable small severity="success" description="L'opération est un succès" />;

case 'rate-limit-error':
case 'domain-error':
})
.with({ status: P.union('rate-limit-error', 'domain-error') }, (formState) => {
return <FormAlertError description={formState.message} />;
})

case 'unknown-error':
return <FormAlertError description="Une erreur est survenue" />;

case 'validation-error':
return (
<FormAlertError description="Erreur lors de la validation des données du formulaire" />
);
.with({ status: 'validation-error' }, () => (
<FormAlertError description="Erreur lors de la validation des données du formulaire" />
))

default:
return null;
}
.with({ status: 'unknown-error' }, () => (
<FormAlertError description="Une erreur est survenue" />
))
.otherwise(() => null);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FC } from 'react';
import Alert from '@codegouvfr/react-dsfr/Alert';
import { fr } from '@codegouvfr/react-dsfr';
import { useFormStatus } from 'react-dom';

import { FormStateCsvColumnError } from '@/utils/formAction';

type FormFeedbackCsvColumnErrorsProps = {
formState: FormStateCsvColumnError;
};

export const FormFeedbackCsvColumnErrors: FC<FormFeedbackCsvColumnErrorsProps> = ({
formState,
}) => {
const { pending } = useFormStatus();

if (pending) {
return undefined;
}

return (
<Alert
small
severity="error"
title={`Des colonnes essentielles sont manquantes dans le fichier :`}
className="mt-6"
description={
<ul className={`list-disc pl-3 my-6 ${fr.cx('fr-accordions-group')}`}>
{formState.errors.map((error, index) => (
<li key={`column-error-${index}`}>{error.column}</li>
))}
</ul>
}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@ import Accordion from '@codegouvfr/react-dsfr/Accordion';
import { fr } from '@codegouvfr/react-dsfr';
import { useFormStatus } from 'react-dom';

import { CsvError } from '@potentiel-libraries/csv';
import { CsvLineError } from '@potentiel-libraries/csv';

import { FormState } from '@/utils/formAction';
import { FormStateCsvLineError } from '@/utils/formAction';

type FormFeedbackCsvErrorsProps = {
formState: FormState;
type FormFeedbackCsvLineErrorsProps = {
formState: FormStateCsvLineError;
};

export const FormFeedbackCsvErrors: FC<FormFeedbackCsvErrorsProps> = ({ formState }) => {
export const FormFeedbackCsvLineErrors: FC<FormFeedbackCsvLineErrorsProps> = ({ formState }) => {
const { pending } = useFormStatus();

if (pending || formState.status !== 'csv-error') {
if (pending) {
return undefined;
}

const regroupedErrors = formState.errors.reduce(
const regroupeCsvLineErrors = formState.errors.reduce(
(acc, error) => {
if (!acc[error.line]) {
acc[error.line] = [];
}
acc[error.line].push(error);
return acc;
},
{} as Record<string, CsvError[]>,
{} as Record<string, CsvLineError[]>,
);

return (
Expand All @@ -38,7 +38,7 @@ export const FormFeedbackCsvErrors: FC<FormFeedbackCsvErrorsProps> = ({ formStat
className="mt-6"
description={
<div className={`list-disc pl-3 my-6 ${fr.cx('fr-accordions-group')}`}>
{Object.entries(regroupedErrors).map(([ligne, erreurs]) => (
{Object.entries(regroupeCsvLineErrors).map(([ligne, erreurs]) => (
<Accordion label={`Ligne ${Number(ligne) + 1}`} defaultExpanded key={ligne}>
<ul className="list-disc pl-3">
{erreurs.map((erreur) => (
Expand Down
Loading