<?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/>.
 *
 */
 
// Extension de classe qui étend DB (pour permettre l’autoload)

// Ces méthodes ne concernent que les tables "sacoche_livret_*".

class DB_STRUCTURE_LIVRET
{

/**
 * recuperer_classe_periode_max
 *
 * @param int    $classe_id
 * @param string $page_ref_periode
 * @return array
 */
public static function DB_recuperer_classe_periode_max($classe_id,$page_ref_periode)
{
  $DB_SQL = 'SELECT jointure_periode, jointure_etat '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE groupe_id=:groupe_id AND livret_page_ref=:livret_page_ref '
          . 'ORDER BY jointure_periode DESC '
          . 'LIMIT 1 ';
  $DB_VAR = array(
    ':groupe_id'       => $classe_id,
    ':livret_page_ref' => $page_ref_periode,
  );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_classes_livret_sans_chef
 *
 * @param void
 * @return string
 */
public static function DB_lister_classes_livret_sans_chef()
{
  $DB_SQL = 'SELECT GROUP_CONCAT(DISTINCT groupe_id) '
          . 'FROM sacoche_groupe '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(groupe_id) '
          . 'WHERE groupe_type="classe" AND groupe_chef_id=0 ';
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * Vérifier qu’il y a au moins une classe dans l’établissement
 * Vérifier qu’il y a au moins une classe associée au livret, et sinon essayer de faire le boulot automatiquement
 *
 * @param void
 * @return bool | int
 */
public static function DB_initialiser_jointures_livret_classes()
{
  // Vérifier qu’il y a au moins une classe dans l’établissement
  $DB_SQL = 'SELECT COUNT(*) '
          . 'FROM sacoche_groupe '
          . 'WHERE groupe_type="classe" ';
  $nb_classes_structure = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  if( !$nb_classes_structure )
  {
    return FALSE;
  }
  // Vérifier que les classes sont associées au livret
  $DB_SQL = 'SELECT COUNT(DISTINCT groupe_id) '
          . 'FROM sacoche_livret_jointure_groupe ';
  $nb_classes_livret = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  // Mais sans tenir compte des classes d’un niveau inférieur ou supérieur à l’objet du Livret Scolaire
  $DB_SQL = 'SELECT COUNT(DISTINCT groupe_id) '
          . 'FROM sacoche_groupe '
          . 'WHERE groupe_type="classe" AND NOT (niveau_id BETWEEN 1033 AND 200000) ';
  $nb_classes_hors_lsu = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  $nb_classes_concernees = $nb_classes_structure - $nb_classes_hors_lsu;
  if( $nb_classes_livret >= $nb_classes_concernees )
  {
    return TRUE;
  }
  // Fonction pour essayer de déterminer le bon type de période associé à une classe
  function determiner_periode_probable( $listing_periodes , $periode_nb )
  {
    if(!empty($listing_periodes))
    {
      if( substr_count( $listing_periodes , '3' ) )
      { return array('31','32','33'); }
      if( substr_count( $listing_periodes , '2' ) )
      { return array('21','22'); }
      if( substr_count( $listing_periodes , '4' ) )
      { return array('41','42','43','44'); }
      if( substr_count( $listing_periodes , '5' ) )
      { return array('51','52','53','54','55'); }
    }
    if( $periode_nb == 3 )
    { return array('31','32','33'); }
    if( $periode_nb == 2 )
    { return array('21','22'); }
    if( $periode_nb == 4 )
    { return array('41','42','43','44'); }
    if( $periode_nb == 5 )
    { return array('51','52','53','54','55'); }
    return array('31','32','33');
  }
  // Initialiser les jointures livret / classes
  $nb_associations = 0;
  $DB_SQL = 'SELECT groupe_id, niveau_ordre, '
          . 'COUNT( periode_id ) AS periode_nb, '
          . 'GROUP_CONCAT(SUBSTRING(periode_livret,1,1)) AS listing_periodes, '
          . 'GROUP_CONCAT(jointure_periode) AS listing_jointures '
          . 'FROM sacoche_groupe '
          . 'LEFT JOIN sacoche_jointure_groupe_periode USING(groupe_id) '
          . 'LEFT JOIN sacoche_livret_jointure_groupe USING(groupe_id) '
          . 'LEFT JOIN sacoche_periode USING(periode_id) '
          . 'LEFT JOIN sacoche_niveau USING(niveau_id) '
          . 'WHERE groupe_type="classe" '
          . 'GROUP BY groupe_id '
          . 'HAVING listing_jointures IS NULL ';
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  foreach($DB_TAB as $DB_ROW)
  {
    $jointure_periode = determiner_periode_probable( $DB_ROW['listing_periodes'] , $DB_ROW['periode_nb'] );
    switch($DB_ROW['niveau_ordre'])
    {
      case 3 : // GS
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cycle1' , 'cycle' , array(0) );
        $nb_associations += 1;
        break;
      case 11 : // CP
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cp' , 'periode' , $jointure_periode );
        $nb_associations += 1;
        break;
      case 21 : // CE1
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'ce1' , 'periode' , $jointure_periode );
        $nb_associations += 1;
        break;
      case 22 : // CE2
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'ce2' , 'periode' , $jointure_periode );
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cycle2' , 'cycle' , array(0) );
        $nb_associations += 2;
        break;
      case 31 : // CM1
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cm1' , 'periode' , $jointure_periode );
        $nb_associations += 1;
        break;
      case 32 : // CM2
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cm2' , 'periode' , $jointure_periode );
        $nb_associations += 1;
        break;
      case 100 : // Sixièmes
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , '6e' , 'periode' , $jointure_periode );
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cycle3' , 'cycle' , array(0) );
        $nb_associations += 2;
        break;
      case 101 : // Cinquièmes
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , '5e' , 'periode' , $jointure_periode );
        $nb_associations += 1;
        break;
      case 102 : // Quatrièmes
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , '4e' , 'periode' , $jointure_periode );
        $nb_associations += 1;
        break;
      case 103 : // Troisièmes
        // À compter de 2025, en 3e, c’est obligatoirement trimestres ou semestres.
        $jointure_periode_3e = in_array(count($jointure_periode),array(3,2)) ? $jointure_periode : array('31','32','33') ;
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , '3e'     , 'periode' , $jointure_periode_3e );
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'cycle4' , 'cycle'   , array(0) );
        DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , 'dnb'    , 'annuel'  , array(0) );
        $nb_associations += 2;
        break;
    }
  }
  return $nb_associations;
}

/**
 * Vérifier qu’il y a un chef associé à chaque classe, et sinon essayer de faire le boulot automatiquement
 *
 * @param void
 * @return bool | int
 */
public static function DB_initialiser_jointures_classe_chef()
{
  // Nettoyer des classes avec un chef supprimé ou au compte inactif
  $DB_SQL = 'UPDATE sacoche_groupe '
          . 'LEFT JOIN sacoche_user ON sacoche_groupe.groupe_chef_id=sacoche_user.user_id '
          . 'SET groupe_chef_id=0 '
          . 'WHERE groupe_type="classe" AND groupe_chef_id!=0 AND ( user_id IS NULL OR user_sortie_date<=NOW() ) ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  // Chercher si des classes associées au livret sont sans chef
  $listing_groupe = DB_STRUCTURE_LIVRET::DB_lister_classes_livret_sans_chef();
  if( empty($listing_groupe) )
  {
    return TRUE;
  }
  $nb_classes_concernees = substr_count($listing_groupe,',')+1;
  // On essaye de trouver le chef d’établissement / directeur d’école tout seul, s’il n’y en a qu’un.
  $DB_SQL = 'SELECT user_id FROM sacoche_user WHERE user_profil_sigle="DIR" AND user_sortie_date>NOW() ';
  $DB_COL = DB::queryCol(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  if( !empty($DB_COL) && (count($DB_COL)==1) )
  {
    $chef_id = (int)current($DB_COL);
    $DB_SQL = 'UPDATE sacoche_groupe '
            . 'SET groupe_chef_id='.$chef_id.' '
            . 'WHERE groupe_id IN('.$listing_groupe.') ';
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
    return $nb_classes_concernees;
  }
  // Sinon on prend celui qui est déjà le plus rattaché aux classes, si tel est le cas.
  $DB_SQL = 'SELECT groupe_chef_id, COUNT(*) AS nombre '
          . 'FROM sacoche_groupe '
          . 'WHERE groupe_type="classe" AND groupe_chef_id!=0 '
          . 'GROUP BY groupe_chef_id '
          . 'ORDER BY nombre DESC '
          . 'LIMIT 1 ';
  $DB_ROW = DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  if(!empty($DB_ROW))
  {
    $chef_id = $DB_ROW['groupe_chef_id'];
    $DB_SQL = 'UPDATE sacoche_groupe '
            . 'SET groupe_chef_id='.$chef_id.' '
            . 'WHERE groupe_id IN('.$listing_groupe.') ';
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
    return $nb_classes_concernees;
  }
  return 0;
}

/**
 * 2025 : Vérifier que les classes de 3e sont en trimestres ou semestres
 *
 * @param void
 * @return int
 */
public static function DB_verifier_pediodicite_classes_3e()
{
  $nb_classes = 0;
  $DB_SQL = 'SELECT groupe_id, livret_page_ref, '
          . 'GROUP_CONCAT(jointure_periode) AS listing_jointure_periode '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE livret_page_ref=:page_ref '
          . 'GROUP BY groupe_id ';
  $DB_VAR = array( ':page_ref' => '3e' );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_jointures_old = explode(',',$DB_ROW['listing_jointure_periode']);
    if(!in_array(count($tab_jointures_old),array(3,2)))
    {
      DB_STRUCTURE_LIVRET::DB_supprimer_jointure_groupe( $DB_ROW['groupe_id'] , $DB_ROW['livret_page_ref'] , 'periode' );
      DB_STRUCTURE_LIVRET::DB_ajouter_jointure_groupe( $DB_ROW['groupe_id'] , $DB_ROW['livret_page_ref'] , 'periode' , array(31,32,33) );
      $nb_classes += 1;
    }
  }
  return $nb_classes;
}

/**
 * 2025 : Vérifier que les classes de 3e comportent des élèves avec une MEF 3e
 *
 * @param void
 * @return array
 */
