Google Docs, ou Google Documents, devenu plus récemment Google Drive, est une solution de partage en ligne de documents simple à mettre en œuvre. Si elle permet de partager de nombreux types de documents, de feuilles de calcul ou de présentations, cette solution est aussi largement utilisée pour ses fonctionnalités de création de formulaires. La simplicité des interfaces proposées permet de produire rapidement et intuitivement des formulaires relativement complexes et d’en recueillir les réponses durablement dans des feuilles de calcul. Mais la difficulté commence lorsqu’il s’agit d’introduire un peu de dynamisme et de mécanismes métiers…

L’utilisation de scripts Javascript au travers de Google App Scripts (GAS) permet de conférer un peu « d’intelligence » à vos formulaires afin qu’ils s’adaptent aux réponses déjà récoltées ou encore qu’ils tiennent compte d’un paramétrage susceptible d’évoluer. Nous allons voir ici, sur un cas de test simplifié, comment créer une question dont les choix proposés seront à la fois basés sur un paramétrage et résultant des réponses déjà recueillies.
Afin d’imager le propos, nous imaginerons un formulaire simplifié permettant à des bénévoles de s’inscrire à l’organisation d’un évènement particulier. Il y aura différents postes à pourvoir de capacités différentes, et les affectations proposées devront donc tenir compte à la fois du paramétrage mais aussi des réponses déjà obtenues.
GAS_formulaire_visible

Détails techniques

Nous allons utiliser ici uniquement les outils fournis par Google et il ne vous faudra donc qu’un compte Google Drive. Les outils en question se présentent sous la forme de trois éditeurs : pour le formulaire, pour la partie script et pour le classeur de données.

Création du formulaire

Commencez par vous connecter à votre compte Google Drive. La page d’accueil vous fournit la liste de vos documents, vous y verrez apparaitre par la suite votre formulaire et le classeur qui contiendra le paramétrage et les réponses. Cliquez sur le bouton rouge en haut à gauche « CREATE » et choisissez « Form ».

Votre formulaire s’ouvre en mode édition. Vous pouvez en changer le nom, ici « Inscriptions », et commencer à mettre en place les éléments souhaités. Ici, nous créerons trois questions, ou « Items », pour recueillir les nom, prénom et affectation du bénévole. Les deux premières sont de simples « Text » et la troisième est de type « Question à choix multiple » avec un proposition unique comme sur la capture ci-contre:
GAS_formulaire_enEdition

Votre formulaire est prêt, maintenant il va s’agir d’injecter les données attendues dans les options proposées pour la troisième question.

Mise en place du paramétrage

Un classeur de calcul devrait normalement avoir été généré et associé à votre formulaire pour en récupérer les réponses. Vous pourrez le trouver en retournant sur la page Google Drive, il devrait porter par défaut le nom « Inscriptions (Responses) ».

Si nécessaire vous pouvez régénérer un classeur pour votre formulaire depuis le mode édition de ce dernier en allant dans « Responses>Change response destination ».
GAS_formulaire_lierDonnees
Ouvrez le classeur depuis l’accueil de Google Drive. Il devrait contenir une seule page présentant la ligne d’entête du tableau qui permettra d’enregistrer les données collectées pour chaque réponse :
GAS_Donnees_recoltees
Nous allons ajouter une page intitulée « Statics » à ce classeur. Et nous allons y insérer les données concernant les différents postes disponibles pour l’organisation de notre évènement, et le nombre de places à pourvoir pour chacun. Votre classeur contiendra donc les données suivantes :
GAS_Donnees_parametrees

Exploitation du paramétrage par script

Nous avons défini ici différents postes et le nombre de places disponibles pour chacun. Nous allons maintenant voir comment utiliser ce paramétrage pour constituer les choix proposés à la question d’affectation du formulaire.
Pour ajouter un script à votre projet, ouvrez à nouveau le mode édition du formulaire et allez dans « Tools>Script Editor… ». Un nouvel écran s’ouvre, choisissez « Blank project » et remplacez le contenu du fichier qui vient d’être généré, à priori « Code.gs », par le script suivant :

// Donnees identifiants formulaire, classeur et feuille de calcul  
var FORM_ID = '1XzbN1yF5n04nrkIP1mNUEkYqKGs-Gvipbxp-P0Enp6I';
var SPREADSHEET_ID = '0AnvTenoS96t8dERTLW02SE9WTzBOM3BidHVkN1BGU1E';
var SHEETSTATIC_NAME = 'Statics';

