Repository: betagouv/place-asile
Pull request #101: :recycle: Optimize csv import
Author: login: Alezco
Created: 2025-05-07T15:06:54Z | Merged: 2025-05-15T13:59:34Z

12 secondes gagnées sur `yarn prisma:seed`

![i-am-speed-784857043](https://github.com/user-attachments/assets/c682acf0-6162-4414-b752-2ec4b51b225e)



Diff:
diff --git a/prisma/seed.ts b/prisma/seed.ts
index d7a829666..12f02c6b5 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -1,18 +1,19 @@
 import { PrismaClient, Structure } from "@prisma/client";
 import { wipeTables } from "./utils/wipe";
-import {
-  extractAdressesFromCsv,
+import { CsvExtract } from "./utils/csv-extract";
+import { extractActivitesFromOds } from "./utils/activites-extract";
+
+const prisma = new PrismaClient();
+const {
   extractStructuresFromCsv,
+  extractAdressesFromCsv,
   extractContactsFromCsv,
   extractAdresseTypologiesFromCsv,
   extractControlesFromCsv,
   extractEvaluationsFromCsv,
   extractEIGsFromCsv,
   extractStructureTypologiesFromCsv,
-} from "./utils/csv-extract";
-import { extractActivitesFromOds } from "./utils/activites-extract";
-
-const prisma = new PrismaClient();
+} = new CsvExtract();
 
 const convertAddressToCoordinates = async (address: string) => {
   console.log(`> Converting ${address}`);
diff --git a/prisma/utils/csv-extract.ts b/prisma/utils/csv-extract.ts
index 13f53c266..c4327e8a7 100644
--- a/prisma/utils/csv-extract.ts
+++ b/prisma/utils/csv-extract.ts
@@ -14,57 +14,6 @@ import {
   StructureTypologie,
 } from "@prisma/client";
 
-const convertToBoolean = (value: string): boolean => {
-  return value === "Oui";
-};
-
-const convertToRepartition = (repartition: string): Repartition => {
-  const repartitions: Record<string, Repartition> = {
-    Diffus: Repartition.DIFFUS,
-    Collectif: Repartition.COLLECTIF,
-    Mixte: Repartition.MIXTE,
-  };
-  return repartitions[repartition.trim()];
-};
-
-const convertToPublicType = (typePublic: string): PublicType => {
-  const typesPublic: Record<string, PublicType> = {
-    "tout public": PublicType.TOUT_PUBLIC,
-    famille: PublicType.FAMILLE,
-    "personnes isolées": PublicType.PERSONNES_ISOLEES,
-  };
-  return typesPublic[typePublic.trim().toLowerCase()];
-};
-
-const convertToStructureType = (structureType: string): StructureType => {
-  const typesStructures: Record<string, StructureType> = {
-    CADA: StructureType.CADA,
-    HUDA: StructureType.HUDA,
-    CPH: StructureType.CPH,
-    CAES: StructureType.CAES,
-    PRAHDA: StructureType.PRAHDA,
-  };
-  return typesStructures[structureType.trim()];
-};
-
-const convertToControleType = (controleType: string): ControleType => {
-  const typesControles: Record<string, ControleType> = {
-    Inopiné: ControleType.INOPINE,
-    Programmé: ControleType.PROGRAMME,
-  };
-  return typesControles[controleType.trim()];
-};
-
-const getSheet = (sheetIndex: number) => {
-  const sheets = xlsx.parse(`${__dirname}/../data/DB.xlsx`, {
-    cellDates: true,
-    blankrows: false,
-  });
-  const sheet = sheets[sheetIndex].data;
-  sheet.shift();
-  return sheet;
-};
-
 const STRUCTURE_INDEX = 0;
 const ADRESSE_INDEX = 1;
 const CONTACT_INDEX = 2;
@@ -74,124 +23,181 @@ const EIG_INDEX = 5;
 const STRUCTURE_TYPOLOGIE_INDEX = 6;
 const ADRESSE_TYPOLOGIE_INDEX = 7;
 
-export const extractStructuresFromCsv = (): Omit<
-  Structure,
-  "id" | "latitude" | "longitude"
->[] => {
-  const sheet = getSheet(STRUCTURE_INDEX);
-  return sheet.map((line) => ({
-    dnaCode: line[0],
-    operateur: line[1],
-    type: convertToStructureType(line[2]),
-    nbPlaces: Number(line[3]),
-    adresseAdministrative: line[4].trim(),
-    communeAdministrative: line[5].trim(),
-    codePostalAdministratif: String(line[6]),
-    departementAdministratif: line[7].trim(),
-    nom: line[8],
-    debutConvention: line[9] ? new Date(line[9]) : null,
-    finConvention: line[10] ? new Date(line[10]) : null,
-    cpom: convertToBoolean(line[11]),
-    creationDate: new Date(line[12]),
-    finessCode: line[13] ? String(line[13]) : null,
-    lgbt: convertToBoolean(line[14]),
-    fvvTeh: convertToBoolean(line[15]),
-    public: convertToPublicType(line[16]),
-    debutPeriodeAutorisation: line[17] ? new Date(line[17]) : null,
-    finPeriodeAutorisation: line[18] ? new Date(line[18]) : null,
-    debutCpom: line[19] ? new Date(line[19]) : null,
-    finCpom: line[20] ? new Date(line[20]) : null,
-    placesACreer: Number(line[21]),
-    placesAFermer: Number(line[22]),
-    echeancePlacesACreer: line[23] ? new Date(line[23]) : null,
-    echeancePlacesAFermer: line[24] ? new Date(line[24]) : null,
-    notes: line[25],
-  }));
-};
-
-export const extractAdressesFromCsv = (): Omit<Adresse, "id">[] => {
-  const sheet = getSheet(ADRESSE_INDEX);
-  return sheet.map((line) => ({
-    id: line[0],
-    structureDnaCode: line[1],
-    adresse: line[2],
-    codePostal: String(line[3]),
-    commune: line[4],
-    repartition: convertToRepartition(line[5]),
-  }));
-};
-
-export const extractContactsFromCsv = (): Omit<Contact, "id">[] => {
-  const sheet = getSheet(CONTACT_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    prenom: line[1],
-    nom: line[2],
-    telephone: String(line[3]),
-    email: line[4],
-    role: line[5],
-  }));
-};
-
-export const extractControlesFromCsv = (): Omit<Controle, "id">[] => {
-  const sheet = getSheet(CONTROLE_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    type: convertToControleType(line[2]),
-  }));
-};
-
-export const extractEvaluationsFromCsv = (): Omit<Evaluation, "id">[] => {
-  const sheet = getSheet(EVALUATION_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    notePersonne: Number(line[2]),
-    notePro: Number(line[3]),
-    noteStructure: Number(line[4]),
-    note: Number(line[5]),
-  }));
-};
-
-export const extractEIGsFromCsv = (): Omit<
-  EvenementIndesirableGrave,
-  "id"
->[] => {
-  const sheet = getSheet(EIG_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    numeroDossier: String(line[1]),
-    evenementDate: new Date(line[2]),
-    declarationDate: new Date(line[3]),
-    type: line[4],
-  }));
-};
-
-export const extractStructureTypologiesFromCsv = (): Omit<
-  StructureTypologie,
-  "id"
->[] => {
-  const sheet = getSheet(STRUCTURE_TYPOLOGIE_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    pmr: Number(line[2]),
-    lgbt: Number(line[3]),
-    fvvTeh: Number(line[4]),
-  }));
-};
-
-export const extractAdresseTypologiesFromCsv = (): Omit<
-  AdresseTypologie,
-  "id"
->[] => {
-  const sheet = getSheet(ADRESSE_TYPOLOGIE_INDEX);
-  return sheet.map((line) => ({
-    adresseId: line[0],
-    date: new Date(line[2]),
-    nbPlacesTotal: line[3],
-    qpv: line[4],
-    logementSocial: line[5],
-  }));
-};
+export class CsvExtract {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  private sheets: { name: string; data: any[][] }[] = [];
+
+  constructor() {
+    const sheets = xlsx.parse(`${__dirname}/../data/DB.xlsx`, {
+      cellDates: true,
+      blankrows: false,
+    });
+    this.sheets = sheets;
+  }
+
+  private convertToBoolean = (value: string): boolean => {
+    return value === "Oui";
+  };
+
+  private convertToRepartition = (repartition: string): Repartition => {
+    const repartitions: Record<string, Repartition> = {
+      Diffus: Repartition.DIFFUS,
+      Collectif: Repartition.COLLECTIF,
+      Mixte: Repartition.MIXTE,
+    };
+    return repartitions[repartition.trim()];
+  };
+
+  private convertToPublicType = (typePublic: string): PublicType => {
+    const typesPublic: Record<string, PublicType> = {
+      "tout public": PublicType.TOUT_PUBLIC,
+      famille: PublicType.FAMILLE,
+      "personnes isolées": PublicType.PERSONNES_ISOLEES,
+    };
+    return typesPublic[typePublic.trim().toLowerCase()];
+  };
+
+  private convertToStructureType = (structureType: string): StructureType => {
+    const typesStructures: Record<string, StructureType> = {
+      CADA: StructureType.CADA,
+      HUDA: StructureType.HUDA,
+      CPH: StructureType.CPH,
+      CAES: StructureType.CAES,
+      PRAHDA: StructureType.PRAHDA,
+    };
+    return typesStructures[structureType.trim()];
+  };
+
+  private convertToControleType = (controleType: string): ControleType => {
+    const typesControles: Record<string, ControleType> = {
+      Inopiné: ControleType.INOPINE,
+      Programmé: ControleType.PROGRAMME,
+    };
+    return typesControles[controleType.trim()];
+  };
+
+  private getSheet = (sheetIndex: number) => {
+    const sheet = this.sheets[sheetIndex].data;
+    sheet.shift();
+    return sheet;
+  };
+
+  public extractStructuresFromCsv = (): Omit<
+    Structure,
+    "id" | "latitude" | "longitude"
+  >[] => {
+    const sheet = this.getSheet(STRUCTURE_INDEX);
+    return sheet.map((line) => ({
+      dnaCode: line[0],
+      operateur: line[1],
+      type: this.convertToStructureType(line[2]),
+      nbPlaces: Number(line[3]),
+      adresseAdministrative: line[4].trim(),
+      communeAdministrative: line[5].trim(),
+      codePostalAdministratif: String(line[6]),
+      departementAdministratif: line[7].trim(),
+      nom: line[8],
+      debutConvention: line[9] ? new Date(line[9]) : null,
+      finConvention: line[10] ? new Date(line[10]) : null,
+      cpom: this.convertToBoolean(line[11]),
+      creationDate: new Date(line[12]),
+      finessCode: line[13] ? String(line[13]) : null,
+      lgbt: this.convertToBoolean(line[14]),
+      fvvTeh: this.convertToBoolean(line[15]),
+      public: this.convertToPublicType(line[16]),
+      debutPeriodeAutorisation: line[17] ? new Date(line[17]) : null,
+      finPeriodeAutorisation: line[18] ? new Date(line[18]) : null,
+      debutCpom: line[19] ? new Date(line[19]) : null,
+      finCpom: line[20] ? new Date(line[20]) : null,
+      placesACreer: Number(line[21]),
+      placesAFermer: Number(line[22]),
+      echeancePlacesACreer: line[23] ? new Date(line[23]) : null,
+      echeancePlacesAFermer: line[24] ? new Date(line[24]) : null,
+      notes: line[25],
+    }));
+  };
+
+  public extractAdressesFromCsv = (): Omit<Adresse, "id">[] => {
+    const sheet = this.getSheet(ADRESSE_INDEX);
+    return sheet.map((line) => ({
+      id: line[0],
+      structureDnaCode: line[1],
+      adresse: line[2],
+      codePostal: String(line[3]),
+      commune: line[4],
+      repartition: this.convertToRepartition(line[5]),
+    }));
+  };
+
+  public extractContactsFromCsv = (): Omit<Contact, "id">[] => {
+    const sheet = this.getSheet(CONTACT_INDEX);
+    return sheet.map((line) => ({
+      structureDnaCode: line[0],
+      prenom: line[1],
+      nom: line[2],
+      telephone: String(line[3]),
+      email: line[4],
+      role: line[5],
+    }));
+  };
+
+  public extractControlesFromCsv = (): Omit<Controle, "id">[] => {
+    const sheet = this.getSheet(CONTROLE_INDEX);
+    return sheet.map((line) => ({
+      structureDnaCode: line[0],
+      date: new Date(line[1]),
+      type: this.convertToControleType(line[2]),
+    }));
+  };
+
+  public extractEvaluationsFromCsv = (): Omit<Evaluation, "id">[] => {
+    const sheet = this.getSheet(EVALUATION_INDEX);
+    return sheet.map((line) => ({
+      structureDnaCode: line[0],
+      date: new Date(line[1]),
+      notePersonne: Number(line[2]),
+      notePro: Number(line[3]),
+      noteStructure: Number(line[4]),
+      note: Number(line[5]),
+    }));
+  };
+
+  public extractEIGsFromCsv = (): Omit<EvenementIndesirableGrave, "id">[] => {
+    const sheet = this.getSheet(EIG_INDEX);
+    return sheet.map((line) => ({
+      structureDnaCode: line[0],
+      numeroDossier: String(line[1]),
+      evenementDate: new Date(line[2]),
+      declarationDate: new Date(line[3]),
+      type: line[4],
+    }));
+  };
+
+  public extractStructureTypologiesFromCsv = (): Omit<
+    StructureTypologie,
+    "id"
+  >[] => {
+    const sheet = this.getSheet(STRUCTURE_TYPOLOGIE_INDEX);
+    return sheet.map((line) => ({
+      structureDnaCode: line[0],
+      date: new Date(line[1]),
+      pmr: Number(line[2]),
+      lgbt: Number(line[3]),
+      fvvTeh: Number(line[4]),
+    }));
+  };
+
+  public extractAdresseTypologiesFromCsv = (): Omit<
+    AdresseTypologie,
+    "id"
+  >[] => {
+    const sheet = this.getSheet(ADRESSE_TYPOLOGIE_INDEX);
+    return sheet.map((line) => ({
+      adresseId: line[0],
+      date: new Date(line[2]),
+      nbPlacesTotal: line[3],
+      qpv: line[4],
+      logementSocial: line[5],
+    }));
+  };
+}


