github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/directeurs/contraintes.go (about)

     1  package directeurs
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/benoitkugler/goACVE/server/core/datamodel"
    10  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    11  	"github.com/benoitkugler/goACVE/server/documents"
    12  	"github.com/benoitkugler/goACVE/server/shared"
    13  	"github.com/lib/pq"
    14  )
    15  
    16  func (rc DriverCampComplet) loadContraintes(host string) (map[int64]ContrainteComplete, error) {
    17  	cts, err := rc.fetchContraintes()
    18  	if err != nil {
    19  		return nil, err
    20  	}
    21  	var idsDocs rd.Ids
    22  	for _, contrainte := range cts {
    23  		if idDoc := contrainte.IdDocument; idDoc.IsNotNil() {
    24  			idsDocs = append(idsDocs, idDoc.Int64)
    25  		}
    26  	}
    27  	// on ajoute les documents
    28  	rows, err := rc.DB.Query("SELECT * FROM documents WHERE id = ANY($1)", idsDocs.AsSQL())
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	docs, err := rd.ScanDocuments(rows)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	out := make(map[int64]ContrainteComplete, len(cts))
    37  	for id, contrainte := range cts {
    38  		ctC := ContrainteComplete{Contrainte: contrainte}
    39  		if idDoc := contrainte.IdDocument; idDoc.IsNotNil() {
    40  			ctC.Document, err = documents.PublieDocument(rc.Signing, host, docs[idDoc.Int64])
    41  			if err != nil {
    42  				return nil, err
    43  			}
    44  		}
    45  		out[id] = ctC
    46  	}
    47  	return out, nil
    48  }
    49  
    50  // renvoie les contraintes builtin et les contraintes perso, pour les participants, sans leurs documents
    51  func (rc DriverCampComplet) fetchContraintes() (rd.Contraintes, error) {
    52  	// on charge l'équipe pour trouver le directeur
    53  	if err := rc.loadDataEquipiers(); err != nil {
    54  		return nil, err
    55  	}
    56  	builtins := []string{string(rd.CTestNautique)}
    57  	rows, err := rc.DB.Query("SELECT * FROM contraintes WHERE builtin = ANY($1)", pq.StringArray(builtins))
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	contraintes, err := rd.ScanContraintes(rows)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	dir, has := rc.camp.GetDirecteur()
    67  	if !has {
    68  		return contraintes, err
    69  	}
    70  	// on ajoute les contraintes perso
    71  	contraintesPerso, err := rd.SelectContraintesByIdPersonnes(rc.DB, dir.Id)
    72  	for k, v := range contraintesPerso { // fusion
    73  		contraintes[k] = v
    74  	}
    75  	return contraintes, err
    76  
    77  }
    78  
    79  func (rc DriverCampComplet) createContrainte(contrainte rd.Contrainte) error {
    80  	// on charge l'équipe pour trouver le directeur
    81  	if err := rc.loadDataEquipiers(); err != nil {
    82  		return err
    83  	}
    84  	dir, has := rc.camp.GetDirecteur()
    85  	if !has {
    86  		return fmt.Errorf("Un directeur est requis pour ajouter une demande de document personnalisée.")
    87  	}
    88  	// on attache la contrainte au directeur
    89  	contrainte.IdPersonne = rd.NewOptionnalId(dir.Id)
    90  	_, err := contrainte.Insert(rc.DB)
    91  	return err
    92  }
    93  
    94  func (rc DriverCampComplet) updateContrainte(modif rd.Contrainte) (rd.Contrainte, error) {
    95  	// on charge l'équipe pour trouver le directeur
    96  	if err := rc.loadDataEquipiers(); err != nil {
    97  		return modif, err
    98  	}
    99  	dir, has := rc.camp.GetDirecteur()
   100  	if !has {
   101  		return modif, fmt.Errorf("Un directeur est requis pour modifier une demande de document personnalisée.")
   102  	}
   103  	contrainte, err := rd.SelectContrainte(rc.DB, modif.Id)
   104  	if err != nil {
   105  		return modif, err
   106  	}
   107  	if contrainte.Builtin != "" {
   108  		return modif, fmt.Errorf("Cette contrainte est prédéfinie.")
   109  	}
   110  	if !contrainte.IdPersonne.Is(dir.Id) {
   111  		return contrainte, errors.New("Cette contrainte ne vous appartient pas.")
   112  	}
   113  	contrainte.Nom = modif.Nom
   114  	contrainte.Description = modif.Description
   115  	contrainte.MaxDocs = modif.MaxDocs
   116  	return contrainte.Update(rc.DB)
   117  }
   118  
   119  // crée les métas données d'un document à remplir
   120  func (rc DriverCampComplet) lieDocument(host string, idContrainte int64) (documents.PublicDocument, error) {
   121  	// on charge l'équipe pour trouver le directeur
   122  	if err := rc.loadDataEquipiers(); err != nil {
   123  		return documents.PublicDocument{}, err
   124  	}
   125  	dir, has := rc.camp.GetDirecteur()
   126  	if !has {
   127  		return documents.PublicDocument{}, fmt.Errorf("Un directeur est requis pour modifier une demande de document personnalisée.")
   128  	}
   129  
   130  	tx, err := rc.DB.Begin()
   131  	if err != nil {
   132  		return documents.PublicDocument{}, err
   133  	}
   134  
   135  	contrainte, err := rd.SelectContrainte(tx, idContrainte)
   136  	if err != nil {
   137  		return documents.PublicDocument{}, shared.Rollback(tx, err)
   138  	}
   139  	if contrainte.IdDocument.IsNotNil() {
   140  		err = errors.New("Un document est déjà lié à cette contrainte.")
   141  		return documents.PublicDocument{}, shared.Rollback(tx, err)
   142  	}
   143  	if !contrainte.IdPersonne.Is(dir.Id) {
   144  		err = errors.New("Cette contrainte ne vous appartient pas.")
   145  		return documents.PublicDocument{}, shared.Rollback(tx, err)
   146  	}
   147  
   148  	document := rd.Document{Description: "Document à remplir", DateHeureModif: rd.Time(time.Now())}
   149  	document, err = document.Insert(tx)
   150  	if err != nil {
   151  		return documents.PublicDocument{}, shared.Rollback(tx, err)
   152  	}
   153  
   154  	contrainte.IdDocument = rd.NewOptionnalId(document.Id)
   155  	contrainte, err = contrainte.Update(tx)
   156  	if err != nil {
   157  		return documents.PublicDocument{}, shared.Rollback(tx, err)
   158  	}
   159  
   160  	public, err := documents.PublieDocument(rc.Signing, host, document)
   161  	if err != nil {
   162  		return documents.PublicDocument{}, shared.Rollback(tx, err)
   163  	}
   164  	err = tx.Commit()
   165  	return public, err
   166  }
   167  
   168  // supprime une contrainte personnalisée et tous les documents associés
   169  // et retourne le nombre de documents concernés
   170  func (rc DriverCampComplet) deleteContrainte(id int64) (int, error) {
   171  	// on vérifie le propriétaire
   172  	if err := rc.loadDataEquipiers(); err != nil {
   173  		return 0, err
   174  	}
   175  	dir, has := rc.camp.GetDirecteur()
   176  	if !has { // entre autre la contrainte ne peut appartenir au directeur
   177  		return 0, errors.New("La demande de document ne vous appartient pas.")
   178  	}
   179  	contrainte, err := rd.SelectContrainte(rc.DB, id)
   180  	if err != nil {
   181  		return 0, err
   182  	}
   183  	if !contrainte.IdPersonne.Is(dir.Id) || contrainte.Builtin != "" { // bizarre mais on préfère être safe
   184  		return 0, errors.New("La demande de document ne vous appartient pas.")
   185  	}
   186  
   187  	tx, err := rc.DB.Begin()
   188  	if err != nil {
   189  		return 0, err
   190  	}
   191  
   192  	// lien groupes
   193  	_, err = rd.DeleteGroupeContraintesByIdContraintes(tx, id)
   194  	if err != nil {
   195  		return 0, shared.Rollback(tx, err)
   196  	}
   197  
   198  	// lien documents
   199  	rows, err := tx.Query("DELETE FROM document_personnes WHERE id_contrainte = $1 RETURNING id_document", id)
   200  	if err != nil {
   201  		return 0, shared.Rollback(tx, err)
   202  	}
   203  	docIds, err := rd.ScanIds(rows)
   204  	if err != nil {
   205  		return 0, shared.Rollback(tx, err)
   206  	}
   207  
   208  	// contenu
   209  	_, err = rd.DeleteContenuDocumentsByIdDocuments(tx, docIds...)
   210  	if err != nil {
   211  		return 0, shared.Rollback(tx, err)
   212  	}
   213  
   214  	// document eux mêmes
   215  	_, err = rd.DeleteDocumentsByIds(tx, docIds...)
   216  	if err != nil {
   217  		return 0, shared.Rollback(tx, err)
   218  	}
   219  
   220  	// finalement la contrainte elle-même
   221  	_, err = rd.DeleteContrainteById(tx, contrainte.Id)
   222  	if err != nil {
   223  		return 0, shared.Rollback(tx, err)
   224  	}
   225  	err = tx.Commit()
   226  	return len(docIds), err
   227  }
   228  
   229  // remplace les contraintes actuelles des groupes du camp par celles données
   230  func (rc DriverCampComplet) updateGroupesContraintes(contraintes DemandeContraintesIn) error {
   231  	groupes, _, _, err := loadGroupes(rc.DB, rc.camp.Id)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	// on vérifie l'origine des groupes
   236  	for _, cont := range contraintes.GroupeContraintes {
   237  		if _, in := groupes[cont.IdGroupe]; !in {
   238  			return fmt.Errorf("Le groupe %d n'est pas lié au séjour.", cont.IdGroupe)
   239  		}
   240  	}
   241  	for i := range contraintes.CampContraintes {
   242  		contraintes.CampContraintes[i].IdCamp = rc.camp.Id
   243  	}
   244  
   245  	tx, err := rc.DB.Begin()
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	// on supprime les contraintes existantes ...
   251  	_, err = rd.DeleteGroupeContraintesByIdGroupes(tx, groupes.Ids()...)
   252  	if err != nil {
   253  		return shared.Rollback(tx, err)
   254  	}
   255  	_, err = rd.DeleteCampContraintesByIdCamps(tx, rc.camp.Id)
   256  	if err != nil {
   257  		return shared.Rollback(tx, err)
   258  	}
   259  	// ... et on ajoute les nouvelles
   260  	err = rd.InsertManyGroupeContraintes(tx, contraintes.GroupeContraintes...)
   261  	if err != nil {
   262  		return shared.Rollback(tx, err)
   263  	}
   264  	err = rd.InsertManyCampContraintes(tx, contraintes.CampContraintes...)
   265  	if err != nil {
   266  		return shared.Rollback(tx, err)
   267  	}
   268  	err = tx.Commit()
   269  	return err
   270  }
   271  
   272  func resoudContraintes(part datamodel.AccesParticipant) rd.Set {
   273  	cts := rd.NewSet()
   274  	// on regroupe les ids des contraintes
   275  	for _, campContrainte := range part.Base.CampContraintes[part.RawData().IdCamp] {
   276  		cts.Add(campContrainte.IdContrainte)
   277  	}
   278  	groupe, hasGroupe := part.GetGroupe()
   279  	if hasGroupe {
   280  		for _, campContrainte := range part.Base.GroupeContraintes[groupe.Id] {
   281  			cts.Add(campContrainte.IdContrainte)
   282  		}
   283  	}
   284  	return cts
   285  }
   286  
   287  // renvoie le nombre de participants (du camp courant)
   288  // ayant mis un ligne un document pour la contrainte donnée
   289  func (rc DriverCampComplet) previewDocsContrainte(idContrainte int64) PreviewDocumentsParticipantsOut {
   290  	participants := append(rc.camp.GetInscrits(nil), rc.camp.GetAttente(nil)...)
   291  
   292  	cache := rc.camp.Base.ResoudDocumentsPersonnes()
   293  	var out PreviewDocumentsParticipantsOut
   294  	for _, part := range participants {
   295  		cts := resoudContraintes(part)
   296  		if !cts.Has(idContrainte) {
   297  			// on ignore les documents non exigés
   298  			continue
   299  		}
   300  		out.NbDemandes++
   301  		for _, doc := range part.GetPersonne().GetDocuments(cache) {
   302  			// on ne garde que les docs concerné
   303  			if idContrainte == doc.GetContrainte().Id {
   304  				out.NbRecus++
   305  				break
   306  			}
   307  		}
   308  	}
   309  	return out
   310  }
   311  
   312  // renvoie les documents des participants (de tous les groupes) correspondant à la contrainte
   313  // ou, si elle est est nulle, à toutes les contraintes appliquées aux groupes
   314  func (rc DriverCampComplet) downloadDocsParticipants(idContrainte rd.OptionnalId) (*bytes.Buffer, error) {
   315  	participants := append(rc.camp.GetInscrits(nil), rc.camp.GetAttente(nil)...)
   316  	datamodel.TriParticipants(participants, true)
   317  
   318  	cache := rc.camp.Base.ResoudDocumentsPersonnes()
   319  	var idsDocs rd.Ids
   320  	prefixes := map[int64]string{}
   321  	for _, part := range participants {
   322  		cts := resoudContraintes(part)
   323  		if idContrainte.Valid && !cts.Has(idContrainte.Int64) {
   324  			// on ignore les documents non exigés
   325  			continue
   326  		}
   327  		personne := part.GetPersonne()
   328  		for _, doc := range personne.GetDocuments(cache) {
   329  			docCId := doc.GetContrainte().Id
   330  			// si la contrainte demandée est non nulle, on ne garde que les docs concerné
   331  			// si la contrainte est nulle, on renvoie tous les documents exigés
   332  			if idContrainte.Is(docCId) || (idContrainte.IsNil() && cts.Has(docCId)) {
   333  				idsDocs = append(idsDocs, doc.Id)
   334  				prefixes[doc.Id] = (doc.GetContrainte().Nom + " " + personne.RawData().NomPrenom()).String()
   335  			}
   336  		}
   337  
   338  	}
   339  	return rc.packageDocs(idsDocs.AsSet().Keys(), prefixes)
   340  }