Remplir un agenda de vacances d'équipe

Niveau de codage: Débutant
Durée: 15 min
Type de projet: Automatisation avec un déclencheur basé sur le temps

Objectifs

  • Comprendre ce que la solution fait
  • Comprendre ce que font les services Apps Script dans la solution.
  • Configurez le script.
  • Exécutez le script.

À propos de cette solution

Un calendrier des congés partagé est un excellent outil pour faciliter la collaboration au sein de votre équipe. Chacun peut déterminer en un coup d'œil qui est absent. Cette solution vous permet de voir quand vos collègues ne sont pas au bureau, sans avoir à saisir manuellement les informations.

exemple de calendrier de vacances

Fonctionnement

Cette solution renseigne un calendrier de congés partagé en fonction des calendriers individuels de chaque personne d'un groupe Google. Lorsqu'un employé prend des congés, il ajoute un événement à son agenda Google personnel à l'aide d'un mot clé tel que "Vacances" ou "Absent".

Chaque heure, le script analyse les agendas des membres du groupe et synchronise les événements appropriés avec l'agenda partagé. Vous pouvez modifier la fréquence à laquelle le script recherche de nouveaux événements.

Cette solution n'accède qu'aux événements Agenda que vos collègues ont rendus visibles pour vous via leurs paramètres de confidentialité.

Services Apps Script

Cette solution utilise les services suivants:

Prérequis

Pour utiliser cet exemple, vous devez remplir les conditions préalables suivantes:

  • Un compte Google (l'approbation de l'administrateur peut être nécessaire pour les comptes Google Workspace).
  • Un navigateur Web avec accès à Internet

Configurer le script

Créer un calendrier des congés d'équipe

  1. Ouvrez Google Agenda.
  2. Créez un agenda appelé "Vacances de l'équipe".
  3. Dans les paramètres de l'agenda, sous Intégrer l'agenda, copiez l'ID de l'agenda.

Créer le projet Apps Script

  1. Cliquez sur le bouton suivant pour ouvrir le projet Apps Script Vacation Calendar (Calendrier des vacances).
    Ouvrir le projet
  2. Cliquez sur Vue d'ensemble .
  3. Sur la page de présentation, cliquez sur Créer une copie Icône pour créer une copie.
  4. Dans votre projet Apps Script copié, définissez la variable TEAM_CALENDAR_ID sur l'ID du calendrier que vous avez créé précédemment.
  5. Définissez la variable GROUP_EMAIL sur l'adresse e-mail d'un groupe Google contenant les membres de votre équipe.
  6. À côté de Services, cliquez sur Ajouter un service .
  7. Sélectionnez API Google Agenda, puis cliquez sur Ajouter.

Exécuter le script

  1. Dans le projet Apps Script copié, dans le menu déroulant des fonctions, sélectionnez setup (configuration).
  2. Cliquez sur Exécuter.
  3. Lorsque vous y êtes invité, autorisez le script. Si l'écran d'autorisation OAuth affiche l'avertissement Cette application n'est pas validée, sélectionnez Avancé > Accéder à {Nom du projet} (non sécurisé).

  4. Une fois terminé, revenez dans Agenda pour vérifier que le calendrier des vacances de l'équipe est renseigné avec des événements.

Examiner le code

Pour consulter le code Apps Script de cette solution, cliquez sur Afficher le code source ci-dessous:

Afficher le code source

Code.gs

solutions/automations/vacation-calendar/Code.js
// To learn how to use this script, refer to the documentation:
// https://developers.google.com/apps-script/samples/automations/vacation-calendar

