<?php
/**
 * @version $Id$
 * @author Thomas Crespin <thomas.crespin@sesamath.net>
 * @copyright Thomas Crespin 2009-2022
 * 
 * ****************************************************************************************************
 * SACoche <https://sacoche.sesamath.net> - Suivi d’Acquisitions de Compétences
 * © Thomas Crespin pour Sésamath <https://www.sesamath.net> - Tous droits réservés.
 * Logiciel placé sous la licence libre Affero GPL 3 <https://www.gnu.org/licenses/agpl-3.0.html>.
 * ****************************************************************************************************
 * 
 * Ce fichier est une partie de SACoche.
 * 
 * SACoche est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant les termes 
 * de la “GNU Affero General Public License” telle que publiée par la Free Software Foundation :
 * soit la version 3 de cette licence, soit (à votre gré) toute version ultérieure.
 * 
 * SACoche est distribué dans l’espoir qu’il vous sera utile, mais SANS AUCUNE GARANTIE :
 * sans même la garantie implicite de COMMERCIALISABILITÉ ni d’ADÉQUATION À UN OBJECTIF PARTICULIER.
 * Consultez la Licence Publique Générale GNU Affero pour plus de détails.
 * 
 * Vous devriez avoir reçu une copie de la Licence Publique Générale GNU Affero avec SACoche ;
 * si ce n’est pas le cas, consultez : <http://www.gnu.org/licenses/>.
 * 
 */

if(!defined('SACoche')) {exit('Ce fichier ne peut être appelé directement !');}
if(($_SESSION['SESAMATH_ID']==ID_DEMO)&&($_POST['f_action']!='initialiser')){Json::end( FALSE , 'Action désactivée pour la démo.' );}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Récupération des valeurs transmises
// ////////////////////////////////////////////////////////////////////////////////////////////////////

$OBJET        = Clean::post('f_objet'     , 'texte');
$ACTION       = Clean::post('f_action'    , 'texte');
$BILAN_TYPE   = Clean::post('f_bilan_type', 'texte');
$periode_id   = Clean::post('f_periode'   , 'entier');
$classe_id    = Clean::post('f_classe'    , 'entier');
$groupe_id    = Clean::post('f_groupe'    , 'entier');
$eleve_id     = Clean::post('f_user'      , 'entier', 0);
$rubrique_id  = Clean::post('f_rubrique'  , 'entier', 0);
$appreciation = Clean::post('f_appr'      , 'appreciation');
$moyenne      = Clean::post('f_note'      , 'decimal', -1);
// Autres chaines spécifiques...
$tab_matiere_id = Clean::post('f_listing_matieres', array('array',','));
$tab_matiere_id = array_filter( Clean::map('entier',$tab_matiere_id) , 'positif' );
$liste_matiere_id = implode(',',$tab_matiere_id);

$is_sous_groupe = ($groupe_id) ? TRUE : FALSE ;
$groupe_id      = ($groupe_id) ? $groupe_id : $classe_id ; // Le groupe = le groupe transmis ou sinon la classe (cas le plus fréquent).

$tab_action = array('initialiser','enregistrer_appr','supprimer_appr','enregistrer_note','supprimer_note');

// On vérifie les paramètres principaux

if( (!in_array($ACTION,$tab_action)) || (!isset($tab_types[$BILAN_TYPE])) || ($OBJET!='saisir_multiple') || !$periode_id || !$classe_id )
{
  Json::end( FALSE , 'Erreur avec les données transmises !<br>'.pathinfo(__FILE__,PATHINFO_FILENAME).':'.__LINE__ );
}

// On vérifie que le bilan est bien accessible en modification et on récupère les infos associées

$DB_ROW = DB_STRUCTURE_OFFICIEL::DB_recuperer_bilan_officiel_infos($classe_id,$periode_id,$BILAN_TYPE);
if(empty($DB_ROW))
{
  Json::end( FALSE , 'Association classe / période introuvable !' );
}
$date_sql_debut    = $DB_ROW['jointure_date_debut'];
$date_sql_fin      = $DB_ROW['jointure_date_fin'];
$BILAN_ETAT        = $DB_ROW['officiel_'.$BILAN_TYPE];
$periode_nom       = $DB_ROW['periode_nom'];
$classe_nom        = $DB_ROW['groupe_nom'];
$CONFIGURATION_REF = $DB_ROW['configuration_ref'];

if(!$BILAN_ETAT)
{
  Json::end( FALSE , 'Bilan introuvable !' );
}
if( !in_array( $BILAN_ETAT , array('2rubrique','3mixte') ) )
{
  Json::end( FALSE , 'Bilan interdit d’accès pour cette action !' );
}

