[JavaScript, Google API] Google документы станут полновесными с 1 июня. Пишем скрипт для обхода этого ограничения

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
27-Май-2021 20:31

ПредысторияGoogle изменяет политику хранения данных с 1 июня 2021 года. Вкратце: документы и фото теперь станут полновесными и будут учитываться в общей квоте 15Гб. К тому же, при неактивности аккаунта более двух лет, Google может удалить ваши данные.Я часто работаю с Google документами, и при активном использовании дисковая квота закончится довольно быстро. Но есть и хорошая новость: документы, созданные до 1 июня 2021 года так и останутся невесомыми, поэтому вы не получите превышение квоты в одночасье.У меня сразу возникла мысль сделать документов "в запас". Ниже я расскажу, как это можно осуществить, не тратя много времени и сил.Пишем скрипт, создающий документыСкрипт буду писать с помощью Google Apps Script. Создаём новую таблицу Google, заходим в редактор скриптов (Инструменты - Редактор скриптов).Создаём три файла
  • main.gs - основной код для создания файлов
  • menu.gs - код для создания пользовательского меню при открытии таблицы
  • index.html - шаблон страницы для отображения информации
Сначала в файле menu.gs создаём функцию onOpen(). Это простой триггер, который выполняется при открытии таблицы. Его задача - создать пользовательское меню, из которого можно запустить функцию.
function onOpen(e) {                         // Выполняется при открытии таблицы
  SpreadsheetApp.getUi()                     // Получаем интерфейс пользователя
      .createMenu('Меню')                    // Создаём меню
      .addItem('Создать документы', 'main') // Создаём команду меню
      .addToUi();                            // Добавляем меню в интерфейс
}
В файле main.gs создадим функцию main(), которая и будет запускаться с кнопки в меню.
function main() { // Меню - Создать документы
  // Создаём HTML документ из шаблона
  let template = HtmlService.createTemplateFromFile(`index`);
  // Показываем модальное окно с HTML
  SpreadsheetApp.getUi()
    .showModelessDialog(template.evaluate(),`Создаю документы...`);
}
Пора разобраться с index.html. это обычный HTML файл, который отобразится в модальном окне. там можно использовать стили, скрипты и т.п.index.html
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script>
      let timer = function(){ // Обслуживает таймер
        // Получаем текущее время
        let now = (new Date()).getTime();
        // Задаём режим обновления таймера - 1 раз в секунду
        setInterval(function(){
          // Получаем прошедшее время. now будет доступно из-за замыкания.
          let time = (new Date()).getTime() - now;
          // Вычисляем минуты
          let minutes = Math.floor(time/60000);
          // И секунды
          let seconds = Math.floor(time%60000/1000);
          // Обновляем данные в поле lifeTime
          updateData("lifeTime", `${minutes<10?"0"+minutes:minutes}:${seconds<10?"0"+seconds:seconds}`);
        },1000);
      };
      updateData = function(id, value){ // Обновляет информацию в одном элементе по id
        let element = document.getElementById(id);
        if (element) {
          element.innerHTML = value;
        };
      };
      refreshData = function(){ // Обновляем все данные
        google.script.run.withSuccessHandler(function(data){
        updateData("createTables", data.createTables);   // Таблиц создано:
        updateData("createDocs", data.createDocs);      // Документов создано:
        updateData("createForms", data.createForms);    // Форм создано
        updateData("createSlides", data.createSlides);  // Презентаций создано
        updateData("log", data.log);                    // Лог
      }).getData(); // Получаем данные
    };
      setTimeout(setInterval(refreshData, 2000),1000);   // Данные обновляем раз в 2 секунды
      timer();                                          // Запуск таймера
      google.script.run.doMagic();                // Запуск функции для создания документов
    </script>
  </head>
  <body>
    <div class=".container bg-dark text-white text-center">
      <div class="row">
        <div class="col">
          Таблиц
        </div>
        <div class="col">
          Документов
        </div>
        <div class="col">
          Форм
        </div>
        <div class="col">
          Презентаций
        </div>
      </div>
      <div class="row">
        <div class="col" id="createTables">
          0
        </div>
        <div class="col" id="createDocs">
          0
        </div>
        <div class="col" id="createForms">
          0
        </div>
        <div class="col" id="createSlides">
          0
        </div>
      </div>
    </div>
     <div class=".container bg-dark text-white">
      <div class="row">
        <div class="col text-right" id="label_lifeTime">
          Время работы:
        </div>
        <div class="col  text-left" id="lifeTime">
          00:00
        </div>
      </div>
    </div>
    <div bg-dark text-white id="label_log">Лог: </div>
    <ul class="list-group" id="log">
    </ul>
  </body>