/*
Copyright 2022 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Set the ID of the team calendar to add events to. You can find the calendar's
// ID on the settings page.
let TEAM_CALENDAR_ID = 'ENTER_TEAM_CALENDAR_ID_HERE';
// Set the email address of the Google Group that contains everyone in the team.
// Ensure the group has less than 500 members to avoid timeouts.
// Change to an array in order to add indirect members frrm multiple groups, for example:
// let GROUP_EMAIL = ['ENTER_GOOGLE_GROUP_EMAIL_HERE', 'ENTER_ANOTHER_GOOGLE_GROUP_EMAIL_HERE'];
let GROUP_EMAIL = 'ENTER_GOOGLE_GROUP_EMAIL_HERE';

let ONLY_DIRECT_MEMBERS = false;

let KEYWORDS = ['vacation', 'ooo', 'out of office', 'offline'];
let MONTHS_IN_ADVANCE = 3;

/**
 * Sets up the script to run automatically every hour.
 */
function setup() {
  let triggers = ScriptApp.getProjectTriggers();
  if (triggers.length > 0) {
    throw new Error('Triggers are already setup.');
  }
  ScriptApp.newTrigger('sync').timeBased().everyHours(1).create();
  // Runs the first sync immediately.
  sync();
}

/**
 * Looks through the group members' public calendars and adds any
 * 'vacation' or 'out of office' events to the team calendar.
 */
function sync() {
  // Defines the calendar event date range to search.
  let today = new Date();
  let maxDate = new Date();
  maxDate.setMonth(maxDate.getMonth() + MONTHS_IN_ADVANCE);

  // Determines the time the the script was last run.
  let lastRun = PropertiesService.getScriptProperties().getProperty('lastRun');
  lastRun = lastRun ? new Date(lastRun) : null;

  // Gets the list of users in the Google Group.
  let users = getAllMembers(GROUP_EMAIL);
  if (ONLY_DIRECT_MEMBERS){
    users = GroupsApp.getGroupByEmail(GROUP_EMAIL).getUsers();
  } else if (Array.isArray(GROUP_EMAIL)) {
    users = getUsersFromGroups(GROUP_EMAIL);
  }

  // For each user, finds events having one or more of the keywords in the event
  // summary in the specified date range. Imports each of those to the team
  // calendar.
  let count = 0;
  users.forEach(function(user) {
    let username = user.getEmail().split('@')[0];
    KEYWORDS.forEach(function(keyword) {
      let events = findEvents(user, keyword, today, maxDate, lastRun);
      events.forEach(function(event) {
        importEvent(username, event);
        count++;
      }); // End foreach event.
    }); // End foreach keyword.
  }); // End foreach user.

  PropertiesService.getScriptProperties().setProperty('lastRun', today);
  console.log('Imported ' + count + ' events');
}

/**
 * Imports the given event from the user's calendar into the shared team
 * calendar.
 * @param {string} username The team member that is attending the event.
 * @param {Calendar.Event} event The event to import.
 */
function importEvent(username, event) {
  event.summary = '[' + username + '] ' + event.summary;
  event.organizer = {
    id: TEAM_CALENDAR_ID,
  };
  event.attendees = [];

  // If the event is not of type 'default', it can't be imported, so it needs
  // to be changed.
  if (event.eventType != 'default') {
    event.eventType = 'default';
    delete event.outOfOfficeProperties;
    delete event.focusTimeProperties;
  }

  console.log('Importing: %s', event.summary);
  try {
    Calendar.Events.import(event, TEAM_CALENDAR_ID);
  } catch (e) {
    console.error('Error attempting to import event: %s. Skipping.',
        e.toString());
  }
}

/**
 * In a given user's calendar, looks for occurrences of the given keyword
 * in events within the specified date range and returns any such events
 * found.
 * @param {Session.User} user The user to retrieve events for.
 * @param {string} keyword The keyword to look for.
 * @param {Date} start The starting date of the range to examine.
 * @param {Date} end The ending date of the range to examine.
 * @param {Date} optSince A date indicating the last time this script was run.
 * @return {Calendar.Event[]} An array of calendar events.
 */