// Forcer la récupération des paramètres du bilan, au cas où un changement de paramétrage viendrait d’être effectué.
// La mémorisation se fait quand même en session pour des raisons historiques (les premiers bilans archivés utilisent cette variable) et un peu pratique (variable globale accessible partout).
$tab_configuration = DB_STRUCTURE_OFFICIEL_CONFIG::DB_recuperer_configuration( $BILAN_TYPE , $CONFIGURATION_REF );
if(empty($tab_configuration))
{
  Json::end( FALSE , 'Configuration '.$BILAN_TYPE.' / '.$CONFIGURATION_REF.' non récupérée !' );
}
foreach($tab_configuration as $key => $val)
{
  Session::_set('OFFICIEL',Clean::upper($BILAN_TYPE.'_'.$key) , $val);
}
Session::_set('OFFICIEL',Clean::upper($BILAN_TYPE).'_CONFIG_REF' , $CONFIGURATION_REF);

// Fermeture de session (mais pas destruction, juste écriture et libération des données pour éviter un verrouillage en écriture)
Session::write_close();

// Avant ce n’était que pour les bulletins, maintenant c’est pour tous les bilans officiels
$is_appreciation_groupe = (!$eleve_id) ? TRUE : FALSE ;

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Cas 1 : enregistrement d’une appréciation ou d’une note
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if($ACTION=='enregistrer_appr')
{
  if( !$appreciation || !$rubrique_id )
  {
    Json::end( FALSE , 'Erreur avec les données transmises !<br>'.pathinfo(__FILE__,PATHINFO_FILENAME).':'.__LINE__ );
  }
  enregistrer_appreciation( $BILAN_TYPE , $periode_id , $eleve_id , $classe_id , $groupe_id , $rubrique_id , $_SESSION['USER_ID'] , $appreciation );
  Json::end(TRUE);
}