</html>
В ней подключаем стили, создаём скрипт, в котором три функции:
  • updateData(id, value) - ищет на странице элемент по id и обновляет содержимое
  • refreshData() - обновляет все данные на странице, кроме таймера
  • timer() - обслуживает таймер
Для создания документов в файле main.gs создаём универсальную функцию.function create()
const FILES_TO_CREATE = 50;
function create(filesToCreate = FILES_TO_CREATE, folderId, prefix="file_", app, key) {
  // Получаем словарь ключ-значение для текущего скрипта.
  let props = PropertiesService.getScriptProperties();
  // Получаем значение для ключа data. Преобразуем в объект
  let data = JSON.parse(props.getProperty(`data`));
  try{
    // Получаем директорию по id
    let folder = DriveApp.getFolderById(folderId);
    for(var i=0; i<filesToCreate; i++){ // Создаём filesToCreate документов
      // Создаём документ, получаем его id
      let ssId = app.create(`${prefix}${(new Date()).getTime()}`).getId();
      // Получаем файл по его id
      let ss = DriveApp.getFileById(ssId);
      // Копируем файл в папку
      folder.addFile(ss);
      // Удаляем файл из корневой папки
      DriveApp.getRootFolder().removeFile(ss);
      // Увеличиваем счётчик созданных файлов
      data[key]=1+data[key];
      // Сохраняем новые данные
      props.setProperty(`data`, JSON.stringify(data));
    };
  }catch(err){
    // В случае ошибки - пишем её в лог
    logToHtml(`Error: ${err}`, LOG_TYPES.danger);
  };
  // Возвращаем из функции количество созданных файлов
  return +i;
};
И конкретные реализации - для таблиц, документов, форм и презентаций.Обёртки для функции create()
function createSheets(key) {
  // Получаем id папки для таблиц
  let folderId =  PropertiesService.getScriptProperties().getProperty(`sheetsFolder`);
  // Создаём нужное количество таблиц
  let count = create(FILES_TO_CREATE, folderId, `sheet_`, SpreadsheetApp, key);
  // Сохраняем в лог
  logToHtml(`${count} sheets were created`, LOG_TYPES.success);
}
function createDocs(key) {
  let folderId =  PropertiesService.getScriptProperties().getProperty(`docsFolder`);
  let count = create(FILES_TO_CREATE, folderId, `doc_`, DocumentApp, key);
  logToHtml(`${count} docs were created`, LOG_TYPES.success);
}
function createForms(key) {
  let folderId =  PropertiesService.getScriptProperties().getProperty(`formsFolder`);
  let count = create(FILES_TO_CREATE, folderId, `form_`, FormApp, key);
  logToHtml(`${count} forms were created`, LOG_TYPES.success);
}
function createSlides(key) {
  let folderId =  PropertiesService.getScriptProperties().getProperty(`slidesFolder`);
  let count = create(FILES_TO_CREATE, folderId, `slide_`, SlidesApp, key);
  logToHtml(`${count} slides were created`, LOG_TYPES.success);
}
Далее делаем функцию для создания папок.
function createFolders(){
  // Получаем словарь ключ-значение для текущего скрипта
  let props = PropertiesService.getScriptProperties();
  // Задаём структуру папок
  let folders = [
    {key:`rootFolder`,   name:`Прозапас`                         },
    {key:`sheetsFolder`, name:`Sheets`, parentFolder:`rootFolder`},
    {key:`docsFolder`,   name:`Docs`,   parentFolder:`rootFolder`},
    {key:`formsFolder`,  name:`Forms`,  parentFolder:`rootFolder`},
    {key:`slidesFolder`, name:`Slides`, parentFolder:`rootFolder`},
    ];
  // Проходим по структуре и создаём папки
    folders.forEach(folder=>{
      if (!props.getProperty(folder.key)){ // Если папка ещё не создана
        // Если есть параметр rootFolder, то используем его, иначе выбираем корневую папку
        let parentFolder = folder.parentFolder?DriveApp.getFolderById(props.getProperty(folder.parentFolder)):DriveApp.getRootFolder();
        // Создаём папку
        let folderId = parentFolder.createFolder(folder.name).getId();
        // Сохраняем информацию о ней
        props.setProperty(folder.key, folderId);
      };
    });
}
И функцию для создания всех типов документов:Основная функция, которая запускает создание папок и документов
function doMagic(){
  let props = PropertiesService.getScriptProperties();
  // Структура данных
  let data = {
    createTables:0,
    createDocs:0,
    createForms:0,
    createSlides:0,
    startTime:new Date(),
    log:``,
  };
  // Сохраняем данные в словарь скрипта
  props.setProperty(`data`, JSON.stringify(data));
  try{
    createFolders();               // Создаём папки
    createSheets(`createTables`);  // Создаём таблицы
    createDocs(`createDocs`);      // Создаём документы
    createForms(`createForms`);    // Создаём формы
    createSlides(`createSlides`);  // Создаём презентации
    // Сообщаем, что всё готово
    SpreadsheetApp.getUi().alert(`Готово!`);
  }catch(err){
    // При ошибке сообщаем об этом
    SpreadsheetApp.getUi().alert(`Ошибка! ${err}`);
  };
};
Остаётся создать функцию для получения данных - так модальное окно может получить актуальные данные для отображения.
function getData(){ // Получает данные из словаря
  // Получаем словарь ключ-значение
  let props = PropertiesService.getScriptProperties();
  // Получаем нужные данные и преобразуем в объект
  let data = JSON.parse(props.getProperty(`data`));
  return data; //Возвращаем данные
};
И функция для записи строки в лог:
const LOG_TYPES = { // Типы сообщений. Взято из bootstrap
  primary:   "primary",
  secondary: "secondary",
  success:   "success",
  danger:    "danger",
  warning:   "warning",
  info:      "info",
};
function logToHtml(log, type = LOG_TYPES.primary){
  // Получаем данные
  let data = getData();
  // Добавляем li тег (лог отображается в виде списка) с данными
  data.log+=`<li class="list-group-item text-${type}">${log}</li>\n`;
  // Сохраняем данные
  let props = PropertiesService.getScriptProperties();
  props.setProperty(`data`, JSON.stringify(data));
};
Что получилосьОстаётся только запустить скрипт из меню. При первом запуске Google запросит права - это нормально. После этого откроется окно, в котором можно наблюдать за прогрессом.
Модальное окно для визуализации прогрессаОграничения скрипта
  • Время жизни скрипта ограничено 6 минутами. За это время он успеет создать несколько сотен документов. Можно обойти это ограничение, закинув все функции непосредственно в HTML код модального окна и обращаться к диску по API, но об этом в следующий раз
  • Есть ограничение на количество ежедневно созданных документов. Рано или поздно будет появляться ошибка Exception: Служба была вызвана слишком много раз за день: docs create. Тогда скрипт можно запустить на следующий день
TL;DRВсё вышеописанное я собрал в таблицу, которую можно скопировать себе(Файл - Создать копию), запустить(Меню - Создать файлы) и получить к себе на диск несколько сотен файлов. При необходимости процедуру повторить.Спасибо за внимание. Буду рад получить фидбэк по коду. Удачи!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_javascript, #_google_api, #_google_apps_script, #_google_docs, #_javascript, #_tutorial, #_html, #_javascript, #_google_api
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 18:39
Часовой пояс: UTC + 5