Skip to content

Commit

Permalink
feat(indicateurs): possibilite de telechargement du modele, correctio…
Browse files Browse the repository at this point in the history
…n d'un bug de telechargement de l'excel
  • Loading branch information
dthib committed Aug 21, 2024
1 parent 41412cc commit 915e288
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 29 deletions.
1 change: 1 addition & 0 deletions backend/.env.default
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
TRAJECTOIRE_SNBC_SHEET_ID=
TRAJECTOIRE_SNBC_XLSX_ID=
TRAJECTOIRE_SNBC_RESULT_FOLDER_ID=
GCLOUD_SERVICE_ACCOUNT_KEY=
3 changes: 2 additions & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ $ npm install
Les variables d'environnement suivantes doivent être définies dans un fichier .env (voir le fichier [.env.default](.env.default)):

- **TRAJECTOIRE_SNBC_SHEET_ID**: Identifiant du Google spreadsheet utilisé pour le calcul de la trajectoire.
- **TRAJECTOIRE_SNBC_XLSX_ID**: Identifiant du Xlsx original utilisé pour le calcul de la trajectoire (stocké sur le drive)
- **TRAJECTOIRE_SNBC_RESULT_FOLDER_ID**: Identifiant du dossier Google Drive dans lequel les spreadsheets des trajectoires calculées doivent être sauvés (un fichier par EPCI). A noter qu'il existe **un dossier par environnement** (dev, preprod, prod).
- **GCLOUD_SERVICE_ACCOUNT_KEY**: contenu au format json du fichier de clé de compte de service permettant l'utilisation des api Google Drive et Google Spreadsheet.

Ces variables d'environnement sont définies:

- pour **TRAJECTOIRE_SNBC_SHEET_ID** et **TRAJECTOIRE_SNBC_RESULT_FOLDER_ID** dans les [variables d'environnement de Github](https://github.com/incubateur-ademe/territoires-en-transitions/settings/environments/1431973268/edit) utilisées pour configurer le [déploiement Koyeb](https://app.koyeb.com/services/c7001069-ca11-4fd7-86c6-7feb45b9b68d/settings). Les identifiants peuvent également être récupérés à partir du drive de `territoiresentransitions`.
- pour **TRAJECTOIRE_SNBC_SHEET_ID**, **TRAJECTOIRE_SNBC_XLSX_ID** et **TRAJECTOIRE_SNBC_RESULT_FOLDER_ID** dans les [variables d'environnement de Github](https://github.com/incubateur-ademe/territoires-en-transitions/settings/environments/1431973268/edit) utilisées pour configurer le [déploiement Koyeb](https://app.koyeb.com/services/c7001069-ca11-4fd7-86c6-7feb45b9b68d/settings). Les identifiants peuvent également être récupérés à partir du drive de `territoiresentransitions`.
- pour **GCLOUD_SERVICE_ACCOUNT_KEY** dans le [gestionnaire de secret de Koyeb](https://app.koyeb.com/secrets)

## Scripts disponibles
Expand Down
21 changes: 21 additions & 0 deletions backend/src/common/services/gcloud.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Logger } from '@nestjs/common';
import * as fs from 'fs';

const logger = new Logger('gcloud.helper');

export const initApplicationCredentials = () => {
if (
process.env.GCLOUD_SERVICE_ACCOUNT_KEY &&
!process.env.GOOGLE_APPLICATION_CREDENTIALS
) {
const serviceAccountFile = `${__dirname}/keyfile.json`;
logger.log(
`Writing Google Cloud credentials to file: ${serviceAccountFile}`,
);
fs.writeFileSync(
serviceAccountFile,
process.env.GCLOUD_SERVICE_ACCOUNT_KEY,
);
process.env.GOOGLE_APPLICATION_CREDENTIALS = serviceAccountFile;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export class TrajectoiresController {
return response;
}

@Get('snbc/modele')
downloadModeleSnbc(@Res() res: Response, @Next() next: NextFunction) {
this.logger.log(`Téléchargement du modele de trajectoire SNBC`);
this.trajectoiresService.downloadModeleTrajectoireSnbc(res, next);
}

@Get('snbc/telechargement')
downloadDataSnbc(
@Query() request: CollectiviteRequest,
Expand Down
88 changes: 70 additions & 18 deletions backend/src/indicateurs/service/trajectoires.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,63 @@ export default class TrajectoiresService {
private readonly sheetService: SheetService,
) {}

getIdentifiantSpreadsheetCalcul() {
return process.env.TRAJECTOIRE_SNBC_SHEET_ID!;
}

getIdentifiantXlsxCalcul() {
return process.env.TRAJECTOIRE_SNBC_XLSX_ID!;
}

getIdentifiantDossierResultat() {
return process.env.TRAJECTOIRE_SNBC_RESULT_FOLDER_ID;
return process.env.TRAJECTOIRE_SNBC_RESULT_FOLDER_ID!;
}

getNomFichierTrajectoire(epci: EpciType) {
return `Trajectoire SNBC - ${epci.siren} - ${epci.nom}`;
}

async downloadModeleTrajectoireSnbc(res: Response, next: NextFunction) {
try {
if (!this.getIdentifiantXlsxCalcul()) {
throw new InternalServerErrorException(
"L'identifiant du Xlsx pour le calcul des trajectoires SNBC est manquant",
);
}

const xlsxBuffer = await this.sheetService.getFileData(
this.getIdentifiantXlsxCalcul(),
);
const nomFichier = await this.sheetService.getFileName(
this.getIdentifiantXlsxCalcul(),
);
// Set the output file name.
res.attachment(nomFichier);

// Send the workbook.
res.send(xlsxBuffer);
} catch (error) {
next(error);
}
}

async downloadTrajectoireSnbc(
request: CalculTrajectoireRequest,
res: Response,
next: NextFunction,
) {
try {
const resultatVerification = await this.verificationDonneesSnbc(request);
if (!this.getIdentifiantXlsxCalcul()) {
throw new InternalServerErrorException(
"L'identifiant du Xlsx pour le calcul des trajectoires SNBC est manquant",
);
}

const resultatVerification = await this.verificationDonneesSnbc(
request,
undefined,
true,
);

if (
resultatVerification.status ===
Expand Down Expand Up @@ -166,7 +208,7 @@ export default class TrajectoiresService {
const nomFichier = this.getNomFichierTrajectoire(epci);

const xlsxBuffer = await this.sheetService.getFileData(
'14dZbAf8yRqhfqKNnvXUTMgTT34dqkc32',
this.getIdentifiantXlsxCalcul(),
);

// Utilisation de xlsx-template car:
Expand Down Expand Up @@ -244,6 +286,18 @@ export default class TrajectoiresService {
let mode: CalculTrajectoireResultatMode =
CalculTrajectoireResultatMode.NOUVEAU_SPREADSHEET;

if (!this.getIdentifiantSpreadsheetCalcul()) {
throw new InternalServerErrorException(
"L'identifiant de la feuille de calcul pour les trajectoires SNBC est manquante",
);
}

if (!this.getIdentifiantDossierResultat()) {
throw new InternalServerErrorException(
"L'identifiant du dossier pour le stockage des trajectoires SNBC calculées est manquant",
);
}

// Création de la source métadonnée SNBC si elle n'existe pas
let indicateurSourceMetadonnee =
await this.indicateurSourcesService.getIndicateurSourceMetadonnee(
Expand Down Expand Up @@ -348,16 +402,10 @@ export default class TrajectoiresService {
}
epci = resultatVerification.epci;

if (!process.env.TRAJECTOIRE_SNBC_SHEET_ID) {
throw new InternalServerErrorException(
"L'identifiant de la feuille de calcul pour les trajectoires SNBC est manquante",
);
}

const nomFichier = this.getNomFichierTrajectoire(epci);
let trajectoireCalculSheetId = await this.sheetService.getFileIdByName(
nomFichier,
process.env.TRAJECTOIRE_SNBC_RESULT_FOLDER_ID,
this.getIdentifiantDossierResultat(),
);
if (
trajectoireCalculSheetId &&
Expand All @@ -375,14 +423,12 @@ export default class TrajectoiresService {
await this.sheetService.deleteFile(trajectoireCalculSheetId);
}
trajectoireCalculSheetId = await this.sheetService.copyFile(
process.env.TRAJECTOIRE_SNBC_SHEET_ID,
this.getIdentifiantSpreadsheetCalcul(),
nomFichier,
process.env.TRAJECTOIRE_SNBC_RESULT_FOLDER_ID
? [process.env.TRAJECTOIRE_SNBC_RESULT_FOLDER_ID]
: undefined,
[this.getIdentifiantDossierResultat()],
);
this.logger.log(
`Fichier de trajectoire SNBC créé à partir du master ${process.env.TRAJECTOIRE_SNBC_SHEET_ID} avec l'identifiant ${trajectoireCalculSheetId}`,
`Fichier de trajectoire SNBC créé à partir du master ${this.getIdentifiantSpreadsheetCalcul()} avec l'identifiant ${trajectoireCalculSheetId}`,
);
}

Expand Down Expand Up @@ -643,9 +689,9 @@ export default class TrajectoiresService {
identifiantIndicateurValeur2015.indicateur_valeur.resultat !==
undefined // 0 est une valeur valide
) {
/*console.log(
`${identifiant}: ${indicateurValeur.indicateur_valeur.resultat} ${indicateurValeur.indicateur_definition?.unite}`,
);*/
console.log(
`${identifiant}: ${identifiantIndicateurValeur2015.indicateur_valeur.resultat} ${identifiantIndicateurValeur2015.indicateur_definition?.unite}`,
);

// Si il n'y a pas déjà eu une valeur manquante qui a placé la valeur à null
if (valeurARemplir.valeur !== null) {
Expand All @@ -669,6 +715,12 @@ export default class TrajectoiresService {
}
}
} else {
identifiantIndicateurValeurs.forEach((v) => {
console.log(
`${identifiant}: ${v.indicateur_valeur.resultat} ${v.indicateur_definition?.unite} (${v.indicateur_valeur.date_valeur})`,
);
});

const interpolationResultat = this.getInterpolationValeur(
identifiantIndicateurValeurs.map((v) => v.indicateur_valeur),
);
Expand Down
12 changes: 2 additions & 10 deletions backend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
} from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import * as Sentry from '@sentry/nestjs';
import * as fs from 'fs';
import { AppModule } from './app.module';
import { initApplicationCredentials } from './common/services/gcloud.helper';
import './common/services/sentry.service';
import { SENTRY_DSN } from './common/services/sentry.service';
import { TrpcRouter } from './trpc.router';
Expand All @@ -18,15 +18,7 @@ const port = process.env.PORT || 8080;
logger.log(`Launching NestJS app on port ${port}`);

async function bootstrap() {
if (process.env.GCLOUD_SERVICE_ACCOUNT_KEY) {
const serviceAccountFile = `${__dirname}/keyfile.json`;
logger.log('Writing Google Cloud credentials to file:', serviceAccountFile);
fs.writeFileSync(
serviceAccountFile,
process.env.GCLOUD_SERVICE_ACCOUNT_KEY,
);
process.env.GOOGLE_APPLICATION_CREDENTIALS = serviceAccountFile;
}
initApplicationCredentials();

const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
Expand Down
13 changes: 13 additions & 0 deletions backend/src/spreadsheets/services/sheet.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Response } from 'express';
import * as gaxios from 'gaxios';
import * as auth from 'google-auth-library';
import { drive_v3, google, sheets_v4 } from 'googleapis';
import { initApplicationCredentials } from '../../common/services/gcloud.helper';
import {
SheetValueInputOption,
SheetValueRenderOption,
Expand Down Expand Up @@ -35,6 +36,7 @@ export default class SheetService {
| auth.Impersonated
> {
if (!this.authClient) {
initApplicationCredentials();
this.authClient = await google.auth.getClient({
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
Expand Down Expand Up @@ -136,6 +138,17 @@ export default class SheetService {
this.logger.log(`Spreadsheet ${fileId} correctement supprimé.`);
}

async getFileName(fileId: string): Promise<string> {
const authClient = await this.getAuthClient();
const getOptions: drive_v3.Params$Resource$Files$Get = {
auth: authClient,
fileId: fileId,
fields: 'name',
};
const res = await drive.files.get(getOptions);
return res.data.name!;
}

async getFileData(fileId: string): Promise<Buffer> {
const authClient = await this.getAuthClient();
const getOptions: drive_v3.Params$Resource$Files$Get = {
Expand Down

0 comments on commit 915e288

Please sign in to comment.