// Donnees propres a la question a manipuler
var RANGEREF_AFFECTATION = 'A2:B6';
var TITRE_AFFECTATION = 'Affectation';

function controlerChoixQuestionAffectation() {
  // recuperation des choix pour liste deroulante
  var spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID);
  var sheetStatic = spreadsheet.getSheetByName(SHEETSTATIC_NAME);
  var dataStatic = sheetStatic.getRange(RANGEREF_AFFECTATION).getValues();
  
  // recuperation des donnees deja saisies
  var sheet = spreadsheet.getActiveSheet();
  var dataSheet = sheet.getDataRange().getValues();
  var dataSaisies = getDataParTitreColonne(sheet, TITRE_AFFECTATION, dataSheet.length).getValues();
  
  // Filtration des choix possibles en fonction des reponses enregistrees
  var dataAAfficher = filtrerListeAffectation(dataStatic, dataSaisies)
  
  // maj de la liste deroulante affichee
  var liste = getItemParTitre(TITRE_AFFECTATION);
  majListeSelonData(liste.asMultipleChoiceItem(), dataAAfficher);
}

function filtrerListeAffectation(listeComplete, elementsAFiltrer) {
  // util_checkData(listeComplete);
  for (var i = 0; i < elementsAFiltrer.length; i+=1){
    for (var j = 0; j < listeComplete.length; j+=1){
      if (elementsAFiltrer[i].toString() === listeComplete[j][0].toString()) {
        if (listeComplete[j][1] > 1) {
          listeComplete[j][1]--;
        } else {
          listeComplete.splice(j, 1);
        }
      }
    }
  }
  // util_checkData(listeComplete);
  return listeComplete;
}

function majListeSelonData(liste, data){
  var choices = [];
  for (var i = 0; i < data.length; i+=1){
    choices.push(liste.createChoice(data[i][0]));
  }
  liste.setChoices(choices);
}

function getItemParTitre(titreItem) {
  var items = FormApp.openById(FORM_ID).getItems();
  for (var i = 0; i < items.length; i += 1){
    var item = items[i];
    if (item.getTitle() === titreItem){
      break;
    }
  }
  return item;
}

function getDataParTitreColonne(sheet, titreColonne, tailleColonne) {
  for (var i = 1; i <= sheet.getLastColumn(); i+=1){
    var rangeTest = sheet.getRange(1, i, tailleColonne);
    if (rangeTest.getValue() === titreColonne){
      // util_checkDataRange(rangeTest);
      break;
    }
  }
  return rangeTest;
}

function util_checkData(data){
  Logger.log("data.length = " + data.length);
  for (var i = 0; i < data.length; i+=1){
    Logger.log("data[" + i + "] = " + data[i]);
  }
}

function util_checkDataRange(dataRange){
  Logger.log("dataRange.getValue() = " + dataRange.getValue());
  Logger.log("dataRange.getRow() = " + dataRange.getRow());
  Logger.log("dataRange.getColumn() = " + dataRange.getColumn());
  Logger.log("dataRange.getNumRows() = " + dataRange.getNumRows());
  Logger.log("dataRange.getNumColumns() = " + dataRange.getNumColumns());
}

Les points clés de ce script sont les suivants :
var FORM_ID = '1XzbN1yF5n04nrkIP1mNUEkYqKGs-Gvipbxp-P0Enp6I';
var SPREADSHEET_ID = '0AnvTenoS96t8dERTLW02SE9WTzBOM3BidHVkN1BGU1E';

Il s’agit des option de paramétrage du script que vous devez modifier de façon à référencer votre formulaire et votre classeur de données. Vous pouvez extraire ces identifiants des url de chacun des deux modes d’édition. Par exemple, l’url d’édition du formulaire était : https://docs.google.com/forms/d/1XzbN1yF5n04nrkIP1mNUEkYqKGs-Gvipbxp-P0Enp6I/edit#, et celle du classeur : https://docs.google.com/spreadsheet/ccc?key=0AnvTenoS96t8dERTLW02SE9WTzBOM3BidHVkN1BGU1E&usp=drive_web#gid=0

var SHEETSTATIC_NAME = 'Statics';
var RANGEREF_AFFECTATION = 'A2:B6';
var TITRE_AFFECTATION = 'Affectation';

