یک تقویم تعطیلات تیمی را پر کنید

سطح کدنویسی : مبتدی
مدت زمان : ۱۵ دقیقه
نوع پروژه : اتوماسیون با محرک زمان

اهداف

  • بفهمید که راه حل چه کاری انجام می‌دهد.
  • درک کنید که سرویس‌های Apps Script در این راهکار چه کاری انجام می‌دهند.
  • اسکریپت را تنظیم کنید.
  • اسکریپت را اجرا کنید.

درباره این راهکار

یک تقویم تعطیلات مشترک ابزاری عالی برای کمک به همکاری تیم شماست؛ هر کسی می‌تواند با یک نگاه مشخص کند چه کسی خارج از دفتر است. این راهکار به شما امکان می‌دهد بدون نیاز به وارد کردن دستی، ببینید همکارانتان چه زمانی خارج از دفتر هستند.

مثال تقویم تعطیلات

چگونه کار می‌کند؟

این راهکار، یک تقویم تعطیلات مشترک را بر اساس تقویم‌های شخصی هر فرد در یک گروه گوگل ایجاد می‌کند. وقتی کسی مرخصی می‌گیرد، با استفاده از یک کلمه کلیدی مانند «تعطیلات» یا «خارج از دفتر» یک رویداد را به تقویم گوگل شخصی خود اضافه می‌کند.

هر ساعت، اسکریپت تقویم‌های اعضای گروه را اسکن می‌کند و رویدادهای مناسب را با تقویم مشترک همگام‌سازی می‌کند. می‌توانید تعداد دفعات اسکن اسکریپت برای رویدادهای جدید را تغییر دهید .

این راهکار فقط به رویدادهای تقویم دسترسی دارد که همکارانتان از طریق تنظیمات حریم خصوصی خود برای شما قابل مشاهده کرده‌اند.

سرویس‌های اسکریپت برنامه‌ها

این راهکار از سرویس‌های زیر استفاده می‌کند:

پیش‌نیازها

برای استفاده از این نمونه، به پیش‌نیازهای زیر نیاز دارید:

  • یک حساب گوگل (حساب‌های کاربری گوگل ورک‌اسپیس ممکن است نیاز به تأیید مدیر داشته باشند).
  • یک مرورگر وب با دسترسی به اینترنت.

اسکریپت را تنظیم کنید

یک تقویم تعطیلات تیمی ایجاد کنید

  1. تقویم گوگل را باز کنید.
  2. یک تقویم جدید با عنوان «تعطیلات تیمی» ایجاد کنید .
  3. در تنظیمات تقویم، در قسمت «ادغام تقویم» ، شناسه تقویم را کپی کنید.

پروژه Apps Script را ایجاد کنید

  1. برای باز کردن پروژه اسکریپت برنامه‌های تقویم تعطیلات ، روی دکمه زیر کلیک کنید.
    پروژه را باز کنید
  2. روی نمای کلی کلیک کنید.
  3. در صفحه مرور کلی، روی «ایجاد کپی» کلیک کنید آیکون مربوط به کپی کردن .
  4. در پروژه‌ی Apps Script کپی‌شده، متغیر TEAM_CALENDAR_ID برابر با شناسه‌ی تقویمی که قبلاً ایجاد کرده‌اید، قرار دهید.
  5. متغیر GROUP_EMAIL روی آدرس ایمیل یک گروه گوگل که شامل اعضای تیم شما است، تنظیم کنید.
  6. در کنار سرویس‌ها ، روی سرویس کلیک کنید.
  7. API تقویم گوگل را انتخاب کنید و روی افزودن کلیک کنید.

اسکریپت را اجرا کنید

  1. در پروژه‌ی Apps Script کپی‌شده‌تان، در منوی کشویی function، گزینه‌ی setup را انتخاب کنید.
  2. روی اجرا کلیک کنید.
  3. وقتی از شما خواسته شد، اسکریپت را تأیید کنید. اگر صفحه رضایت OAuth هشدار « این برنامه تأیید نشده است» را نشان می‌دهد، با انتخاب Advanced > Go to {Project Name} (unsafe) ادامه دهید.

  4. پس از تکمیل، به تقویم برگردید تا تأیید کنید که تقویم تعطیلات تیمی با رویدادها پر شده است.

کد را مرور کنید

برای بررسی کد Apps Script برای این راهکار، روی مشاهده کد منبع در زیر کلیک کنید:

مشاهده کد منبع

کد.gs

راهکارها/اتوماسیون‌ها/تقویم تعطیلات/کد.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;
}

اصلاحات

شما می‌توانید اتوماسیون تقویم تعطیلات تیمی را تا جایی که می‌خواهید متناسب با نیازهایتان ویرایش کنید. در زیر یک تغییر اختیاری برای اصلاح ماشه آمده است.

تغییر تعداد دفعات اسکن اسکریپت برای رویدادهای جدید

برای تغییر تعداد دفعات اجرای اسکریپت، مراحل زیر را انجام دهید:

  1. در پروژه Apps Script، روی Triggers کلیک کنید.
  2. در کنار تریگر، روی تریگر کلیک کنید.
  3. تغییرات خود را انتخاب کنید و روی ذخیره کلیک کنید.

مشارکت‌کنندگان

این نمونه توسط گوگل و با کمک متخصصان توسعه‌دهنده گوگل نگهداری می‌شود.

مراحل بعدی