Reviews:
user: login: ledjay
body: LGTM, mais c'est quel partie qui a eu un impact sur le temps de traitement ?
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T12:45:32Z
user: login: Alezco
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T13:46:42Z

Review comments:
diff_hunk: @@ -74,124 +23,181 @@ const EIG_INDEX = 5;
 const STRUCTURE_TYPOLOGIE_INDEX = 6;
 const ADRESSE_TYPOLOGIE_INDEX = 7;
 
-export const extractStructuresFromCsv = (): Omit<
-  Structure,
-  "id" | "latitude" | "longitude"
->[] => {
-  const sheet = getSheet(STRUCTURE_INDEX);
-  return sheet.map((line) => ({
-    dnaCode: line[0],
-    operateur: line[1],
-    type: convertToStructureType(line[2]),
-    nbPlaces: Number(line[3]),
-    adresseAdministrative: line[4].trim(),
-    communeAdministrative: line[5].trim(),
-    codePostalAdministratif: String(line[6]),
-    departementAdministratif: line[7].trim(),
-    nom: line[8],
-    debutConvention: line[9] ? new Date(line[9]) : null,
-    finConvention: line[10] ? new Date(line[10]) : null,
-    cpom: convertToBoolean(line[11]),
-    creationDate: new Date(line[12]),
-    finessCode: line[13] ? String(line[13]) : null,
-    lgbt: convertToBoolean(line[14]),
-    fvvTeh: convertToBoolean(line[15]),
-    public: convertToPublicType(line[16]),
-    debutPeriodeAutorisation: line[17] ? new Date(line[17]) : null,
-    finPeriodeAutorisation: line[18] ? new Date(line[18]) : null,
-    debutCpom: line[19] ? new Date(line[19]) : null,
-    finCpom: line[20] ? new Date(line[20]) : null,
-    placesACreer: Number(line[21]),
-    placesAFermer: Number(line[22]),
-    echeancePlacesACreer: line[23] ? new Date(line[23]) : null,
-    echeancePlacesAFermer: line[24] ? new Date(line[24]) : null,
-    notes: line[25],
-  }));
-};
-
-export const extractAdressesFromCsv = (): Omit<Adresse, "id">[] => {
-  const sheet = getSheet(ADRESSE_INDEX);
-  return sheet.map((line) => ({
-    id: line[0],
-    structureDnaCode: line[1],
-    adresse: line[2],
-    codePostal: String(line[3]),
-    commune: line[4],
-    repartition: convertToRepartition(line[5]),
-  }));
-};
-
-export const extractContactsFromCsv = (): Omit<Contact, "id">[] => {
-  const sheet = getSheet(CONTACT_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    prenom: line[1],
-    nom: line[2],
-    telephone: String(line[3]),
-    email: line[4],
-    role: line[5],
-  }));
-};
-
-export const extractControlesFromCsv = (): Omit<Controle, "id">[] => {
-  const sheet = getSheet(CONTROLE_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    type: convertToControleType(line[2]),
-  }));
-};
-
-export const extractEvaluationsFromCsv = (): Omit<Evaluation, "id">[] => {
-  const sheet = getSheet(EVALUATION_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    notePersonne: Number(line[2]),
-    notePro: Number(line[3]),
-    noteStructure: Number(line[4]),
-    note: Number(line[5]),
-  }));
-};
-
-export const extractEIGsFromCsv = (): Omit<
-  EvenementIndesirableGrave,
-  "id"
->[] => {
-  const sheet = getSheet(EIG_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    numeroDossier: String(line[1]),
-    evenementDate: new Date(line[2]),
-    declarationDate: new Date(line[3]),
-    type: line[4],
-  }));
-};
-
-export const extractStructureTypologiesFromCsv = (): Omit<
-  StructureTypologie,
-  "id"
->[] => {
-  const sheet = getSheet(STRUCTURE_TYPOLOGIE_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    pmr: Number(line[2]),
-    lgbt: Number(line[3]),
-    fvvTeh: Number(line[4]),
-  }));
-};
-
-export const extractAdresseTypologiesFromCsv = (): Omit<
-  AdresseTypologie,
-  "id"
->[] => {
-  const sheet = getSheet(ADRESSE_TYPOLOGIE_INDEX);
-  return sheet.map((line) => ({
-    adresseId: line[0],
-    date: new Date(line[2]),
-    nbPlacesTotal: line[3],
-    qpv: line[4],
-    logementSocial: line[5],
-  }));
-};
+export class CsvExtract {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
path: prisma/utils/csv-extract.ts
user: login: ledjay
body: hannnnnnn le any ! :p 
created_at: 2025-05-15T12:41:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -74,124 +23,181 @@ const EIG_INDEX = 5;
 const STRUCTURE_TYPOLOGIE_INDEX = 6;
 const ADRESSE_TYPOLOGIE_INDEX = 7;
 
-export const extractStructuresFromCsv = (): Omit<
-  Structure,
-  "id" | "latitude" | "longitude"
->[] => {
-  const sheet = getSheet(STRUCTURE_INDEX);
-  return sheet.map((line) => ({
-    dnaCode: line[0],
-    operateur: line[1],
-    type: convertToStructureType(line[2]),
-    nbPlaces: Number(line[3]),
-    adresseAdministrative: line[4].trim(),
-    communeAdministrative: line[5].trim(),
-    codePostalAdministratif: String(line[6]),
-    departementAdministratif: line[7].trim(),
-    nom: line[8],
-    debutConvention: line[9] ? new Date(line[9]) : null,
-    finConvention: line[10] ? new Date(line[10]) : null,
-    cpom: convertToBoolean(line[11]),
-    creationDate: new Date(line[12]),
-    finessCode: line[13] ? String(line[13]) : null,
-    lgbt: convertToBoolean(line[14]),
-    fvvTeh: convertToBoolean(line[15]),
-    public: convertToPublicType(line[16]),
-    debutPeriodeAutorisation: line[17] ? new Date(line[17]) : null,
-    finPeriodeAutorisation: line[18] ? new Date(line[18]) : null,
-    debutCpom: line[19] ? new Date(line[19]) : null,
-    finCpom: line[20] ? new Date(line[20]) : null,
-    placesACreer: Number(line[21]),
-    placesAFermer: Number(line[22]),
-    echeancePlacesACreer: line[23] ? new Date(line[23]) : null,
-    echeancePlacesAFermer: line[24] ? new Date(line[24]) : null,
-    notes: line[25],
-  }));
-};
-
-export const extractAdressesFromCsv = (): Omit<Adresse, "id">[] => {
-  const sheet = getSheet(ADRESSE_INDEX);
-  return sheet.map((line) => ({
-    id: line[0],
-    structureDnaCode: line[1],
-    adresse: line[2],
-    codePostal: String(line[3]),
-    commune: line[4],
-    repartition: convertToRepartition(line[5]),
-  }));
-};
-
-export const extractContactsFromCsv = (): Omit<Contact, "id">[] => {
-  const sheet = getSheet(CONTACT_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    prenom: line[1],
-    nom: line[2],
-    telephone: String(line[3]),
-    email: line[4],
-    role: line[5],
-  }));
-};
-
-export const extractControlesFromCsv = (): Omit<Controle, "id">[] => {
-  const sheet = getSheet(CONTROLE_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    type: convertToControleType(line[2]),
-  }));
-};
-
-export const extractEvaluationsFromCsv = (): Omit<Evaluation, "id">[] => {
-  const sheet = getSheet(EVALUATION_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    notePersonne: Number(line[2]),
-    notePro: Number(line[3]),
-    noteStructure: Number(line[4]),
-    note: Number(line[5]),
-  }));
-};
-
-export const extractEIGsFromCsv = (): Omit<
-  EvenementIndesirableGrave,
-  "id"
->[] => {
-  const sheet = getSheet(EIG_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    numeroDossier: String(line[1]),
-    evenementDate: new Date(line[2]),
-    declarationDate: new Date(line[3]),
-    type: line[4],
-  }));
-};
-
-export const extractStructureTypologiesFromCsv = (): Omit<
-  StructureTypologie,
-  "id"
->[] => {
-  const sheet = getSheet(STRUCTURE_TYPOLOGIE_INDEX);
-  return sheet.map((line) => ({
-    structureDnaCode: line[0],
-    date: new Date(line[1]),
-    pmr: Number(line[2]),
-    lgbt: Number(line[3]),
-    fvvTeh: Number(line[4]),
-  }));
-};
-
-export const extractAdresseTypologiesFromCsv = (): Omit<
-  AdresseTypologie,
-  "id"
->[] => {
-  const sheet = getSheet(ADRESSE_TYPOLOGIE_INDEX);
-  return sheet.map((line) => ({
-    adresseId: line[0],
-    date: new Date(line[2]),
-    nbPlacesTotal: line[3],
-    qpv: line[4],
-    logementSocial: line[5],
-  }));
-};
+export class CsvExtract {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
path: prisma/utils/csv-extract.ts
user: login: Alezco
body: Compliqué de faire sans dans ce cas de figure ^^ 
created_at: 2025-05-15T13:46:42Z
author_association: COLLABORATOR

PR comments:
user: login: Alezco
created_at: 2025-05-07T15:08:55Z
body: Closes https://github.com/betagouv/place-asile/issues/83
author_association: COLLABORATOR
user: login: Alezco
created_at: 2025-05-15T13:47:40Z
body: @ledjay Au lieu d'être exécutée pour chaque feuillet, l'ouverture du fichier xlsx n'est faite qu'une seule fois dans le constructeur de la classe CsvExtract. Puis, chaque fonction pioche dans le document, qui est stocké en RAM
author_association: COLLABORATOR