Concours

GS dossiers soumis

GS dossiers soumis Check

GS des projets mis en ligne

Lien vers la Google Sheet servant à afficher les projets

Explications pour modifier la Google Sheet

GoogleSheet avec les projets listés doit avoir ces champs:

Horodateur
Adresse e-mail
Label and Name of the Unit
Thesis Director
Proposed Thesis Title in French
Your campus
First Choice departement (not necessarily have to be your own)
Number of PhD students under your supervision as of June 1st
Certificate of Acknowledgment of the Doctoral School Rules
Formulaire sujet de thèse 2024/2025
Hosting Team Name
Director’s Name
Phone Unit
Email Unit
Team Leader’s Name
Team Leader’s Position
Phone HDR
Email Team Leader
Position HDR
Phone Team Leader
Email HDR
Proposed Thesis Title in English
Second Choice departement
Summary (max 5 lines)
Prospective Candidate
List 5 Keywords
Scientific Content of the PhD Program
List of Publications from the Last Two PhD Students under the supervision of the thesis supervisor

Associer ce code:

 

function doGet() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // Prend la première feuille
const dataRange = sheet.getDataRange();
const values = dataRange.getValues();
if (values.length < 2) {
return ContentService.createTextOutput(« [] »).setMimeType(ContentService.MimeType.JSON);
}

 

const headers = values[0];
const output = values.slice(1).map((row, idx) => {
let item = {};
headers.forEach((key, i) => item[key] = row[i]);
item[« ID »] = idx;
return item;
});

 

return ContentService.createTextOutput(JSON.stringify(output))
.setMimeType(ContentService.MimeType.JSON);
}

 

Déployer et récupérer l’URL à utiliser dans la page WordPress d’affichage du contenu

Utiliser ce code (instruction fetch) pour l’affichage des projets:

<!– Librairies –>
<link rel= »stylesheet » href= »https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css »>
<script src= »https://code.jquery.com/jquery-3.7.1.min.js »></script>
<script src= »https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js »></script>
<script src= »https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js »></script>
<script src= »https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js »></script>
<div style= »display:flex; flex-direction:column; gap:1em; font-size:0.9em; »>
  <!– Boutons export PDF par département –>
  <div style= »display:flex; flex-wrap:wrap; gap:0.5em; »>
    <button class= »btn-export-dept » data-dept= »IM »>📄 Export IM</button>
    <button class= »btn-export-dept » data-dept= »MECA »>📄 Export MECA</button>
    <button class= »btn-export-dept » data-dept= »PP »>📄 Export PP</button>
    <button class= »btn-export-dept » data-dept= »IMMUNO »>📄 Export IMMUNO</button>
    <button class= »btn-export-dept » data-dept= »GENYX »>📄 Export GENYX</button>
    <button class= »btn-export-dept » data-dept= »NO DEPT »>📄 Export NO DEPT</button>
  </div>
  <!– Filtres –>
  <div style= »margin-top:1em; »>
    <label for= »filtre-dept »><strong>Filtrer par département :</strong></label>
    <select id= »filtre-dept » style= »margin-left:10px; padding:0.3em; »>
      <option value= » »>Tous les départements</option>
    </select>
  </div>
  <div style= »margin-top:0.5em; »>
    <label><input type= »checkbox » id= »filtre-sans-candidat » style= »margin-right:6px; »>Sans candidats pressentis</label>
  </div>
  <div id= »projets-count » style= »font-size:0.9em; font-weight:500; margin:0.5em 0 1em; color:#333; »>
    0 projets affichés
  </div>
  <div style= »font-style:italic; color:#555; margin:-0.3em 0 1.2em 0; »>
    La recherche ne porte que sur les projets affichés selon les filtres ci‑dessus.
  </div>
  <!– Messages PDF –>
<div id= »pdf-loading » style= »display:none; text-align:center; font-weight:bold; color:#8a1538; font-size:1.1em; »>
  <div class= »spinner » style= »display:inline-block; width:18px; height:18px; border:3px solid #8a1538; border-top:3px solid transparent; border-radius:50%; animation:spin 1s linear infinite; margin-right:10px; »></div>
  <span id= »pdf-loading-text »>Génération du PDF…</span>
