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

от автора

Предыстория

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

Всё вышеописанное я собрал в таблицу, которую можно скопировать себе(Файл — Создать копию), запустить(Меню — Создать файлы) и получить к себе на диск несколько сотен файлов. При необходимости процедуру повторить.

Спасибо за внимание. Буду рад получить фидбэк по коду. Удачи!

ссылка на оригинал статьи https://habr.com/ru/post/559670/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *