Skip to content

Commit

Permalink
fixed progression + auto update progression on tableau de bord (#809)
Browse files Browse the repository at this point in the history
  • Loading branch information
Numero7 authored Feb 13, 2025
2 parents f7ea9ac + 79335ec commit 68b98b5
Show file tree
Hide file tree
Showing 18 changed files with 90 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,20 @@ sealed class ProfilEleve(open val id: String) : ProfilUtilisateur() {
voeuxFavoris = emptyList(),
)

fun estProfilComplet(): Boolean {
return completionProfil() >= 100
fun estProfilComplet(specialitesSelectionnablesParCandidat: List<String>?): Boolean {
return completionProfil(specialitesSelectionnablesParCandidat) >= 100
}

private fun completionProfil(): Int {
private fun completionProfil(specialitesSelectionnablesParCandidat: List<String>?): Int {
var result = 1
if (classe != null) result = result.inc()
if (situation != null) result = result.inc()
if (!baccalaureat.isNullOrEmpty()) result = result.inc()
if (!specialites.isNullOrEmpty()) result = result.inc()
if (!baccalaureat.isNullOrEmpty()) {
result = result.inc()
}
if (specialitesSelectionnablesParCandidat.isNullOrEmpty() || !this.specialites.isNullOrEmpty()) {
result = result.inc()
}
if (!domainesInterets.isNullOrEmpty()) result = result.inc()
if (!centresInterets.isNullOrEmpty()) result = result.inc()
if (dureeEtudesPrevue != null) result = result.inc()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ class ProfilEleveController(
fun getProfilEleve(): ProfilDTO {
val profil = recupererEleveAvecProfilExistant()
val voeuxFavoris = recupererAssociationFormationsVoeuxService.recupererVoeuxFavoris(profil)
val progression =
recupererProgressionService.recupererProgression(
profil,
)
return ProfilDTO(profil, voeuxFavoris, progression)
return ProfilDTO(profil, voeuxFavoris)
}

@PostMapping("/parcoursup")
Expand All @@ -78,7 +74,7 @@ class ProfilEleveController(
@GetMapping("/progression")
@Operation(
summary = "Récupérer le niveau de progression pédagogique",
description = "Récupère le niveau de progression pédagogique, entre 1 et 6",
description = "Récupère le niveau de progression pédagogique, entre 0 et 6",
)
fun getProgressionMPS(): ProgressionDTO {
val profil = recupererEleveAvecProfilExistant()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,8 @@ data class ProfilDTO(
@ArraySchema(arraySchema = Schema(description = "Liste des voeux favoris"))
@JsonProperty("voeuxFavoris")
val voeuxFavoris: List<VoeuFavoriDTO>? = null,
@Schema(
description = "Progression dans les 6 niveaux MPS",
example = "1",
allowableValues = ["1", "2", "3", "4", "5", "6"],
)
@JsonProperty("progression")
val progression: Int? = null,
) {
constructor(profilEleve: ProfilEleve.AvecProfilExistant, voeuxFavoris: List<VoeuFavori>? = null, progression: Int? = null) : this(
constructor(profilEleve: ProfilEleve.AvecProfilExistant, voeuxFavoris: List<VoeuFavori>? = null) : this(
situation = profilEleve.situation,
classe = profilEleve.classe,
baccalaureat = profilEleve.baccalaureat,
Expand All @@ -125,7 +118,6 @@ data class ProfilDTO(
corbeilleFormations = profilEleve.corbeilleFormations,
compteParcoursupAssocie = profilEleve.compteParcoursupLie,
voeuxFavoris = (voeuxFavoris ?: profilEleve.voeuxFavoris).map { VoeuFavoriDTO(it) },
progression = progression,
)

data class CommuneDTO(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ data class ProgressionDTO(
@Schema(
description = "Progression dans les six niveaux MPS",
example = "6",
allowableValues = ["1", "2", "3", "4", "5", "6"],
allowableValues = ["0", "1", "2", "3", "4", "5", "6"],
)
val progression: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package fr.gouv.monprojetsup.eleve.usecase

import fr.gouv.monprojetsup.authentification.domain.entity.ProfilEleve
import fr.gouv.monprojetsup.eleve.domain.port.TraceRepository
import fr.gouv.monprojetsup.referentiel.infrastructure.repository.BaccalaureatSpecialiteBDDRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class RecupererProgressionService(
private val traceRepository: TraceRepository,
private val baccalaureatSpecialiteBDDRepository: BaccalaureatSpecialiteBDDRepository,
) {
@Transactional(readOnly = true)
fun recupererProgression(eleve: ProfilEleve.AvecProfilExistant): Int {
Expand All @@ -17,7 +19,13 @@ class RecupererProgressionService(
// 4. >= 3 favoris
// 5. >= eval niveau ambition
// 6. >= favoris Parcoursup
if (!eleve.estProfilComplet()) {
val specialitesSelectionnablesParCandidat =
eleve.baccalaureat?.let {
baccalaureatSpecialiteBDDRepository.recupererLesIdsDesSpecialitesDUnBaccalaureat(
it,
)
}
if (!eleve.estProfilComplet(specialitesSelectionnablesParCandidat)) {
return 0
}
val nbFiches = traceRepository.getNbFichesLues(eleve.id)
Expand Down
4 changes: 4 additions & 0 deletions app/front/src/configuration/dépendances/dépendances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { MettreÀJourSpécialitésÉlèveUseCase } from "@/features/élève/usec
import { MettreÀJourVoeuxÉlèveUseCase } from "@/features/élève/usecase/MettreÀJourVoeuxÉlève";
import { RechercherSpécialitésUseCase } from "@/features/élève/usecase/RechercherSpécialités";
import { RécupérerÉlèveUseCase } from "@/features/élève/usecase/RécupérerProfilÉlève";
import { RécupérerProgressionÉlèveUseCase } from "@/features/élève/usecase/RécupérerProgressionÉlève";
import { SupprimerTousLesMétiersÉlèveUseCase } from "@/features/élève/usecase/SupprimerTousLesMétiersÉlève";
import { SupprimerToutesLesFormationsÉlèveUseCase } from "@/features/élève/usecase/SupprimerToutesLesFormationsÉlève";
import { type FormationRepository } from "@/features/formation/infrastructure/formationRepository.interface";
Expand Down Expand Up @@ -86,6 +87,8 @@ export class Dépendances {

public readonly récupérerProfilÉlèveUseCase: RécupérerÉlèveUseCase;

public readonly récupérerProgressionÉlèveUseCase: RécupérerProgressionÉlèveUseCase;

public readonly associerCompteParcourSupÉlèveUseCase: AssocierCompteParcourSupÉlèveUseCase;

public readonly mettreÀJourSpécialitésÉlèveUseCase: MettreÀJourSpécialitésÉlèveUseCase;
Expand Down Expand Up @@ -182,6 +185,7 @@ export class Dépendances {
this.analyticsRepository,
);
this.récupérerProfilÉlèveUseCase = new RécupérerÉlèveUseCase(this._élèveRepository);
this.récupérerProgressionÉlèveUseCase = new RécupérerProgressionÉlèveUseCase(this._élèveRepository);
this.associerCompteParcourSupÉlèveUseCase = new AssocierCompteParcourSupÉlèveUseCase(this._élèveRepository);
this.mettreÀJourSpécialitésÉlèveUseCase = new MettreÀJourSpécialitésÉlèveUseCase(this._élèveRepository);
this.mettreÀJourVoeuxÉlèveUseCase = new MettreÀJourVoeuxÉlèveUseCase(
Expand Down
3 changes: 1 addition & 2 deletions app/front/src/features/élève/domain/élève.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type FormationMasquéeÉlève = Id;
export type CommuneÉlève = Omit<Commune, "codePostal">;
export type FormationÉlève = Id;

export type ProgressionÉlève = NonNullable<components["schemas"]["ProfilDTO"]["progression"]>;
export type ProgressionÉlève = NonNullable<components["schemas"]["ProfilDTO"]["progression"]> | null;

export type VoeuÉlève = {
id: Id;
Expand Down Expand Up @@ -54,7 +54,6 @@ export type Élève = {
formationsMasquées: FormationMasquéeÉlève[] | null;
notesPersonnelles: NotePersonnelleFormationÉlève[] | null;
ambitions: AmbitionFormationÉlève[] | null;
progression: ProgressionÉlève | null;
};

export const situationÉlève = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { type operations } from "@/types/api-mps";

export type RécupérerProfilÉlèveRéponseHTTP = operations["getProfilEleve"]["responses"]["200"]["content"]["*/*"];
export type RécupérerProgressionÉlèveRéponseHTTP =
operations["getProgressionMPS"]["responses"]["200"]["content"]["*/*"];
export type MettreÀJourProfilÉlèveRéponseHTTP = operations["postProfilEleve"]["responses"]["200"]["content"]["*/*"];
export type AssocierCompteParcourSupÉlèveRéponseHTTP =
operations["postCompteParcoursup"]["responses"]["200"]["content"]["*/*"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import {
type BodyMettreÀJourProfilÉlèveHTTP,
type MettreÀJourProfilÉlèveRéponseHTTP,
type RécupérerProfilÉlèveRéponseHTTP,
type RécupérerProgressionÉlèveRéponseHTTP,
} from "./élèveHttpRepository.interface";
import { type Élève } from "@/features/élève/domain/élève.interface";
import { type Élève, ProgressionÉlève } from "@/features/élève/domain/élève.interface";
import { type ÉlèveRepository } from "@/features/élève/infrastructure/gateway/élèveRepository.interface";
import { RessourceNonTrouvéeErreurHttp } from "@/services/erreurs/erreursHttp";
import { type IMpsApiHttpClient } from "@/services/mpsApiHttpClient/mpsApiHttpClient.interface";

export class ÉlèveHttpRepository implements ÉlèveRepository {
private _ENDPOINT = "/api/v1/profil" as const;
private _ENDPOINT_PROFIL = "/api/v1/profil" as const;

private _ENDPOINT_PROGRESSION = "/api/v1/profil/progression" as const;

public constructor(private _mpsApiHttpClient: IMpsApiHttpClient) {}

public async récupérerProfil(): Promise<Élève | Error> {
const réponse = await this._mpsApiHttpClient.get<RécupérerProfilÉlèveRéponseHTTP>(this._ENDPOINT);
const réponse = await this._mpsApiHttpClient.get<RécupérerProfilÉlèveRéponseHTTP>(this._ENDPOINT_PROFIL);

if (réponse instanceof RessourceNonTrouvéeErreurHttp) {
await this.mettreÀJourProfil({
Expand All @@ -36,7 +39,6 @@ export class ÉlèveHttpRepository implements ÉlèveRepository {
formationsMasquées: null,
notesPersonnelles: null,
ambitions: null,
progression: null,
});

return await this.récupérerProfil();
Expand All @@ -49,9 +51,19 @@ export class ÉlèveHttpRepository implements ÉlèveRepository {
return this._mapperVersLeDomaine(réponse);
}

public async récupérerProgressionÉlève(): Promise<ProgressionÉlève> {
const réponse = await this._mpsApiHttpClient.get<RécupérerProgressionÉlèveRéponseHTTP>(this._ENDPOINT_PROGRESSION);

if (réponse instanceof RessourceNonTrouvéeErreurHttp || réponse instanceof Error) {
return 0;
} else {
return this._mapperProgressionVersLeDomaine(réponse);
}
}

public async mettreÀJourProfil(élève: Élève): Promise<Élève | Error> {
const réponse = await this._mpsApiHttpClient.post<MettreÀJourProfilÉlèveRéponseHTTP>(
this._ENDPOINT,
this._ENDPOINT_PROFIL,
this._mapperVersLApiMps(élève),
);

Expand All @@ -68,7 +80,7 @@ export class ÉlèveHttpRepository implements ÉlèveRepository {
redirectUri: string,
): Promise<boolean | Error> {
const réponse = await this._mpsApiHttpClient.post<AssocierCompteParcourSupÉlèveRéponseHTTP>(
`${this._ENDPOINT}/parcoursup`,
`${this._ENDPOINT_PROFIL}/parcoursup`,
{
codeVerifier,
code,
Expand Down Expand Up @@ -110,6 +122,10 @@ export class ÉlèveHttpRepository implements ÉlèveRepository {
};
}

private _mapperProgressionVersLeDomaine(progression: RécupérerProgressionÉlèveRéponseHTTP): ProgressionÉlève {
return progression.progression;
}

private _mapperVersLeDomaine(élève: RécupérerProfilÉlèveRéponseHTTP): Élève {
return {
compteParcoursupAssocié: élève.compteParcoursupAssocie ?? false,
Expand Down Expand Up @@ -143,7 +159,6 @@ export class ÉlèveHttpRepository implements ÉlèveRepository {
? (formationFavorite.niveauAmbition as 1 | 2 | 3)
: null,
})) ?? null,
progression: élève.progression ?? null,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type Élève } from "@/features/élève/domain/élève.interface";
import { type Élève, ProgressionÉlève } from "@/features/élève/domain/élève.interface";

export type ÉlèveRepository = {
récupérerProfil: () => Promise<Élève | Error>;
récupérerProgressionÉlève: () => Promise<ProgressionÉlève>;
mettreÀJourProfil: (élève: Élève) => Promise<Élève | Error>;
associerCompteParcourSup: (codeVerifier: string, code: string, redirectUri: string) => Promise<boolean | Error>;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/require-await */
import { type Élève } from "@/features/élève/domain/élève.interface";
import { type Élève, ProgressionÉlève } from "@/features/élève/domain/élève.interface";
import { type ÉlèveRepository } from "@/features/élève/infrastructure/gateway/élèveRepository.interface";

export class ÉlèveSessionStorageRepository implements ÉlèveRepository {
Expand All @@ -22,7 +22,6 @@ export class ÉlèveSessionStorageRepository implements ÉlèveRepository {
formationsMasquées: null,
ambitions: null,
notesPersonnelles: null,
progression: null,
moyenneGénérale: null,
};

Expand All @@ -38,6 +37,10 @@ export class ÉlèveSessionStorageRepository implements ÉlèveRepository {
return this._élève;
}

public async récupérerProgressionÉlève(): Promise<ProgressionÉlève> {
return 6;
}

public async mettreÀJourProfil(élève: Élève): Promise<Élève | Error> {
this._élève = élève;
sessionStorage.setItem(this._SESSION_STORAGE_PREFIX, JSON.stringify(this._élève));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import profilSVG from "@/assets/profil.svg";
import { actionsToastStore } from "@/components/Toast/useToastStore/useToastStore";
import { environnement } from "@/configuration/environnement";
import { i18n } from "@/configuration/i18n/i18n";
import { élèveQueryOptions } from "@/features/élève/ui/élèveQueries";
import { progressionQueryOptions } from "@/features/élève/ui/élèveQueries";
import { CartePrimaireTableauDeBordÉlèveProps } from "@/features/élève/ui/TableauDeBordÉlèvePage/CartePrimaireTableauDeBordÉlève/CartePrimaireTableauDeBordÉlève.interface";
import { useQuery } from "@tanstack/react-query";
import { getRouteApi } from "@tanstack/react-router";
Expand All @@ -13,7 +13,7 @@ export default function useTableauDeBordÉlèvePage() {
const route = getRouteApi("/_auth/");
const { associationPS } = route.useSearch();
const { déclencherToast } = actionsToastStore();
const { data: élève } = useQuery(élèveQueryOptions);
const { data: progression } = useQuery(progressionQueryOptions);

if (associationPS === "ok") {
déclencherToast(
Expand Down Expand Up @@ -54,6 +54,6 @@ export default function useTableauDeBordÉlèvePage() {
cartes,
associationParcoursupPossible:
environnement.VITE_PARCOURSUP_OAUTH2_URL && environnement.VITE_PARCOURSUP_OAUTH2_CLIENT,
progression: élève?.progression,
progression,
};
}
8 changes: 8 additions & 0 deletions app/front/src/features/élève/ui/élèveQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { queryOptions } from "@tanstack/react-query";

export const queryÉlèveKeys = {
PROFIL: "élève",
PROGRESSION: "progression",
};

export const élèveQueryOptions = queryOptions({
Expand All @@ -15,3 +16,10 @@ export const élèveQueryOptions = queryOptions({
return réponse;
},
});

export const progressionQueryOptions = queryOptions({
queryKey: [queryÉlèveKeys.PROGRESSION],
queryFn: async () => {
return await dépendances.récupérerProgressionÉlèveUseCase.run();
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ProgressionÉlève } from "@/features/élève/domain/élève.interface";
import { type ÉlèveRepository } from "@/features/élève/infrastructure/gateway/élèveRepository.interface";

export class RécupérerProgressionÉlèveUseCase {
public constructor(private readonly _élèveRepository: ÉlèveRepository) {}

public async run(): Promise<ProgressionÉlève> {
return await this._élèveRepository.récupérerProgressionÉlève();
}
}
6 changes: 6 additions & 0 deletions app/front/src/routes/_auth/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { queryÉlèveKeys } from "@/features/élève/ui/élèveQueries";
import TableauDeBordÉlèvePage from "@/features/élève/ui/TableauDeBordÉlèvePage/TableauDeBordÉlèvePage";
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
Expand All @@ -9,4 +10,9 @@ const tableauDeBordSearchSchema = z.object({
export const Route = createFileRoute("/_auth/")({
validateSearch: (searchParamètres) => tableauDeBordSearchSchema.parse(searchParamètres),
component: TableauDeBordÉlèvePage,
loader: ({ context: { queryClient }, cause }) => {
if (cause !== "stay") {
queryClient.removeQueries({ queryKey: [queryÉlèveKeys.PROGRESSION] });
}
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class MpsApiHttpClient implements IMpsApiHttpClient {
});
};

private _récupérerJWT = (): string => {
private readonly _récupérerJWT = (): string => {
const sessionStorageOIDC = sessionStorage.getItem(
`oidc.user:${environnement.VITE_KEYCLOAK_URL}/realms/${environnement.VITE_KEYCLOAK_ROYAUME}:${environnement.VITE_KEYCLOAK_CLIENT_ID}`,
);
Expand Down
2 changes: 0 additions & 2 deletions app/front/src/tests-e2e/élève/helpers/ÉlèveTestHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export class ÉlèveTestHelper extends GlobalTestHelper {
formationsMasquées: null,
ambitions: null,
notesPersonnelles: null,
progression: null,
...argumentsProfilÉlève,
};

Expand All @@ -56,7 +55,6 @@ export class ÉlèveTestHelper extends GlobalTestHelper {
formationsMasquées: [],
ambitions: [],
notesPersonnelles: [],
progression: 3,
};

await this.initialiserProfilÉlèveParDéfaut({ ...profilÉlèveParDéfaut, ...profilÉlève });
Expand Down
Loading

0 comments on commit 68b98b5

Please sign in to comment.