if($ACTION=='enregistrer_note')
{
  if( ($moyenne<0) || ($ACTION=='tamponner') || ($BILAN_TYPE!='bulletin') || (!$rubrique_id) )
  {
    Json::end( FALSE , 'Erreur avec les données transmises !<br>'.pathinfo(__FILE__,PATHINFO_FILENAME).':'.__LINE__ );
  }
  list( $note , $appreciation ) = enregistrer_note( $BILAN_TYPE , $periode_id , $eleve_id , $rubrique_id , $moyenne );
  Json::end(TRUE);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Cas 2 : suppression d’une appréciation ou d’une note
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if($ACTION=='supprimer_appr')
{
  if( !$rubrique_id )
  {
    Json::end( FALSE , 'Erreur avec les données transmises !<br>'.pathinfo(__FILE__,PATHINFO_FILENAME).':'.__LINE__ );
  }
  // élève ou classe
  $saisie_type        = ($eleve_id) ? 'eleve'   : 'classe' ;
  $eleve_ou_classe_id = ($eleve_id) ? $eleve_id : $classe_id ;
  $saisie_groupe_id   = ($eleve_id) ? 0         : $groupe_id ;
  DB_STRUCTURE_OFFICIEL::DB_supprimer_bilan_officiel_saisie( $BILAN_TYPE , $periode_id , $eleve_ou_classe_id , $saisie_groupe_id , $rubrique_id , $_SESSION['USER_ID'] , $saisie_type );
  Json::end(TRUE);
}

if($ACTION=='supprimer_note')
{
  // Il s’agit de la supprimer définitivement et de ne pas la recalculer : on insère une note vide
  if( ($ACTION=='tamponner') || ($BILAN_TYPE!='bulletin') || (!$rubrique_id) )
  {
    Json::end( FALSE , 'Erreur avec les données transmises !<br>'.pathinfo(__FILE__,PATHINFO_FILENAME).':'.__LINE__ );
  }
  $note = NULL;
  $appreciation = 'Moyenne effacée par '.To::texte_genre_identite($_SESSION['USER_NOM'],FALSE,$_SESSION['USER_PRENOM'],TRUE,$_SESSION['USER_GENRE']);
  DB_STRUCTURE_OFFICIEL::DB_modifier_bilan_officiel_saisie( $BILAN_TYPE , $periode_id , $eleve_id , 0 /*groupe_id*/ , $rubrique_id , 0 /*prof_id*/ , 'eleve' , $note , $appreciation );
  Json::end(TRUE);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Cas 3 : affichage des données du groupe classe et des élèves
// ////////////////////////////////////////////////////////////////////////////////////////////////////

// Récupérer la liste des matières concernées
$tab_matiere      = array();
$liste_matiere_id = isset($liste_matiere_id) ? $liste_matiere_id : '' ;
$only_if_synthese = ($BILAN_TYPE=='bulletin') ? TRUE : FALSE ;
$DB_TAB = DB_STRUCTURE_BILAN::DB_recuperer_matieres_travaillees( $classe_id , $liste_matiere_id , $date_sql_debut , $date_sql_fin , $only_if_synthese );
foreach($DB_TAB as $DB_ROW)
{
  $tab_matiere[$DB_ROW['rubrique_id']] = html($DB_ROW['rubrique_nom']);
}
if(empty($tab_matiere))
{
  Json::end( FALSE , 'Aucun item évalué sur la période '.To::date_sql_to_french($date_sql_debut).' ~ '.To::date_sql_to_french($date_sql_fin).'.' );
}

// Récupérer la liste des élèves concernés : soit d’une classe (en général) soit d’une classe ET d’un sous-groupe pour un prof affecté à un groupe d’élèves
$groupe_nom = (!$is_sous_groupe) ? $classe_nom : $classe_nom.' - '.DB_STRUCTURE_COMMUN::DB_recuperer_groupe_nom($groupe_id) ;

$DB_TAB = (!$is_sous_groupe) ? DB_STRUCTURE_COMMUN::DB_lister_users_regroupement( 'eleve' /*profil_type*/ , 2 /*actuels_et_anciens*/ , 'classe' , $classe_id , 'nom' /*eleves_ordre*/ , 'user_id,user_nom,user_prenom' /*champs*/ , $periode_id )
                             : DB_STRUCTURE_COMMUN::DB_lister_eleves_classe_et_groupe( $classe_id , $groupe_id , 2 /*actuels_et_anciens*/ , $periode_id ) ;
if(empty($DB_TAB))
{
  Json::end( FALSE , 'Aucun élève évalué trouvé dans le regroupement '.$groupe_nom.' !' );
}
$tab_eleve_id    = array( 0 => array( 'eleve_nom' => $groupe_nom , 'eleve_prenom' => '' , 'eleve_dispositif' => array() ) );
$tab_saisie_init = array_fill_keys( array_keys($tab_matiere) , array( 'note'=>NULL , 'appreciation'=>'' ) );
$tab_saisie      = array(); // [eleve_id][rubrique_id] => array(appreciation,note); avec eleve_id=0 pour note ou appréciation sur la classe
$tab_saisie[0] = $tab_saisie_init;
foreach($DB_TAB as $DB_ROW)
{
  $tab_eleve_id[$DB_ROW['user_id']] = array(
    'eleve_nom'        => $DB_ROW['user_nom'] ,
    'eleve_prenom'     => $DB_ROW['user_prenom'] ,
    'eleve_dispositif' => array() ,
  );
  $tab_saisie[$DB_ROW['user_id']] = $tab_saisie_init;
}

$liste_eleve_id = implode(',',array_keys($tab_eleve_id));

// Lister les dispositifs
$DB_TAB_DISPOSITIF = DB_STRUCTURE_COMMUN::DB_lister_eleves_dispositifs( $liste_eleve_id , 'periode' , $periode_id );
if(!empty($DB_TAB_DISPOSITIF))
{
  foreach($DB_TAB_DISPOSITIF as $DB_ROW)
  {
    if(!is_null($DB_ROW['livret_modaccomp_id']))
    {
      $tab_eleve_id[$DB_ROW['user_id']]['eleve_dispositif'][$DB_ROW['livret_modaccomp_code']] = $DB_ROW['info_complement'];
    }
    if(!is_null($DB_ROW['livret_devoirsfaits_id']))
    {
      $tab_eleve_id[$DB_ROW['user_id']]['eleve_dispositif']['DF'] = NULL;
    }
  }
}

// sous-titre
$sous_titre = 'Saisie express des positionnements et appréciations';

$with_moyenne = ($BILAN_TYPE=='bulletin') && $_SESSION['OFFICIEL']['BULLETIN_MOYENNE_SCORES'] ;
$tab_moyenne_exception_matieres = ( ($BILAN_TYPE!='bulletin') || !$_SESSION['OFFICIEL']['BULLETIN_MOYENNE_EXCEPTION_MATIERES'] ) ? array() : explode(',',$_SESSION['OFFICIEL']['BULLETIN_MOYENNE_EXCEPTION_MATIERES']) ;

// (re)calculer les moyennes des élèves
if($with_moyenne && ( $BILAN_ETAT <= $_SESSION['OFFICIEL']['BULLETIN_ETAPE_MAX_MAJ_POSITIONNEMENTS'] ) )
{
  // Attention ! On doit calculer des moyennes de classe, pas de groupe !
  if(!$is_sous_groupe)
  {
    $liste_eleve_id_tmp = $liste_eleve_id;
  }
  else
  {
    $tab_eleve_id_tmp = array();
    $DB_TAB = DB_STRUCTURE_COMMUN::DB_lister_users_regroupement( 'eleve' /*profil_type*/ , 2 /*actuels_et_anciens*/ , 'classe' , $classe_id , 'nom' /*eleves_ordre*/ , 'user_id' /*champs*/ , $periode_id );
    foreach($DB_TAB as $DB_ROW)
    {
      $tab_eleve_id_tmp[] = $DB_ROW['user_id'];
    }
    $liste_eleve_id_tmp = implode(',',$tab_eleve_id_tmp);
  }
  // Dans le cas d’un prof effectuant une saisie, toutes les matières ne sont pas récupérées : il ne faut pas calculer ou supprimer la moyenne générale.
  $memo_moyennes_generale = ($liste_matiere_id) ? FALSE : $_SESSION['OFFICIEL']['BULLETIN_MOYENNE_GENERALE'] ;
  calculer_et_enregistrer_moyennes_eleves_bulletin( $periode_id , $classe_id , $liste_eleve_id_tmp , $liste_matiere_id , $_SESSION['OFFICIEL']['BULLETIN_ONLY_SOCLE'] , $_SESSION['OFFICIEL']['BULLETIN_RETROACTIF'] , $_SESSION['OFFICIEL']['BULLETIN_MOYENNE_CLASSE'] , $memo_moyennes_generale , $_SESSION['OFFICIEL']['BULLETIN_MOYENNE_EXCEPTION_MATIERES'] );
}

// Récupérer les saisies enregistrées pour le bilan officiel concerné, pour le prof concerné, pour la période en cours
$DB_TAB = array_merge
(
  DB_STRUCTURE_OFFICIEL::DB_recuperer_bilan_officiel_saisies_classe( $BILAN_TYPE , $periode_id , $classe_id      , $_SESSION['USER_ID'] , FALSE /*with_periodes_avant*/ , FALSE /*only_synthese_generale*/ ),
  DB_STRUCTURE_OFFICIEL::DB_recuperer_bilan_officiel_saisies_eleves( $BILAN_TYPE , $periode_id , $liste_eleve_id , $_SESSION['USER_ID'] , TRUE /*with_rubrique_nom*/ , FALSE /*with_periodes_avant*/ , FALSE /*only_synthese_generale*/ )
);

// La requête renvoie les appréciations du prof et les notes de toutes les rubriques.
// Il ne faut prendre que les notes qui vont avec les appréciations, i.e. des rubriques du prof.
$tab_rubrique = $tab_matiere;
foreach($DB_TAB as $key => $DB_ROW)
{
  // On ne garde pas les appréciations de synthèse générale, ni les notes des autres rubriques
  if(isset($tab_rubrique[$DB_ROW['rubrique_id']]))
  {
    if($DB_ROW['prof_id'])
    {
      $tab_saisie[$DB_ROW['eleve_id']][$DB_ROW['rubrique_id']]['appreciation'] = $DB_ROW['saisie_appreciation'];
    }
    else
    {
      $note = ( ( !$DB_ROW['eleve_id'] && !$_SESSION['OFFICIEL']['BULLETIN_MOYENNE_CLASSE'] ) || (in_array($DB_ROW['rubrique_id'],$tab_moyenne_exception_matieres)) ) ? NULL : $DB_ROW['saisie_note'] ;
      $tab_saisie[$DB_ROW['eleve_id']][$DB_ROW['rubrique_id']]['note'] = $note;
    }
  }
}

// Récupérer les professeurs principaux

$affichage_prof_principal = ($_SESSION['OFFICIEL'][$tab_types[$BILAN_TYPE]['droit'].'_PROF_PRINCIPAL']) ? TRUE : FALSE ;
$texte_prof_principal = '';

if( $affichage_prof_principal )
{
  $DB_TAB = DB_STRUCTURE_OFFICIEL::DB_lister_profs_principaux($classe_id);
  if(empty($DB_TAB))
  {
    $texte_prof_principal = 'Professeur principal : sans objet.';
  }
  else if(count($DB_TAB)==1)
  {
    $texte_prof_principal = 'Professeur principal : '.To::texte_genre_identite($DB_TAB[0]['user_nom'],FALSE,$DB_TAB[0]['user_prenom'],TRUE,$DB_TAB[0]['user_genre']);
  }
  else
  {
    $tab_pp = array();
    foreach($DB_TAB as $DB_ROW)
    {
      $tab_pp[] = To::texte_genre_identite($DB_ROW['user_nom'],FALSE,$DB_ROW['user_prenom'],TRUE,$DB_ROW['user_genre']);
    }
    $texte_prof_principal = 'Professeurs principaux : '.implode(' ; ',$tab_pp);
  }
}

// Affichage du résultat
$colspan = ($with_moyenne) ? ' colspan="2"' : '' ;
$nb_cols = max( 40 , floor( 120 / count($tab_rubrique) ) );
$effectif = count($tab_eleve_id) - 1;
Json::add_row( 'html' , '<h2>'.$sous_titre.'</h2>' );
Json::add_row( 'html' , '<div><b>'.html($periode_nom.' | '.$groupe_nom).' (&times;'.$effectif.')'.' :</b> <button type="button" class="valider" disabled="disabled">Enregistrement automatique !</button> <button id="fermer_zone_action_eleve" type="button" class="retourner">Retour</button></div>' );
Json::add_row( 'html' , '<div class="i">'.$texte_prof_principal.'</div><hr>' );
Json::add_row( 'html' , '<table><tbody><tr><td class="nu"></td><th'.$colspan.'>'.implode('</th><th'.$colspan.'>',$tab_rubrique).'</th></tr>' );
$pourcent = ($_SESSION['OFFICIEL'][$tab_types[$BILAN_TYPE]['droit'].'_CONVERSION_SUR_20']) ? '' : '&nbsp;%' ;
foreach($tab_eleve_id as $eleve_id => $tab_eleve)
{
  extract($tab_eleve); // $eleve_prenom $eleve_nom $eleve_dispositif
  $indication_dispositifs = empty($eleve_dispositif) ? '' : '<div class="dispositif">'.implode(' ',array_keys($eleve_dispositif)).'</div>' ;
  Json::add_row( 'html' , '<tr data-user_id="'.$eleve_id.'"><th>'.html($eleve_nom).'<br>'.html($eleve_prenom).$indication_dispositifs.'</th>' );
  foreach($tab_rubrique as $rubrique_id => $rubrique_nom)
  {
    extract($tab_saisie[$eleve_id][$rubrique_id]); // $appreciation $note
    if($with_moyenne)
    {
      if($eleve_id)
      {
        $note = ($note!==NULL) ? ( ($_SESSION['OFFICIEL']['BULLETIN_CONVERSION_SUR_20']) ? $note : ($note*5) ) : '' ;
        // type="number" non retenu car pour pouvoir intercepter la sortie du champ de saisie et ne pas soumettre un enregistrement à chaque incrément/décrément
        $champ = ( $BILAN_ETAT <= $_SESSION['OFFICIEL']['BULLETIN_ETAPE_MAX_MAJ_POSITIONNEMENTS'] ) ? '<input required="required" id="note_'.$eleve_id.'_'.$rubrique_id.'" name="f_saisie_express" type="text" value="'.$note.'" size="3">'.$pourcent : $note.$pourcent.' '.infobulle('Modification des positionnements interdite par la configuration de ce bulletin.',TRUE) ;
        Json::add_row( 'html' , '<td data-rubrique_id="'.$rubrique_id.'" class="now">'.$champ.'</td>' );
        
      }
      else if($_SESSION['OFFICIEL']['BULLETIN_MOYENNE_CLASSE'])
      {
        $note = ($note!==NULL) ? ( ($_SESSION['OFFICIEL']['BULLETIN_CONVERSION_SUR_20']) ? $note : ($note*5).$pourcent ) : '-' ;
        Json::add_row( 'html' , '<td class="now">'.$note.'</td>' );
      }
      else
      {
        Json::add_row( 'html' , '<td></td>' );
      }
    }
    $nb_rows = ($appreciation) ? max( 2 , floor( mb_strlen($appreciation)/($nb_cols*2/3) ) ) : 2 ;
    // attribut required sur le textarea pour pouvoir le styler en css si vide avec la propriété :invalid (@see https://stackoverflow.com/questions/7072576/can-i-select-empty-textareas-with-css)
    Json::add_row( 'html' , '<td data-rubrique_id="'.$rubrique_id.'" class="now"><textarea required="required" id="appr_'.$eleve_id.'_'.$rubrique_id.'" name="f_saisie_express" rows="'.$nb_rows.'" cols="'.$nb_cols.'">'.html($appreciation).'</textarea></td>' );
  }
  Json::add_row( 'html' , '</tr>' );
}
Json::add_row( 'html' , '</tbody></table>' );
Json::end( TRUE );

?>