function findEvents(user, keyword, start, end, optSince) {
  let params = {
    q: keyword,
    timeMin: formatDateAsRFC3339(start),
    timeMax: formatDateAsRFC3339(end),
    showDeleted: true,
  };
  if (optSince) {
    // This prevents the script from examining events that have not been
    // modified since the specified date (that is, the last time the
    // script was run).
    params.updatedMin = formatDateAsRFC3339(optSince);
  }
  let pageToken = null;
  let events = [];
  do {
    params.pageToken = pageToken;
    let response;
    try {
      response = Calendar.Events.list(user.getEmail(), params);
    } catch (e) {
      console.error('Error retriving events for %s, %s: %s; skipping',
          user, keyword, e.toString());
      continue;
    }
    events = events.concat(response.items.filter(function(item) {
      return shouldImportEvent(user, keyword, item);
    }));
    pageToken = response.nextPageToken;
  } while (pageToken);
  return events;
}

/**
 * Determines if the given event should be imported into the shared team
 * calendar.
 * @param {Session.User} user The user that is attending the event.
 * @param {string} keyword The keyword being searched for.
 * @param {Calendar.Event} event The event being considered.
 * @return {boolean} True if the event should be imported.
 */
function shouldImportEvent(user, keyword, event) {
  // Filters out events where the keyword did not appear in the summary
  // (that is, the keyword appeared in a different field, and are thus
  // is not likely to be relevant).
  if (event.summary.toLowerCase().indexOf(keyword) < 0) {
    return false;
  }
  if (!event.organizer || event.organizer.email == user.getEmail()) {
    // If the user is the creator of the event, always imports it.
    return true;
  }
  // Only imports events the user has accepted.
  if (!event.attendees) return false;
  let matching = event.attendees.filter(function(attendee) {
    return attendee.self;
  });
  return matching.length > 0 && matching[0].responseStatus == 'accepted';
}

/**
 * Returns an RFC3339 formated date String corresponding to the given
 * Date object.
 * @param {Date} date a Date.
 * @return {string} a formatted date string.
 */
function formatDateAsRFC3339(date) {
  return Utilities.formatDate(date, 'UTC', 'yyyy-MM-dd\'T\'HH:mm:ssZ');
}

/**
* Get both direct and indirect members (and delete duplicates).
* @param {string} the e-mail address of the group.
* @return {object} direct and indirect members.
*/
function getAllMembers(groupEmail) {
  var group = GroupsApp.getGroupByEmail(groupEmail);
  var users = group.getUsers();
  var childGroups = group.getGroups();
  for (var i = 0; i < childGroups.length; i++) {
    var childGroup = childGroups[i];
    users = users.concat(getAllMembers(childGroup.getEmail()));
  }
  // Remove duplicate members
  var uniqueUsers = [];
  var userEmails = {};
  for (var i = 0; i < users.length; i++) {
    var user = users[i];
    if (!userEmails[user.getEmail()]) {
      uniqueUsers.push(user);
      userEmails[user.getEmail()] = true;
    }
  }
  return uniqueUsers;
}

/**
* Get indirect members from multiple groups (and delete duplicates).
* @param {array} the e-mail addresses of multiple groups.
* @return {object} indirect members of multiple groups.
*/
function getUsersFromGroups(groupEmails) {
  let users = [];
  for (let groupEmail of groupEmails) {
    let groupUsers = GroupsApp.getGroupByEmail(groupEmail).getUsers();
    for (let user of groupUsers) {
      if (!users.some(u => u.getEmail() === user.getEmail())) {
        users.push(user);
      }
    }
  }
  return users;
}

Modifications

Vous pouvez modifier l'automatisation du calendrier des congés de l'équipe autant que vous le souhaitez pour l'adapter à vos besoins. Vous trouverez ci-dessous une modification facultative pour modifier le déclencheur.

Modifier la fréquence à laquelle le script recherche de nouveaux événements

Pour modifier la fréquence d'exécution du script, procédez comme suit:

  1. Dans le projet Apps Script, cliquez sur Déclencheurs .
  2. À côté du déclencheur, cliquez sur Modifier le déclencheur .
  3. Sélectionnez les modifications que vous souhaitez apporter, puis cliquez sur Enregistrer.

Contributeurs

Cet exemple est géré par Google avec l'aide des experts Google pour les développeurs.

Étapes suivantes