[scodoc-devel] [SVN] Scolar : [1795] - Gere plusieurs responsables de semestre ( directeurs d'etudes);

eviennet at lipn.univ-paris13.fr eviennet at lipn.univ-paris13.fr
Mar 18 Sep 21:04:22 CEST 2018


Une pièce jointe HTML a été nettoyée...
URL: https://listes.univ-paris13.fr/pipermail/scodoc-devel/attachments/20180918/dfa41c39/attachment-0001.htm 
-------------- section suivante --------------
Modified: branches/ScoDoc7/VERSION.py
===================================================================
--- branches/ScoDoc7/VERSION.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/VERSION.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -8,6 +8,7 @@
 SCONEWS = """
 <h4>Année 2018</h4>
 <ul>
+<li>Co-responsables de semestres</li>
 <li>Amélioration page d'accueil département</li>
 <li>Corrections diverses et petites améliorations</li>
 <li>Avis de poursuites d'études plus robustes et configurables</li>

Modified: branches/ScoDoc7/ZNotes.py
===================================================================
--- branches/ScoDoc7/ZNotes.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/ZNotes.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -857,6 +857,8 @@
         if args['etapes']:
             args['formsemestre_id'] = formsemestre_id
             sco_formsemestre.write_formsemestre_etapes(self, args)
+        if args['responsables']:
+            sco_formsemestre.write_formsemestre_responsables(self, args)
         
         # create default partition
         partition_id = sco_groups.partition_create(self, formsemestre_id, default=True, redirect=0, REQUEST=REQUEST)
@@ -925,7 +927,7 @@
     view_formsemestre_by_etape = sco_formsemestre.view_formsemestre_by_etape
     
     def _check_access_diretud(self, formsemestre_id, REQUEST, required_permission=ScoImplement):
-        """Check if access granted: responsable_id or ScoImplement
+        """Check if access granted: responsable or ScoImplement
         Return True|False, HTML_error_page
         """
         authuser = REQUEST.AUTHENTICATED_USER
@@ -933,13 +935,13 @@
         header = self.sco_header(page_title='Accès interdit',
                                  REQUEST=REQUEST)
         footer = self.sco_footer(REQUEST)
-        if ((sem['responsable_id'] != str(authuser))
+        if ((str(authuser) not in sem['responsables'])
             and not authuser.has_permission(required_permission,self)):
             return False, '\n'.join( [
                 header,
                 '<h2>Opération non autorisée pour %s</h2>' % authuser,
                 '<p>Responsable de ce semestre : <b>%s</b></p>'
-                % sem['responsable_id'],
+                % ', '.join(sem['responsables']),
                 footer ])
         else:
             return True, ''
@@ -1468,7 +1470,7 @@
         # admin, resp. module ou resp. semestre
         if (uid != M['responsable_id']
             and not authuser.has_permission(ScoImplement, self)
-            and uid != sem['responsable_id']):
+            and (uid not in sem['responsables'])):
             if raise_exc:
                 raise AccessDenied('Modification impossible pour %s' % uid)
             else:
@@ -1490,7 +1492,7 @@
         uid = str(authuser)
         # admin ou resp. semestre avec flag resp_can_change_resp
         if (not authuser.has_permission(ScoImplement, self)
-            and ((uid != sem['responsable_id'])
+            and ((uid not in sem['responsables'])
                  or (not sem['resp_can_change_ens']))):
                 raise AccessDenied('Modification impossible pour %s' % uid)
         return M, sem
@@ -1807,7 +1809,7 @@
         M = self.do_moduleimpl_list( args={ 'moduleimpl_id':moduleimpl_id } )[0]
         sem = sco_formsemestre.get_formsemestre(self, M['formsemestre_id'])
         
-        if (not authuser.has_permission(ScoEditAllEvals,self)) and uid != M['responsable_id'] and uid != sem['responsable_id']:
+        if (not authuser.has_permission(ScoEditAllEvals,self)) and uid != M['responsable_id'] and uid not in sem['responsables']:
             if sem['ens_can_edit_eval']:
                 for ens in M['ens']:
                     if ens['ens_id'] == uid:
@@ -2103,11 +2105,11 @@
                 
         if sco_parcours_dut.formsemestre_has_decisions(self, sem['formsemestre_id']):
             # il y a des décisions de jury dans ce semestre !
-            return authuser.has_permission(ScoEditAllNotes,self) or uid == sem['responsable_id']
+            return authuser.has_permission(ScoEditAllNotes,self) or uid in sem['responsables']
         else:
             if ((not authuser.has_permission(ScoEditAllNotes,self))
                 and uid != M['responsable_id']
-                and uid != sem['responsable_id']):
+                and uid not in sem['responsables']):
                 # enseignant (chargé de TD) ?
                 if allow_ens:
                     for ens in M['ens']:
@@ -2263,7 +2265,7 @@
             edit = 0
         sem = sco_formsemestre.get_formsemestre(self, formsemestre_id)
         # check custom access permission
-        can_edit_app = ((str(authuser) == sem['responsable_id'])
+        can_edit_app = ((str(authuser) in sem['responsables'])
                         or (authuser.has_permission(ScoEtudInscrit,self)))
         if not can_edit_app:
             raise AccessDenied("vous n'avez pas le droit d'ajouter une appreciation")
@@ -2332,7 +2334,7 @@
         if authuser.has_permission(ScoEtudChangeGroups, self):
             return True # admin, chef dept
         uid = str(authuser)
-        if uid == sem['responsable_id']:
+        if uid in sem['responsables']:
             return True
         return False
 
@@ -2362,7 +2364,7 @@
         if authuser.has_permission(ScoImplement, self):
             return True # admin, chef dept
         uid = str(authuser)
-        if uid == sem['responsable_id']:
+        if uid in sem['responsables']:
             return True
 
         return False

Modified: branches/ScoDoc7/config/postupgrade-db.py
===================================================================
--- branches/ScoDoc7/config/postupgrade-db.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/config/postupgrade-db.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -578,7 +578,17 @@
     check_field(cnx, 'notes_modules', 'module_type',
                 ['alter table notes_modules add column module_type int',
                 ])
-    
+
+    # Responsables de semestres
+    check_table( cnx, 'notes_formsemestre_responsables', [
+    """CREATE TABLE notes_formsemestre_responsables (
+    formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE,
+    responsable_id text NOT NULL,
+    UNIQUE(formsemestre_id, responsable_id)
+    ) WITH OIDS;""",
+    """INSERT into notes_formsemestre_responsables (formsemestre_id, responsable_id) SELECT formsemestre_id, responsable_id FROM notes_formsemestre WHERE responsable_id is not NULL;""",
+    """ALTER table notes_formsemestre DROP column responsable_id;""",
+        ])
     # Add here actions to performs after upgrades:
     cnx.commit()
     cnx.close()

Modified: branches/ScoDoc7/misc/createtables.sql
===================================================================
--- branches/ScoDoc7/misc/createtables.sql	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/misc/createtables.sql	2018-09-18 19:04:21 UTC (rev 1795)
@@ -368,7 +368,7 @@
 	titre text,
 	date_debut date,
 	date_fin   date,
-	responsable_id text,
+	-- responsable_id text,
 	-- gestion_absence integer default 1,   -- XXX obsolete
 	-- bul_show_decision integer default 1, -- XXX obsolete
 	-- bul_show_uevalid integer default 1,  -- XXX obsolete
@@ -392,6 +392,14 @@
 	elt_annee_apo text -- code element annee Apogee, eg VRT1A ou V2INLA,V2INCA
 ) WITH OIDS;
 
+-- id des utilsateurs responsables (aka directeurs des etudes) du semestre:
+CREATE TABLE notes_formsemestre_responsables (
+    formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE,
+    responsable_id text NOT NULL,
+    UNIQUE(formsemestre_id, responsable_id)
+) WITH OIDS;
+
+-- Etape Apogee associes au semestre:
 CREATE TABLE notes_formsemestre_etapes (
     formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE,
     etape_apo text NOT NULL

Modified: branches/ScoDoc7/sco_abs_notification.py
===================================================================
--- branches/ScoDoc7/sco_abs_notification.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_abs_notification.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -110,9 +110,11 @@
     
     if abs_notify_is_above_threshold(context, etudid, nbabs, nbabsjust, formsemestre_id):
         if sem and prefs['abs_notify_respsem']:
-            u = context.Users.user_info(sem['responsable_id'])
-            if u['email']:
-                destinations.append(u['email'])
+            # notifie chaque responsable du semestre
+            for responsable_id in sem['responsables']:
+                u = context.Users.user_info(responsable_id)
+                if u['email']:
+                    destinations.append(u['email'])
         if prefs['abs_notify_chief'] and prefs['email_chefdpt']:
             destinations.append(prefs['email_chefdpt'])
         if prefs['abs_notify_email']:

Modified: branches/ScoDoc7/sco_apogee_csv.py
===================================================================
--- branches/ScoDoc7/sco_apogee_csv.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_apogee_csv.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -774,7 +774,17 @@
         et ceux des semestres ScoDoc associés
         Retourne deux ensembles
         """
-        maq_elems = { self.cols[col_id]['Code'] for col_id in self.col_ids[4:] }
+        try:
+            maq_elems = { self.cols[col_id]['Code'] for col_id in self.col_ids[4:] }
+        except KeyError:
+            # une colonne déclarée dans l'en-tête n'est pas présente
+            declared = self.col_ids[4:] # id des colones dans l'en-tête
+            present = sorted(self.cols.keys()) # colones presentes
+            log('Fichier Apogee invalide:')            
+            log('Colonnes declarees: %s' % declared)
+            log('Colonnes presentes: %s' % present )
+            raise FormatError('''Fichier Apogee invalide<br/>Colonnes declarees: <tt>%s</tt>
+            <br/>Colonnes presentes: <tt>%s</tt>''' % (declared, present))
         # l'ensemble de tous les codes des elements apo des semestres:
         sem_elems = reduce( set.union, self.get_codes_by_sem().values(), set() )
         

Modified: branches/ScoDoc7/sco_archives.py
===================================================================
--- branches/ScoDoc7/sco_archives.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_archives.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -201,6 +201,7 @@
             log('Archiver.get: invalid filename "%s"' % filename)
             raise ValueError('invalid filename')
         fname = os.path.join(archive_id, filename)
+        log('reading archive file %s' % fname)
         return open(fname).read()
 
     def get_archived_file(self, context, REQUEST, oid, archive_name, filename):

Modified: branches/ScoDoc7/sco_bulletins.py
===================================================================
--- branches/ScoDoc7/sco_bulletins.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_bulletins.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -58,7 +58,10 @@
     (preferences bul_pdf_*)
     """
     C = sem.copy()
-    C['responsable'] = context.Users.user_info(user_name=sem['responsable_id'])['prenomnom']
+    C['responsable'] = ' ,'.join(
+        [ context.Users.user_info(user_name=responsable_id)['prenomnom']
+          for responsable_id in sem['responsables'] ] )
+    
     annee_debut = sem['date_debut'].split('/')[2]
     annee_fin = sem['date_fin'].split('/')[2]
     if annee_debut != annee_fin:
@@ -636,7 +639,7 @@
     sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
     return (context.get_preference('bul_mail_allowed_for_all', formsemestre_id)
             or authuser.has_permission(ScoImplement, context)
-            or sem['responsable_id'] == str(authuser))
+            or str(authuser) in sem['responsables'])
 
 
 
@@ -807,7 +810,7 @@
     menuBul = [
         { 'title' : 'Réglages bulletins',
           'url' : 'formsemestre_edit_options?formsemestre_id=%s&amp;target_url=%s' % (formsemestre_id, qurl),
-          'enabled' : (uid == sem['responsable_id']) or authuser.has_permission(ScoImplement, context),
+          'enabled' : (uid in sem['responsables']) or authuser.has_permission(ScoImplement, context),
           },
         { 'title' : 'Version papier (pdf, format "%s")' % sco_bulletins_generator.bulletin_get_class_name_displayed(context, formsemestre_id),
           'url' : url + '?formsemestre_id=%s&amp;etudid=%s&amp;format=pdf&amp;version=%s' % (formsemestre_id,etudid,version),
@@ -825,7 +828,7 @@
           },
         { 'title' : 'Ajouter une appréciation',
           'url' : 'appreciation_add_form?etudid=%s&amp;formsemestre_id=%s' % (etudid, formsemestre_id),
-          'enabled' : ((authuser == sem['responsable_id'])
+          'enabled' : ((authuser in sem['responsables'])
                        or (authuser.has_permission(ScoEtudInscrit,context)))
           },
         { 'title' : "Enregistrer une validation d'UE antérieure",

Modified: branches/ScoDoc7/sco_bulletins_standard.py
===================================================================
--- branches/ScoDoc7/sco_bulletins_standard.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_bulletins_standard.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -123,7 +123,7 @@
         # ---- APPRECIATIONS
         # le dir. des etud peut ajouter des appreciations,
         # mais aussi le chef (perm. ScoEtudInscrit)
-        can_edit_app = ((str(self.authuser) == self.infos['responsable_id'])
+        can_edit_app = ((str(self.authuser) in self.infos['responsables'])
                         or (self.authuser.has_permission(ScoEtudInscrit,self.context)))
         H.append('<div class="bull_appreciations">')
         for app in self.infos['appreciations_list']:

Modified: branches/ScoDoc7/sco_formsemestre.py
===================================================================
--- branches/ScoDoc7/sco_formsemestre.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_formsemestre.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -40,7 +40,7 @@
     'notes_formsemestre',
     'formsemestre_id',
     ('formsemestre_id', 'semestre_id', 'formation_id','titre',
-     'date_debut', 'date_fin', 'responsable_id',
+     'date_debut', 'date_fin',
      'gestion_compensation', 'gestion_semestrielle',
      'etat', 'bul_hide_xml', 'bul_bgcolor',
      'modalite', 'resp_can_edit', 'resp_can_change_ens',
@@ -82,9 +82,11 @@
     
     sems = _formsemestreEditor.list(cnx, *a, **kw)
     
-    # Ajoute les étapes Apogee:
+    # Ajoute les étapes Apogee et les responsables:
     for sem in sems:
         sem['etapes'] = read_formsemestre_etapes(context, sem['formsemestre_id'])
+        sem['responsables'] = read_formsemestre_responsables(context, sem['formsemestre_id'])
+    
     # Filtre sur code etape si indiqué:
     if 'args' in kw:
         etape = kw['args'].get('etape_apo', None)
@@ -161,6 +163,7 @@
     sem['session_id'] = sco_formsemestre_edit.get_formsemestre_session_id(context, sem, F, parcours)
     sem['etapes'] = read_formsemestre_etapes(context, sem['formsemestre_id'])
     sem['etapes_apo_str'] = formsemestre_etape_apo_str(sem)
+    sem['responsables'] = read_formsemestre_responsables(context, sem['formsemestre_id'])
 
 def formsemestre_etape_apo_str(sem):
     "chaine décrivant le(s) codes étapes Apogée"
@@ -176,9 +179,21 @@
 
     _formsemestreEditor.edit(cnx, sem, **kw )
     write_formsemestre_etapes(context, sem)
-        
+    write_formsemestre_responsables(context, sem)
+    
     context._inval_cache(formsemestre_id=sem['formsemestre_id']) #> modif formsemestre
 
+def read_formsemestre_responsables(context, formsemestre_id):
+    """recupere liste des responsables de ce semestre
+    :returns: liste de chaines
+    """
+    r = SimpleDictFetch(context, "SELECT responsable_id FROM notes_formsemestre_responsables WHERE formsemestre_id = %(formsemestre_id)s",
+                        { 'formsemestre_id' : formsemestre_id } )
+    return [ x['responsable_id'] for x in r ]
+
+def write_formsemestre_responsables(context, sem):
+    return _write_formsemestre_aux(context, sem, 'responsables', 'responsable_id')
+
 def read_formsemestre_etapes(context, formsemestre_id):
     """recupere liste des codes etapes associés à ce semestre
     :returns: liste d'instance de ApoEtapeVDI
@@ -188,20 +203,26 @@
     return [ ApoEtapeVDI(x['etape_apo']) for x in r if x['etape_apo'] ]
 
 def write_formsemestre_etapes(context, sem):
-    ""
+    return _write_formsemestre_aux(context, sem, 'etapes', 'etape_apo')
+
+def _write_formsemestre_aux(context, sem, fieldname, valuename):
+    """fieldname: 'etapes' ou 'responsables'
+    valuename: 'etape_apo' ou 'responsable_id'
+    """
     if not 'etapes' in sem:
         return
     cnx = context.GetDBConnexion(autocommit=False)
     cursor = cnx.cursor(cursor_factory=ScoDocCursor)
+    tablename = 'notes_formsemestre_' + fieldname
     try:
-        cursor.execute("DELETE from notes_formsemestre_etapes where formsemestre_id = %(formsemestre_id)s", { 'formsemestre_id' : sem['formsemestre_id'] } )
-        for etape_apo in sem['etapes']:
-            if etape_apo:
-                cursor.execute("INSERT INTO notes_formsemestre_etapes (formsemestre_id, etape_apo) VALUES (%(formsemestre_id)s, %(etape_apo)s)", 
+        cursor.execute('DELETE from ' + tablename + ' where formsemestre_id = %(formsemestre_id)s', { 'formsemestre_id' : sem['formsemestre_id'] } )
+        for item in sem[fieldname]:
+            if item:
+                cursor.execute('INSERT INTO ' + tablename + ' (formsemestre_id, ' + valuename + ') VALUES (%(formsemestre_id)s, %(' + valuename + ')s)', 
                                { 'formsemestre_id' : sem['formsemestre_id'],
-                                 'etape_apo' : str(etape_apo) } )     
+                                 valuename : str(item) } )     
     except:
-        log("Warning: exception in write_formsemestre_etapes !")
+        log("Warning: exception in write_formsemestre_aux !")
         cnx.rollback()
         raise
     cnx.commit()
@@ -273,8 +294,8 @@
      
 def sem_set_responsable_name(context, sem):
     "ajoute champs responsable_name"
-    user_info = context.Users.user_info(sem['responsable_id'])
-    sem['responsable_name'] = user_info['nomprenom']
+    sem['responsable_name'] = ', '.join([ context.Users.user_info(responsable_id)['nomprenom']
+                                          for responsable_id in sem['responsables'] ])
 
 def sem_in_annee_scolaire(context, sem, year=False, REQUEST=None):
     """n'utilise que la date de debut, pivot au 1er aout

Modified: branches/ScoDoc7/sco_formsemestre_edit.py
===================================================================
--- branches/ScoDoc7/sco_formsemestre_edit.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_formsemestre_edit.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -91,8 +91,8 @@
     """
     sem = sem or sco_formsemestre.get_formsemestre(context, formsemestre_id)
     authuser = REQUEST.AUTHENTICATED_USER
-    if not authuser.has_permission(ScoImplement,context):
-        if not sem['resp_can_edit'] or str(authuser) != sem['responsable_id']:
+    if not authuser.has_permission(ScoImplement,context): # pas chef
+        if not sem['resp_can_edit'] or str(authuser) not in sem['responsables']:
             return False
     return sem
 
@@ -108,7 +108,7 @@
             # il faut ScoImplement pour creer un semestre
             raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
         else:
-            if not sem['resp_can_edit'] or str(authuser) != sem['responsable_id']:
+            if not sem['resp_can_edit'] or str(authuser) not in sem['responsables']:
                 raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
     
     # Liste des enseignants avec forme pour affichage / saisie avec suggestion
@@ -147,8 +147,10 @@
         for x in ams:
             initvalues[str(x['module_id'])] = login2display.get(x['responsable_id'], x['responsable_id'])
         
-        initvalues['responsable_id'] = login2display.get(sem['responsable_id'], sem['responsable_id'])
-
+        initvalues['responsable_id'] = login2display.get(sem['responsables'][0], sem['responsables'][0])
+        if len(sem['responsables']) > 1:
+            initvalues['responsable_id2'] = login2display.get(sem['responsables'][1], sem['responsables'][1])
+    
     # Liste des ID de semestres
     cnx = context.GetDBConnexion()
     cursor = cnx.cursor(cursor_factory=ScoDocCursor)
@@ -206,14 +208,27 @@
                              'title' : 'Directeur des études',
                              'explanation' : 'taper le début du nom et choisir dans le menu',
                              'allowed_values' : allowed_user_names,
-                             'allow_null' : False,
+                             'allow_null' : False, # il faut au moins un responsable de semestre
                              'text_suggest_options' : { 
                                              'script' : 'Users/get_userlist_xml?',
                                              'varname' : 'start',
                                              'json': False,
                                              'noresults' : 'Valeur invalide !',
                                              'timeout':60000 }
-                             }), 
+                             }),
+        ('responsable_id2', { 'input_type' : 'text_suggest',
+                             'size' : 50,
+                             'title' : 'Co-directeur des études',
+                             'explanation' : '',
+                             'allowed_values' : allowed_user_names,
+                             'allow_null' : True, # optionnel
+                             'text_suggest_options' : { 
+                                             'script' : 'Users/get_userlist_xml?',
+                                             'varname' : 'start',
+                                             'json': False,
+                                             'noresults' : 'Valeur invalide !',
+                                             'timeout':60000 }
+                             }),                             
         ('titre', { 'size' : 40, 'title' : 'Nom de ce semestre',
                     'explanation' : """n'indiquez pas les dates, ni le semestre, ni la modalité dans le titre: ils seront automatiquement ajoutés <input type="button" value="remettre titre par défaut" onClick="document.tf.titre.value='%s';"/>""" % _default_sem_title(F)
                     }),
@@ -446,10 +461,15 @@
 
         # remap les identifiants de responsables:
         tf[2]['responsable_id'] = context.Users.get_user_name_from_nomplogin(tf[2]['responsable_id'])
+        tf[2]['responsable_id2'] = context.Users.get_user_name_from_nomplogin(tf[2]['responsable_id2'])
+        tf[2]['responsables'] = [ tf[2]['responsable_id'] ]
+        if  tf[2]['responsable_id2']:
+            tf[2]['responsables'].append( tf[2]['responsable_id2'] )
+        
         for module_id in tf[2]['tf-checked']:
             mod_resp_id = context.Users.get_user_name_from_nomplogin(tf[2][module_id])
             if mod_resp_id is None:
-                # Si un module n'a pas de responsable (ou inconnu), l'affecte au directeur des etudes:
+                # Si un module n'a pas de responsable (ou inconnu), l'affecte au 1er directeur des etudes:
                 mod_resp_id = tf[2]['responsable_id']
             tf[2][module_id] = mod_resp_id
 
@@ -573,7 +593,8 @@
     
     initvalues = {
         'formsemestre_id' : sem['formsemestre_id'],
-        'responsable_id' : login2display.get(sem['responsable_id'], sem['responsable_id']) }
+        'responsable_id' : login2display.get(sem['responsables'][0], sem['responsables'][0])
+        }
     
     H = [
         context.html_sem_header(REQUEST, 'Copie du semestre', sem,
@@ -638,7 +659,7 @@
 
 
 def do_formsemestre_clone(context, orig_formsemestre_id, 
-                          responsable_id, 
+                          responsable_id, # new resp. 
                           date_debut, date_fin,  # 'dd/mm/yyyy'
                           clone_evaluations=False,
                           clone_partitions=False,
@@ -652,7 +673,7 @@
     # 1- create sem
     args = orig_sem.copy()
     del args['formsemestre_id']
-    args['responsable_id'] = responsable_id
+    args['responsables'] = [ responsable_id ]
     args['date_debut'] = date_debut
     args['date_fin'] = date_fin
     args['etat'] = 1 # non verrouillé

Modified: branches/ScoDoc7/sco_formsemestre_status.py
===================================================================
--- branches/ScoDoc7/sco_formsemestre_status.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_formsemestre_status.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -181,22 +181,22 @@
           },
         { 'title' : 'Modifier le semestre',
           'url' : 'formsemestre_editwithmodules?formation_id=%(formation_id)s&amp;formsemestre_id=%(formsemestre_id)s' % sem,
-          'enabled' : (authuser.has_permission(ScoImplement, context) or (sem['responsable_id'] == str(REQUEST.AUTHENTICATED_USER) and sem['resp_can_edit'])) and (sem['etat'] == '1'),
+          'enabled' : (authuser.has_permission(ScoImplement, context) or (str(REQUEST.AUTHENTICATED_USER) in sem['responsables'] and sem['resp_can_edit'])) and (sem['etat'] == '1'),
           'helpmsg' : 'Modifie le contenu du semestre (modules)'
           },
         { 'title' : 'Préférences du semestre',
           'url' : 'formsemestre_edit_preferences?formsemestre_id=%(formsemestre_id)s' % sem,
-          'enabled' : (authuser.has_permission(ScoImplement, context) or (sem['responsable_id'] == str(REQUEST.AUTHENTICATED_USER) and sem['resp_can_edit'])) and (sem['etat'] == '1'),
+          'enabled' : (authuser.has_permission(ScoImplement, context) or (str(REQUEST.AUTHENTICATED_USER) in sem['responsables'] and sem['resp_can_edit'])) and (sem['etat'] == '1'),
           'helpmsg' : 'Préférences du semestre'
           },
         { 'title' : 'Réglages bulletins',
           'url' :  'formsemestre_edit_options?formsemestre_id=' + formsemestre_id,
-          'enabled' : (uid == sem['responsable_id']) or authuser.has_permission(ScoImplement, context),
+          'enabled' : (uid in sem['responsables']) or authuser.has_permission(ScoImplement, context),
           'helpmsg' : 'Change les options'
           },
         { 'title' : change_lock_msg,
           'url' :  'formsemestre_change_lock?formsemestre_id=' + formsemestre_id,
-          'enabled' : (uid == sem['responsable_id']) or authuser.has_permission(ScoImplement, context),
+          'enabled' : (uid in sem['responsables']) or authuser.has_permission(ScoImplement, context),
           'helpmsg' : ''
           },
         { 'title' : 'Description du semestre',
@@ -473,9 +473,9 @@
     
     inscrits = context.Notes.do_formsemestre_inscription_list( args={ 'formsemestre_id' : formsemestre_id } )
     sem['nbinscrits'] = len(inscrits)
-    u = context.Users.user_info(sem['responsable_id'])
-    sem['resp'] = u['prenomnom']
-    sem['nomcomplet'] = u['nomcomplet']
+    uresps = [ context.Users.user_info(responsable_id) for responsable_id in sem['responsables'] ]
+    sem['resp'] = ', '.join( [ u['prenomnom'] for u in uresps ] )
+    sem['nomcomplet'] =  ', '.join( [ u['nomcomplet'] for u in uresps ] )
 
     
 # Description du semestre sous forme de table exportable

Modified: branches/ScoDoc7/sco_ue_external.py
===================================================================
--- branches/ScoDoc7/sco_ue_external.py	2018-09-11 21:49:36 UTC (rev 1794)
+++ branches/ScoDoc7/sco_ue_external.py	2018-09-18 19:04:21 UTC (rev 1795)
@@ -74,7 +74,7 @@
     # Contrôle d'accès:
     authuser = REQUEST.AUTHENTICATED_USER
     if not authuser.has_permission(ScoImplement,context):
-        if not sem['resp_can_edit'] or str(authuser) != sem['responsable_id']:
+        if not sem['resp_can_edit'] or str(authuser) not in sem['responsables']:
             raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
     #
     formation_id = sem['formation_id']
@@ -110,7 +110,7 @@
     moduleimpl_id = context.do_moduleimpl_create( {
         'module_id' : module_id,
         'formsemestre_id' : formsemestre_id,
-        'responsable_id' :  sem['responsable_id']
+        'responsable_id' :  sem['responsables'][0] # affecte le 1er responsable du semestre comme resp. du module
         })
 
     return moduleimpl_id
@@ -171,7 +171,7 @@
     # Contrôle d'accès:
     authuser = REQUEST.AUTHENTICATED_USER
     if not authuser.has_permission(ScoImplement,context):
-        if not sem['resp_can_edit'] or str(authuser) != sem['responsable_id']:
+        if not sem['resp_can_edit'] or str(authuser) not in sem['responsables']:
             raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
 
     etud = context.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0]


Plus d'informations sur la liste de diffusion scodoc-devel