Điền vào lịch kỳ nghỉ của nhóm

Cấp độ lập trình: Sơ cấp
Thời lượng: 15 phút
Loại dự án: Tự động hoá bằng trình kích hoạt theo thời gian

Mục tiêu

  • Tìm hiểu những việc mà giải pháp này thực hiện.
  • Tìm hiểu những việc mà các dịch vụ của Google Apps Script thực hiện trong giải pháp này.
  • Thiết lập tập lệnh.
  • Chạy tập lệnh.

Giới thiệu về giải pháp này

Lịch nghỉ phép dùng chung là một công cụ hữu ích giúp nhóm của bạn cộng tác. Bất kỳ ai cũng có thể nhanh chóng xác định được người đang nghỉ phép. Giải pháp này giúp bạn biết được khi nào đồng nghiệp nghỉ phép mà không cần nhập thông tin theo cách thủ công.

Ví dụ về lịch nghỉ của nhóm có các sự kiện không có mặt tại văn phòng

Cách hoạt động

Giải pháp này điền thông tin vào lịch nghỉ phép dùng chung dựa trên lịch cá nhân của từng người trong một nhóm trên Google Groups. Khi đặt lịch nghỉ phép, họ thêm một sự kiện vào Lịch Google cá nhân bằng một từ khoá như "Nghỉ phép" hoặc "Không có mặt tại văn phòng".

Mỗi giờ, tập lệnh sẽ quét lịch của các thành viên trong nhóm và đồng bộ hoá các sự kiện thích hợp với lịch dùng chung. Bạn có thể thay đổi tần suất tập lệnh quét các sự kiện mới.

Giải pháp này chỉ truy cập vào các sự kiện trên Lịch mà đồng nghiệp đã đặt ở chế độ hiển thị cho bạn bằng chế độ cài đặt quyền riêng tư.

Dịch vụ Apps Script

Giải pháp này sử dụng các dịch vụ sau:

Điều kiện tiên quyết

Để sử dụng mẫu này, bạn cần đáp ứng các điều kiện tiên quyết sau:

  • Tài khoản Google (tài khoản Google Workspace có thể cần được quản trị viên phê duyệt).
  • Trình duyệt web có quyền truy cập vào Internet.

Thiết lập tập lệnh

Để thiết lập tập lệnh điền thông tin vào lịch nghỉ phép của nhóm, hãy hoàn tất các bước sau:

Tạo lịch nghỉ phép của nhóm

  1. Mở Lịch.
  2. Tạo một lịch mới có tên là "Lịch nghỉ phép của nhóm".
  3. Trong phần cài đặt của lịch, trong mục Tích hợp lịch, hãy sao chép Mã lịch.

Tạo dự án Apps Script

  1. Để mở dự án Apps Script Lịch nghỉ phép, hãy nhấp vào nút sau: Mở dự án
  2. Nhấp vào Tổng quan .
  3. Trên trang tổng quan, hãy nhấp vào Tạo bản sao Biểu tượng tạo bản sao.
  4. Trong dự án Apps Script đã sao chép, hãy đặt biến TEAM_CALENDAR_ID thành mã của lịch mà bạn đã tạo trước đó.
  5. Đặt biến GROUP_EMAIL thành địa chỉ email của một nhóm trên Google Groups có chứa các thành viên trong nhóm của bạn.
  6. Bên cạnh Dịch vụ, hãy nhấp vào biểu tượng Thêm dịch vụ .
  7. Chọn API Lịch Google rồi nhấp vào Thêm.

Chạy tập lệnh

  1. Trong dự án Apps Script đã sao chép, trong trình đơn thả xuống hàm, hãy chọn thiết lập.
  2. Nhấp vào Chạy.
  3. Khi được nhắc, hãy cho phép tập lệnh chạy. <<../_snippets/oauth.md>>
  4. Sau khi hoàn tất, hãy quay lại Lịch để xác nhận rằng lịch nghỉ phép của nhóm đã được điền thông tin về các sự kiện.

Xem lại mã

Để xem lại mã Apps Script cho giải pháp này, hãy nhấp vào Xem mã nguồn:

Xem mã nguồn

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.
const 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'];
const GROUP_EMAIL = "ENTER_GOOGLE_GROUP_EMAIL_HERE";

const ONLY_DIRECT_MEMBERS = false;

const KEYWORDS = ["vacation", "ooo", "out of office", "offline"];
const MONTHS_IN_ADVANCE = 3;

/**
 * Sets up the script to run automatically every hour.
 */
function setup() {
  const 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.
  const today = new Date();
  const 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;
  for (const user of users) {
    const username = user.getEmail().split("@")[0];
    const events = findEvents(user, today, maxDate, lastRun);
    for (const event of events) {
      importEvent(username, event);
      count++;
    }
  }

  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";
    event.outOfOfficeProperties = undefined;
    event.focusTimeProperties = undefined;
  }

  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, start, end, optSince) {
  const params = {
    eventTypes: "outOfOffice",
    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);
    pageToken = response.nextPageToken;
  } while (pageToken);
  return events;
}

/**
 * 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) {
  const group = GroupsApp.getGroupByEmail(groupEmail);
  let users = group.getUsers();
  const childGroups = group.getGroups();
  for (let i = 0; i < childGroups.length; i++) {
    const childGroup = childGroups[i];
    users = users.concat(getAllMembers(childGroup.getEmail()));
  }
  // Remove duplicate members
  const uniqueUsers = [];
  const userEmails = {};
  for (let i = 0; i < users.length; i++) {
    const 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) {
  const users = [];
  for (const groupEmail of groupEmails) {
    const groupUsers = GroupsApp.getGroupByEmail(groupEmail).getUsers();
    for (const user of groupUsers) {
      if (!users.some((u) => u.getEmail() === user.getEmail())) {
        users.push(user);
      }
    }
  }
  return users;
}

Sửa đổi

Bạn có thể chỉnh sửa quy trình tự động hoá lịch nghỉ phép của nhóm theo ý muốn để phù hợp với nhu cầu của mình. Sau đây là một thay đổi không bắt buộc để sửa đổi điều kiện kích hoạt.

Thay đổi tần suất tập lệnh quét các sự kiện mới

Để thay đổi tần suất chạy tập lệnh, hãy thực hiện các bước sau:

  1. Trong dự án Apps Script, hãy nhấp vào Trình kích hoạt .
  2. Bên cạnh trình kích hoạt, hãy nhấp vào biểu tượng Chỉnh sửa trình kích hoạt .
  3. Chọn các thay đổi rồi nhấp vào Lưu.

Người đóng góp

Mẫu này do Google duy trì với sự trợ giúp của các Chuyên gia về Google Developers.

Các bước tiếp theo