public static function DB_lister_classes_3e_avec_eleve_sans_mef_3e()
{
  $DB_SQL = 'SELECT DISTINCT groupe_id, groupe_nom, COUNT(DISTINCT user_id) AS nombre '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'LEFT JOIN sacoche_groupe USING(groupe_id) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_groupe.groupe_id = sacoche_user.eleve_classe_id '
          . 'WHERE livret_page_ref = :page_ref AND user_sortie_date > NOW() AND eleve_dnb_mef_id = :not_mef '
          . 'GROUP BY groupe_id ';
  $DB_VAR = array(
    ':page_ref' => '3e',
    ':not_mef'  => 0,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * 2025 : Vérifier que les classes de 3e comportent des élèves avec une MEF 3e (bis)
 *
 * @param void
 * @return array
 */
public static function DB_lister_eleve_mef_3e_hors_classe_3e()
{
  $DB_SQL = 'SELECT DISTINCT groupe_id, groupe_nom, COUNT(DISTINCT user_id) AS nombre '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_groupe ON sacoche_user.eleve_classe_id = sacoche_groupe.groupe_id '
          . 'LEFT JOIN sacoche_livret_jointure_groupe USING(groupe_id) '
          . 'WHERE eleve_dnb_mef_id > :not_mef AND user_sortie_date > NOW() AND livret_page_periodicite = :periodicite AND livret_page_ref != :page_ref '
          . 'GROUP BY groupe_id ';
  $DB_VAR = array(
    ':not_mef'     => 0,
    ':periodicite' => 'periode',
    ':page_ref'    => '3e',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_pages
 *
 * @param bool   $with_info_classe
 * @return array
 */
public static function DB_lister_pages( $with_info_classe )
{
  $DB_SQL = 'SELECT sacoche_livret_page.* ';
  if($with_info_classe)
  {
    $DB_SQL.= ', COUNT( DISTINCT groupe_id ) AS groupe_nb '
            . ', GROUP_CONCAT( DISTINCT groupe_nom SEPARATOR "<br>" ) AS listing_groupe_nom '
            . ', COUNT( DISTINCT element_id ) AS element_nb ';
  }
  $DB_SQL.= 'FROM sacoche_livret_page ';
  if($with_info_classe)
  {
    $DB_SQL.= 'LEFT JOIN sacoche_livret_jointure_groupe USING(livret_page_ref) '
            . 'LEFT JOIN sacoche_groupe USING(groupe_id) '
            . 'LEFT JOIN sacoche_livret_jointure_referentiel ON sacoche_livret_page.livret_page_rubrique_type = sacoche_livret_jointure_referentiel.livret_rubrique_type '
            . 'GROUP BY livret_page_ref ';
    // Ajouter un ordre sur le nom du groupe ne fonctionne pas : traité en PHP après
  }
  $DB_SQL.= 'ORDER BY livret_page_ordre ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Informations diverses
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_periodes_eleves
 *
 * @param string $liste_eleves_id
 * @return array
 */
public static function DB_lister_periodes_eleves( $liste_eleves_id )
{
  $DB_SQL = 'SELECT user_id, GROUP_CONCAT(periode_livret) AS listing_periodes '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_jointure_groupe_periode ON sacoche_user.eleve_classe_id = sacoche_jointure_groupe_periode.groupe_id '
          . 'LEFT JOIN sacoche_periode USING(periode_id) '
          . 'WHERE user_id IN('.$liste_eleves_id.') '
          . 'GROUP BY user_id ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * recuperer_chef_infos
 *
 * @param int    $classe_chef_id
 * @return array
 */
public static function DB_recuperer_chef_infos( $classe_chef_id )
{
  $DB_SQL = 'SELECT user_id, user_sconet_id, user_genre, user_nom, user_prenom '
          . 'FROM sacoche_user '
          . 'WHERE user_id=:user_id AND user_profil_sigle="DIR" AND user_sortie_date>NOW() ';
  $DB_VAR = array(
    ':user_id' => $classe_chef_id,
  );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_page_info
 *
 * @param string   $page_ref
 * @return array
 */
public static function DB_recuperer_page_info($page_ref)
{
  $DB_SQL = 'SELECT * '
          . 'FROM sacoche_livret_page '
          . 'WHERE livret_page_ref=:page_ref ';
  $DB_VAR = array( ':page_ref' => $page_ref );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_periode_info
 *
 * @param string   $periode_livret
 * @param int      $classe_id
 * @return array
 */
public static function DB_recuperer_periode_info( $periode_livret , $classe_id )
{
  $DB_SQL = 'SELECT periode_id, jointure_date_debut, jointure_date_fin '
          . 'FROM sacoche_periode '
          . 'LEFT JOIN sacoche_jointure_groupe_periode USING(periode_id) '
          . 'WHERE periode_livret=:periode_livret AND groupe_id=:groupe_id ';
  $DB_VAR = array(
    ':periode_livret' => $periode_livret,
    ':groupe_id'      => $classe_id,
  );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_page_groupe_info
 *
 * @param int      $groupe_id
 * @param string   $page_ref
 * @param string   $page_periodicite
 * @param int      $jointure_periode
 * @return array
 */
public static function DB_recuperer_page_groupe_info( $groupe_id , $page_ref , $page_periodicite , $jointure_periode )
{
  $DB_SQL = 'SELECT jointure_etat, jointure_date_verrou, sacoche_livret_page.*, groupe_ref, groupe_nom, groupe_chef_id, groupe_configuration_livret AS configuration_ref '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'LEFT JOIN sacoche_livret_page USING (livret_page_ref) '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'WHERE groupe_id=:groupe_id AND livret_page_ref=:page_ref AND sacoche_livret_page.livret_page_periodicite=:page_periodicite AND jointure_periode=:jointure_periode ';
  $DB_VAR = array(
    ':groupe_id'        => $groupe_id,
    ':page_ref'         => $page_ref,
    ':page_periodicite' => $page_periodicite,
    ':jointure_periode' => $jointure_periode,
  );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_lsun_elements_arborescence
 *
 * @param void
 * @return array
 */
public static function DB_recuperer_lsun_elements_arborescence()
{
  $DB_SQL = 'SELECT livret_element_domaine_nom, niveau_nom, livret_element_theme_nom, livret_element_item_nom, '
          . 'livret_element_domaine_id, niveau_ref, matiere_js, livret_element_theme_id, livret_element_item_id '
          . 'FROM sacoche_livret_element_domaine '
          . 'LEFT JOIN sacoche_niveau USING(niveau_id) '
          . 'LEFT JOIN sacoche_livret_element_theme USING(livret_element_domaine_id) '
          . 'LEFT JOIN sacoche_livret_element_item USING(livret_element_theme_id) '
          . 'ORDER BY niveau_ordre, livret_element_domaine_id, livret_element_theme_id, livret_element_item_id ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Rubriques & Jointures rubriques / référentiels
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_correspondances_matieres_uniques
 *
 * @param string   $rubrique_type
 * @return array
 */
public static function DB_lister_correspondances_matieres_uniques( $rubrique_type )
{
  $DB_SQL = 'SELECT livret_rubrique_ou_matiere_id AS matiere_livret_id, GROUP_CONCAT(element_id) AS matiere_referentiel_id ' // matiere_referentiel_id sera en fait un id unique, on utilise GROUP_CONCAT et GROUP BY pour le COUNT() 
          . 'FROM sacoche_livret_jointure_referentiel '
          . 'WHERE livret_rubrique_type=:rubrique_type '
          . 'GROUP BY matiere_livret_id '
          . 'HAVING COUNT(element_id)=1 ';
  $DB_VAR = array( ':rubrique_type' => $rubrique_type );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_correspondances_matieres_uniques
 *
 * @param string   $rubrique_type
 * @param int      $matiere_livret_id
 * @return int
 */
public static function DB_recuperer_correspondance_matiere_unique( $rubrique_type , $matiere_livret_id )
{
  $DB_SQL = 'SELECT GROUP_CONCAT(element_id) AS matiere_referentiel_id ' // matiere_referentiel_id sera en fait un id unique, on utilise GROUP_CONCAT et GROUP BY pour le COUNT() 
          . 'FROM sacoche_livret_jointure_referentiel '
          . 'WHERE livret_rubrique_type=:rubrique_type AND livret_rubrique_ou_matiere_id=:matiere_livret_id '
          . 'GROUP BY livret_rubrique_ou_matiere_id '
          . 'HAVING COUNT(element_id)=1 ';
  $DB_VAR = array(
    ':rubrique_type'     => $rubrique_type,
    ':matiere_livret_id' => $matiere_livret_id,
  );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_matieres_alimentees
 *
 * @param void
 * @return array
 */
public static function DB_lister_matieres_alimentees()
{
  $DB_SQL = 'SELECT livret_rubrique_type, matiere_id, matiere_nom '
          . 'FROM sacoche_livret_jointure_referentiel '
          . 'INNER JOIN sacoche_matiere ON sacoche_livret_jointure_referentiel.livret_rubrique_ou_matiere_id = sacoche_matiere.matiere_id '
          . 'WHERE livret_rubrique_type IN("c3_matiere","c4_matiere") AND ( matiere_siecle = 1 OR matiere_active = 1 ) AND matiere_experimentale = 0 '
          . 'GROUP BY livret_rubrique_type, matiere_id '
          . 'ORDER BY matiere_nom ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_rubriques
 *
 * CONCAT_WS a une syntaxe un peu différente de CONCAT et ici 2 avantages :
 * - si un champ est NULL, les autres éléments sont retournés, avec que CONCAT renvoie NULL dans ce cas
 * - si un champ est NULL ou vide, il n’est pas concaténé, alors qu’avec CONCAT on obtient un séparateur suivi de rien
 *
 * @param string   $rubrique_type
 * @param bool     $for_edition
 * @return array
 */
public static function DB_lister_rubriques( $rubrique_type , $for_edition )
{
  if( substr($rubrique_type,3) == 'matiere' )
  {
    if($for_edition)
    {
      $DB_SQL = 'SELECT matiere_id AS livret_rubrique_id, matiere_nom AS rubrique, NULL AS sous_rubrique, '
              . 'matiere_id AS rubrique_id_elements, matiere_id AS rubrique_id_appreciation, matiere_id AS rubrique_id_position, '
              . 'matiere_code AS rubrique_id_livret, matiere_siecle '
              . 'FROM sacoche_livret_jointure_referentiel '
              . 'INNER JOIN sacoche_matiere ON sacoche_livret_jointure_referentiel.livret_rubrique_ou_matiere_id = sacoche_matiere.matiere_id '
              . 'WHERE livret_rubrique_type=:rubrique_type AND ( matiere_siecle = 1 OR matiere_active = 1 ) AND matiere_experimentale = 0 '
              . 'ORDER BY matiere_ordre ASC ';
    }
    else
    {
      $DB_SQL = 'SELECT matiere_id AS livret_rubrique_id, CONCAT( REPLACE( REPLACE(matiere_siecle,"1","SIECLE") , "0","REFÉRENTIEL AUTRE" ) , " - ", matiere_nom ) AS livret_rubrique_nom, matiere_code '
              . 'FROM sacoche_matiere '
              . 'WHERE ( matiere_siecle = 1 OR matiere_active = 1 ) AND matiere_experimentale = 0 '
              . 'ORDER BY matiere_siecle DESC, matiere_ordre ASC ';
    }
  }
  else
  {
    if($for_edition)
    {
      $rubrique_nom = 'livret_rubrique_domaine AS rubrique, livret_rubrique_sous_domaine AS sous_rubrique ' ;
      $rubrique_ids = ', livret_rubrique_id_elements AS rubrique_id_elements, livret_rubrique_id_appreciation AS rubrique_id_appreciation, livret_rubrique_id_position AS rubrique_id_position ' ;
      $livret_ids   = ', livret_rubrique_code_livret AS rubrique_id_livret ' ;
    }
    else
    {
      $rubrique_nom = 'CONCAT_WS( " - ", livret_rubrique_domaine, livret_rubrique_sous_domaine) AS livret_rubrique_nom ' ;
      $rubrique_ids = '' ;
      $livret_ids   = '' ;
    }
    $DB_SQL = 'SELECT livret_rubrique_id, '.$rubrique_nom.$rubrique_ids.$livret_ids
            . 'FROM sacoche_livret_rubrique '
            . 'WHERE livret_rubrique_type=:rubrique_type '
            . 'ORDER BY livret_rubrique_ordre ASC ';
  }
  $DB_VAR = array( ':rubrique_type' => $rubrique_type );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_jointures_rubriques_référentiels
 *
 * @param string   $rubrique_type
 * @param int      $rubrique_id   facultatif, pour restreindre à une rubrique
 * @return array
 */
public static function DB_lister_jointures_rubriques_référentiels( $rubrique_type , $rubrique_id=NULL )
{
  $where_rubrique = ($rubrique_id) ? 'AND livret_rubrique_ou_matiere_id=:rubrique_id ' : '' ;
  $DB_SQL = 'SELECT livret_rubrique_ou_matiere_id, GROUP_CONCAT(element_id SEPARATOR ",") AS listing_elements '
          . 'FROM sacoche_livret_jointure_referentiel '
          . 'WHERE livret_rubrique_type=:rubrique_type '
          . 'GROUP BY livret_rubrique_ou_matiere_id ';
  $DB_VAR = array(
    ':rubrique_type' => $rubrique_type,
    ':rubrique_id'   => $rubrique_id,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_arborescence_professeur
 * Retourner l’arborescence des items travaillés par des élèves donnés (ou un seul), durant une période donnée, par un professeur donné
 *
 * @param string $liste_eleve_id   id des élèves séparés par des virgules ; il peut n’y avoir qu’un id, en particulier si c’est un élève qui demande un bilan
 * @param string $liste_prof_id    id du prof
 * @param int    $only_socle       1 pour ne retourner que les items reliés au socle, 0 sinon
 * @param string $date_sql_debut
 * @param string $date_sql_fin
 * @return array
 */
public static function DB_recuperer_items_profs( $liste_eleve_id , $liste_prof_id , $only_socle , $date_sql_debut , $date_sql_fin )
{
  $join_s2016       = ($only_socle)   ? 'LEFT JOIN sacoche_jointure_referentiel_socle USING (item_id) ' : '' ;
  $where_eleve      = (strpos($liste_eleve_id,',')) ? 'eleve_id IN('.$liste_eleve_id.') '    : 'eleve_id='.$liste_eleve_id.' ' ; // Pour IN(...) NE PAS passer la liste dans $DB_VAR sinon elle est convertie en nb entier
  $where_prof       = (strpos($liste_prof_id,','))  ? 'prof_id IN('.$liste_prof_id.') '      : 'prof_id='.$liste_prof_id.' ' ;   // Pour IN(...) NE PAS passer la liste dans $DB_VAR sinon elle est convertie en nb entier
  $where_socle      = ($only_socle)                 ? 'AND socle_composante_id IS NOT NULL ' : '' ;
  $where_date_debut = ($date_sql_debut)             ? 'AND saisie_date>=:date_debut '        : '';
  $where_date_fin   = ($date_sql_fin)               ? 'AND saisie_date<=:date_fin '          : '';
  $DB_SQL = 'SELECT item_id , prof_id , item_nom , '
          . 'item_coef , referentiel_calcul_methode AS calcul_methode , referentiel_calcul_limite AS calcul_limite , referentiel_calcul_retroactif AS calcul_retroactif '
          . 'FROM sacoche_saisie '
          . 'LEFT JOIN sacoche_referentiel_item USING (item_id) '
          . 'LEFT JOIN sacoche_referentiel_theme USING (theme_id) '
          . 'LEFT JOIN sacoche_referentiel_domaine USING (domaine_id) '
          . 'LEFT JOIN sacoche_matiere USING (matiere_id) '
          . 'LEFT JOIN sacoche_niveau USING (niveau_id) '
          . 'LEFT JOIN sacoche_referentiel USING (matiere_id,niveau_id) '
          . $join_s2016
          . 'WHERE matiere_active = 1 AND matiere_experimentale = 0 AND niveau_actif = 1 '
          . 'AND '.$where_eleve.'AND '.$where_prof
          . $where_socle.$where_date_debut.$where_date_fin
          . 'GROUP BY item_id, prof_id ';
  $DB_VAR = array(
    ':date_debut' => $date_sql_debut,
    ':date_fin'   => $date_sql_fin,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_items_jointures_rubriques
 *
 * @param string   $rubrique_type
 * @param string   $rubrique_join
 * @param int      $only_socle    1 pour ne retourner que les items reliés au socle, 0 sinon
 * @param int      $rubrique_id   facultatif, pour restreindre à une rubrique
 * @return array
 */
public static function DB_recuperer_items_jointures_rubriques( $rubrique_type , $rubrique_join , $only_socle , $rubrique_id=NULL )
{
  $champ_position = (substr($rubrique_type,3)=='domaine') ? 'livret_rubrique_id_position' : 'livret_rubrique_ou_matiere_id' ;
  $champ_elements = (substr($rubrique_type,3)=='domaine') ? 'livret_rubrique_id_elements' : 'livret_rubrique_ou_matiere_id' ;
  $join_rubrique  = (substr($rubrique_type,3)=='domaine') ? 'INNER JOIN sacoche_livret_rubrique ON sacoche_livret_jointure_referentiel.livret_rubrique_ou_matiere_id = sacoche_livret_rubrique.livret_rubrique_id ' : '' ;
  $join_s2016     = ($only_socle)  ? 'INNER JOIN sacoche_jointure_referentiel_socle USING (item_id) ' : '' ;
  $where_socle    = ($only_socle)  ? 'AND socle_composante_id IS NOT NULL '  : '' ;
  $where_rubrique = ($rubrique_id) ? 'AND '.$champ_position.'=:rubrique_id ' : '' ;
  $DB_SQL = 'SELECT '.$champ_position.' AS rubrique_id_position , '.$champ_elements.' AS rubrique_id_elements , item_id '
          . 'FROM sacoche_livret_jointure_referentiel '
          . $join_rubrique;
  if( $rubrique_join == 'matiere' )
  {
    $DB_SQL.= 'INNER JOIN sacoche_referentiel_domaine ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_domaine.matiere_id '
            . 'INNER JOIN sacoche_referentiel_theme USING (domaine_id) '
            . 'INNER JOIN sacoche_referentiel_item USING (theme_id) '
            . 'INNER JOIN sacoche_matiere USING (matiere_id) '
            . 'INNER JOIN sacoche_niveau USING (niveau_id) ';
  }
  if( $rubrique_join == 'domaine' )
  {
    $DB_SQL.= 'INNER JOIN sacoche_referentiel_theme ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_theme.domaine_id '
            . 'INNER JOIN sacoche_referentiel_item USING (theme_id) '
            . 'INNER JOIN sacoche_referentiel_domaine USING (domaine_id) '
            . 'INNER JOIN sacoche_matiere USING (matiere_id) '
            . 'INNER JOIN sacoche_niveau USING (niveau_id) ';
  }
  if( $rubrique_join == 'theme' )
  {
    $DB_SQL.= 'INNER JOIN sacoche_referentiel_item ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_item.theme_id '
            . 'INNER JOIN sacoche_referentiel_theme USING (theme_id) '
            . 'INNER JOIN sacoche_referentiel_domaine USING (domaine_id) '
            . 'INNER JOIN sacoche_matiere USING (matiere_id) '
            . 'INNER JOIN sacoche_niveau USING (niveau_id) ';
  }
  if( $rubrique_join == 'item' )
  {
    $DB_SQL.= 'INNER JOIN sacoche_referentiel_item ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_item.item_id '
            . 'INNER JOIN sacoche_referentiel_theme USING (theme_id) '
            . 'INNER JOIN sacoche_referentiel_domaine USING (domaine_id) '
            . 'INNER JOIN sacoche_matiere USING (matiere_id) '
            . 'INNER JOIN sacoche_niveau USING (niveau_id) ';
  }
  $DB_SQL.= $join_s2016
          . 'WHERE matiere_active = 1 AND matiere_experimentale = 0 AND niveau_actif = 1 '
          . 'AND sacoche_livret_jointure_referentiel.livret_rubrique_type = :rubrique_type '
          . $where_socle.$where_rubrique
          . 'GROUP BY '.$champ_position.', '.$champ_elements.', item_id ';
  $DB_VAR = array(
    ':rubrique_type' => $rubrique_type,
    ':rubrique_id'   => $rubrique_id,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_profs_jointure_rubrique
 *
 * @param string   $rubrique_type
 * @param string   $rubrique_join
 * @param int      $matiere_id
 * @param int      $classe_id   facultatif, pour restreindre à une classe
 * @return array
 */
public static function DB_recuperer_profs_jointure_rubrique( $rubrique_type , $rubrique_join , $matiere_id , $classe_id )
{
  $join_classe  = ($classe_id) ? 'LEFT JOIN sacoche_jointure_user_groupe USING (user_id) ' : '' ;
  $where_classe = ($classe_id) ? 'AND groupe_id=:groupe_id ' : '' ;
  $DB_SQL = 'SELECT DISTINCT user_id '
          . 'FROM sacoche_livret_jointure_referentiel ';
  if( $rubrique_join == 'user' )
  {
    $DB_SQL.= 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_referentiel.element_id = sacoche_user.user_id ';
  }
  else
  {
    if( $rubrique_join == 'matiere' )
    {
      $DB_SQL.= 'LEFT JOIN sacoche_matiere ON sacoche_livret_jointure_referentiel.element_id = sacoche_matiere.matiere_id ';
    }
    if( $rubrique_join == 'domaine' )
    {
      $DB_SQL.= 'LEFT JOIN sacoche_referentiel_domaine ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_domaine.domaine_id ';
    }
    if( $rubrique_join == 'theme' )
    {
      $DB_SQL.= 'LEFT JOIN sacoche_referentiel_theme ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_theme.theme_id '
              . 'LEFT JOIN sacoche_referentiel_domaine USING (domaine_id) ';
    }
    if( $rubrique_join == 'item' )
    {
      $DB_SQL.= 'LEFT JOIN sacoche_referentiel_item ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel_item.item_id '
              . 'LEFT JOIN sacoche_referentiel_item USING (theme_id) '
              . 'LEFT JOIN sacoche_referentiel_domaine USING (domaine_id) ';
    }
    $DB_SQL.= 'LEFT JOIN sacoche_jointure_user_matiere USING (matiere_id) '
            . 'LEFT JOIN sacoche_user USING (user_id) ';
  }
  $DB_SQL.= $join_classe
          . 'WHERE livret_rubrique_type = :rubrique_type AND livret_rubrique_ou_matiere_id = :matiere_id AND user_sortie_date>NOW() '
          . $where_classe;
  $DB_VAR = array(
    ':rubrique_type' => $rubrique_type,
    ':matiere_id'    => $matiere_id,
    ':groupe_id'     => $classe_id,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR, TRUE);
}

/**
 * recuperer_profs_classe
 *
 * @param int      $classe_id
 * @return array
 */
public static function DB_recuperer_profs_classe( $classe_id )
{
  $DB_SQL = 'SELECT user_id '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_jointure_user_groupe USING (user_id) '
          . 'WHERE groupe_id=:groupe_id AND user_sortie_date>NOW() '
          . 'ORDER BY user_nom, user_prenom ';
  $DB_VAR = array(
    ':groupe_id'  => $classe_id,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR, TRUE);
}

/**
 * tester_page_jointure_rubrique
 *
 * @param string $rubrique_type
 * @param string $rubrique_join
 * @param int    $rubrique_id
 * @return bool
 */

public static function DB_tester_page_jointure_rubrique( $rubrique_type , $rubrique_join , $rubrique_id )
{
  if( in_array( $rubrique_type , array('c1_theme','c2_domaine','c3_domaine') ) )
  {
    $DB_SQL = 'SELECT 1 '
            . 'FROM sacoche_livret_page '
            . 'LEFT JOIN sacoche_livret_rubrique ON livret_page_rubrique_type = livret_rubrique_type '
            . 'WHERE livret_page_rubrique_type=:rubrique_type AND livret_page_rubrique_join=:rubrique_join AND livret_rubrique_id=:rubrique_id ';
    $DB_VAR = array(
      ':rubrique_type' => $rubrique_type,
      ':rubrique_join' => $rubrique_join,
      ':rubrique_id'   => $rubrique_id,
    );
    return (bool)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  }
  else
  {
    $DB_SQL1 = 'SELECT 1 ';
    $DB_SQL1.= 'FROM sacoche_livret_page ';
    $DB_SQL1.= 'WHERE livret_page_rubrique_type=:rubrique_type AND livret_page_rubrique_join=:rubrique_join ';
    $DB_SQL2 = 'SELECT 1 ';
    $DB_SQL2.= 'FROM sacoche_matiere ';
    $DB_SQL2.= 'WHERE matiere_id=:rubrique_id AND ( matiere_active = 1 OR matiere_siecle = 1 ) AND matiere_experimentale = 0 ';
    $DB_VAR = array(
      ':rubrique_type' => $rubrique_type,
      ':rubrique_join' => $rubrique_join,
      ':rubrique_id'   => $rubrique_id,
    );
    return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL1 , $DB_VAR) && DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL2 , $DB_VAR);
  }
}

/**
 * modifier_jointure_référentiel
 *
 * @param string $rubrique_type
 * @param int    $rubrique_id
 * @param int    $element_id
 * @param bool   $etat   TRUE pour ajouter ; FALSE pour retirer
 * @return void
 */
public static function DB_modifier_jointure_référentiel( $rubrique_type , $rubrique_id , $element_id , $etat )
{
  if($etat)
  {
    // IGNORE car comme on peut cocher parmi les éléments déjà utilisés on n’est pas à l’abri d’une demande de liaison déjà existante.
    $DB_SQL = 'INSERT IGNORE INTO sacoche_livret_jointure_referentiel ( livret_rubrique_type , livret_rubrique_ou_matiere_id , element_id ) '
            . 'VALUES( :rubrique_type , :rubrique_id , :element_id ) ';
  }
  else
  {
    $DB_SQL = 'DELETE FROM sacoche_livret_jointure_referentiel '
            . 'WHERE livret_rubrique_type=:rubrique_type AND livret_rubrique_ou_matiere_id=:rubrique_id AND element_id=:element_id ';
  }
  $DB_VAR = array(
    ':rubrique_type' => $rubrique_type,
    ':rubrique_id'   => $rubrique_id,
    ':element_id'    => $element_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_type_jointure
 *
 * @param string $rubrique_type
 * @param string $rubrique_join
 * @return void
 */
public static function DB_modifier_type_jointure( $rubrique_type , $rubrique_join )
{
  $DB_SQL = 'UPDATE sacoche_livret_page '
          . 'SET livret_page_rubrique_join=:rubrique_join '
          . 'WHERE livret_page_rubrique_type=:rubrique_type ';
  $DB_VAR = array(
    ':rubrique_type' => $rubrique_type,
    ':rubrique_join' => $rubrique_join,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_jointures_référentiel
 *
 * @param string $rubrique_type
 * @return void
 */
public static function DB_supprimer_jointures_référentiel( $rubrique_type )
{
  $DB_SQL = 'DELETE FROM sacoche_livret_jointure_referentiel '
          . 'WHERE livret_rubrique_type=:rubrique_type ';
  $DB_VAR = array( ':rubrique_type' => $rubrique_type );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Seuils / Paramétrages de colonnes
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_colonnes_infos
 *
 * @param string $livret_colonne_type (facultatif)
 * @return array
 */
public static function DB_lister_colonnes_infos($livret_colonne_type=NULL)
{
  $select = ($livret_colonne_type) ? '' : 'livret_colonne_type, ' ;
  $where  = ($livret_colonne_type) ? 'WHERE livret_colonne_type=:livret_colonne_type ' : '' ;
  $DB_SQL = 'SELECT '.$select.'livret_colonne_id, livret_colonne_ordre, livret_colonne_titre, livret_colonne_legende, livret_colonne_seuil_defaut_min, livret_colonne_seuil_defaut_max, livret_colonne_couleur_1 '
          . 'FROM sacoche_livret_colonne '
          . $where
          . 'ORDER BY livret_colonne_ordre ASC ';
  $DB_VAR = array( ':livret_colonne_type' => $livret_colonne_type );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR, TRUE );
}

/**
 * lister_seuils_valeurs
 *
 * @return array
 */
public static function DB_lister_seuils_valeurs()
{
  $DB_SQL = 'SELECT CONCAT(livret_page_ref,"_",livret_colonne_id) AS livret_colonne_id , livret_seuil_min , livret_seuil_max '
          . 'FROM sacoche_livret_seuil '
          . 'ORDER BY livret_colonne_id ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , array() , TRUE );
}

/**
 * lister_page_seuils_infos
 *
 * @param string $livret_page_ref
 * @param string $livret_page_colonne facultatif et requis uniquement pour 6e 5e 4e 3e où il peut y avoir position ou objectif
 * @return array
 */
public static function DB_lister_page_seuils_infos( $livret_page_ref , $livret_page_colonne=NULL )
{
  $where  = (!$livret_page_colonne) ? '' : 'AND livret_colonne_type=:livret_colonne_type ' ;
  $DB_SQL = 'SELECT livret_colonne_id , livret_seuil_min , livret_seuil_max , livret_colonne_legende '
          . 'FROM sacoche_livret_seuil '
          . 'INNER JOIN sacoche_livret_colonne USING (livret_colonne_id) '
          . 'WHERE livret_page_ref=:livret_page_ref '.$where
          . 'ORDER BY livret_colonne_id ASC ';
  $DB_VAR = array(
    ':livret_page_ref'     => $livret_page_ref,
    ':livret_colonne_type' => $livret_page_colonne,
    );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR );
}

/**
 * modifier_page_colonne
 *
 * @param string $page_ref
 * @param string $colonne
 * @param int    $moy_classe
 * @return void
 */
public static function DB_modifier_page_colonne( $page_ref , $colonne , $moy_classe )
{
  $DB_SQL = 'UPDATE sacoche_livret_page '
          . 'SET livret_page_colonne=:colonne, livret_page_moyenne_classe=:moy_classe '
          . 'WHERE livret_page_ref=:page_ref ';
  $DB_VAR = array(
    ':page_ref'   => $page_ref,
    ':colonne'    => $colonne,
    ':moy_classe' => $moy_classe,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_seuils
 *
 * @param string $page_ref
 * @param array  $tab_seuils
 * @return void
 */
public static function DB_modifier_seuils( $page_ref , $tab_seuils )
{
  $DB_SQL = 'UPDATE sacoche_livret_seuil '
          . 'SET livret_seuil_min=:seuil_min, livret_seuil_max=:seuil_max '
          . 'WHERE livret_page_ref=:page_ref AND livret_colonne_id=:colonne_id ';
  $DB_VAR = array( ':page_ref' => $page_ref );
  foreach($tab_seuils as $colonne_id => $tab)
  {
    $DB_VAR[':colonne_id'] = $colonne_id;
    $DB_VAR[':seuil_min'] = $tab['min'];
    $DB_VAR[':seuil_max'] = $tab['max'];
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  }
}

/**
 * DB_modifier_legende
 *
 * @param int    $colonne_id
 * @param string $colonne_legende
 * @return void
 */
public static function DB_modifier_legende( $colonne_id , $colonne_legende )
{
  $DB_SQL = 'UPDATE sacoche_livret_colonne '
          . 'SET livret_colonne_legende=:colonne_legende '
          . 'WHERE livret_colonne_id=:colonne_id ';
  $DB_VAR = array(
    ':colonne_id'      => $colonne_id,
    ':colonne_legende' => $colonne_legende,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Jointures livret / groupe
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * compter_impression_archives
 *
 * @param string $annee_scolaire
 * @return array
 */
public static function DB_compter_impression_archives($annee_scolaire)
{
  $DB_SQL = 'SELECT COUNT(officiel_archive_id) AS nombre, eleve_classe_id, periode_livret '
          . 'FROM sacoche_officiel_archive '
          . 'LEFT JOIN sacoche_periode USING(periode_id) '
          . 'LEFT JOIN sacoche_user USING(user_id) '
          . 'WHERE archive_type=:archive_type AND annee_scolaire=:annee_scolaire '
          . 'GROUP BY eleve_classe_id, periode_livret ';
  $DB_VAR = array(
    ':archive_type'   => "livret",
    ':annee_scolaire' => $annee_scolaire,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * tester_classes_periode
 *
 * @param void
 * @return bool
 */
public static function DB_tester_classes_periode()
{
  $DB_SQL = 'SELECT COUNT(*) '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE livret_page_periodicite=:periodicite ';
  $DB_VAR = array( ':periodicite' => 'periode' );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * tester_jointure_classe_livret
 *
 * @param string $liste_page_ref
 * @return int
 */
public static function DB_tester_jointure_classe_livret($liste_page_ref)
{
  $DB_SQL = 'SELECT COUNT(*) '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE livret_page_ref IN('.$liste_page_ref.') ';
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_jointures_classes_livret
 *
 * @param void
 * @return array
 */
public static function DB_lister_jointures_classes_livret()
{
  $DB_SQL = 'SELECT groupe_id, livret_page_ref, sacoche_livret_jointure_groupe.livret_page_periodicite, jointure_periode, jointure_etat, jointure_date_export, '
          . 'livret_page_rubrique_type, livret_page_moyennes_annuelles, periode_id, jointure_date_debut, jointure_date_fin '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'LEFT JOIN sacoche_livret_page USING(livret_page_ref) '
          . 'LEFT JOIN sacoche_periode ON ( sacoche_livret_jointure_groupe.livret_page_periodicite = "periode" AND sacoche_livret_jointure_groupe.jointure_periode = sacoche_periode.periode_livret ) '
          . 'LEFT JOIN sacoche_jointure_groupe_periode USING(groupe_id, periode_id) '
          . 'ORDER BY groupe_id,livret_page_ordre, periode_ordre ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_jointures_eleve_livret_periodes
 *
 * @param int $eleve_id
 * @return array
 */
public static function DB_lister_jointures_eleve_livret_periodes($eleve_id)
{
  $DB_SQL = 'SELECT jointure_periode '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_livret_jointure_groupe ON sacoche_user.eleve_classe_id = sacoche_livret_jointure_groupe.groupe_id '
          . 'LEFT JOIN sacoche_periode ON sacoche_livret_jointure_groupe.jointure_periode = sacoche_periode.periode_livret '
          . 'WHERE user_id=:eleve_id AND livret_page_periodicite = "periode" '
          . 'ORDER BY periode_ordre ';
  $DB_VAR = array( ':eleve_id' => $eleve_id );
  return DB::queryCol(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_classes_avec_jointures_livret
 *
 * @param int   $groupe_id   facultatif, pour restreindre à une classe donnée
 * @return array
 */
public static function DB_lister_classes_avec_jointures_livret( $groupe_id = NULL )
{
  $where_groupe = ($groupe_id) ? 'AND groupe_id=:groupe_id ' : '' ;
  $index_classe = ($groupe_id) ? FALSE : TRUE ;
  $DB_SQL = 'SELECT groupe_id, groupe_nom, groupe_chef_id, livret_page_ref, livret_page_moment, livret_page_titre_classe, livret_page_resume, livret_page_moyennes_annuelles, '
          . $_SESSION['sql_any_value'].'(sacoche_livret_jointure_groupe.livret_page_periodicite) AS periodicite, GROUP_CONCAT(jointure_periode) AS listing_periodes '
          . 'FROM sacoche_groupe '
          . 'LEFT JOIN sacoche_niveau USING(niveau_id) '
          . 'LEFT JOIN sacoche_livret_jointure_groupe USING(groupe_id) '
          . 'LEFT JOIN sacoche_livret_page USING(livret_page_ref) '
          . 'WHERE groupe_type="classe" '.$where_groupe
          . 'GROUP BY groupe_id, livret_page_ref '
          . 'ORDER BY niveau_id ASC, groupe_nom ASC ';
  $DB_VAR = array( ':groupe_id' => $groupe_id );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR , $index_classe);
}

/**
 * ajouter_jointure_groupe
 *
 * @param int      $groupe_id
 * @param string   $page_ref
 * @param string   $page_periodicite
 * @param array    $tab_jointure_periode
 * @return array
 */
public static function DB_ajouter_jointure_groupe( $groupe_id , $page_ref , $page_periodicite , $tab_jointure_periode )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_groupe( groupe_id, livret_page_ref, livret_page_periodicite, jointure_periode, jointure_etat) '
          . 'VALUES                                    (:groupe_id,       :page_ref,       :page_periodicite,:jointure_periode,:jointure_etat)';
  $DB_VAR = array(
    ':groupe_id'        => $groupe_id,
    ':page_ref'         => $page_ref,
    ':page_periodicite' => $page_periodicite,
    ':jointure_etat'    => '1vide',
  );
  foreach($tab_jointure_periode as $jointure_periode)
  {
    $DB_VAR[':jointure_periode'] = $jointure_periode;
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  }
}

/**
 * modifier_jointure_groupe
 *
 * @param int      $groupe_id    id du groupe (en fait, obligatoirement une classe)
 * @param string   $page_ref
 * @param string   $page_periodicite
 * @param int      $jointure_periode
 * @param string   $etat         nouvel état
 * @return int     0 ou 1 si modifié
 */
public static function DB_modifier_jointure_groupe( $groupe_id , $page_ref , $page_periodicite , $jointure_periode , $etat )
{
  $update_date = ($etat=='5complet') ? ', jointure_date_verrou=NOW() ' : '' ;
  $DB_SQL = 'UPDATE sacoche_livret_jointure_groupe '
          . 'SET jointure_etat=:etat '.$update_date
          . 'WHERE groupe_id=:groupe_id AND livret_page_ref=:page_ref AND livret_page_periodicite=:page_periodicite AND jointure_periode=:jointure_periode ';
  $DB_VAR = array(
    ':groupe_id'        => $groupe_id,
    ':page_ref'         => $page_ref,
    ':page_periodicite' => $page_periodicite,
    ':jointure_periode' => $jointure_periode,
    ':etat'             => $etat,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return DB::rowCount(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * modifier_jointure_date_export
 *
 * @param int      $groupe_id    id du groupe (en fait, obligatoirement une classe)
 * @param string   $page_ref
 * @param string   $page_periodicite
 * @param int      $jointure_periode
 * @return void
 */
public static function DB_modifier_jointure_date_export( $groupe_id , $page_ref , $page_periodicite , $jointure_periode )
{
  $DB_SQL = 'UPDATE sacoche_livret_jointure_groupe '
          . 'SET jointure_date_export=NOW() '
          . 'WHERE groupe_id=:groupe_id AND livret_page_ref=:page_ref AND livret_page_periodicite=:page_periodicite AND jointure_periode=:jointure_periode ';
  $DB_VAR = array(
    ':groupe_id'        => $groupe_id,
    ':page_ref'         => $page_ref,
    ':page_periodicite' => $page_periodicite,
    ':jointure_periode' => $jointure_periode,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_jointure_groupe
 *
 * @param int      $groupe_id
 * @param string   $page_ref
 * @param string   $page_periodicite
 * @return void
 */
public static function DB_supprimer_jointure_groupe( $groupe_id , $page_ref , $page_periodicite )
{
  $DB_SQL = 'DELETE FROM sacoche_livret_jointure_groupe '
          . 'WHERE groupe_id=:groupe_id AND livret_page_ref=:page_ref AND livret_page_periodicite=:page_periodicite';
  $DB_VAR = array(
    ':groupe_id'        => $groupe_id,
    ':page_ref'         => $page_ref,
    ':page_periodicite' => $page_periodicite,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// EPI | AP | Parcours
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * pages_for_dispositif
 *
 * @param string   $dispositif     ap | epi | parcours
 * @param string   $parcours_code  facultatif, seulement si parcours
 * @return array
 */
public static function DB_lister_pages_for_dispositif( $dispositif , $parcours_code=NULL )
{
  $test = ($dispositif!='parcours') ? ' = 1 ' : ' LIKE "%'.$parcours_code.'%" ' ;
  $DB_SQL = 'SELECT livret_page_ref, livret_page_ordre, livret_page_moment, livret_page_rubrique_join '
          . 'FROM sacoche_livret_page '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref) '
          . 'WHERE livret_page_'.$dispositif.$test
          . 'GROUP BY livret_page_ref '
          . 'ORDER BY livret_page_ordre ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * tester_page_avec_dispositif
 *
 * @param string $page_ref
 * @param string $dispositif     epi | ap | parcours
 * @param string $parcours_code  facultatif, seulement si parcours
 * @return int
 */

public static function DB_tester_page_avec_dispositif( $page_ref , $dispositif , $parcours_code=NULL )
{
  $test = ($dispositif!='parcours') ? ' = 1 ' : ' LIKE "%'.$parcours_code.'%" ' ;
  $DB_SQL = 'SELECT livret_page_ordre '
          . 'FROM sacoche_livret_page '
          . 'WHERE livret_page_ref=:page_ref AND livret_page_'.$dispositif.$test;
  $DB_VAR = array( ':page_ref' => $page_ref );
  return (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Supprimer les jointures prof / dispositif dépendant d’une matière plus alimentée
 *
 * @param string   $dispositif     ap | epi
 * @return int
 */
public static function DB_nettoyer_jointure_dispositif_matiere( $dispositif )
{
  $DB_SQL = 'DELETE sacoche_livret_jointure_'.$dispositif.'_prof '
          . 'FROM sacoche_livret_jointure_'.$dispositif.'_prof '
          . 'LEFT JOIN sacoche_livret_'.$dispositif.' USING(livret_'.$dispositif.'_id) '
          . 'LEFT JOIN sacoche_livret_page USING(livret_page_ref) '
          . 'LEFT JOIN sacoche_livret_jointure_referentiel ON sacoche_livret_page.livret_page_rubrique_type = sacoche_livret_jointure_referentiel.livret_rubrique_type AND sacoche_livret_jointure_'.$dispositif.'_prof.matiere_id = livret_rubrique_ou_matiere_id '
          . 'WHERE livret_rubrique_ou_matiere_id IS NULL ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  return DB::rowCount(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * Supprimer les dispositifs non reliés à un enseignant
 *
 * @param string   $dispositif     ap | epi
 * @return int
 */
public static function DB_nettoyer_dispositif_sans_prof( $dispositif )
{
  $nb_prof_min = ($dispositif=='ap') ? 1 : 2 ;
  $DB_SQL = 'SELECT livret_'.$dispositif.'_id AS dispositif_id '
          . 'FROM sacoche_livret_'.$dispositif.' '
          . 'LEFT JOIN sacoche_livret_jointure_'.$dispositif.'_prof USING(livret_'.$dispositif.'_id) '
          . 'GROUP BY livret_'.$dispositif.'_id '
          . 'HAVING COUNT(prof_id)<'.$nb_prof_min;
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  $nb_delete = count($DB_TAB);
  if($nb_delete)
  {
    foreach($DB_TAB as $DB_ROW)
    {
      call_user_func_array( array('DB_STRUCTURE_LIVRET', 'DB_supprimer_'.$dispositif), array($DB_ROW['dispositif_id']) );
    }
  }
  return $nb_delete;
}

/**
 * Lister les dispositifs paramétrés pour un pré-remplissage automatique en fonction des appréciations saisies à la période précédente.
 *
 * @param groupe_id
 * @return array
 */
public static function DB_lister_dispositif_report_auto( $groupe_id )
{
  $DB_VAR = array( ':groupe_id' => $groupe_id );
  $DB_SQL = 'SELECT "ap" as rubrique_type, livret_ap_id as rubrique_id '
          . 'FROM sacoche_livret_ap '
          . 'WHERE livret_ap_report_auto=1 AND groupe_id=:groupe_id ';
  $tab_ap = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  $DB_SQL = 'SELECT "epi" as rubrique_type, livret_epi_id as rubrique_id '
          . 'FROM sacoche_livret_epi '
          . 'WHERE livret_epi_report_auto=1 AND groupe_id=:groupe_id ';
  $tab_epi = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  $DB_SQL = 'SELECT "parcours" as rubrique_type, livret_parcours_id as rubrique_id '
          . 'FROM sacoche_livret_parcours '
          . 'WHERE livret_parcours_report_auto=1 AND groupe_id=:groupe_id ';
  $tab_parcours = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return array_merge($tab_ap, $tab_epi, $tab_parcours);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// EPI
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_epi
 *
 * @param int|string $classe_id_or_listing_classe_id facultatif, pour restreindre à une classe ou un ensemble de classes
 * @param string     $page_ref                       facultatif, pour restreindre à une page du livret
 * @return array
 */
public static function DB_lister_epi( $classe_id_or_listing_classe_id = NULL , $page_ref = NULL )
{
  if($classe_id_or_listing_classe_id && $page_ref)
  {
    $where = 'WHERE livret_page_ref=:page_ref AND groupe_id=:groupe_id ';
  }
  elseif($classe_id_or_listing_classe_id)
  {
    $where = 'WHERE groupe_id IN('.$classe_id_or_listing_classe_id.') ';
  }
  else
  {
    $where = ''; // Profil administrateur ou directeur
  }
  $saisie_count = ($page_ref) ? '' : 'COUNT(livret_saisie_id) AS epi_used, ' ;
  $saisie_join  = ($page_ref) ? '' : 'LEFT JOIN sacoche_livret_saisie ON sacoche_livret_epi.livret_epi_id=sacoche_livret_saisie.rubrique_id AND rubrique_type="epi" ' ;
  $DB_SQL = 'SELECT sacoche_livret_epi.*, sacoche_livret_epi_theme.*, livret_page_ordre, livret_page_moment, groupe_nom, '.$saisie_count
          . 'GROUP_CONCAT( DISTINCT CONCAT(matiere_id,"_",user_id) SEPARATOR " ") AS matiere_prof_id, '
          . 'GROUP_CONCAT( DISTINCT CONCAT(matiere_nom," - ",user_nom," ",user_prenom) SEPARATOR "'.BRJS.'") AS matiere_prof_texte '
          . 'FROM sacoche_livret_epi '
          . 'LEFT JOIN sacoche_livret_epi_theme USING(livret_epi_theme_code) '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref, groupe_id) '
          . 'LEFT JOIN sacoche_livret_jointure_epi_prof USING(livret_epi_id) '
          . 'LEFT JOIN sacoche_livret_page USING(livret_page_ref) '
          . 'LEFT JOIN sacoche_groupe USING(groupe_id) '
          . 'LEFT JOIN sacoche_matiere USING(matiere_id) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_epi_prof.prof_id = sacoche_user.user_id '
          . $saisie_join
          . $where
          . 'GROUP BY livret_epi_id '
          . 'ORDER BY livret_page_ordre ASC, groupe_nom ASC, livret_epi_theme_nom ASC ';
  $DB_VAR = array(
    ':page_ref'  => $page_ref ,
    ':groupe_id' => $classe_id_or_listing_classe_id ,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * compter_epi_par_page
 *
 * @param void
 * @return array
 */
public static function DB_compter_epi_par_page()
{
  $DB_SQL = 'SELECT livret_page_ref, livret_epi_theme_code, COUNT(DISTINCT livret_epi_id) AS nombre '
          . 'FROM sacoche_livret_epi '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref, groupe_id) '
          . 'GROUP BY livret_page_ref, livret_epi_theme_code ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * tester_epi
 *
 * @param string $theme_code
 * @param string $page_ref
 * @param int    $groupe_id
 * @param int    $epi_id   inutile si recherche pour un ajout, mais id à éviter si recherche pour une modification
 * @return int
 */
// Clef unique UNIQUE KEY livret_epi (livret_epi_theme_code, livret_page_ref, groupe_id) retirée : on tolère plusieurs EPI avec la même thématique pour un élève.
/*
public static function DB_tester_epi( $theme_code , $page_ref , $groupe_id , $epi_id=FALSE )
{
  $where_epi_id = ($epi_id) ? 'AND livret_epi_id!=:epi_id ' : '' ;
  $DB_SQL = 'SELECT livret_epi_id '
          . 'FROM sacoche_livret_epi '
          . 'WHERE livret_epi_theme_code=:theme_code AND livret_page_ref=:page_ref AND groupe_id=:groupe_id '.$where_epi_id
          . 'LIMIT 1 '; // utile
  $DB_VAR = array(
    ':theme_code' => $theme_code,
    ':page_ref'   => $page_ref,
    ':groupe_id'  => $groupe_id,
    ':epi_id'     => $epi_id,
  );
  return (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}
*/

/**
 * ajouter_epi
 *
 * @param string $theme_code
 * @param string $page_ref
 * @param int    $groupe_id
 * @param string $epi_titre
 * @param int    $report_auto
 * @return int
 */
public static function DB_ajouter_epi( $theme_code , $page_ref , $groupe_id , $epi_titre , $report_auto )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_epi( livret_epi_theme_code,livret_page_ref, groupe_id,livret_epi_titre,livret_epi_report_auto) '
          . 'VALUES                        (           :theme_code,      :page_ref,:groupe_id,      :epi_titre,          :report_auto)';
  $DB_VAR = array(
    ':theme_code'  => $theme_code,
    ':page_ref'    => $page_ref,
    ':groupe_id'   => $groupe_id,
    ':epi_titre'   => $epi_titre,
    ':report_auto' => $report_auto,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * ajouter_epi_jointure
 *
 * @param int    $epi_id
 * @param int    $matiere_id
 * @param int    $prof_id
 * @return void
 */
public static function DB_ajouter_epi_jointure( $epi_id , $matiere_id , $prof_id )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_epi_prof( livret_epi_id, matiere_id, prof_id) '
          . 'VALUES                                      (       :epi_id,:matiere_id,:prof_id)';
  $DB_VAR = array(
    ':epi_id'     => $epi_id,
    ':matiere_id' => $matiere_id,
    ':prof_id'    => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_epi
 *
 * @param int    $epi_id
 * @param string $theme_code
 * @param string $page_ref
 * @param int    $groupe_id
 * @param string $epi_titre
 * @param int    $report_auto
 * @return void
 */
public static function DB_modifier_epi( $epi_id , $theme_code , $page_ref , $groupe_id , $epi_titre , $report_auto )
{
  $DB_SQL = 'UPDATE sacoche_livret_epi '
          . 'SET livret_epi_theme_code=:theme_code, livret_page_ref=:page_ref, groupe_id=:groupe_id, livret_epi_titre=:epi_titre, livret_epi_report_auto=:report_auto '
          . 'WHERE livret_epi_id=:epi_id ';
  $DB_VAR = array(
    ':epi_id'      => $epi_id,
    ':theme_code'  => $theme_code,
    ':page_ref'    => $page_ref,
    ':groupe_id'   => $groupe_id,
    ':epi_titre'   => $epi_titre,
    ':report_auto' => $report_auto,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_epi
 *
 * @param int    $epi_id
 * @return void
 */
public static function DB_supprimer_epi( $epi_id )
{
  // le dispositif
  $DB_SQL = 'DELETE sacoche_livret_epi, sacoche_livret_jointure_epi_prof '
          . 'FROM sacoche_livret_epi '
          . 'LEFT JOIN sacoche_livret_jointure_epi_prof USING (livret_epi_id) '
          . 'WHERE livret_epi_id=:epi_id ';
  $DB_VAR = array( ':epi_id' => $epi_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  // les saisies
  DB_STRUCTURE_LIVRET::DB_supprimer_saisies_dispositif( 'epi' , $epi_id );
}

/**
 * supprimer_epi_jointure
 *
 * @param int    $epi_id
 * @return void
 */
public static function DB_supprimer_epi_jointure( $epi_id )
{
  $DB_SQL = 'DELETE FROM sacoche_livret_jointure_epi_prof '
          . 'WHERE livret_epi_id=:epi_id ';
  $DB_VAR = array( ':epi_id' => $epi_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// EPI Thématiques
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * tester_epi_theme
 *
 * @param string $theme_code
 * @return int
 */
public static function DB_tester_epi_theme( $theme_code )
{
  $DB_SQL = 'SELECT livret_epi_theme_origine '
          . 'FROM sacoche_livret_epi_theme '
          . 'WHERE livret_epi_theme_code=:theme_code ';
  $DB_VAR = array( ':theme_code' => $theme_code );
  return (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * ajouter_epi_theme
 *
 * @param string $theme_code
 * @param string $theme_nom
 * @return void
 */
public static function DB_ajouter_epi_theme( $theme_code , $theme_nom )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_epi_theme( livret_epi_theme_code, livret_epi_theme_origine, livret_epi_theme_nom) '
          . 'VALUES                              (           :theme_code,           :theme_origine,           :theme_nom)';
  $DB_VAR = array(
    ':theme_code'    => $theme_code,
    ':theme_origine' => 2,
    ':theme_nom'     => $theme_nom,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_epi_theme
 *
 * @param string $theme_code
 * @param string $theme_nom
 * @return void
 */
public static function DB_modifier_epi_theme( $theme_code , $theme_nom )
{
  $DB_SQL = 'UPDATE sacoche_livret_epi_theme '
          . 'SET livret_epi_theme_nom=:theme_nom '
          . 'WHERE livret_epi_theme_code=:theme_code ';
  $DB_VAR = array(
    ':theme_code' => $theme_code,
    ':theme_nom'  => $theme_nom,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_epi_theme
 *
 * @param string $theme_code
 * @param int    $theme_used
 * @return void
 */
public static function DB_supprimer_epi_theme( $theme_code , $theme_used )
{
  // la thématique
  $DB_SQL = 'DELETE FROM sacoche_livret_epi_theme '
          . 'WHERE livret_epi_theme_code=:theme_code ';
  $DB_VAR = array( ':theme_code' => $theme_code );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  // les dispositifs et les saisies
  if($theme_used)
  {
    $DB_SQL = 'SELECT livret_epi_id '
            . 'FROM sacoche_livret_epi '
            . 'WHERE livret_epi_theme_code=:theme_code ';
    $DB_COL = DB::queryCol(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
    foreach($DB_COL as $livret_epi_id)
    {
      DB_STRUCTURE_LIVRET::DB_supprimer_epi( $livret_epi_id );
    }
  }
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// AP
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_ap
 *
 * @param int|string $classe_id_or_listing_classe_id facultatif, pour restreindre à une classe ou un ensemble de classes
 * @param string     $page_ref                       facultatif, pour restreindre à une page du livret
 * @return array
 */
public static function DB_lister_ap( $classe_id_or_listing_classe_id = NULL , $page_ref = NULL )
{
  if($classe_id_or_listing_classe_id && $page_ref)
  {
    $where = 'WHERE livret_page_ref=:page_ref AND groupe_id=:groupe_id ';
  }
  elseif($classe_id_or_listing_classe_id)
  {
    $where = 'WHERE groupe_id IN('.$classe_id_or_listing_classe_id.') ';
  }
  else
  {
    $where = ''; // Profil administrateur ou directeur
  }
  $saisie_count = ($page_ref) ? '' : 'COUNT(livret_saisie_id) AS ap_used, ' ;
  $saisie_join  = ($page_ref) ? '' : 'LEFT JOIN sacoche_livret_saisie ON sacoche_livret_ap.livret_ap_id=sacoche_livret_saisie.rubrique_id AND rubrique_type="ap" ' ;
  $DB_SQL = 'SELECT sacoche_livret_ap.*, livret_page_ordre, livret_page_moment, groupe_nom, '.$saisie_count
          . 'GROUP_CONCAT( DISTINCT CONCAT(matiere_id,"_",user_id) SEPARATOR " ") AS matiere_prof_id, '
          . 'GROUP_CONCAT( DISTINCT CONCAT(matiere_nom," - ",user_nom," ",user_prenom) SEPARATOR "'.BRJS.'") AS matiere_prof_texte '
          . 'FROM sacoche_livret_ap '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref, groupe_id) '
          . 'LEFT JOIN sacoche_livret_jointure_ap_prof USING(livret_ap_id) '
          . 'LEFT JOIN sacoche_livret_page USING(livret_page_ref) '
          . 'LEFT JOIN sacoche_groupe USING(groupe_id) '
          . 'LEFT JOIN sacoche_matiere USING(matiere_id) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_ap_prof.prof_id = sacoche_user.user_id '
          . $saisie_join
          . $where
          . 'GROUP BY livret_ap_id '
          . 'ORDER BY livret_page_ordre ASC, groupe_nom ASC ';
  $DB_VAR = array(
    ':page_ref'  => $page_ref ,
    ':groupe_id' => $classe_id_or_listing_classe_id ,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * compter_ap_par_page
 *
 * @param void
 * @return array
 */
public static function DB_compter_ap_par_page()
{
  $DB_SQL = 'SELECT livret_page_ref, COUNT(DISTINCT livret_ap_id) AS nombre '
          . 'FROM sacoche_livret_ap '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref, groupe_id) '
          . 'GROUP BY livret_page_ref ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * ajouter_ap
 *
 * @param string $page_ref
 * @param int    $groupe_id
 * @param string $ap_titre
 * @param int    $report_auto
 * @return int
 */
public static function DB_ajouter_ap( $page_ref , $groupe_id , $ap_titre , $report_auto )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_ap( livret_page_ref, groupe_id, livret_ap_titre, livret_ap_report_auto) '
          . 'VALUES                       (       :page_ref,:groupe_id,       :ap_titre,          :report_auto)';
  $DB_VAR = array(
    ':page_ref'    => $page_ref,
    ':groupe_id'   => $groupe_id,
    ':ap_titre'    => $ap_titre,
    ':report_auto' => $report_auto,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * ajouter_ap_jointure
 *
 * @param int    $ap_id
 * @param int    $matiere_id
 * @param int    $prof_id
 * @return void
 */
public static function DB_ajouter_ap_jointure( $ap_id , $matiere_id , $prof_id )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_ap_prof( livret_ap_id, matiere_id, prof_id) '
          . 'VALUES                                     (       :ap_id,:matiere_id,:prof_id)';
  $DB_VAR = array(
    ':ap_id'     => $ap_id,
    ':matiere_id' => $matiere_id,
    ':prof_id'    => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_ap
 *
 * @param int    $ap_id
 * @param string $page_ref
 * @param int    $groupe_id
 * @param string $ap_titre
 * @param int    $report_auto
 * @return void
 */
public static function DB_modifier_ap( $ap_id , $page_ref , $groupe_id , $ap_titre , $report_auto )
{
  $DB_SQL = 'UPDATE sacoche_livret_ap '
          . 'SET livret_page_ref=:page_ref, groupe_id=:groupe_id, livret_ap_titre=:ap_titre, livret_ap_report_auto=:report_auto '
          . 'WHERE livret_ap_id=:ap_id ';
  $DB_VAR = array(
    ':ap_id'       => $ap_id,
    ':page_ref'    => $page_ref,
    ':groupe_id'   => $groupe_id,
    ':ap_titre'    => $ap_titre,
    ':report_auto' => $report_auto,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_ap
 *
 * @param int    $ap_id
 * @return void
 */
public static function DB_supprimer_ap( $ap_id )
{
  // le dispositif
  $DB_SQL = 'DELETE sacoche_livret_ap, sacoche_livret_jointure_ap_prof '
          . 'FROM sacoche_livret_ap '
          . 'LEFT JOIN sacoche_livret_jointure_ap_prof USING (livret_ap_id) '
          . 'WHERE livret_ap_id=:ap_id ';
  $DB_VAR = array( ':ap_id' => $ap_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  // les saisies
  DB_STRUCTURE_LIVRET::DB_supprimer_saisies_dispositif( 'ap' , $ap_id );
}

/**
 * supprimer_ap_jointure
 *
 * @param int    $ap_id
 * @return void
 */
public static function DB_supprimer_ap_jointure( $ap_id )
{
  $DB_SQL = 'DELETE FROM sacoche_livret_jointure_ap_prof '
          . 'WHERE livret_ap_id=:ap_id ';
  $DB_VAR = array( ':ap_id' => $ap_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Parcours
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_parcours_type
 *
 * @param void
 * @return array
 */
public static function DB_lister_parcours_type()
{
  $DB_SQL = 'SELECT sacoche_livret_parcours_type.* '
          . 'FROM sacoche_livret_parcours_type '
          . 'ORDER BY livret_parcours_type_nom ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , array() , TRUE);
}

/**
 * lister_parcours
 *
 * @param string     $parcours_code                  facultatif, pour restreindre à un type de parcours
 * @param int|string $classe_id_or_listing_classe_id facultatif, pour restreindre à une classe ou un ensemble de classes
 * @param string     $page_ref                       facultatif, pour restreindre à une page du livret
 * @return array
 */
public static function DB_lister_parcours( $parcours_code , $classe_id_or_listing_classe_id = NULL , $page_ref = NULL )
{
  $tab_where = array();
  if($parcours_code)
  {
    $tab_where[] = 'livret_parcours_type_code=:parcours_code ';
  }
  if($classe_id_or_listing_classe_id && $page_ref)
  {
    $tab_where[] = 'livret_page_ref=:page_ref AND groupe_id=:groupe_id ';
  }
  else if($classe_id_or_listing_classe_id)
  {
    $tab_where[] = 'groupe_id IN('.$classe_id_or_listing_classe_id.') ';
  }
  $saisie_count = ($page_ref) ? '' : 'COUNT(livret_saisie_id) AS parcours_used, ' ;
  $saisie_join  = ($page_ref) ? '' : 'LEFT JOIN sacoche_livret_saisie ON sacoche_livret_parcours.livret_parcours_id=sacoche_livret_saisie.rubrique_id AND rubrique_type="parcours" ' ;
  $DB_SQL = 'SELECT sacoche_livret_parcours.*, livret_parcours_type_nom, livret_page_ordre, livret_page_moment, groupe_nom, '.$saisie_count
          . 'GROUP_CONCAT( DISTINCT user_id SEPARATOR " ") AS prof_id, '
          . 'GROUP_CONCAT( DISTINCT CONCAT(user_nom," ",user_prenom) SEPARATOR "'.BRJS.'") AS prof_texte '
          . 'FROM sacoche_livret_parcours '
          . 'INNER JOIN sacoche_livret_parcours_type USING(livret_parcours_type_code) '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref, groupe_id) '
          . 'LEFT JOIN sacoche_livret_jointure_parcours_prof USING(livret_parcours_id) '
          . 'LEFT JOIN sacoche_livret_page USING(livret_page_ref) '
          . 'LEFT JOIN sacoche_groupe USING(groupe_id) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_parcours_prof.prof_id = sacoche_user.user_id '
          . $saisie_join
          . 'WHERE '.implode('AND ',$tab_where)
          . 'GROUP BY livret_parcours_type_code, livret_page_ref, groupe_id '
          . 'ORDER BY livret_page_ordre ASC, groupe_nom ASC ';
  $DB_VAR = array(
    ':parcours_code' => $parcours_code ,
    ':page_ref'      => $page_ref ,
    ':groupe_id'     => $classe_id_or_listing_classe_id ,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * compter_parcours_par_page
 *
 * @param void
 * @return array
 */
public static function DB_compter_parcours_par_page()
{
  $DB_SQL = 'SELECT livret_page_ref, livret_parcours_type_code, COUNT(DISTINCT livret_parcours_id) AS nombre '
          . 'FROM sacoche_livret_parcours '
          . 'INNER JOIN sacoche_livret_jointure_groupe USING(livret_page_ref, groupe_id) '
          . 'GROUP BY livret_page_ref, livret_parcours_type_code ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * tester_parcours
 *
 * Remarque : Il n’y a qu’un parcours par classe, on ne teste pas l’enseignant.
 *
 * @param string $parcours_code
 * @param string $page_ref
 * @param int    $groupe_id
 * @param int    $matiere_id
 * @param int    $parcours_id   inutile si recherche pour un ajout, mais id à éviter si recherche pour une modification
 * @return int
 */
public static function DB_tester_parcours( $parcours_code , $page_ref , $groupe_id , $parcours_id=FALSE )
{
  $where_parcours_id = ($parcours_id) ? 'AND livret_parcours_id!=:parcours_id ' : '' ;
  $DB_SQL = 'SELECT livret_parcours_id '
          . 'FROM sacoche_livret_parcours '
          . 'WHERE livret_parcours_type_code=:parcours_code AND livret_page_ref=:page_ref AND groupe_id=:groupe_id '.$where_parcours_id
          . 'LIMIT 1 '; // utile
  $DB_VAR = array(
    ':parcours_code' => $parcours_code,
    ':page_ref'      => $page_ref,
    ':groupe_id'     => $groupe_id,
    ':parcours_id'   => $parcours_id,
  );
  return (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * ajouter_parcours
 *
 * @param string $parcours_code
 * @param string $page_ref
 * @param int    $groupe_id
 * @param int    $report_auto
 * @return int
 */
public static function DB_ajouter_parcours( $parcours_code , $page_ref , $groupe_id , $report_auto )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_parcours( livret_parcours_type_code, livret_page_ref, groupe_id, livret_parcours_report_auto) '
          . 'VALUES                             (            :parcours_code,       :page_ref,:groupe_id,                :report_auto)';
  $DB_VAR = array(
    ':parcours_code' => $parcours_code,
    ':page_ref'      => $page_ref,
    ':groupe_id'     => $groupe_id,
    ':report_auto'   => $report_auto,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * ajouter_parcours_jointure
 *
 * @param int    $parcours_id
 * @param int    $prof_id
 * @return void
 */
public static function DB_ajouter_parcours_jointure( $parcours_id , $prof_id )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_parcours_prof(livret_parcours_id, prof_id) '
          . 'VALUES                                           (      :parcours_id,:prof_id)';
  $DB_VAR = array(
    ':parcours_id' => $parcours_id,
    ':prof_id'     => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_parcours
 *
 * @param int    $parcours_id
 * @param string $parcours_code
 * @param string $page_ref
 * @param int    $groupe_id
 * @param int    $report_auto
 * @return void
 */
public static function DB_modifier_parcours( $parcours_id , $parcours_code , $page_ref , $groupe_id , $report_auto )
{
  $DB_SQL = 'UPDATE sacoche_livret_parcours '
          . 'SET livret_parcours_type_code=:parcours_code, livret_page_ref=:page_ref, groupe_id=:groupe_id, livret_parcours_report_auto=:report_auto '
          . 'WHERE livret_parcours_id=:parcours_id ';
  $DB_VAR = array(
    ':parcours_id'   => $parcours_id,
    ':parcours_code' => $parcours_code,
    ':page_ref'      => $page_ref,
    ':groupe_id'     => $groupe_id,
    ':report_auto'   => $report_auto,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_parcours
 *
 * @param int    $parcours_id
 * @return void
 */
public static function DB_supprimer_parcours( $parcours_id )
{
  // le dispositif
  $DB_SQL = 'DELETE sacoche_livret_parcours, sacoche_livret_jointure_parcours_prof '
          . 'FROM sacoche_livret_parcours '
          . 'LEFT JOIN sacoche_livret_jointure_parcours_prof USING (livret_parcours_id) '
          . 'WHERE livret_parcours_id=:parcours_id ';
  $DB_VAR = array( ':parcours_id' => $parcours_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  // les saisies
  DB_STRUCTURE_LIVRET::DB_supprimer_saisies_dispositif( 'parcours' , $parcours_id );
}

/**
 * supprimer_parcours_jointure
 *
 * @param int    $parcours_id
 * @return void
 */
public static function DB_supprimer_parcours_jointure( $parcours_id )
{
  $DB_SQL = 'DELETE FROM sacoche_livret_jointure_parcours_prof '
          . 'WHERE livret_parcours_id=:parcours_id ';
  $DB_VAR = array( ':parcours_id' => $parcours_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Cadre de Référence des Compétences Numériques (CRCN)
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * tester_classes_crcn
 *
 * @param void
 * @return bool
 */
public static function DB_tester_classes_crcn()
{
  $DB_SQL = 'SELECT COUNT(*) '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE livret_page_ref IN("cm2","6e") ';
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_crcn_niveaux
 *
 * @param void
 * @return array
 */
public static function DB_lister_crcn_niveaux()
{
  $DB_SQL = 'SELECT crcn_niveau_numero, crcn_niveau_cycle, crcn_niveau_categorie, crcn_niveau_description '
          . 'FROM sacoche_crcn_niveau '
          . 'ORDER BY crcn_niveau_numero ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_crcn_domaines_competences
 *
 * @param bool   $with_detail
 * @return array
 */
public static function DB_lister_crcn_domaines_competences($with_detail=TRUE)
{
  $select_echelle_descriptives = ($with_detail) ? ', crcn_competence_niveau_1, crcn_competence_niveau_2, crcn_competence_niveau_3, crcn_competence_niveau_4, crcn_competence_niveau_5 ' : '' ;
  $DB_SQL = 'SELECT crcn_domaine_id, crcn_domaine_ref, crcn_domaine_libelle, '
          . 'crcn_competence_id, crcn_competence_ref, crcn_competence_libelle, crcn_competence_description '
          . $select_echelle_descriptives
          . 'FROM sacoche_crcn_domaine '
          . 'LEFT JOIN sacoche_crcn_competence USING(crcn_domaine_id) '
          . 'ORDER BY crcn_domaine_ordre ASC, crcn_competence_ordre ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_crcn_saisies
 *
 * @param string $listing_eleve_id
 * @param bool   $only_positif
 * @return array
 */
public static function DB_lister_crcn_saisies( $listing_eleve_id , $only_positif=FALSE )
{
  $where_positif = ($only_positif) ? 'AND crcn_niveau_numero IS NOT NULL ' : '' ;
  $DB_SQL = 'SELECT eleve_id, crcn_competence_id, crcn_niveau_numero, prof_id, crcn_saisie_date, '
          . 'user_nom AS prof_nom, user_prenom AS prof_prenom, user_genre AS prof_genre '
          . 'FROM sacoche_crcn_saisie '
          . 'LEFT JOIN sacoche_user ON sacoche_crcn_saisie.prof_id = sacoche_user.user_id '
          . 'WHERE eleve_id IN('.$listing_eleve_id.') '.$where_positif;
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * ajouter_crcn_saisies
 *
 * @param int    $eleve_id
 * @param int    $competence_id
 * @param int    $niveau_numero
 * @param int    $prof_id
 * @return void
 */
public static function DB_ajouter_crcn_saisies( $eleve_id , $competence_id , $niveau_numero , $prof_id )
{
  $DB_SQL = 'INSERT INTO sacoche_crcn_saisie ( eleve_id , crcn_competence_id , crcn_niveau_numero , prof_id , crcn_saisie_date ) '
          . 'VALUES                          (:eleve_id ,     :competence_id ,     :niveau_numero ,:prof_id , NOW() ) ';
  $DB_VAR = array(
    ':eleve_id'      => $eleve_id,
    ':competence_id' => $competence_id,
    ':niveau_numero' => $niveau_numero,
    ':prof_id'       => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_crcn_saisies
 *
 * @param int    $eleve_id
 * @param int    $competence_id
 * @param int    $niveau_numero
 * @param int    $prof_id
 * @return void
 */
public static function DB_modifier_crcn_saisies( $eleve_id , $competence_id , $niveau_numero , $prof_id )
{
  $DB_SQL = 'UPDATE sacoche_crcn_saisie '
          . 'SET crcn_niveau_numero=:niveau_numero , prof_id=:prof_id , crcn_saisie_date=NOW() '
          . 'WHERE eleve_id=:eleve_id AND crcn_competence_id=:competence_id ';
  $DB_VAR = array(
    ':eleve_id'      => $eleve_id,
    ':competence_id' => $competence_id,
    ':niveau_numero' => $niveau_numero,
    ':prof_id'       => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Modalités d’accompagnement
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * tester_modaccomp
 *
 * @param string $modaccomp_code
 * @return bool
 */
public static function DB_tester_modaccomp( $modaccomp_code )
{
  $DB_SQL = 'SELECT 1 '
          . 'FROM sacoche_livret_modaccomp '
          . 'WHERE livret_modaccomp_code=:modaccomp_code ';
  $DB_VAR = array( ':modaccomp_code' => $modaccomp_code );
  return (bool) DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_eleve_modaccomp_gestion
 *
 * @param string $only_groupes_id   facultatif, pour restreindre à un ensemble de groupes
 * @return array
 */
public static function DB_lister_eleve_modaccomp_gestion( $only_groupes_id = NULL )
{
  $where_groupe = ($only_groupes_id) ? 'AND groupe_id IN('.$only_groupes_id.') ' : '' ;
  $DB_SQL = 'SELECT livret_modaccomp_id, user_id, livret_modaccomp_code, info_complement, '
          . 'livret_modaccomp_nom, user_nom, user_prenom, groupe_nom, GROUP_CONCAT(periode_livret) AS listing_periodes '
          . 'FROM sacoche_livret_jointure_modaccomp_eleve '
          . 'LEFT JOIN sacoche_livret_jointure_modaccomp_periode USING(livret_modaccomp_id) '
          . 'LEFT JOIN sacoche_livret_modaccomp USING(livret_modaccomp_code) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_modaccomp_eleve.eleve_id = sacoche_user.user_id '
          . 'LEFT JOIN sacoche_groupe ON sacoche_user.eleve_classe_id = sacoche_groupe.groupe_id '
          . 'WHERE user_sortie_date>NOW() AND groupe_id IS NOT NULL '.$where_groupe
          . 'GROUP BY eleve_id, livret_modaccomp_code '
          . 'ORDER BY groupe_nom ASC, user_nom ASC, user_prenom ASC, livret_modaccomp_code ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_eleve_modaccomp_information
 *
 * @param string $liste_eleves_id
 * @param int    $periode_livret
 * @return array
 */
public static function DB_lister_eleve_modaccomp_information( $liste_eleves_id , $periode_livret )
{
  $DB_SQL = 'SELECT user_id, livret_modaccomp_code, livret_modaccomp_nom, info_complement '
          . 'FROM sacoche_livret_jointure_modaccomp_eleve '
          . 'LEFT JOIN sacoche_livret_jointure_modaccomp_periode USING(livret_modaccomp_id) '
          . 'LEFT JOIN sacoche_livret_modaccomp USING(livret_modaccomp_code) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_modaccomp_eleve.eleve_id = sacoche_user.user_id '
          . 'LEFT JOIN sacoche_groupe ON sacoche_user.eleve_classe_id = sacoche_groupe.groupe_id '
          . 'WHERE user_sortie_date>NOW() AND groupe_id IS NOT NULL AND eleve_id IN('.$liste_eleves_id.') AND periode_livret=:periode_livret' ;
  $DB_VAR = array(
    ':periode_livret' => $periode_livret,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * ajouter_eleve_modaccomp
 *
 * @param int    $eleve_id
 * @param string $modaccomp_code
 * @param string $info_complement
 * @return array
 */
public static function DB_ajouter_eleve_modaccomp( $eleve_id , $modaccomp_code , $info_complement )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_modaccomp_eleve ( eleve_id , livret_modaccomp_code , info_complement) '
          . 'VALUES                                              (:eleve_id ,       :modaccomp_code ,:info_complement) '
          . 'ON DUPLICATE KEY UPDATE info_complement=:info_complement ';
  $DB_VAR = array(
    ':eleve_id'        => $eleve_id,
    ':modaccomp_code'  => $modaccomp_code,
    ':info_complement' => $info_complement,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return array( DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME) , DB::rowCount(SACOCHE_STRUCTURE_BD_NAME) );
}

/**
 * supprimer_eleve_modaccomp
 *
 * @param int    $livret_modaccomp_id
 * @return void
 */
public static function DB_supprimer_eleve_modaccomp( $livret_modaccomp_id )
{
  $DB_SQL = 'DELETE sacoche_livret_jointure_modaccomp_eleve, sacoche_livret_jointure_modaccomp_periode '
          . 'FROM sacoche_livret_jointure_modaccomp_eleve '
          . 'LEFT JOIN sacoche_livret_jointure_modaccomp_periode USING (livret_modaccomp_id) '
          . 'WHERE livret_modaccomp_id=:livret_modaccomp_id ';
  $DB_VAR = array(
    ':livret_modaccomp_id' => $livret_modaccomp_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_eleve_modaccomp_periode
 *
 * @param int    $livret_modaccomp_id
 * @param int    $periode_livret
 * @param bool   $is_actif
 * @return void
 */
public static function DB_modifier_eleve_modaccomp_periode( $livret_modaccomp_id , $periode_livret , $is_actif )
{
  if($is_actif)
  {
    $DB_SQL = 'INSERT INTO sacoche_livret_jointure_modaccomp_periode ( livret_modaccomp_id , periode_livret) '
            . 'VALUES                                                (:livret_modaccomp_id ,:periode_livret) '
            . 'ON DUPLICATE KEY UPDATE periode_livret=:periode_livret ';
  }
  else
  {
    $DB_SQL = 'DELETE FROM sacoche_livret_jointure_modaccomp_periode '
            . 'WHERE livret_modaccomp_id=:livret_modaccomp_id AND periode_livret=:periode_livret ';
  }
  $DB_VAR = array(
    ':livret_modaccomp_id' => $livret_modaccomp_id,
    ':periode_livret'      => $periode_livret,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_eleve_modaccomp_complement
 *
 * @param int    $livret_modaccomp_id
 * @param string $info_complement
 * @return void
 */
public static function DB_modifier_eleve_modaccomp_complement( $livret_modaccomp_id , $info_complement )
{
  $DB_SQL = 'UPDATE sacoche_livret_jointure_modaccomp_eleve '
          . 'SET info_complement=:info_complement '
          . 'WHERE livret_modaccomp_id=:livret_modaccomp_id ';
  $DB_VAR = array(
    ':livret_modaccomp_id' => $livret_modaccomp_id,
    ':info_complement'     => $info_complement,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * actualiser_eleve_modaccomp_periode
 *
 * @param void
 * @return int
 */
public static function DB_actualiser_eleve_modaccomp_periode()
{
  $nb_associations = 0;
  $DB_TAB = DB_STRUCTURE_LIVRET::DB_lister_eleve_modaccomp_gestion();
  if(empty($DB_TAB))
  {
    return $nb_associations;
  }
  $tab_memo_eleve_periode = array();
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_memo_eleve_periodicite[$DB_ROW['user_id']] = array(
      'modaccomp_id'   => $DB_ROW['livret_modaccomp_id'],
      'liste_periodes' => $DB_ROW['listing_periodes'],
      'periodicite'    => $DB_ROW['listing_periodes'][0],
    );
  }
  $listing_eleves = implode(',',array_keys($tab_memo_eleve_periodicite));
  $DB_TAB = DB_STRUCTURE_LIVRET::DB_lister_periodes_eleves($listing_eleves);
  foreach($DB_TAB as $DB_ROW)
  {
    if($DB_ROW['listing_periodes'])
    {
      $modaccomp_id    = $tab_memo_eleve_periodicite[$DB_ROW['user_id']]['modaccomp_id'];
      $periodicite_old = $tab_memo_eleve_periodicite[$DB_ROW['user_id']]['periodicite'];
      $periodicite_new = $DB_ROW['listing_periodes'][0];
      if( $periodicite_old != $periodicite_new )
      {
        $tab_periodes_old = explode(',',$tab_memo_eleve_periodicite[$DB_ROW['user_id']]['liste_periodes']);
        foreach($tab_periodes_old as $periode_livret_old)
        {
          DB_STRUCTURE_LIVRET::DB_modifier_eleve_modaccomp_periode( $modaccomp_id , $periode_livret_old , FALSE );
        }
        for( $periode_num = 1 ; $periode_num <= $periodicite_new ; $periode_num++ )
        {
          $periode_livret_new = $periodicite_new*10 + $periode_num;
          DB_STRUCTURE_LIVRET::DB_modifier_eleve_modaccomp_periode( $modaccomp_id , $periode_livret_new , TRUE );
          $nb_associations++;
        }
      }
    }
  }
  return $nb_associations;
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Devoirs faits
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * lister_eleve_devoirsfaits_gestion
 *
 * @param string $only_groupes_id   facultatif, pour restreindre à un ensemble de groupes
 * @return array
 */
public static function DB_lister_eleve_devoirsfaits_gestion( $only_groupes_id = NULL )
{
  $where_groupe = ($only_groupes_id) ? 'AND groupe_id IN('.$only_groupes_id.') ' : '' ;
  $DB_SQL = 'SELECT livret_devoirsfaits_id, user_id, '
          . 'user_nom, user_prenom, groupe_nom, GROUP_CONCAT(periode_livret) AS listing_periodes '
          . 'FROM sacoche_livret_jointure_devoirsfaits_eleve '
          . 'LEFT JOIN sacoche_livret_jointure_devoirsfaits_periode USING(livret_devoirsfaits_id) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_devoirsfaits_eleve.eleve_id = sacoche_user.user_id '
          . 'LEFT JOIN sacoche_groupe ON sacoche_user.eleve_classe_id = sacoche_groupe.groupe_id '
          . 'WHERE user_sortie_date>NOW() AND groupe_id IS NOT NULL '.$where_groupe
          . 'GROUP BY eleve_id '
          . 'ORDER BY groupe_nom ASC, user_nom ASC, user_prenom ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * lister_eleve_devoirsfaits_information
 *
 * @param string $liste_eleves_id
 * @param int    $periode_livret
 * @return array
 */
public static function DB_lister_eleve_devoirsfaits_information( $liste_eleves_id , $periode_livret )
{
  $DB_SQL = 'SELECT eleve_id '
          . 'FROM sacoche_livret_jointure_devoirsfaits_eleve '
          . 'LEFT JOIN sacoche_livret_jointure_devoirsfaits_periode USING(livret_devoirsfaits_id) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_devoirsfaits_eleve.eleve_id = sacoche_user.user_id '
          . 'LEFT JOIN sacoche_groupe ON sacoche_user.eleve_classe_id = sacoche_groupe.groupe_id '
          . 'WHERE user_sortie_date>NOW() AND groupe_id IS NOT NULL AND eleve_id IN('.$liste_eleves_id.') AND periode_livret=:periode_livret' ;
  $DB_VAR = array(
    ':periode_livret' => $periode_livret,
  );
  return DB::queryCol(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * ajouter_eleve_devoirsfaits
 *
 * @param int    $eleve_id
 * @return array
 */
public static function DB_ajouter_eleve_devoirsfaits( $eleve_id )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_devoirsfaits_eleve ( eleve_id ) '
          . 'VALUES                                                 (:eleve_id ) '
          . 'ON DUPLICATE KEY UPDATE eleve_id=eleve_id ';
  $DB_VAR = array(
    ':eleve_id' => $eleve_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return array( DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME) , DB::rowCount(SACOCHE_STRUCTURE_BD_NAME) );
}

/**
 * supprimer_eleve_devoirsfaits
 *
 * @param int    $livret_devoirsfaits_id
 * @return void
 */
public static function DB_supprimer_eleve_devoirsfaits( $livret_devoirsfaits_id )
{
  $DB_SQL = 'DELETE sacoche_livret_jointure_devoirsfaits_eleve, sacoche_livret_jointure_devoirsfaits_periode '
          . 'FROM sacoche_livret_jointure_devoirsfaits_eleve '
          . 'LEFT JOIN sacoche_livret_jointure_devoirsfaits_periode USING (livret_devoirsfaits_id) '
          . 'WHERE livret_devoirsfaits_id=:livret_devoirsfaits_id ';
  $DB_VAR = array(
    ':livret_devoirsfaits_id' => $livret_devoirsfaits_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_eleve_devoirsfaits_periode
 *
 * @param int    $livret_devoirsfaits_id
 * @param int    $periode_livret
 * @param bool   $is_actif
 * @return void
 */
public static function DB_modifier_eleve_devoirsfaits_periode( $livret_devoirsfaits_id , $periode_livret , $is_actif )
{
  if($is_actif)
  {
    $DB_SQL = 'INSERT INTO sacoche_livret_jointure_devoirsfaits_periode ( livret_devoirsfaits_id , periode_livret) '
            . 'VALUES                                                   (:livret_devoirsfaits_id ,:periode_livret) '
            . 'ON DUPLICATE KEY UPDATE periode_livret=:periode_livret ';
  }
  else
  {
    $DB_SQL = 'DELETE FROM sacoche_livret_jointure_devoirsfaits_periode '
            . 'WHERE livret_devoirsfaits_id=:livret_devoirsfaits_id AND periode_livret=:periode_livret ';
  }
  $DB_VAR = array(
    ':livret_devoirsfaits_id' => $livret_devoirsfaits_id,
    ':periode_livret'         => $periode_livret,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * actualiser_eleve_devoirsfaits_periode
 *
 * @param void
 * @return int
 */
public static function DB_actualiser_eleve_devoirsfaits_periode()
{
  $nb_associations = 0;
  $DB_TAB = DB_STRUCTURE_LIVRET::DB_lister_eleve_devoirsfaits_gestion();
  if(empty($DB_TAB))
  {
    return $nb_associations;
  }
  $tab_memo_eleve_periode = array();
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_memo_eleve_periodicite[$DB_ROW['user_id']] = array(
      'devoirsfaits_id' => $DB_ROW['livret_devoirsfaits_id'],
      'liste_periodes'  => $DB_ROW['listing_periodes'],
      'periodicite'     => $DB_ROW['listing_periodes'][0],
    );
  }
  $listing_eleves = implode(',',array_keys($tab_memo_eleve_periodicite));
  $DB_TAB = DB_STRUCTURE_LIVRET::DB_lister_periodes_eleves($listing_eleves);
  foreach($DB_TAB as $DB_ROW)
  {
    if($DB_ROW['listing_periodes'])
    {
      $devoirsfaits_id = $tab_memo_eleve_periodicite[$DB_ROW['user_id']]['devoirsfaits_id'];
      $periodicite_old = $tab_memo_eleve_periodicite[$DB_ROW['user_id']]['periodicite'];
      $periodicite_new = $DB_ROW['listing_periodes'][0];
      if( $periodicite_old != $periodicite_new )
      {
        $tab_periodes_old = explode(',',$tab_memo_eleve_periodicite[$DB_ROW['user_id']]['liste_periodes']);
        foreach($tab_periodes_old as $periode_livret_old)
        {
          DB_STRUCTURE_LIVRET::DB_modifier_eleve_devoirsfaits_periode( $devoirsfaits_id , $periode_livret_old , FALSE );
        }
        for( $periode_num = 1 ; $periode_num <= $periodicite_new ; $periode_num++ )
        {
          $periode_livret_new = $periodicite_new*10 + $periode_num;
          DB_STRUCTURE_LIVRET::DB_modifier_eleve_devoirsfaits_periode( $devoirsfaits_id , $periode_livret_new , TRUE );
          $nb_associations++;
        }
      }
    }
  }
  return $nb_associations;
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Langues de culture régionale
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * tester_classes_langcultregion
 *
 * @param void
 * @return bool
 */
public static function DB_tester_classes_langcultregion()
{
  $DB_SQL = 'SELECT COUNT(*) '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE livret_page_ref=:page_ref ';
  $DB_VAR = array( ':page_ref' => 'cycle4' );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * tester_langcultregion
 *
 * @param string $langcultregion_code
 * @return bool
 */
public static function DB_tester_langcultregion( $langcultregion_code )
{
  $DB_SQL = 'SELECT 1 '
          . 'FROM sacoche_livret_langcultregion '
          . 'WHERE livret_langcultregion_code=:langcultregion_code ';
  $DB_VAR = array( ':langcultregion_code' => $langcultregion_code );
  return (bool) DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_eleve_langcultregion
 *
 * @param string $liste_eleve    facultatif, pour restreindre à des élèves donnés
 * @return array
 */
public static function DB_lister_eleve_langcultregion( $liste_eleve = NULL )
{
  $where = ($liste_eleve) ? 'AND eleve_id IN('.$liste_eleve.') ' : '' ;
  $DB_SQL = 'SELECT eleve_id, livret_langcultregion_id, livret_langcultregion_code , livret_langcultregion_nom '
          . 'FROM sacoche_livret_jointure_langcultregion_eleve '
          . 'LEFT JOIN sacoche_livret_langcultregion USING(livret_langcultregion_code) '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_jointure_langcultregion_eleve.eleve_id = sacoche_user.user_id '
          . 'LEFT JOIN sacoche_groupe ON sacoche_user.eleve_classe_id = sacoche_groupe.groupe_id '
          . 'WHERE user_sortie_date>NOW() AND groupe_id IS NOT NULL '.$where;
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * modifier_eleve_langcultregion
 *
 * @param string $langcultregion_code
 * @param int    $eleve_id
 * @return void
 */
public static function DB_modifier_eleve_langcultregion( $langcultregion_code , $eleve_id )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_jointure_langcultregion_eleve ( livret_langcultregion_code , eleve_id ) '
          . 'VALUES                                                   (       :langcultregion_code ,:eleve_id )'
          . 'ON DUPLICATE KEY UPDATE livret_langcultregion_code=:langcultregion_code ';
  $DB_VAR = array(
    ':langcultregion_code' => $langcultregion_code,
    ':eleve_id'            => $eleve_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_eleve_langcultregion
 *
 * @param string $listing_eleve_id
 * @return void
 */
public static function DB_supprimer_eleve_langcultregion( $listing_eleve_id )
{
  $DB_SQL = 'DELETE FROM sacoche_livret_jointure_langcultregion_eleve '
          . 'WHERE eleve_id IN('.$listing_eleve_id.') ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  // on n’efface pas la saisie éventuelle pour la retrouver en cas de fausse manip ; son reliquat ne gène ni la saisie ni la collecte
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Moyennes annuelles 3e
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * tester_classes_moyennes3e
 *
 * @param void
 * @return bool
 */
public static function DB_tester_classes_moyennes3e()
{
  $DB_SQL = 'SELECT COUNT(*) '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'WHERE livret_page_ref=:page_ref ';
  $DB_VAR = array( ':page_ref' => '3e' );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Formation 3e
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * tester_dnb_mef
 *
 * @param int $dnb_mef_id
 * @return bool
 */
public static function DB_tester_dnb_mef($dnb_mef_id)
{
  $DB_SQL = 'SELECT 1 '
          . 'FROM sacoche_livret_dnb_mef '
          . 'WHERE livret_dnb_mef_id=:dnb_mef_id ';
  $DB_VAR = array( ':dnb_mef_id' => $dnb_mef_id );
  return (bool) DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_matieres_mef_3e
 *
 * @param void listing_user_id   pour restreindre à un élève, à une liste d’élèves, ou sinon recherche pour tous les élèves
 * @return array
 */
public static function DB_lister_matieres_mef_3e($listing_user_id=NULL)
{
  if(empty($listing_user_id))
  {
    // tous les élèves : on veut juste savoir les matières attendues
    $select_user = 'livret_dnb_mef_intitule, ';
    $where = 'WHERE user_sortie_date>NOW() AND eleve_dnb_mef_id!=0 ';
    $groupby = 'GROUP BY livret_dnb_mef_id, livret_dnb_matiere_code ';
  }
  elseif(is_numeric($listing_user_id))
  {
    // un seul élève, on récupère les matières pour lui... mais aussi l'intitulé du MEF pour la page de moyenne DNB
    $select_user = 'user_id, livret_dnb_mef_intitule, '; // par cohérence, si une liste d’élèves comporte un seul élève
    $where = 'WHERE user_id=:user_id ';
    $groupby = '';
  }
  else
  {
    // liste d’élèves, on veut les matières par élève
    $select_user = 'user_id, ';
    $where = 'WHERE user_id IN('.$listing_user_id.') ';
    $groupby = 'GROUP BY user_id, livret_dnb_matiere_code ';
  }
  $DB_SQL = 'SELECT '.$select_user.'livret_dnb_mef_id, livret_dnb_matiere_code, livret_dnb_matiere_obligatoire, livret_dnb_matiere_dispense_possible, livret_dnb_matiere_nom '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_livret_dnb_mef ON sacoche_user.eleve_dnb_mef_id = sacoche_livret_dnb_mef.livret_dnb_mef_id '
          . 'LEFT JOIN sacoche_livret_dnb_jointure_mef_matiere USING(livret_dnb_mef_id) '
          . 'LEFT JOIN sacoche_livret_dnb_matiere USING(livret_dnb_matiere_id) '
          . $where.'AND livret_dnb_modalite_election_code != "D" ' // cas mal géré... compliqué...  le code_matière n’est pas un masque utilisable puisque pouvant être toutes les matières...
          . $groupby
          . 'ORDER BY livret_dnb_matiere_id ASC ';
  $DB_VAR = array( ':user_id' => $listing_user_id );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Options de formulaires
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Retourner un tableau [valeur texte optgroup] des thématiques d’EPI
 *
 * @param bool test_used   Pour demander à tester s’il y a un EPI associé à une thématique
 * @return array
 */
public static function DB_OPT_epitheme($test_used)
{
  $saisie_count = (!$test_used) ? '' : ', COUNT(livret_epi_id) AS theme_used ' ;
  $saisie_join  = (!$test_used) ? '' : 'LEFT JOIN sacoche_livret_epi USING(livret_epi_theme_code) ' ;
  $group_by     = (!$test_used) ? '' : 'GROUP BY livret_epi_theme_code ' ;
  $DB_SQL = 'SELECT livret_epi_theme_code AS valeur, livret_epi_theme_nom AS texte, livret_epi_theme_origine AS optgroup '.$saisie_count
          . 'FROM sacoche_livret_epi_theme '
          . $saisie_join
          . $group_by
          . 'ORDER BY livret_epi_theme_origine ASC, livret_epi_theme_nom ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * Retourner un tableau [valeur texte] des modalités d’accompagnement
 *
 * @param void
 * @return array
 */
public static function DB_OPT_modaccomp()
{
  $DB_SQL = 'SELECT livret_modaccomp_code AS valeur, CONCAT( livret_modaccomp_code ," : " , livret_modaccomp_nom ) AS texte '
          . 'FROM sacoche_livret_modaccomp '
          . 'ORDER BY livret_modaccomp_code ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * Retourner un tableau [valeur texte] des langues de culture régionale
 *
 * @param void
 * @return array
 */
public static function DB_OPT_langcultregion()
{
  $DB_SQL = 'SELECT livret_langcultregion_code AS valeur, CONCAT( livret_langcultregion_code ," : " , livret_langcultregion_nom ) AS texte '
          . 'FROM sacoche_livret_langcultregion '
          . 'ORDER BY livret_langcultregion_id ASC ';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * Retourner un tableau [valeur texte] des classes associées à une page du livret
 *
 * @param string $page_ref
 * @param string $only_groupes_id   facultatif, pour restreindre à un ensemble de groupes
 * @return array
 */
public static function DB_OPT_groupes_for_page( $page_ref , $only_groupes_id = NULL )
{
  $where_groupe = ($only_groupes_id) ? 'AND groupe_id IN('.$only_groupes_id.') ' : '' ;
  $DB_SQL = 'SELECT groupe_id AS valeur, groupe_nom AS texte '
          . 'FROM sacoche_livret_jointure_groupe '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'LEFT JOIN sacoche_niveau USING(niveau_id) '
          . 'WHERE livret_page_ref=:page_ref '.$where_groupe
          . 'GROUP BY groupe_id '
          . 'ORDER BY niveau_id ASC, groupe_nom ASC ';
  $DB_VAR = array( ':page_ref' => $page_ref );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return !empty($DB_TAB) ? $DB_TAB : ( ($where_groupe) ? 'Aucune classe trouvée sur laquelle vous ayez les droits suffisants.' : 'Aucune classe trouvée.' ) ;
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Nettoyage
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * supprimer_jointure_referentiel_obsolete
 *
 * Lors de la suppression de référentiels ou d’éléments de référentiels, la table sacoche_livret_jointure_referentiel n’est pas nettoyée car c’est un peu pénible, on le fait donc ici.
 *
 * @param void
 * @return void
 */
public static function DB_supprimer_jointure_referentiel_obsolete()
{
  // Les liaisons aux éléments de référentiels
  $tab_element = array(
    'matiere' => 'sacoche_matiere',
    'domaine' => 'sacoche_referentiel_domaine',
    'theme'   => 'sacoche_referentiel_theme',
    'item'    => 'sacoche_referentiel_item',
    'user'    => "sacoche_user",
  );
  foreach($tab_element as $element => $table)
  {
    $DB_SQL = 'DELETE sacoche_livret_jointure_referentiel '
            . 'FROM sacoche_livret_jointure_referentiel '
            . 'LEFT JOIN sacoche_livret_page ON sacoche_livret_jointure_referentiel.livret_rubrique_type = sacoche_livret_page.livret_page_rubrique_type '
            . 'LEFT JOIN '.$table.' ON sacoche_livret_jointure_referentiel.element_id = '.$table.'.'.$element.'_id '
            . 'WHERE livret_page_rubrique_join="'.$element.'" AND '.$table.'.'.$element.'_id IS NULL ';
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  }
  // Les liaisons aux matières du Livret Scolaire ; test si la matière n’existe plus ou n’est plus active
  $DB_SQL = 'DELETE sacoche_livret_jointure_referentiel '
          . 'FROM sacoche_livret_jointure_referentiel '
          . 'LEFT JOIN sacoche_livret_page ON sacoche_livret_jointure_referentiel.livret_rubrique_type = sacoche_livret_page.livret_page_rubrique_type '
          . 'LEFT JOIN sacoche_matiere AS matiere1 ON sacoche_livret_jointure_referentiel.livret_rubrique_ou_matiere_id = matiere1.matiere_id '
          . 'LEFT JOIN sacoche_matiere AS matiere2 ON sacoche_livret_jointure_referentiel.element_id = matiere2.matiere_id '
          . 'WHERE livret_page_rubrique_join="matiere" AND ( (matiere1.matiere_id IS NULL) OR (matiere2.matiere_id IS NULL) OR ( matiere1.matiere_active=0 AND matiere1.matiere_siecle=0 ) OR ( matiere2.matiere_active=0 AND matiere2.matiere_siecle=0 ) OR matiere1.matiere_experimentale=1 OR matiere2.matiere_experimentale=1 ) ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  // Les liaisons aux matières du Livret Scolaire ; test s’il existe au moins un référentiel dans cette matière
  $DB_SQL = 'DELETE sacoche_livret_jointure_referentiel '
          . 'FROM sacoche_livret_jointure_referentiel '
          . 'LEFT JOIN sacoche_livret_page ON sacoche_livret_jointure_referentiel.livret_rubrique_type = sacoche_livret_page.livret_page_rubrique_type '
          . 'LEFT JOIN sacoche_referentiel ON sacoche_livret_jointure_referentiel.element_id = sacoche_referentiel.matiere_id '
          . 'WHERE livret_page_rubrique_join="matiere" AND sacoche_referentiel.matiere_id IS NULL ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * supprimer_bilans_officiels
 *
 * @param void
 * @return void
 */
public static function DB_vider_livret()
{
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_ap');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_epi');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_export');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_parcours');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_jointure_ap_prof');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_jointure_epi_prof');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_jointure_parcours_prof');
  // Pour les tables suivantes, cela peut avoir du sens de conserver les informations d’une année sur l’autre.
/*
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_jointure_modaccomp_eleve');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_jointure_langcultregion_eleve');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_jointure_devoirsfaits_eleve');
*/
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_saisie');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_saisie_jointure_prof');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'TRUNCATE sacoche_livret_saisie_memo_detail');
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'UPDATE sacoche_livret_jointure_groupe SET jointure_etat="1vide", jointure_date_verrou=NULL, jointure_date_export=NULL');
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Saisies
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * recuperer_donnees_eleves
 *
 * @param string $livret_page_ref
 * @param string $livret_page_periodicite
 * @param int    $jointure_periode
 * @param string $liste_rubrique_type   Vide pour toutes les rubriques
 * @param string $liste_eleve_id
 * @param int    $prof_id               Pour restreindre aux saisies d’un prof.
 * @param bool   $with_periodes_avant   On récupère aussi les données des périodes antérieures.
 * @return array
 */
public static function DB_recuperer_donnees_eleves( $livret_page_ref , $livret_page_periodicite , $jointure_periode , $liste_rubrique_type , $liste_eleve_id , $prof_id , $with_periodes_avant )
{
  $select_periode = ($with_periodes_avant) ? ', jointure_periode ' : '' ;
  $where_periode  = ($with_periodes_avant) ? ( ($jointure_periode) ? 'AND jointure_periode!=0 ' : '' ) : 'AND jointure_periode=:jointure_periode ' ;
  $where_rubrique = ($liste_rubrique_type) ? 'AND rubrique_type IN('.$liste_rubrique_type.') ' : '' ;
  $where_prof     = ($prof_id) ? 'AND ( sacoche_livret_saisie.prof_id IN('.$prof_id.',0) OR sacoche_livret_saisie_jointure_prof.prof_id='.$prof_id.' ) ' : '' ;
  $order_periode  = ($with_periodes_avant) ? ', jointure_periode ASC ' : '' ;
  $DB_SQL = 'SELECT livret_saisie_id, rubrique_type, rubrique_id, cible_id AS eleve_id, saisie_objet, saisie_valeur, saisie_origine, '
          . 'sacoche_livret_saisie.prof_id AS user_id, user_genre, user_nom, user_prenom, matiere_code, matiere_nom, '
          . 'GROUP_CONCAT(sacoche_livret_saisie_jointure_prof.prof_id) AS listing_profs, '
          . 'acquis_detail '.$select_periode
          . 'FROM sacoche_livret_saisie '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_saisie.prof_id=sacoche_user.user_id '
          . 'LEFT JOIN sacoche_livret_saisie_jointure_prof USING(livret_saisie_id) '
          . 'LEFT JOIN sacoche_livret_saisie_memo_detail USING(livret_saisie_id) '
          . 'LEFT JOIN sacoche_matiere ON sacoche_livret_saisie.rubrique_id = sacoche_matiere.matiere_id ' // (parfois obligatoire, parfois n’a pas de sens : pas grave)
          . 'WHERE livret_page_ref=:livret_page_ref AND livret_page_periodicite=:livret_page_periodicite '.$where_periode.$where_rubrique.' AND cible_nature=:cible_nature AND cible_id IN ('.$liste_eleve_id.') '.$where_prof
          . 'GROUP BY (livret_saisie_id) '
          . 'ORDER BY matiere_ordre ASC '
          . $order_periode;
  $DB_VAR = array(
    ':livret_page_ref'         => $livret_page_ref,
    ':livret_page_periodicite' => $livret_page_periodicite,
    ':jointure_periode'        => $jointure_periode,
    ':cible_nature'            => 'eleve',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_donnees_classe
 *
 * @param string $livret_page_ref
 * @param string $livret_page_periodicite
 * @param int    $jointure_periode
 * @param string $liste_rubrique_type   Vide pour toutes les rubriques
 * @param int    $classe_id
 * @param int    $prof_id               Pour restreindre aux saisies d’un prof.
 * @param bool   $with_periodes_avant   On récupère aussi les données des périodes antérieures.
 * @return array
 */
public static function DB_recuperer_donnees_classe( $livret_page_ref , $livret_page_periodicite , $jointure_periode , $liste_rubrique_type , $classe_id , $prof_id , $with_periodes_avant )
{
  $select_periode = ($with_periodes_avant) ? ', jointure_periode ' : '' ;
  $where_periode  = ($with_periodes_avant) ? ( ($jointure_periode) ? 'AND jointure_periode!=0 ' : '' ) : 'AND jointure_periode=:jointure_periode ' ;
  $where_rubrique = ($liste_rubrique_type) ? 'AND rubrique_type IN('.$liste_rubrique_type.') ' : '' ;
  $where_prof     = ($prof_id) ? 'AND ( sacoche_livret_saisie.prof_id='.$prof_id.' OR sacoche_livret_saisie_jointure_prof.prof_id='.$prof_id.' ) ' : '' ;
  $order_periode  = ($with_periodes_avant) ? ', jointure_periode ASC ' : '' ;
  $DB_SQL = 'SELECT livret_saisie_id, rubrique_type, rubrique_id, saisie_objet, saisie_valeur, saisie_origine, '
          . 'sacoche_livret_saisie.prof_id AS user_id, user_genre, user_nom, user_prenom, matiere_code, '
          . 'GROUP_CONCAT(sacoche_livret_saisie_jointure_prof.prof_id) AS listing_profs  '.$select_periode
          . 'FROM sacoche_livret_saisie '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_saisie.prof_id=sacoche_user.user_id '
          . 'LEFT JOIN sacoche_livret_saisie_jointure_prof USING(livret_saisie_id) '
          . 'LEFT JOIN sacoche_matiere ON sacoche_livret_saisie.rubrique_id = sacoche_matiere.matiere_id ' // Pour ordonner au moins en cas de liaisons aux matières (pas de sens sinon, mais pas grave)
          . 'WHERE livret_page_ref=:livret_page_ref AND livret_page_periodicite=:livret_page_periodicite '.$where_periode.$where_rubrique.' AND cible_nature=:cible_nature AND cible_id=:cible_id '.$where_prof
          . 'GROUP BY (livret_saisie_id) '
          . 'ORDER BY matiere_ordre ASC '
          . $order_periode;
  $DB_VAR = array(
    ':livret_page_ref'         => $livret_page_ref,
    ':livret_page_periodicite' => $livret_page_periodicite,
    ':jointure_periode'        => $jointure_periode,
    ':cible_nature'            => 'classe',
    ':cible_id'                => $classe_id,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_saisie_precise
 *
 * @param string $livret_page_ref
 * @param string $livret_page_periodicite
 * @param int    $jointure_periode
 * @param string $rubrique_type
 * @param int    $rubrique_id
 * @param int    $cible_nature
 * @param int    $cible_id
 * @param string $saisie_objet
 * @return array
 */
public static function DB_recuperer_saisie_precise( $livret_page_ref , $livret_page_periodicite , $jointure_periode , $rubrique_type , $rubrique_id , $cible_nature , $cible_id , $saisie_objet )
{
  $DB_SQL = 'SELECT livret_saisie_id, saisie_valeur, saisie_origine, '
          . 'sacoche_livret_saisie.prof_id AS user_id, user_genre, user_nom, user_prenom, '
          . 'GROUP_CONCAT(sacoche_livret_saisie_jointure_prof.prof_id) AS listing_profs '
          . 'FROM sacoche_livret_saisie '
          . 'LEFT JOIN sacoche_user ON sacoche_livret_saisie.prof_id=sacoche_user.user_id '
          . 'LEFT JOIN sacoche_livret_saisie_jointure_prof USING(livret_saisie_id) '
          . 'WHERE livret_page_ref=:livret_page_ref AND livret_page_periodicite=:livret_page_periodicite AND jointure_periode=:jointure_periode '
          . 'AND rubrique_type=:rubrique_type AND rubrique_id=:rubrique_id AND cible_nature=:cible_nature AND cible_id=:cible_id AND saisie_objet=:saisie_objet '
          . 'GROUP BY (livret_saisie_id) ';
  $DB_VAR = array(
    ':livret_page_ref'         => $livret_page_ref,
    ':livret_page_periodicite' => $livret_page_periodicite,
    ':jointure_periode'        => $jointure_periode,
    ':rubrique_type'           => $rubrique_type,
    ':rubrique_id'             => $rubrique_id,
    ':cible_nature'            => $cible_nature,
    ':cible_id'                => $cible_id,
    ':saisie_objet'            => $saisie_objet,
  );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_ajouter_saisie
 *
 * @param string  $livret_page_ref
 * @param string  $livret_page_periodicite
 * @param int     $jointure_periode
 * @param string  $rubrique_type
 * @param int     $rubrique_id
 * @param string  $cible_nature
 * @param int     $cible_id
 * @param string  $saisie_objet
 * @param decimal $saisie_valeur
 * @param decimal $saisie_origine
 * @param int     $prof_id
 * @return int
 */
public static function DB_ajouter_saisie( $livret_page_ref , $livret_page_periodicite , $jointure_periode , $rubrique_type , $rubrique_id , $cible_nature , $cible_id , $saisie_objet , $saisie_valeur , $saisie_origine , $prof_id )
{
  // INSERT ON DUPLICATE KEY UPDATE est plus performant que REPLACE et mieux par rapport aux id autoincrémentés ou aux contraintes sur les clefs étrangères
  // @see http://stackoverflow.com/questions/9168928/what-are-practical-differences-between-replace-and-insert-on-duplicate-ke
  $DB_SQL = 'INSERT INTO sacoche_livret_saisie ( livret_page_ref,  livret_page_periodicite,  jointure_periode,  rubrique_type,  rubrique_id,  cible_nature,  cible_id,  saisie_objet,  saisie_valeur,  saisie_origine,  prof_id) '
          . 'VALUES                            (:livret_page_ref, :livret_page_periodicite, :jointure_periode, :rubrique_type, :rubrique_id, :cible_nature, :cible_id, :saisie_objet, :saisie_valeur, :saisie_origine, :prof_id) '
          . 'ON DUPLICATE KEY UPDATE saisie_valeur=:saisie_valeur, saisie_origine=:saisie_origine, prof_id=:prof_id ';
  $DB_VAR = array(
    ':livret_page_ref'         => $livret_page_ref,
    ':livret_page_periodicite' => $livret_page_periodicite,
    ':jointure_periode'        => $jointure_periode,
    ':rubrique_type'           => $rubrique_type,
    ':rubrique_id'             => $rubrique_id,
    ':cible_nature'            => $cible_nature,
    ':cible_id'                => $cible_id,
    ':saisie_objet'            => $saisie_objet,
    ':saisie_valeur'           => $saisie_valeur,
    ':saisie_origine'          => $saisie_origine,
    ':prof_id'                 => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  $livret_saisie_id = DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME);
  // Il est arrivé très rarement que $livret_saisie_id vaille NULL car une ligne identique existe déjà (sans doute deux appels simultanés...).
  if( $livret_saisie_id && $prof_id && ($saisie_objet=='appreciation') )
  {
    DB_STRUCTURE_LIVRET::DB_modifier_saisie_jointure_prof( $livret_saisie_id , $prof_id );
  }
  return $livret_saisie_id;
}

/**
 * DB_modifier_saisie
 *
 * @param int     $livret_saisie_id
 * @param string  $saisie_objet
 * @param decimal $saisie_valeur
 * @param decimal $saisie_origine
 * @param int     $prof_id
 * @return void
 */
public static function DB_modifier_saisie( $livret_saisie_id , $saisie_objet , $saisie_valeur , $saisie_origine , $prof_id )
{
  $DB_SQL = 'UPDATE sacoche_livret_saisie '
          . 'SET saisie_valeur=:saisie_valeur, saisie_origine=:saisie_origine, prof_id=:prof_id '
          . 'WHERE livret_saisie_id=:livret_saisie_id ';
  $DB_VAR = array(
    ':livret_saisie_id' => $livret_saisie_id,
    ':saisie_valeur'    => $saisie_valeur,
    ':saisie_origine'   => $saisie_origine,
    ':prof_id'          => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  if( $prof_id && ($saisie_objet=='appreciation') )
  {
    DB_STRUCTURE_LIVRET::DB_modifier_saisie_jointure_prof( $livret_saisie_id , $prof_id );
  }
}

/**
 * DB_modifier_saisie_jointure_prof
 *
 * @param int     $livret_saisie_id
 * @param int     $prof_id
 * @param bool    $delete
 * @return void
 */
public static function DB_modifier_saisie_jointure_prof( $livret_saisie_id , $prof_id , $delete=FALSE )
{
  if(!$delete)
  {
    $DB_SQL = 'INSERT IGNORE INTO sacoche_livret_saisie_jointure_prof ( livret_saisie_id,  prof_id) VALUES (:livret_saisie_id, :prof_id) ';
  }
  else
  {
    $DB_SQL = 'DELETE FROM sacoche_livret_saisie_jointure_prof WHERE livret_saisie_id=:livret_saisie_id AND prof_id=:prof_id ';
  }
  $DB_VAR = array(
    ':livret_saisie_id' => $livret_saisie_id,
    ':prof_id'          => $prof_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_ajouter_saisie_memo_detail
 *
 * @param int     $livret_saisie_id
 * @param string  $acquis_detail
 * @return void
 */
public static function DB_ajouter_saisie_memo_detail( $livret_saisie_id , $acquis_detail )
{
  $DB_SQL = 'INSERT INTO sacoche_livret_saisie_memo_detail ( livret_saisie_id,  acquis_detail) '
          . 'VALUES                                        (:livret_saisie_id, :acquis_detail) '
          . 'ON DUPLICATE KEY UPDATE acquis_detail=:acquis_detail ';
  $DB_VAR = array(
    ':livret_saisie_id' => $livret_saisie_id,
    ':acquis_detail'    => $acquis_detail,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_modifier_saisie_memo_detail
 *
 * @param int     $livret_saisie_id
 * @param string  $acquis_detail
 * @return void
 */
public static function DB_modifier_saisie_memo_detail( $livret_saisie_id , $acquis_detail )
{
  $DB_SQL = 'UPDATE sacoche_livret_saisie_memo_detail '
          . 'SET acquis_detail=:acquis_detail '
          . 'WHERE livret_saisie_id=:livret_saisie_id ';
  $DB_VAR = array(
    ':livret_saisie_id' => $livret_saisie_id,
    ':acquis_detail'    => $acquis_detail,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_supprimer_saisie
 * Ne peut être qu’une note calculée automatiquement, car pour le reste un champ vide est enregistré
 *
 * @param int     $livret_saisie_id
 * @param string  $saisie_objet
 * @return void
 */
public static function DB_supprimer_saisie( $livret_saisie_id )
{
  $DB_SQL = 'DELETE sacoche_livret_saisie, sacoche_livret_saisie_jointure_prof, sacoche_livret_saisie_memo_detail '
          . 'FROM sacoche_livret_saisie '
          . 'LEFT JOIN sacoche_livret_saisie_jointure_prof USING (livret_saisie_id) '
          . 'LEFT JOIN sacoche_livret_saisie_memo_detail USING (livret_saisie_id) '
          . 'WHERE livret_saisie_id=:livret_saisie_id ';
  $DB_VAR = array(
    ':livret_saisie_id' => $livret_saisie_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_supprimer_saisies_dispositif
 *
 * @param string  $rubrique_type
 * @param int     $rubrique_id
 * @return void
 */
public static function DB_supprimer_saisies_dispositif( $rubrique_type , $rubrique_id )
{
  $DB_SQL = 'DELETE sacoche_livret_saisie, sacoche_livret_saisie_jointure_prof '
          . 'FROM sacoche_livret_saisie '
          . 'LEFT JOIN sacoche_livret_saisie_jointure_prof USING (livret_saisie_id) '
          . 'WHERE rubrique_type=:rubrique_type AND rubrique_id=:rubrique_id ';
  $DB_VAR = array(
    ':rubrique_type' => $rubrique_type,
    ':rubrique_id'   => $rubrique_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_recuperer_elements_programme
 *
 * @param string $liste_eleve_id   id des élèves séparés par des virgules ; il peut n’y avoir qu’un id
 * @param int    $liste_item_id    id de items séparés par des virgules
 * @param string $date_sql_debut
 * @param string $date_sql_fin
 * @return array
 */
public static function DB_recuperer_elements_programme( $liste_eleve_id , $liste_item_id , $date_sql_debut , $date_sql_fin )
{
  $where_eleve      = (strpos($liste_eleve_id,',')) ? 'eleve_id IN('.$liste_eleve_id.') ' : 'eleve_id='.$liste_eleve_id.' ' ; // Pour IN(...) NE PAS passer la liste dans $DB_VAR sinon elle est convertie en nb entier
  $where_item       = (strpos($liste_item_id ,',')) ? 'item_id IN('. $liste_item_id .') ' : 'item_id='. $liste_item_id .' ' ; // Pour IN(...) NE PAS passer la liste dans $DB_VAR sinon elle est convertie en nb entier
  $where_date_debut = ($date_sql_debut)             ? 'AND saisie_date>=:date_debut '     : '' ;
  $where_date_fin   = ($date_sql_fin)               ? 'AND saisie_date<=:date_fin '       : '' ;
  $DB_SQL = 'SELECT eleve_id , item_id , item_nom , theme_id , theme_nom , domaine_id , domaine_nom , '
          . 'COUNT( IF(saisie_note NOT IN ("AB","DI","NE","NF","NN","NR","PA"), 1, NULL) ) AS eval_nb , '
          . 'referentiel_mode_livret AS mode_livret '
          . 'FROM sacoche_saisie '
          . 'LEFT JOIN sacoche_referentiel_item USING (item_id) '
          . 'LEFT JOIN sacoche_referentiel_theme USING (theme_id) '
          . 'LEFT JOIN sacoche_referentiel_domaine USING (domaine_id) '
          . 'LEFT JOIN sacoche_referentiel USING (matiere_id,niveau_id) '
          . 'WHERE '.$where_eleve.'AND '.$where_item.$where_date_debut.$where_date_fin
          . 'GROUP BY eleve_id, item_id '
          . 'ORDER BY item_id ASC'; // Pour conserver le même ordre lors des différents appel
  $DB_VAR = array(
    ':date_debut' => $date_sql_debut,
    ':date_fin'   => $date_sql_fin,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Export LSU
// ////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Compter les élèves n’ayant pas d’identifiant Sconet renseigné
 *
 * @param void
 * @return int
 */
public static function DB_compter_eleves_actuels_sans_id_sconet()
{
  $DB_SQL = 'SELECT COUNT(*) AS nombre '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() AND user_sconet_id=:sconet_id ';
  $DB_VAR = array(
    ':profil_type' => 'eleve',
    ':sconet_id'   => 0,
  );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Compter les élèves n’ayant pas d’identifiant national renseigné
 *
 * @param void
 * @return int
 */
public static function DB_compter_eleves_actuels_sans_INE()
{
  $DB_SQL = 'SELECT COUNT(*) AS nombre '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() AND (user_reference NOT REGEXP "^[0-9]{9}[0-9A-Z]{1}[A-Z]{1}$") '; // au 2D c’est 10 chiffres et 1 lettre mais au 1D c’est 9 chiffres et 2 lettres
  $DB_VAR = array(':profil_type'=>'eleve');
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_ajouter_livret_export_eleve
 *
 * @param int     $user_id
 * @param string  $livret_page_ref
 * @param string  $livret_page_periodicite
 * @param int     $jointure_periode
 * @param string  $sacoche_version
 * @param string  $export_contenu
 * @return int
 */
public static function DB_ajouter_livret_export_eleve( $user_id , $livret_page_ref , $livret_page_periodicite , $jointure_periode , $sacoche_version , $export_contenu )
{
  // INSERT ON DUPLICATE KEY UPDATE est plus performant que REPLACE et mieux par rapport aux id autoincrémentés ou aux contraintes sur les clefs étrangères
  // @see http://stackoverflow.com/questions/9168928/what-are-practical-differences-between-replace-and-insert-on-duplicate-ke
  $DB_SQL = 'INSERT INTO sacoche_livret_export ( user_id,  livret_page_ref,  livret_page_periodicite,  jointure_periode,  sacoche_version,  export_contenu) '
          . 'VALUES                            (:user_id, :livret_page_ref, :livret_page_periodicite, :jointure_periode, :sacoche_version, :export_contenu) '
          . 'ON DUPLICATE KEY UPDATE livret_page_ref=:livret_page_ref, sacoche_version=:sacoche_version, export_contenu=:export_contenu ';
  $DB_VAR = array(
    ':user_id'                 => $user_id,
    ':livret_page_ref'         => $livret_page_ref,
    ':livret_page_periodicite' => $livret_page_periodicite,
    ':jointure_periode'        => $jointure_periode,
    ':sacoche_version'         => $sacoche_version,
    ':export_contenu'          => $export_contenu,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * DB_lister_livret_export_eleves
 *
 * @param string  $liste_eleve
 * @param string  $livret_page_periodicite   facultatif, chaine vide pour toutes périodes
 * @param int     $jointure_periode   facultatif, chaine vide pour toutes périodes
 * @return array
 */
public static function DB_lister_livret_export_eleves( $liste_eleve , $livret_page_periodicite , $jointure_periode )
{
  $where_page_periodicite = ($livret_page_periodicite) ? 'AND livret_page_periodicite=:livret_page_periodicite ' : '' ;
  $where_jointure_periode = ($jointure_periode)        ? 'AND jointure_periode=:jointure_periode '               : '' ;
  $DB_SQL = 'SELECT user_id, livret_page_ref, sacoche_version, export_contenu '
          . 'FROM sacoche_livret_export '
          . 'WHERE user_id IN('.$liste_eleve.') '.$where_page_periodicite.$where_jointure_periode
          . 'ORDER BY livret_page_ref ASC, jointure_periode ASC, user_id ASC ';
  $DB_VAR = array(
    ':livret_page_periodicite' => $livret_page_periodicite,
    ':jointure_periode'        => $jointure_periode,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

}
?>