from ast import For import time from datetime import date #from tkinter.tix import Form import numpy as np import pandas as pd from django.db.models import Case, IntegerField, Prefetch, Sum, When from django.db.transaction import atomic from django.forms import model_to_dict from django.http import Http404, JsonResponse from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend from rest_framework import status, viewsets from rest_framework.exceptions import APIException from rest_framework.permissions import IsAdminUser, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from django.db.models import Q from .. import constants from ..filters import AdministreFilter, AdministrePAMFilter, PosteFilter, PostePAMFilter, RelatedOrderingFilter from ..models import (FMOB, Administre, Decision, DecisionChoices, Domaine, Filiere, FormationEmploi, Marque, MarquesGroupe, PcpFeGroupe, Poste, PreferencesListe, SousVivier, SousVivierAssociation, SpecifiqueChoices, Administres_Pams, StatutPamChoices as StatutPam, Postes_Pams,PAM) from ..paginations import HeavyDataPagination from ..reporting import (reporting_suivi_pam_admin, reporting_suivi_pam_poste, reporting_taux_armement_gestionnaire, reporting_taux_armement_pcp) from ..serializers import (CTX_KEY_DECISIONS, CTX_KEY_PROFILES, AdministreSerializer, AdministresPamsSerializer, AlimentationReferentielSerializer, DomaineSerializer, FileSVSerializer, FiliereSerializer, FmobSerializer, MarqueSerializer, MarquesGroupeSerializer, PcpFeGroupeSerializer, PosteSerializer, SousVivierAssociationSerializer, PostesPamsSerializer) from ..utils import sous_viviers_du_cellule, without_keys, generate_sv_id, nf2categorie from ..utils.decisions import get_available_decisions from ..utils.decorators import class_logger from ..utils.permissions import get_profiles_by_adm from ..utils_extraction import (DataFrameTypes, FileTypes, open_excel, read_files_by_type) from .commun import (GestionnairePermission, execution_time_viewset, query_count_viewset) # Vue de calcul des indicateurs # Renvoie les indicateurs pour les vues : # - taux armement FE pour les PCP # - taux armement FE pour les gestionnaires # - suivi pam des gestionnaires pour les administrés # - suivi pam des gestionnaires pour les postes @class_logger @execution_time_viewset @query_count_viewset class ReportingView(APIView): """ Cette classe est dédiée au vue du calcul des indicateurs - taux armement FE pour les PCP - taux armement FE pour les gestionnaires - suivi pam des gestionnaires pour les administrés - suivi pam des gestionnaires pour les postes """ permission_classes = [IsAuthenticated, GestionnairePermission] def __str__(self): return 'ReportingView' def get(self, request): """La fonction get renvoie les indicateurs :type request: rest_framework.request.Request :param request: Request contenant le type, sous_vivier id, niveau fonctinonel et l'id de la formation emploi. :return: - **return** (*json*): Si le type est SUIVI_PAM_GESTIONNAIRE le json contiendra l'information sur le suivi pam des gestionnaires pour les administrés et les postes. Si le type est TAUX_ARMEMENT_FE le json contiendra les indicateurs sur les taux d'armement. """ type = request.query_params["type"] sv_id = [] if 'sv_id' in request.query_params: sv_id = request.query_params["sv_id"] if 'vue' in request.query_params: vue = request.query_params["vue"] if "nf" in request.query_params: nf = request.query_params.getlist("nf") else: nf = ['1A', '1B', '1C', '2.', '3A', '3B', '3B NFS', '4.', '5A', '5B', '5C', '6A', '6B'] if 'fe_id' in request.query_params: # a voir fe_id = request.query_params.getlist('fe_id') else: fe_id = list(FormationEmploi.objects.values_list('fe_code', flat=True)) # Vues pour les indicateurs sur le suivi du pam if (type == "SUIVI_PAM_GESTIONNAIRE"): if "d_code" in request.query_params: d_id = request.query_params.getlist("d_code") else: filieres = list(SousVivierAssociation.objects.filter(sous_vivier__sv_id=sv_id).values_list('filiere_id', flat=True).distinct()) d_id = list(Filiere.objects.filter(f_code__in=filieres).values_list('domaine_id', flat=True).distinct()) if "f_code" in request.query_params: f_id = request.query_params.getlist("f_code") else: f_id = list(SousVivierAssociation.objects.filter(sous_vivier__sv_id=sv_id).values_list('filiere_id', flat=True).distinct()) if "categorie" in request.query_params: categorie = request.query_params.getlist("categorie") else: categorie = list( SousVivierAssociation.objects.filter(sous_vivier__sv_id=sv_id).values_list('sva_categorie', flat=True).distinct()) # résultats pour la vue suivi pam des gestionnaires pour les administrés nb_a_etudier, nb_a_muter, nb_a_maintenir, nb_non_etudie_administres, nb_a_partant, nb_a_non_dispo, nb_prepos_administres, nb_pos_administres, nb_omi_active_administres, nb_omi_en_cours_administres, reste_a_realiser_administres, reste_a_realiser_a_etudier, reste_a_realiser_a_muter = reporting_suivi_pam_admin( sv_id, f_id, d_id, nf, categorie) # résultats pour la vue suivi pam des gestionnaires pour les postes nb_p1, nb_p2, nb_p3, nb_p4, nb_gele, nb_non_etudie_postes, reste_a_realiser_postes, reste_a_realiser_p1, reste_a_realiser_p2, reste_a_realiser_p3, reste_a_realiser_p4, nb_prepos_postes, nb_pos_postes, nb_omi_active_postes, nb_omi_en_cours_postes = reporting_suivi_pam_poste( sv_id, f_id, d_id, nf, categorie) return JsonResponse( {"nb_a_etudier": nb_a_etudier, "nb_a_muter": nb_a_muter, "nb_a_maintenir": nb_a_maintenir, "nb_a_partant": nb_a_partant, "nb_a_non_dispo": nb_a_non_dispo, "nb_non_etudie_administres": nb_non_etudie_administres, "nb_prepos_administres": nb_prepos_administres, "nb_pos_administres": nb_pos_administres, "nb_omi_en_cours_administres": nb_omi_en_cours_administres, "nb_omi_active_administres": nb_omi_active_administres, "reste_a_realiser_administres": reste_a_realiser_administres, "reste_a_realiser_a_etudier": reste_a_realiser_a_etudier, "reste_a_realiser_a_muter": reste_a_realiser_a_muter, "nb_p1": nb_p1, "nb_p2": nb_p2, "nb_p3": nb_p3, "nb_p4": nb_p4, "nb_gele": nb_gele, "nb_non_etudie_postes": nb_non_etudie_postes, "reste_a_realiser_postes": reste_a_realiser_postes, "reste_a_realiser_p1": reste_a_realiser_p1, "reste_a_realiser_p2": reste_a_realiser_p2, "reste_a_realiser_p3": reste_a_realiser_p3, "reste_a_realiser_p4": reste_a_realiser_p4, "nb_prepos_postes": nb_prepos_postes, "nb_pos_postes": nb_pos_postes, "nb_omi_en_cours_postes": nb_omi_en_cours_postes, "nb_omi_active_postes": nb_omi_active_postes}, safe=False) # Vues pour les indicateurs sur les taux d'armement elif (type == 'TAUX_ARMEMENT_FE'): if "d_code" in request.query_params: d_id = request.query_params.getlist("d_code") else: d_id = list(Domaine.objects.all().values_list('d_code', flat=True)) if "f_code" in request.query_params: f_id = request.query_params.getlist("f_code") else: f_id = list(Filiere.objects.all().values_list('f_code', flat=True)) if "categorie" in request.query_params: categorie = request.query_params.getlist("categorie") else: categorie = ['MDR', 'SOFF', 'OFF', 'OGX'] if len(sv_id) == 0: # résultats pour la vue taux armement FE pour les PCP nb_militaires_actuel, nb_postes_actuel, ecart_actuel, taux_armement_actuel, nb_militaires_entrants, nb_militaires_sortants, nb_militaires_projete, taux_armement_projete, taux_armement_cible = reporting_taux_armement_pcp( fe_id, f_id, d_id, nf, categorie) else: # résultats pour la vue taux armement FE pour les gestionnaires nb_militaires_actuel, nb_postes_actuel, ecart_actuel, taux_armement_actuel, nb_militaires_entrants, nb_militaires_sortants, nb_militaires_projete, taux_armement_projete, taux_armement_cible = reporting_taux_armement_gestionnaire( fe_id, f_id, d_id, nf, categorie, sv_id) return JsonResponse({'nb_militaires_actuel': nb_militaires_actuel, "nb_postes_actuel": nb_postes_actuel, 'ecart_actuel': ecart_actuel, 'taux_armement_actuel': taux_armement_actuel, 'nb_militaires_entrants': nb_militaires_entrants, 'nb_militaires_sortants': nb_militaires_sortants, 'nb_militaires_projete': nb_militaires_projete, 'taux_armement_projete': taux_armement_projete, 'taux_armement_cible': taux_armement_cible}, safe=False) else: return JsonResponse({"autre vue": "autres indicateurs demandes"}) # TODO : Supprimer cette vue qui n'est plus utilisée # Vue de chargement des sous-viviers : # - Charge et traite le fichier de définition des sous-viviers # - Attribue les sous-viviers présents aux administrés et postes correspondants @class_logger @execution_time_viewset @query_count_viewset class ChargementSVView(APIView): """ Cette classe est dédiée au vue de chargement des sous-viviers. Charge et traite le fichier de définition des sous-viviers et attribue les sous-viviers présents aux administrés et postes correspondants """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = FileSVSerializer def get(self, request): """La fonction get renvoie une reponse contenant ok :type request: rest_framework.request.Request :param request: Requset :return: - **return** (*json*): json contenant "ok". """ return Response({"get": "ok"}) def post(self, request): """La fonction post charge les sous-viviers :type request: rest_framework.request.Request :param request: Request contenant le fichier SV :return: - **Response** (*Response*): Reponse contient la liste des sous-viviers créés, ignorés et où il y a eu une erreur. """ serializer = FileSVSerializer(data=request.data) if not serializer.is_valid(): return Response( data=serializer.errors, status=status.HTTP_400_BAD_REQUEST ) sv_file = request.data['SV'] sv_df = open_excel(sv_file, sheetname="GESTIONNAIRES", engine='openpyxl') sv = pd.DataFrame(columns=['gestionnaire_id_sap', 'f_code', 'asso_sv_categorie', 'arme']) for i in range(len(sv_df)): sv.loc[i] = [sv_df.iloc[i, 0], sv_df.iloc[i, 3], sv_df.iloc[i, 4], sv_df.iloc[i, 5]] sv.dropna(subset=['f_code'], inplace=True) sv['arme'] = sv['arme'].replace({np.nan: None}) sv.reset_index(drop=True, inplace=True) sv['asso_sv_categorie'].replace({"SOUS-OFFICIER": "SOFF", "OFFICIER": "OFF", "MILITAIRE DU RANG": "MDR"}, inplace=True) sv['sv_id'] = sv.f_code + '_' + sv.asso_sv_categorie sv.f_code = sv.f_code.apply(lambda x: x.split(',')) sv = sv.explode(column='f_code') sv.reset_index(drop=True, inplace=True) errors = [] created = [] ignored = [] sv.fillna(np.nan, inplace=True) sv.replace([np.nan], [None], inplace=True) self.logger.debug(sv.head()) for i in range(len(sv)): try: filiere = Filiere.objects.get(f_code=sv.at[i, "f_code"]) categorie = sv.at[i, "asso_sv_categorie"] asso_sv = SousVivierAssociation.objects.filter(sva_categorie=categorie, filiere__f_code=sv.at[i, "f_code"]) # Vérifier que filiere et catégorie n'ont pas déjà de sous vivier if asso_sv.count() == 0 and categorie is not None: # Créer le sous-vivier correspondant dans la table SousVivier sous_vivier = SousVivier(sv_id=sv.at[i, "sv_id"], sv_libelle=str(sv.at[i, "sv_id"])) self.logger.debug(model_to_dict(sous_vivier)) sous_vivier.save() self.logger.debug(sv.at[i, "sv_id"]) # Insertion du nouveau sous-vivier dans la table d'association (sans le droit arme dans un premier temps) sva = SousVivierAssociation(sva_id=i, sous_vivier_id=sous_vivier.sv_id, filiere_id=filiere.f_code, sva_categorie=categorie, sva_arme=sv.at[i, "arme"]) sva.save() # Mise à jour du sous_vivier_id pour les postes et administres ayant la filiere et la categorie en cours Administre.objects.filter(a_categorie=categorie, a_filiere_id=filiere.f_code).update( sous_vivier_id=sous_vivier.sv_id) # En fonction du fichier excel, changer la mise à jour du sous_vivier Poste.objects.filter(p_categorie=categorie, p_filiere_id=filiere.f_code).sous_viviers.set([sous_vivier.sv_id]) created.append(str(sv.at[i, "asso_sv_categorie"]) + " " + str(sv.at[i, "f_code"])) else: ignored.append(str(sv.at[i, "asso_sv_categorie"]) + " " + str(sv.at[i, "f_code"])) # Si l'association appartient déjà à un sous_vivier, on ne fait rien except Filiere.DoesNotExist: errors.append("Filiere " + sv.at[i, 'f_code'] + "non retrouvée") return Response({"created": created, "ignored": ignored, "errors": errors}) @class_logger @execution_time_viewset @query_count_viewset class AlimentationReferentielView(APIView): """ Vue pour alimenter la base à partir de référentiels """ permission_classes = [IsAuthenticated, IsAdminUser] serializer_class = AlimentationReferentielSerializer def get(self, request): return Response("Formulaire d'alimentation d'OGURE NG (référentiel)") @atomic def post(self, request): """ Charge le(s) fichier(s) et met à jour la base. :param request: requête, contient les fichiers :type request: class:`rest_framework.request.Request` :raises: class:`rest_framework.exceptions.APIException` :return: réponse :rtype: class:`rest_framework.response.Response` """ try: validator = self.serializer_class(data=request.data) validator.is_valid(raise_exception=True) # récupération des fichiers referentiel_fe = validator.validated_data.get('referentiel_fe') if referentiel_fe: Cols = FormationEmploi.Cols col_pk = Cols.PK col_pk_mere = Cols.REL_MERE col_libelle_mere = Cols.LIBELLE col_zone_def = Cols.ZONE_DEFENSE def process_referentiel_fe(referentiel) -> pd.DataFrame: """ Fonction de lecture du référentiel de FE. :param referentiel: Fichier du référentiel FE :type referentiel: XLSX :return: DataFrame :rtype: class:`pandas.DataFrame` """ return open_excel(referentiel, sheetname=0, engine='openpyxl') def convertir_fe(df: pd.DataFrame) -> pd.DataFrame: """ Fonction de conversion du DataFrame de FE. :param df: DataFrame du référentiel FE :type df: class:`pandas.DataFrame` :return: DataFrame :rtype: class:`pandas.DataFrame` """ col_pk_avant = 'FE CREDO' # B col_pk_mere_avant = 'FE mère CREDO' # D col_libelle_mere_avant = 'FE mère LA' # E col_zone_def_avant = 'Zone de Défense' # O return ( df[[col_pk_avant, col_pk_mere_avant, col_libelle_mere_avant, col_zone_def_avant]] .drop_duplicates(subset=col_pk_avant, keep='first') .rename(columns={ col_pk_avant: col_pk, col_pk_mere_avant: col_pk_mere, col_libelle_mere_avant: col_libelle_mere, col_zone_def_avant: col_zone_def }) .astype({col_pk: 'str', col_pk_mere: 'str'}) ) def mettre_a_jour_fe(df: pd.DataFrame) -> None: """ Met à jour les FE base à partir du DataFrame de FE. :param df: DataFrame du référentiel FE :type df: class:`pandas.DataFrame` :return: DataFrame :rtype: class:`pandas.DataFrame` """ TypeModele = FormationEmploi Cols = TypeModele.Cols champs_maj = (Cols.REL_MERE, Cols.ZONE_DEFENSE) modeles_en_base = {m.pk: m for m in TypeModele.objects.select_related(Cols.REL_MERE).only('pk', *champs_maj)} taille_batch = 100 dict_update = {} dict_mere_create = {} error_count = 0 for idx, rec in enumerate(df.to_dict('records')): pk = rec.get(col_pk) try: id_mere = rec.get(col_pk_mere) zone_defense = rec.get(col_zone_def) en_base = modeles_en_base.get(pk) # TODO pas de création pour l'instant (ni de suppression) quand en_base est falsy if en_base: mere = None if id_mere is not None: mere = modeles_en_base.get(id_mere) or dict_mere_create.get(id_mere) if not mere: try: # les FE mères manquantes seront créées mere = TypeModele(pk=id_mere, fe_libelle=rec.get(col_libelle_mere)) except Exception as e: raise RuntimeError(f'la création d\'un modèle de type "{TypeModele.__name__}" (mère) a échoué') from e dict_mere_create.setdefault(id_mere, mere) if mere != getattr(en_base, Cols.REL_MERE, None) or zone_defense != getattr(en_base, Cols.ZONE_DEFENSE, None): try: modele = TypeModele(pk=pk, mere=mere, zone_defense=zone_defense) except Exception as e: raise RuntimeError(f'la création d\'un modèle de type "{TypeModele.__name__}" a échoué') from e dict_update.setdefault(pk, modele) except Exception: error_count = error_count + 1 self.logger.exception('%s une erreur est survenue à la ligne : %s (pk=%s)', TypeModele.__name__, idx, pk) if error_count: self.logger.warning("%s(s) en erreur : %s", TypeModele.__name__, error_count) if dict_mere_create: TypeModele.objects.bulk_create(dict_mere_create.values(), batch_size=taille_batch) self.logger.info('%s(s) mères créée(s) : %s', TypeModele.__name__, len(dict_mere_create)) if dict_update and champs_maj: TypeModele.objects.bulk_update(dict_update.values(), batch_size=taille_batch, fields=champs_maj) self.logger.info('%s(s) mise(s) à jour : %s', TypeModele.__name__, len(dict_update)) df_referentiel_fe = process_referentiel_fe(referentiel_fe) self.logger.info('Lecture du référentiel FE ------> Succès') df_referentiel_fe = convertir_fe(df_referentiel_fe) self.logger.info('Extraction des données du référentiel FE ------> Succès') mettre_a_jour_fe(df_referentiel_fe) self.logger.info('Mise à jour du référentiel FE ------> Succès') return Response({'Insertion réussie'}) except (Http404, APIException): raise except BaseException: message = "Impossible d'alimenter le(s) référentiel(s)" self.logger.exception(message) raise APIException(message) # Vue API des administrés # Pagination OK # Ordering OK # Search NOK # Filtering NOK @class_logger @execution_time_viewset @query_count_viewset class AdministreView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue de l'administre. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = AdministreSerializer filter_backends = [DjangoFilterBackend, RelatedOrderingFilter] # filterset_fields = ['category', 'in_stock'] filterset_class = AdministreFilter ordering_fields = Administre.Cols.PK ordering = [Administre.Cols.PK] pagination_class = HeavyDataPagination # important : mettre à jour quand le serializer change def get_queryset(self): Cols = Administre.Cols ColsFE = FormationEmploi.Cols return Administre.objects.select_related( Cols.REL_FONCTION, Cols.REL_GRADE, Cols.REL_SOUS_VIVIER ).prefetch_related( Cols.M2M_COMPETENCES, Prefetch(Cols.M2M_PAM, queryset=Administres_Pams.objects.select_related(Administres_Pams.Cols.O2M_FMOB)), # Prefetch(Cols.REL_DECISION, queryset=Decision.objects.select_related( # f'{Decision.Cols.REL_POSTE}__{Poste.Cols.REL_FORMATION_EMPLOI}__{ColsFE.REL_MERE}', # )), Prefetch(Cols.REL_FORMATION_EMPLOI, queryset=FormationEmploi.objects.select_related(ColsFE.REL_MERE)), ) def get_serializer(self, *args, **kwargs): ser = super().get_serializer(*args, **kwargs) arg = args[0] if args else None if arg and isinstance(arg, (list, tuple)) and self.request.method == 'GET': # calcule directement, si besoin, les profils et les décisions disponibles pour tous les résultats if CTX_KEY_PROFILES not in ser.context: ser.context[CTX_KEY_PROFILES] = get_profiles_by_adm(self.request.user, *arg) if CTX_KEY_DECISIONS not in ser.context: ser.context[CTX_KEY_DECISIONS] = get_available_decisions(arg, profiles_by_adm=ser.context[CTX_KEY_PROFILES], annee_pam = self.request.query_params['pam__in']) return ser @atomic def put(self, request): """La fonction put met à jour une liste d'administres. :type request: rest_framework.request.Request :param request: Request contenant la liste d'administres. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour des administres . """ try: req_data = request.data is_list = isinstance(req_data, list) validator = self.serializer_class(data=req_data, many=is_list, partial=True) validator.is_valid(raise_exception=True) today = date.today() Cols = Administre.Cols if 'annee_pam' in req_data: annee_pam = req_data.pop('annee_pam') else: annee_pam = '' def copy_data_item(input, pk): """Copie et complète si besoin le dictionnaire en entrée""" result = {**input} if pk and Cols.PK not in result: result[Cols.PK] = pk return result def set_competences(administre_obj, a_comp): """Set competences to administre object""" a_comp_id = [competence.comp_id for competence in a_comp] administre_obj.a_liste_id_competences.set(a_comp_id) return administre_obj if is_list: # Update multiple elements data = [copy_data_item(item, req_data[idx][Cols.PK]) for idx, item in enumerate(validator.validated_data)] fields = [key for key in data[0].keys() if key != Cols.PK] # Direct assignment to the forward side of a many-to-many set is prohibited. Use a_liste_id_competences.set() instead. if Cols.STATUT_PAM in fields: fields_to_update_pam = ['a_statut_pam_annee'] data_statut_pam = [{'id' :str(o['a_id_sap'])+str(annee_pam), 'a_statut_pam_annee' : o[Cols.STATUT_PAM]} for o in data] objs = [Administres_Pams(**data_item) for data_item in data_statut_pam] Administres_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) elif 'a_liste_id_competences' not in fields: objs = [Administre(**data_item) for data_item in data] if Cols.STATUT_PAM in fields: list_error = [] for i in range(len(objs)): old_administre = Administre.objects.get(a_id_sap=objs[i].a_id_sap) administre = objs[i] eip = administre.a_eip fe_code = old_administre.formation_emploi_id avis = administre.a_statut_pam old_avis = old_administre.a_statut_pam try: categorie = constants.CATEGORIE_BY_NF[old_administre.a_nf.upper()] except: erreur = 'la catégorie de l\'administré n\'est pas reconnue dans ogure.' raise Exception(erreur) # liste_error = impact_decisions(old_administre, administre, old_avis, avis, eip, fe_code, categorie) Administre.objects.bulk_update(objs, fields=fields) else: objs_w_comp = [set_competences(Administre(**without_keys(i, ['a_liste_id_competences'])), i['a_liste_id_competences']) for i in data] else: # Update one element data = copy_data_item(validator.validated_data, validator.validated_data[Cols.PK]) # c'est un bug la récupération de a_id_sap en-dessous, non ? a_id_sap = Administre.objects.update(data) db_instance = Administre.objects.filter(a_id_sap=a_id_sap).first() db_instance.tag.clear() return Response({'msg': 'updated'}) except APIException: raise except Exception: message = 'échec de la mise à jour de N administrés' self.logger.exception(message) raise APIException(message) def partial_update(self, request, pk=None): """ La fonction put met à jour un administre. :type request: rest_framework.request.Request :param request: Request contenant l'administre. :type pk: integer :param pk: Primary Key de l'administre. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour de l'administre . """ try: a = get_object_or_404(Administre.objects, a_id_sap=pk) # Permet de déclancher la vérification des droits au niveau de l'objet self.check_object_permissions(self.request, a) req_data = request.data if 'annee_pam' in req_data: annee_pam = req_data.pop('annee_pam') # TODO valider les données (il faut d'abord corriger la MAJ des compétences) # validator = self.serializer_class(data=req_data, partial=True) # validator.is_valid(raise_exception=True) Cols = Administre.Cols # Copie et complète si besoin le dictionnaire en entrée # administre = {**validator.validated_data} administre = {**req_data} administre[Cols.PK] = pk fields = [key for key in administre.keys() if key != Cols.PK] # and key != 'a_liste_id_competences'] if 'a_domaine_futur' in fields: administre['a_domaine_futur_id'] = administre['a_domaine_futur'] del administre['a_domaine_futur'] if 'a_filiere_futur' in fields: administre['a_filiere_futur_id'] = administre['a_filiere_futur'] del administre['a_filiere_futur'] if Cols.STATUT_PAM in fields: old_administre = Administre.objects.get(a_id_sap=pk) eip = old_administre.a_eip fe_code = old_administre.formation_emploi_id avis = administre[Cols.STATUT_PAM] old_avis = old_administre.a_statut_pam try: categorie = constants.CATEGORIE_BY_NF[old_administre.a_nf.upper()] except: erreur = 'la catégorie de l\'administré n\'est pas reconnue dans ogure.' raise APIException(erreur) # liste_error = impact_decisions(old_administre, Administre(**administre), old_avis, avis, eip, fe_code, categorie) if 'a_liste_id_competences' in fields: adm=Administre(pk=pk) #adm=request.data['a_liste_id_competences'] if administre['a_liste_id_competences']: adm.a_liste_id_competences.set(administre['a_liste_id_competences'].split(',')) else: adm.a_liste_id_competences.set("") elif Cols.STATUT_PAM in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_statut_pam_annee = administre[Cols.STATUT_PAM] adm_pam.save() else: copy = { k: v for k, v in administre.items() if k != 'a_liste_id_competences'} adm = Administre(**copy) if fields: Administre.objects.bulk_update([adm], fields=fields) return Response({'msg': 'updated'}) except (Http404, APIException): raise except Exception: message = "échec de la mise à jour de l'administré" self.logger.exception(message) raise APIException(message) # GET /api/administres => 0 administrés # POST /api/administres => Crée 1 nouvel administres => id=1 # GET /api/administres/1 => 1 administre précédemment créé # POST /api/administres/1 => 1 administre précédemment créé @class_logger @execution_time_viewset @query_count_viewset class AdministrePAMView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue de l'administre. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = AdministresPamsSerializer filter_backends = [DjangoFilterBackend, RelatedOrderingFilter] # filterset_fields = ['category', 'in_stock'] filterset_class = AdministrePAMFilter ordering_fields = Administres_Pams.Cols.PK ordering = [Administres_Pams.Cols.PK] pagination_class = HeavyDataPagination # important : mettre à jour quand le serializer change def get_queryset(self): Cols = Administres_Pams.Cols Cols_Administre = Administre.Cols ColsFE = FormationEmploi.Cols return Administres_Pams.objects.select_related( Cols.REL_DECISION, Cols.REL_PAM, Cols.O2M_FMOB ).prefetch_related( Prefetch(Cols.REL_ADMINISTRE, queryset = Administre.objects.select_related(Cols_Administre.REL_FONCTION, Cols_Administre.REL_GRADE, Cols_Administre.REL_SOUS_VIVIER ).prefetch_related( Cols_Administre.M2M_COMPETENCES, Prefetch(Cols_Administre.REL_FORMATION_EMPLOI, queryset=FormationEmploi.objects.select_related(ColsFE.REL_MERE)), )) ) def get_serializer(self, *args, **kwargs): ser = super().get_serializer(*args, **kwargs) arg = args[0] if args else None if arg and isinstance(arg, (list, tuple)) and self.request.method == 'GET': # calcule directement, si besoin, les profils et les décisions disponibles pour tous les résultats if CTX_KEY_PROFILES not in ser.context: ser.context[CTX_KEY_PROFILES] = get_profiles_by_adm(self.request.user, *arg) if CTX_KEY_DECISIONS not in ser.context: ser.context[CTX_KEY_DECISIONS] = get_available_decisions(arg, profiles_by_adm=ser.context[CTX_KEY_PROFILES]) return ser @atomic def put(self, request): """La fonction put met à jour une liste d'administres. :type request: rest_framework.request.Request :param request: Request contenant la liste d'administres. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour des administres . """ try: req_data = request.data is_list = isinstance(req_data, list) # validator = AdministreSerializer(data=req_data, many=is_list, partial=True) # validator.is_valid(raise_exception=True) today = date.today() Cols = Administre.Cols # Extraire l'annee if 'pam_id' in req_data[0]: annee_pam = req_data[0]['pam_id'] def copy_data_item(input, pk): """Copie et complète si besoin le dictionnaire en entrée""" result = {**input} if pk and Cols.PK not in result: result[Cols.PK] = pk return result def set_competences(administre_obj, a_comp): """Set competences to administre object""" a_comp_id = [competence.comp_id for competence in a_comp] administre_obj.a_liste_id_competences.set(a_comp_id) return administre_obj if is_list: data = [copy_data_item(item, req_data[idx][Cols.PK]) for idx, item in enumerate(req_data)] fields = [key for key in data[0].keys() if key != Cols.PK] # Direct assignment to the forward side of a many-to-many set is prohibited. Use a_liste_id_competences.set() instead. # Update Administres Pams dans les if et elif et le else et elif est utilisé pour la mise a jour des Administres if Cols.STATUT_PAM in fields: fields_to_update_pam = ['a_statut_pam_annee'] # Creation d'une liste qui contient l'id ( a_id_sap + annee_pam) avec le statut choisi data_statut_pam = [{'id' :str(o['a_id_sap'])+str(annee_pam), 'a_statut_pam_annee' : o[Cols.STATUT_PAM]} for o in data] # Creation des objets Administres Pams objs = [Administres_Pams(**data_item) for data_item in data_statut_pam] # Mise a jour des objets Administres Pams Administres_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) elif 'a_liste_depts_souhaites_pam' in fields: fields_to_update_pam = ['a_liste_depts_souhaites_pam'] # Creation d'une liste qui contient l'id ( a_id_sap + annee_pam) avec le statut choisi data_statut_pam = [{'id' :str(o['a_id_sap'])+str(annee_pam), 'a_liste_depts_souhaites_pam' : o['a_liste_depts_souhaites_pam']} for o in data] # Creation des objets Administres Pams objs = [Administres_Pams(**data_item) for data_item in data_statut_pam] # Mise a jour des objets Administres Pams Administres_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) elif 'a_liste_id_competences' in fields: objs_w_comp = [set_competences(Administre(**without_keys(i, ['a_liste_id_competences'])), i['a_liste_id_competences']) for i in data] elif 'a_liste_zones_geographiques_shm_pam' in fields: fields_to_update_pam = ['a_liste_zones_geographiques_shm_pam'] data_statut_pam = [{'id' :str(o['a_id_sap'])+str(annee_pam), 'a_liste_zones_geographiques_shm_pam' : o['a_liste_zones_geographiques_shm_pam']} for o in data] objs = [Administres_Pams(**data_item) for data_item in data_statut_pam] # Mise a jour des objets Administres Pams Administres_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) else: objs = [Administre(**data_item) for data_item in data] if Cols.STATUT_PAM in fields: list_error = [] for i in range(len(objs)): old_administre = Administre.objects.get(a_id_sap=objs[i].a_id_sap) administre = objs[i] eip = administre.a_eip fe_code = old_administre.formation_emploi_id avis = administre.a_statut_pam old_avis = old_administre.a_statut_pam try: categorie = constants.CATEGORIE_BY_NF[old_administre.a_nf.upper()] except: erreur = 'la catégorie de l\'administré n\'est pas reconnue dans ogure.' raise Exception(erreur) # liste_error = impact_decisions(old_administre, administre, old_avis, avis, eip, fe_code, categorie) elif 'a_domaine_futur_id' in fields or 'a_filiere_futur_id' in fields or 'a_nf_futur' in fields: for i in range(len(objs)): old_administre = Administre.objects.get(a_id_sap=objs[i].a_id_sap) administre = objs[i] dom = administre.a_domaine_futur_id if 'a_domaine_futur_id' in fields else old_administre.a_domaine_futur_id fil = administre.a_filiere_futur_id if 'a_filiere_futur_id' in fields else old_administre.a_filiere_futur_id nf = administre.a_nf_futur if 'a_nf_futur' in fields else old_administre.a_nf_futur cat = nf2categorie(nf) sv_id = generate_sv_id(dom, fil, cat) sv = SousVivier.objects.filter(sv_id=sv_id) try: sv.exists() administre.sous_vivier_id = sv_id if 'sous_vivier_id' not in fields: fields += ['sous_vivier_id'] except: message = f"La mise à jour a échoué car la sous-vivier d'id : {sv_id} n'est pas en base" self.logger.exception(message) raise APIException(message) Administre.objects.bulk_update(objs, fields=fields) return Response({'msg': 'updated'}) except APIException: raise except Exception: message = 'échec de la mise à jour de N administrés' self.logger.exception(message) raise APIException(message) def partial_update(self, request, pk=None): """ La fonction put met à jour un administre. :type request: rest_framework.request.Request :param request: Request contenant l'administre. :type pk: integer :param pk: Primary Key de l'administre. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour de l'administre . """ try: a = get_object_or_404(Administre.objects, a_id_sap=pk) # Permet de déclancher la vérification des droits au niveau de l'objet self.check_object_permissions(self.request, a) req_data = request.data if 'annee_pam' in req_data: annee_pam = req_data.pop('annee_pam') # TODO valider les données (il faut d'abord corriger la MAJ des compétences) # validator = self.serializer_class(data=req_data, partial=True) # validator.is_valid(raise_exception=True) Cols = Administre.Cols # Copie et complète si besoin le dictionnaire en entrée # administre = {**validator.validated_data} administre = {**req_data} administre[Cols.PK] = pk fields = [key for key in administre.keys() if key != Cols.PK] # and key != 'a_liste_id_competences'] if 'a_domaine_futur' in fields: administre['a_domaine_futur_id'] = administre['a_domaine_futur'] del administre['a_domaine_futur'] if 'a_filiere_futur' in fields: administre['a_filiere_futur_id'] = administre['a_filiere_futur'] del administre['a_filiere_futur'] if 'a_domaine_futur' in fields or 'a_filiere_futur' in fields or 'a_nf_futur' in fields: old_administre = Administre.objects.get(a_id_sap=pk) dom = administre['a_domaine_futur_id'] if 'a_domaine_futur' in fields else old_administre.a_domaine_futur_id fil = administre['a_filiere_futur_id'] if 'a_filiere_futur' in fields else old_administre.a_filiere_futur_id nf = administre['a_nf_futur'] if 'a_nf_futur' in fields else old_administre.a_nf_futur cat = nf2categorie(nf) sv_id = generate_sv_id(dom, fil, cat) sv = SousVivier.objects.filter(sv_id=sv_id) try: sv.exists() administre['sous_vivier_id'] = sv_id fields += ['sous_vivier_id'] except: message = f"La mise à jour a échoué car la sous-vivier d'id : {sv_id} n'est pas en base" self.logger.exception(message) raise APIException(message) if Cols.STATUT_PAM in fields: old_administre = Administre.objects.get(a_id_sap=pk) eip = old_administre.a_eip fe_code = old_administre.formation_emploi_id avis = administre[Cols.STATUT_PAM] old_avis = old_administre.a_statut_pam try: categorie = constants.CATEGORIE_BY_NF[old_administre.a_nf.upper()] except: erreur = 'la catégorie de l\'administré n\'est pas reconnue dans ogure.' raise APIException(erreur) if 'a_liste_id_competences' in fields: adm=Administre(pk=pk) if administre['a_liste_id_competences']: adm.a_liste_id_competences.set(administre['a_liste_id_competences'].split(',')) else: adm.a_liste_id_competences.set("") elif Cols.STATUT_PAM in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_statut_pam_annee = administre[Cols.STATUT_PAM] adm_pam.save() elif 'notes_pam' in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.notes_pam = administre['notes_pam'] adm_pam.save() elif 'a_ciat_pam' in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_ciat_pam = administre['a_ciat_pam'] adm_pam.save() elif 'a_specifique_pam' in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_specifique_pam = administre['a_specifique_pam'] adm_pam.save() elif 'a_liste_depts_souhaites_pam' in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_liste_depts_souhaites_pam = administre['a_liste_depts_souhaites_pam'] adm_pam.save() elif 'a_liste_zones_geographiques_shm_pam' in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_liste_zones_geographiques_shm_pam = administre['a_liste_zones_geographiques_shm_pam'] adm_pam.save() elif 'a_situationfuture_notes_fe' in fields: adm_pam = Administres_Pams.objects.get(id = str(pk)+str(annee_pam)) adm_pam.a_situationfuture_notes_fe = administre['a_situationfuture_notes_fe'] adm_pam.save() else: copy = {k: v for k, v in administre.items() if k != 'a_liste_id_competences'} adm = Administre(**copy) if fields: Administre.objects.bulk_update([adm], fields=fields) return Response({'msg': 'updated'}) except (Http404, APIException): raise except Exception: message = "échec de la mise à jour de l'administré" self.logger.exception(message) raise APIException(message) @execution_time_viewset @query_count_viewset @class_logger class PostePAMView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des postes. """ permission_classes = [IsAuthenticated, GestionnairePermission] lookup_value_regex = r"[\w.]+" serializer_class = PostesPamsSerializer pagination_class = HeavyDataPagination filter_backends = [DjangoFilterBackend, RelatedOrderingFilter] filterset_class = PostePAMFilter # ordering_fields = '__all_related__' ordering = ['poste_id','-p_pam'] # important : mettre à jour quand le serializer change def get_queryset(self): """Cette fonction permet d'ajouter plus de logique à l'attribut queryset.""" Cols = Postes_Pams.Cols Poste_Cols = Poste.Cols queryset = Postes_Pams.objects.select_related( Cols.REL_PAM ).prefetch_related( Prefetch(Cols.O2M_DECISION, queryset=Decision.objects.select_related(f'{Decision.Cols.REL_ADMINISTRE}__{Administre.Cols.REL_GRADE}')), Prefetch(Cols.REL_POSTE, queryset=Poste.objects.select_related(Poste_Cols.REL_FONCTION) .prefetch_related( Poste_Cols.M2M_COMPETENCES, Poste_Cols.M2M_SOUS_VIVIERS, Prefetch(Poste_Cols.REL_FORMATION_EMPLOI, queryset=FormationEmploi.objects.select_related(FormationEmploi.Cols.REL_MERE)))) ) sous_viviers_name = self.request.query_params.get('sous_vivier') if sous_viviers_name is not None: queryset = queryset.filter(poste__sous_viviers__in=sous_viviers_name.split('-')) return queryset def put(self, request): """La fonction put met à jour une liste des postes. :type request: rest_framework.request.Request :param request: Request contenant la liste des postes. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour des postes . """ data = request.data self.logger.debug('------------------------- Modification Multiple de Poste verification ---------------------') self.logger.debug('Request data : %s', data) if 'pam_id' in data[0]: annee_pam = data[0]['pam_id'] # serialized = self.serializer_class(data=data, many=isinstance(data, list), partial=True) # serialized.is_valid(raise_exception=True) if isinstance(data, list): # Update multiple elements try: fields = [key for key in data[0].keys() if key != 'p_id' ] if 'p_avis_fe' in fields: fields_to_update_pam = ['p_avis_fe_pam', 'p_avis_pam'] # Creation d'une liste qui contient l'id ( p_id + annee_pam) avec l'avis choisi data_statut_pam = [{'id' :str(o['p_id'])+str(annee_pam), 'p_avis_fe_pam' : o['p_avis_fe'], 'p_avis_pam' : o['p_avis_fe']} for o in data] # Creation des objets Poste Pams objs = [Postes_Pams(**data_item) for data_item in data_statut_pam] # Mise a jour des objets Poste Pams Postes_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) elif 'p_avis' in fields: fields_to_update_pam = ['p_avis_pam'] data_statut_pam = [{'id' :str(o['p_id'])+str(annee_pam), 'p_avis_pam' : o['p_avis']} for o in data] objs = [Postes_Pams(**data_item) for data_item in data_statut_pam] Postes_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) elif 'p_direct_commissionne' in fields: fields_to_update_pam = ['p_direct_commissionne_pam'] data_statut_pam = [{'id' :str(o['p_id'])+str(annee_pam), 'p_direct_commissionne_pam' : o['p_direct_commissionne']} for o in data] objs = [Postes_Pams(**data_item) for data_item in data_statut_pam] Postes_Pams.objects.bulk_update(objs, fields=fields_to_update_pam) elif 'p_itd_cellule' in fields: sous_viviers = sous_viviers_du_cellule(data[0]['p_itd_cellule']) self.logger.debug('Modification Multiple de p_itd_cellule, les sous_viviers de la cellule sont : %s', sous_viviers) if sous_viviers: objs = [Poste(**data[i]) for i in range(len(data))] Poste.objects.bulk_update(objs, fields=fields) objs_w_comp = [Poste(**without_keys(data[i], ['p_itd_cellule'])).sous_viviers.set(sous_viviers) for i in range(len(data))] else: return JsonResponse({'info': f"La mise à jour n'a pas réussi : la cellule '{data[0]['p_itd_cellule']}' n'a pas de sous viviers enregistrés dans l'application."}) elif 'competence_id' not in fields: if 'supression' in fields: elem = data[0] Postes_Pams.objects.filter(p_pam_id=elem['pam_id'],poste_id=elem['p_id']).delete() if 'departement' and 'codeFe' and 'lieu' and 'zoneDeDefense' and 'codeFeMere' and 'etr' in fields: elem = data[0] #generate random id : create compteur de poste en sureff no_sureff = str(Postes_Pams.objects.filter(p_pam_id=str(annee_pam),id__contains='_').count() + 1) pam_actuel = PAM.objects.get(pam_statut='PAM en cours').pam_id #on crée l'instance de formation d'emploi fe = FormationEmploi.objects.get(fe_code=elem['codeFe']) fe_poste_duplique = fe fe_poste_duplique.zone_defense =elem['zoneDeDefense'] fe_poste_duplique.fe_garnison_lieu = elem['lieu'] fe_poste_duplique.save() #créer une copie d'instance de poste qui existe déjà en base: poste = Poste.objects.get(p_id=elem['p_id']) poste_duplique = poste poste_duplique.p_id = poste.p_id + '_'+ no_sureff poste_duplique.p_dep= elem['departement'] poste_duplique.p_fonction = elem['etr'] poste_duplique.formation_emploi = fe_poste_duplique poste_duplique.save() if elem['pam_id'] == pam_actuel: poste_sureffA = Postes_Pams.objects.get(id=elem['p_id'] + elem['pam_id']) poste_sureffA.id = elem['p_id']+ '_'+ no_sureff + elem['pam_id'] poste_sureffA.poste_id = elem['p_id']+ '_'+ no_sureff poste_sureffA.poste = poste_duplique poste_sureffA.info_reo = f"SUREFF {str(annee_pam)}" poste_sureffA.save() poste_sureffA1 = Postes_Pams.objects.get(id=elem['p_id'] + str(int(elem['pam_id'])+1)) poste_sureffA1.id = elem['p_id']+ '_'+ no_sureff +str(int(elem['pam_id'])+1) poste_sureffA1.poste_id = elem['p_id']+ '_'+ no_sureff poste_sureffA1.poste = poste_duplique poste_sureffA1.info_reo = f"SUREFF {str(annee_pam)}" poste_sureffA1.save() else: poste_sureffA1 = Postes_Pams.objects.get(id=elem['p_id'] + elem['pam_id']) poste_sureffA1.id = elem['p_id']+ '_'+ no_sureff + elem['pam_id'] poste_sureffA1.poste_id = elem['p_id']+ '_'+ no_sureff poste_sureffA1.poste = poste_duplique poste_sureffA1.info_reo = f"SUREFF {str(annee_pam)}" poste_sureffA1.save() else: objs = [Poste(**data[i]) for i in range(len(data))] Poste.objects.bulk_update(objs, fields=fields) else: objs_w_comp = [Poste(**without_keys(data[i],['competence_id'])).competences.set(data[i]['competence_id'].split(',')) for i in range(len(data))] except: raise Exception("bulk update error") return Response({'msg': 'updated'}) def partial_update(self, request, pk=None): """ La fonction put met à jour un poste. :type request: rest_framework.request.Request :param request: Request contenant le poste. :type pk: integer :param pk: Primary Key du poste. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour du poste . """ p = get_object_or_404(Poste.objects, p_id=pk) # Permet de déclancher la vérification des droits au niveau de l'objet self.check_object_permissions(self.request, p) poste = request.data self.logger.debug('------------------------- Modification Unitaire de Poste verification ---------------------') self.logger.debug('Request data : %s', poste) if 'annee_pam' in poste: annee_pam = poste.pop('annee_pam') fields_not_updated = ['p_id', 'p_poids_filiere', 'p_poids_nf', 'p_poids_competences'] fields = [key for key in poste.keys() if key not in fields_not_updated] poste['p_id'] = pk if 'competence' in fields: pos=Poste(pk=pk) if poste['competence']: pos.competences.set(poste['competence'].split(',')) else: pos.competences.set("") elif 'sous_vivier_id' in fields: pos=Poste(pk=pk) pos.sous_viviers.add(poste['sous_vivier_id']) elif 'p_avis_fe_pam' in fields: poste_pam = Postes_Pams.objects.get(id = str(pk)+str(annee_pam)) poste_pam.p_avis_pam = poste['p_avis_fe_pam'] poste_pam.p_avis_fe_pam = poste['p_avis_fe_pam'] poste_pam.save() elif 'p_avis_pam' in fields: poste_pam = Postes_Pams.objects.get(id = str(pk)+str(annee_pam)) poste_pam.p_avis_pam = poste['p_avis_pam'] poste_pam.save() elif 'p_direct_commissionne_pam' in fields: poste_pam = Postes_Pams.objects.get(id = str(pk)+str(annee_pam)) poste_pam.p_direct_commissionne_pam = poste['p_direct_commissionne_pam'] poste_pam.save() elif 'p_priorisation_pcp_pam' in fields: poste_pam = Postes_Pams.objects.get(id = str(pk)+str(annee_pam)) poste_pam.p_priorisation_pcp_pam = poste['p_priorisation_pcp_pam'] poste_pam.save() elif 'p_notes_gestionnaire_pam' in fields: poste_pam = Postes_Pams.objects.get(id = str(pk)+str(annee_pam)) poste_pam.p_notes_gestionnaire_pam = poste['p_notes_gestionnaire_pam'] poste_pam.save() else: copy = {k: v for k, v in poste.items() if k != 'competence'} pos = Poste(**copy) # l'orde des if est tres important if 'p_specifique' in fields and poste['p_specifique'] == SpecifiqueChoices.ITD: pos.p_itd_affecte = False fields += ['p_itd_affecte'] elif 'p_itd_cellule' in fields: sous_viviers = sous_viviers_du_cellule(poste['p_itd_cellule']) if sous_viviers: pos.sous_viviers.set(sous_viviers) pos.p_itd_affecte = True fields += ['p_itd_affecte'] else : self.logger.debug(f"La mise à jour n'a pas réussi, La cellule '{poste['p_itd_cellule']}' n'a pas de sous viviers enregistrés dans l'application.") return JsonResponse({'info': f"La mise à jour n'a pas réussi : la cellule '{poste['p_itd_cellule']}' n'a pas de sous viviers enregistrés dans l'application."}) if fields: Poste.objects.bulk_update([pos], fields=fields) return Response({'msg': 'updated'}) @execution_time_viewset @query_count_viewset @class_logger class PosteView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des postes. """ permission_classes = [IsAuthenticated, GestionnairePermission] lookup_value_regex = r"[\w.]+" serializer_class = PosteSerializer pagination_class = HeavyDataPagination filter_backends = [DjangoFilterBackend, RelatedOrderingFilter] filterset_class = PosteFilter ordering_fields = '__all_related__' ordering = ['p_id'] # important : mettre à jour quand le serializer change def get_queryset(self): """Cette fonction permet d'ajouter plus de logique à l'attribut queryset.""" Cols = Poste.Cols queryset = Poste.objects.select_related( Cols.REL_FONCTION ).prefetch_related( Cols.M2M_COMPETENCES, Cols.M2M_SOUS_VIVIERS, Prefetch(Cols.M2M_PAM, queryset=Postes_Pams.objects.select_related(Postes_Pams.Cols.O2M_DECISION)), Prefetch(Cols.O2M_DECISION, queryset=Decision.objects.select_related(f'{Decision.Cols.REL_ADMINISTRE}__{Administre.Cols.REL_GRADE}')), Prefetch(Cols.REL_FORMATION_EMPLOI, queryset=FormationEmploi.objects.select_related(FormationEmploi.Cols.REL_MERE)), ).annotate( p_nb_prepositionne=Sum( Case(When(decisions__de_decision=DecisionChoices.PREPOSITIONNE, then=1), default=0, output_field=IntegerField())), p_nb_positionne=Sum(Case(When(decisions__de_decision=DecisionChoices.POSITIONNE, then=1), default=0, output_field=IntegerField())), p_nb_omi_en_cours=Sum(Case(When(decisions__de_decision=DecisionChoices.OMI_EN_COURS, then=1), default=0, output_field=IntegerField())), p_nb_omi_active=Sum(Case(When(decisions__de_decision=DecisionChoices.OMI_ACTIVE, then=1), default=0, output_field=IntegerField())), ) sous_viviers_name = self.request.query_params.get('sous_vivier') if sous_viviers_name is not None: queryset = queryset.filter(sous_viviers=sous_viviers_name) return queryset def put(self, request): """La fonction put met à jour une liste des postes. :type request: rest_framework.request.Request :param request: Request contenant la liste des postes. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour des postes . """ data = request.data self.logger.debug('------------------------- Modification Multiple de Poste verification ---------------------') self.logger.debug('Request data : %s', data) serialized = self.serializer_class(data=data, many=isinstance(data, list), partial=True) serialized.is_valid(raise_exception=True) if isinstance(data, list): # Update multiple elements try: fields = [key for key in data[0].keys() if key != 'p_id' ] if 'p_itd_cellule' in fields: sous_viviers = sous_viviers_du_cellule(data[0]['p_itd_cellule']) self.logger.debug('Modification Multiple de p_itd_cellule, les sous_viviers de la cellule sont : %s', sous_viviers) if sous_viviers: objs = [Poste(**data[i]) for i in range(len(data))] Poste.objects.bulk_update(objs, fields=fields) objs_w_comp = [Poste(**without_keys(data[i],['p_itd_cellule'])).sous_viviers.set(sous_viviers) for i in range(len(data))] else : return JsonResponse({'info': f"La mise à jour n'a pas réussi, La cellule '{data[0]['p_itd_cellule']}' n'a pas de sous viviers enregistrés dans l'application."}) elif 'competence_id' not in fields: objs = [Poste(**data[i]) for i in range(len(data))] Poste.objects.bulk_update(objs, fields=fields) else: objs_w_comp = [Poste(**without_keys(data[i],['competence_id'])).competences.set(data[i]['competence_id'].split(',')) for i in range(len(data))] except: raise Exception("bulk update error") else: # Update one element a_id_sap = Poste.objects.update(serialized.validated_data) db_instance = Poste.objects.filter( a_id_sap=a_id_sap).first() db_instance.tag.clear() return Response({'msg': 'updated'}) def partial_update(self, request, pk=None): """ La fonction put met à jour un poste. :type request: rest_framework.request.Request :param request: Request contenant le poste. :type pk: integer :param pk: Primary Key du poste. :return: - **Response** (*Response*): Reponse contient un message de la réussite de met à jour du poste . """ p = get_object_or_404(Poste.objects, p_id=pk) # Permet de déclancher la vérification des droits au niveau de l'objet self.check_object_permissions(self.request, p) poste = request.data self.logger.debug('------------------------- Modification Unitaire de Poste verification ---------------------') self.logger.debug('Request data : %s', poste) fields_not_updated = ['p_id', 'p_poids_filiere', 'p_poids_nf', 'p_poids_competences'] fields = [key for key in poste.keys() if key not in fields_not_updated] poste['p_id'] = pk if 'competence' in fields: pos=Poste(pk=pk) if poste['competence']: pos.competences.set(poste['competence'].split(',')) else: pos.competences.set("") elif 'sous_vivier_id' in fields: pos=Poste(pk=pk) pos.sous_viviers.add(poste['sous_vivier_id']) else: copy = { k: v for k, v in poste.items() if k != 'competence'} pos = Poste(**copy) if 'p_avis_fe' in fields: pos.p_avis = poste['p_avis_fe'] fields += ['p_avis'] # l'orde des if est tres important if 'p_specifique' in fields and poste['p_specifique'] == SpecifiqueChoices.ITD: pos.p_itd_affecte = False fields += ['p_itd_affecte'] elif 'p_itd_cellule' in fields: sous_viviers = sous_viviers_du_cellule(poste['p_itd_cellule']) if sous_viviers: pos.sous_viviers.set(sous_viviers) pos.p_itd_affecte = True fields += ['p_itd_affecte'] else : self.logger.debug(f"La mise à jour n'a pas réussi, La cellule '{poste['p_itd_cellule']}' n'a pas de sous viviers enregistrés dans l'application.") return JsonResponse({'info': f"La mise à jour n'a pas réussi, La cellule '{poste['p_itd_cellule']}' n'a pas de sous viviers enregistrés dans l'application."}) if fields: Poste.objects.bulk_update([pos], fields=fields) return Response({'msg': 'updated'}) @execution_time_viewset @query_count_viewset class ListesPreferencesView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des ListesPreferences. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = PreferencesListe queryset = PreferencesListe.objects.all() @execution_time_viewset @query_count_viewset class MarqueView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des marques. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = MarqueSerializer queryset = Marque.objects.all() @execution_time_viewset @query_count_viewset class MarquesGroupeView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des marquegroupes. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = MarquesGroupeSerializer queryset = MarquesGroupe.objects.all() @execution_time_viewset @query_count_viewset class FmobView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des Fmob. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = FmobSerializer queryset = FMOB.objects.all() @execution_time_viewset @query_count_viewset class FiliereView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des filieres. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = FiliereSerializer queryset = Filiere.objects.all() @execution_time_viewset @query_count_viewset class DomaineView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue des domaines. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = DomaineSerializer queryset = Domaine.objects.all() @execution_time_viewset @query_count_viewset class SousVivierAssociationView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue du SousVivierAssociation. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = SousVivierAssociationSerializer queryset = SousVivierAssociation.objects.all() @execution_time_viewset @query_count_viewset class PcpFeGroupeView(viewsets.ModelViewSet): """ Cette classe est dédiée au vue du PcpFeGroupe. """ permission_classes = [IsAuthenticated, GestionnairePermission] serializer_class = PcpFeGroupeSerializer queryset = PcpFeGroupe.objects.all()