Ici, ces paramètres correspondent à la feuille du classeur utilisée pour stocker le paramétrage, au range de cellules utilisé et au titre de la question qui doit être mise à jour. Il n’est pas nécessaire de les modifier si vous avez suivi précisément les indications

function controlerChoixQuestionAffectation()
Il s’agit de la méthode principale de notre script. c’est elle qui doit être lancé pour mettre à jour les propositions pour la question.

function filtrerListeAffectation(listeComplete, elementsAFiltrer)
Cette méthode exploite la liste de paramètres et celle des données déjà enregistrées pour produire une nouvelle liste qui doit être affichée. En pratique, les réponses sont décomptées du nombre de place disponible pour chaque poste, et si il n’en reste plus à pourvoir, le poste est supprimé de la liste qui est retournée.

function getItemParTitre(titreItem)
Méthode permettant de récupérer un objet de type item correspondant à la question à mettre à jour.

function majListeSelonData(liste, data)
Cette méthode remplace les éléments, ici de type MultipleChoiceItem, d’une liste de choix par ceux transmis. Il serait possible de faire la même chose sur une liste déroulante, plutôt qu’un choix multiple, en utilisant la méthode « asListItem() » plutôt que « asMultipleChoiceItem() » lors de l’appel à cette méthode.

function getDataParTitreColonne(sheet, titreColonne, tailleColonne)
Cette méthode extrait les données correspondant à une colonne spécifique de la feuille désignée.

Déclenchement du script

Enfin, nous allons ajouter les déclencheurs qui provoqueront l’exécution de notre méthode de mise à jour du formulaire.

Pour cela allez dans « Resources>Current project’s triggers ». J’aurai pensé que déclencher sur une seule action serait suffisante, mais les résultats ne sont pas probants donc n’hésitez pas à ajouter les trois déclencheurs comme sur la capture suivante :
GAS_Declencheurs

Vous pouvez tester votre méthode à l’aide du menu « Run>controlerChoixQuestionAffectation » et vérifier le déroulement à l’aide des logs disponibles depuis « View>Logs ». J’ai laissé volontairement une ou deux méthodes utilitaires permettant de tracer les informations qui transitent afin de vous aider à customiser votre script.

Vous pouvez désormais aussi vous rendre directement sur la page « publique » en passant par le mode édition et en cliquant sur « View live form ». Vous voyez immédiatement que la liste des options pour la question « Affectation » à été mise à jour selon le paramétrage mis en place. vous pouvez maintenant essayez de saisir les informations demandées et choisir une affectation : une fois votre formulaire soumit, les informations apparaissent dans la première feuille de votre classeur de résultats. Et lorsque vous avez enregistré le nombre attendu d’affectation pour un poste, l’option disparait des propositions pour les futurs affichages du formulaire.

NB :
A la date à laquelle j’ai écris cet article, il existait un bug sur les triggers et en particulier sur celui lié à la soumission du formulaire : issue 3883 – google-apps-script-issues. Ce bug rendait le déclenchement inconstant et il arrivait que le script ne soit pas exécuté. Il a depuis été corrigé et le déclenchement semble désormais fiable.

Conclusion

Les fonctionnalités offertes par Google Drive pour créer des formulaires sont très intéressantes et leur couplage, à la fois avec un classeur de données faisant office de base de données et avec une technologie de scripting, rend la solution extrêmement prometteuse. Néanmoins, il s’agit d’un ensemble d’outils et de technologies relativement nouveaux, et en attendant leur fiabilisation, il me semble préférable de limiter leur usage à des contextes restreints (criticité faible des données manipulées et nombre d’utilisateurs limité).

2 commentaires

2 réponses à Google Docs : question à choix paramétrés et dynamiques

  1. JPA on 25 septembre 2014 at 16:40 Répondre

    Hello,
    Merci beaucoup pour ce tuto.
    Sais-tu si le bug a été fixé ?
    Bonne journée,
    JPA

    • Tif on 5 octobre 2014 at 14:55 Répondre

      Bonjour JPA,
      Merci pour le retour.
      D’après le formulaire de suivi Google pour le bug, il est résolu depuis quelques jours (16/09). J’ai vérifié rapidement et je n’arrive effectivement plus à reproduire le problème.
      Bonne journée,
      Tif

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *


*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>