</div>
  <div id= »pdf-success » style= »display:none; text-align:center; font-weight:bold; color:green; margin-top:1em; »>
    🎉 PDF généré avec succès !
  </div>
  <!– Tableau + fiche –>
  <div style= »display:flex; flex-wrap:wrap; gap:2em; »>
    <div style= »flex:1; min-width:300px; »>
      <table id= »table-theses » class= »display » style= »width:100% »>
        <thead>
          <tr>
            <th>Thesis Title</th>
            <th>Unit</th>
            <th>Thesis Director</th>
            <th>Dept</th>
            <th>Details</th>
          </tr>
        </thead>
        <tbody></tbody>
      </table>
    </div>
    <div style= »flex:1; min-width:300px; »>
      <button id= »btn-pdf » style= »margin-bottom:1em; padding:0.5em 1em; background:#fff; border:2px solid #8a1538; border-radius:10px; width:100%; cursor:pointer; font-weight:bold; display:none; »>
        📥 Exporter cette fiche en PDF
      </button>
      <div id= »fiche-detaillee » style= »border:1px solid #ccc; padding:1em; border-radius:8px; background:#f9f9f9; font-size:0.9em; »></div>
    </div>
  </div>
</div>
<script>
/* ============  CONSTANTES COLONNES  ============ */
const DEPT_KEY = « First Choice departement (not necessarily have to be your own) »;
const CAND_KEY = « Prospective Candidate »;
/* ============  VARIABLES GLOBALES  ============ */
let enrichedData = [];
let currentVisibleRows = [];
/* ============  AU CHARGEMENT  ============ */
$(document).ready(function () {
  /* —- 1.  RÉCUPÉRATION DES DONNÉES —- */
  fetch(« https://script.google.com/macros/s/AKfycbzRL7A7EOgCP2H23rdS_ksjmyxj5fa2teMJubbK36y6MgVPjNT7cjujNYGiXcJCl4AG/exec »)
    .then(r => r.json())
    .then(data => {
      enrichedData = data;
      /* —- 2.  INITIALISATION DATATABLE —- */
      const dt = $(‘#table-theses’).DataTable({
        data: [],
        pageLength: 10,
        dom: ‘<« top »lfp>rt’,
        order: [],
        columns: [
          { data: « Proposed Thesis Title in English » },
          { data: « Label and Name of the Unit » },
          { data: « Thesis Director » },
          { data: DEPT_KEY },
          { data: « action », orderable: false, searchable: false }
        ]
      });
      /* —- 3.  MENU DÉROULANT DES DÉPARTEMENTS —- */
      const uniqueDepts = […new Set(
        enrichedData.map(d => (d[DEPT_KEY] || «  »).trim()).filter(Boolean)
      )].sort();
      uniqueDepts.forEach(dep =>
        $(‘#filtre-dept’).append(`<option value= »${dep} »>${dep}</option>`)
      );
      /* —- 4.  FILTRAGE TABLEAU + COMPTEUR —- */
      function appliquerFiltres() {
        const depSel = $(‘#filtre-dept’).val();
        const noCand = $(‘#filtre-sans-candidat’).is(‘:checked’);
        currentVisibleRows = enrichedData
          .map((d, i) => ({ …d, _index: i }))             // garde l’index original
          .filter(d => {
            const depVal = (d[DEPT_KEY] || «  »).trim();
            const depOK  = !depSel || depVal === depSel;
            const candVal = (d[CAND_KEY] || «  »).trim().toLowerCase();
            const candOK  = !noCand || candVal === «  » || candVal === « no »;
            return depOK && candOK;
          });
        const rowsForDT = currentVisibleRows.map((d, idx) => ({
          …d,
          action: `<button class= »details-btn » data-index= »${idx} » style= »cursor:pointer; »>🔍</button>`
        }));
        dt.clear().rows.add(rowsForDT).draw();
        $(‘#projets-count’).text(
          `${currentVisibleRows.length} projet${currentVisibleRows.length !== 1 ? ‘s’ :  »} affiché${currentVisibleRows.length !== 1 ? ‘s’ :  »}`
        );
      }
      $(‘#filtre-dept, #filtre-sans-candidat’).on(‘change’, appliquerFiltres);
      appliquerFiltres();                                    // affichage initial
      /* —- 5.  CLIC SUR 🔍  (fiche détaillée) —- */
      $(‘#table-theses’).on(‘click’, ‘.details-btn’, function () {
        const d = currentVisibleRows[$(this).data(‘index’)];
        const fiche = `
          <div style=’display:flex; justify-content:space-between; gap:1em;’>
            <h2 style=’flex:4;margin:0;’>${d[« Proposed Thesis Title in English »]}</h2>
            <div style=’flex:1;’>
              <div style=’font-weight:bold;border:1px solid #000;padding:4px;text-align:center;’>
                ${(d[DEPT_KEY] || « NO DEPT »)}
              </div>
              <div style=’font-weight:bold;border:1px solid #000;padding:4px;text-align:center;margin-top:5px;font-size:0.8em;font-style:italic;’>
                2nd choice : ${d[« Second Choice departement »] || «  »}
              </div>
            </div>
          </div>
          <p><strong>Prospective Candidate :</strong> ${d[CAND_KEY]}</p>
          <p><strong>Short Abstract :</strong> ${d[« Summary (max 5 lines) »]}</p>
          <p><strong>Keywords :</strong> ${d[« List 5 Keywords »]}</p>
          <p><strong>Thesis Director :</strong> ${d[« Thesis Director »]} (${d[« Email HDR »]})</p>
          <p><strong>Unit :</strong> ${d[« Label and Name of the Unit »]}</p>
          <p><strong>Director of Unit :</strong> ${d[« Director’s Name »]}</p>
          <p><strong>Scientific Content :</strong><br>${d[« Scientific Content of the PhD Program »]}</p>
        `;
        $(‘#fiche-detaillee’).html(fiche).show();
        $(‘#btn-pdf’).show();
      });
      /* —- 6.  EXPORT PDF PAR DÉPARTEMENT —- */
      const getDeptCode = v => (v || «  »)
        .trim()
        .split(/[\s(]/)[0]
        .toUpperCase();
      $(document).on(‘click’, ‘.btn-export-dept’, async function () {
        const deptCode = $(this).data(‘dept’).toUpperCase();   // IM, MECA, …
        $(‘#pdf-loading’).show();
        $(‘#pdf-success’).hide();
        const rows = enrichedData.filter(r => {
          const code = getDeptCode(r[DEPT_KEY]);
          return (deptCode === ‘NO DEPT’ && !code) || code === deptCode;
        });
        await generatePdfFromRows(rows, `theses-${deptCode}.pdf`);
        $(‘#pdf-loading’).hide();
        $(‘#pdf-success’).show();
        /* NEW ➜ compteur initial */
$(‘#pdf-loading-text’).text(`Génération du PDF… 0/${rows.length}`);
      });
    });
  /* —- 7.  EXPORT PDF INDIVIDUEL —- */
  document.getElementById(‘btn-pdf’).addEventListener(‘click’, async () => {
    const ficheRow = currentVisibleRows[0]; // on exporte la fiche visible
    if (ficheRow) {
      await generatePdfFromRows([ficheRow], ‘fiche_these.pdf’);
    }
  });
});
/* ============  FONCTION PDF UTILITAIRE  ============ */
async function generatePdfFromRows(rows, fileName) {
  const { jsPDF } = window.jspdf;
  const pdf  = new jsPDF(‘p’, ‘mm’, ‘a4’);
  const pW   = pdf.internal.pageSize.getWidth();
  const pH   = pdf.internal.pageSize.getHeight();
  const marg = 10, availW = pW – 2 * marg;
  /* boucle lignes -> pages */
  for (const [i, d] of rows.entries()) {
    /* conteneur HTML temporaire */
    const box = document.createElement(‘div’);
    box.style.cssText = `
  width:800px;
  padding:1em;
  border:1px solid #ccc;
  border-radius:8px;
  background:#fff;
  font-size:0.9em;        /*  ⇦  TEXTE PLUS PETIT  */
  line-height:1.35em;
`;
box.innerHTML = `
  <div style=’display:flex; justify-content:space-between; gap:1em;’>
    <h2 style=’flex:4;margin:0;font-size:1.1em;’>${d[« Proposed Thesis Title in English »]}</h2>
        <div style=’flex:1;’>
          <div style=’font-weight:bold;border:1px solid #000;padding:4px;text-align:center;’>
            ${(d[DEPT_KEY] || « NO DEPT »)}
          </div>
          <div style=’font-weight:bold;border:1px solid #000;padding:4px;text-align:center;margin-top:5px;font-size:0.8em;font-style:italic;’>
            2nd choice : ${d[« Second Choice departement »] || «  »}
          </div>
        </div>
      </div>
      <p><strong>Prospective Candidate :</strong> ${d[« Prospective Candidate »]}</p>
      <p><strong>Short Abstract :</strong> ${d[« Summary (max 5 lines) »]}</p>
      <p><strong>Keywords :</strong> ${d[« List 5 Keywords »]}</p>
      <p><strong>Thesis Director :</strong> ${d[« Thesis Director »]} (${d[« Email HDR »]})</p>
      <p><strong>Unit :</strong> ${d[« Label and Name of the Unit »]}</p>
      <p><strong>Director of Unit :</strong> ${d[« Director’s Name »]}</p>
      <p><strong>Scientific Content :</strong><br>${d[« Scientific Content of the PhD Program »]}</p>`;
    /* NEW ➜ mise à jour du compteur */
$(‘#pdf-loading-text’).text(`Génération du PDF… ${i + 1}/${rows.length}`);
    document.body.appendChild(box);
    const canvas   = await html2canvas(box, { scale: 2 });
    const imgData  = canvas.toDataURL(‘image/png’);
    const ratio    = canvas.width / canvas.height;
    const imgWmm   = availW;
    const imgHmm   = imgWmm / ratio;
    if (i > 0) pdf.addPage();
    if (imgHmm <= pH – 20) {
      pdf.addImage(imgData, ‘PNG’, marg, 10, imgWmm, imgHmm);
    } else {
      /* découpe verticale si trop long */
      const pageHmm = pH – 20;
      let cursorHmm = 0;
      while (cursorHmm < imgHmm) {
        const partHpx = Math.min(canvas.height, (pageHmm * canvas.width) / imgWmm);
        const slice   = document.createElement(‘canvas’);
        slice.width   = canvas.width;
        slice.height  = partHpx;
        slice.getContext(‘2d’).drawImage(
          canvas, 0, cursorHmm * (canvas.height / imgHmm),
          canvas.width, partHpx,
          0, 0, canvas.width, partHpx
        );
        if (cursorHmm > 0) pdf.addPage();
        pdf.addImage(slice.toDataURL(‘image/png’), ‘PNG’, marg, 10, imgWmm, pageHmm);
        cursorHmm += pageHmm;
      }
    }
    document.body.removeChild(box);
  }
  pdf.save(fileName);
}
</script>
<style>
/* enlever les petites flèches de tri */
th.sorting::after, th.sorting::before{display:none!important}
/* typographie du tableau */
#table-theses, #table-theses_wrapper{font-size:0.9em}
/* fiche détaillée */
#fiche-detaillee{line-height:1.4em}
/* spinner */
@keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
/* style boutons export */
.btn-export-dept{
  padding:0.5em 1em;
  background:#fff;
  border:2px solid #8a1538;
  border-radius:10px;
  font-weight:bold;
  cursor:pointer;
}
</style>
Composition jury

Code html (formulaire.html) et php (process.php) dans CodePHP du site