Ответы пользователя по тегу Google Apps Script
  • Как скачать файл средствами google apps script?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вариантов загрузки может быть несколько. Как и способов создания файла. Из вопроса не ясно, как создается файл, каие требования предъявляются к загрузке, какой интерфейс используется.
    Предположим, что:
    1. Файл создается в момент загрузки
    2. Файл сохраняется на Диске
    3. Файл загружается через основной интерфейс приложения Таблицы Google

    Основной идеей экспорта/скачивания файлов из Диска является получение ссылки на ресурс
    var file = DriveApp.createFile(fileName, content, 'text/html');
    var downloadUrl = file.getDownloadUrl();

    Теперь достаточно создать событие на стороне клиента, котрое вызовет загрузку. Вариантов может быть несколько. Вот один из них:
    // Создаем окно на клиенте и запускаем
    var a = window.document.createElement('a');
    a.href = downloadUrl;
    a.text = 'Download';
    document.body.appendChild(a);
    a.click();

    В данном месте самое сложное - поставить пользователю интерфейс для реализации загрузки. Это может быть и письмо, и нотификация, и все, что угодно, что может нажимать ссылки.

    Другим вариантом может быть поставка блока данных через Client-side API. Принцип останется прежним, изменится только формат передаваемых данных. С сервера Blob, на клиенте загрузка локального Blob-ресурса.

    Полный код и ссылка на рабочее приложение https://gitlab.com/google-apps-script-russian/zagr...
    Ответ написан
    Комментировать
  • Почему "После списка аргументов отсутствует закрывающая круглая скобка (")"). (строка 2, файл Код)"?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Скорей всего проблема в использовании символа ";". В языке Google Apps Script он используется как разделитель команд интерпретатору. Вы не можете их использовать в таком контексте.
    Ответ написан
    Комментировать
  • Как разрешить Google Apps Script отправлять email для любого пользователя Spreadsheet?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Описания недостаточно, код не видно, хотя, можно уже предполагать, где скрывается ошибка. Разделите понятия:

    Судя по описанию, User-B прошел авторизацию, а остальные пользователи нет. Заставьте этих пользователей пройти авторизацию, и у них появится разрешение на запуск функций.

    Как заставить пользователей авторизоваться - это проблема разработчика, а не Google. Но, не имея в техническом плане серьезных противоречий, этот подход сложен для понимания практического. Примеры приложений, код которых может быть одинаков до запятой.
    • Отправить присьмо при изменении. Простой триггер onEdit(). Может отпрвлять только то, кто прошел авторизацию.
    • Отправить присьмо при изменении. Устанавливаемый триггер. Триггер установлен кем-то одним. Может отпрвлять только то, кто прошел авторизацию, т.е. один. Письма будут приходить согласно условию в коде.
    • Отправить присьмо при изменении. Устанавливаемый триггер. Триггер установлен каждым умником, кто залез в код. Может отпрвлять только тот, кто прошел авторизацию. Письма будут приходить согласно условию в коде умноженное на количество умников, потому что это будет один и тот же код, срабатываемый для каждого триггера в каждом аккаунте.

    Разрабатывая клиент-серверное приложение, вам необходимо заботиться о многопользовательском доступе. Необходимо разработать процесс и порядок доступа, и только после этого разбираться, как это реализовать. Например,
    • Каждый пользователь сам себе будет отправлять письма? Зачем и нах!?
    • Каждый пользователь будет отправлять письма на заданный адрес?
    • Письма будет отправлять только один аккаунт всем участникам подписки, группе, редакторам и т.д.?
    • Письма будет отправлять только один аккаунт, тому кто совершил изменения? Для аккаунтов googlemail.com это недоступно.

    Лично мне было бы неприятно, что кто-то требует от меня отпрвлять непонятно куда и зачем какие-то письма, выжирая и так скудную квоту.

    Пересмотрите свое приложение. Какие цели оно преследует? Чего вы уже добились? Так ли это работает? Приведите пример кода и суть приложения.
    Ответ написан
    1 комментарий
  • Метод getFilesByName() не находит файлы, если в имени с кириллицей присутствуют буквы в верхнем регистре?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Как раз сейчас описываю этот баг. Поставьте звездочку тут.

    Используйте менее точный поиск с дополнительной обработкой результатов. И сервис DriveApp и сервис Drive умеют в строке запроса обрабатывать директиву title contains. См. Search for Files. На данный момент, 2017-08-13, используется API v2, и для поиска в данном случае - это принципиально. Следующий код вернет то, что вы ожидаете:
    function getFileByName(name) { 
      var files = DriveApp.searchFiles(Utilities.formatString('title contains "%s" and trashed=false', name));
      var result = undefined;
      while(files.hasNext()){
        var file = files.next();
        if(file.getName() === name){
          result = file;
          break;
        }
      }
      return result;
    }
    Ответ написан
  • Как связать 2 Google Form между собой?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Задача нетривиальная. Даже не знаю, стоит ли ее решать через Google Apps Script.

    Положим, что есть две Формы. Во-первых, необходимо место под генерацию промокодов. А это должна быть Таблица. Во-вторых, при использовании промокода необходимо выяснить его валидность. А, ну далее продолжать не стоит - Формы неинтерактивны. Форма - это интерфейс сбора информации. Интерфейс - это совокупность средств, методов и правил взаимодействия, в котором нет возможности обратной связи. У разработчика нет доступа к реализации таких методов как загрузка Формы, проверка данных на стороне клиента и пр. Т.е. разработчик не в состоянии реализовать интерфейс Форм.
    Ему доступен интерфейс хранилища Форм. Т.е. разработчик может обратиться по адресу к Форме, получить ее данные, изменить ее свойства.

    Только качественный и проверенный контент
    Ответ написан
  • Как настроить автоответчик писем на заполнение форм на разных языках?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    У вас этот код никогда не заработает нормально. Почитайте комментарии на приведенном вами ресурсе.

    Главная ошибка - это ссылка на активную Таблицу SpreadsheetApp.getActiveSpreadsheet(); В триггере OnFormSubmit не может быть активной Таблицы, потому что никакой пользователь ее не открыл. А если даже и открыл, то он может не быть регистратором триггера или его инициатором.

    Обработка ошибок
    Google Apps Script (речь о данном моменте времени развития языка) не имеет развитых методов отладки. Поэтому обвяжите основной вызов:
    /**
    * В Таблице должен быть лист "Лог".
    * Чтобы лист существовал всегда
    * https://github.com/oshliaer/google-apps-script-snippets#get-a-sheet-by-name
    **/
    function handler(e) {
      try {
        sendFormByEmail(getNewEmailMessage(e));
      } catch(err) {
        e.source.getSheetByName('Лог').appendRow([new Date(), err.message, err.stack]);
      }
    }

    Отлично, теперь все, что упало в рантайме, будет вам известно.

    Текущая Таблица/Лист
    Я приываю не только автора топика, но вообще всех: "Пожалуйста, не трогайте ActiveSpreadsheet, ActiveSheet и ActiveRange, т.к. это не VBA. Это #ВАЩЕ_НАФИГ_ДРУГАЯ_ВСЕЛЕННАЯ!". Обратите внимание на код выше e.source.getSheetByName(). Это все, что надо.
    А вот пример комутатора:
    /**
    * Определение языка на основе ответа
    **/
    function getNewEmailMessage(e) {
      var sheet = e.range.getSheet();
      var lang = 'EN';
      switch(sheet.getName()){
        case 'RU':
           return 'Ответ на русском';
           break;
        case 'EN':
           return 'Ответ на нерусском';
           break;
      }
      return 'Ответ на русском';
    }

    Осталось добавить аргументов и переписать sendFormByEmail согласно принимаемым параметрам.

    Триггер на несколько Форм

    Вместо заключения
    Только качественный и проверенный контент
    Ответ написан
    4 комментария
  • Почему не работает Get запрос в Google Apps Script через file_get_contents?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Виктор Фамильевич, для того, чтобы вызвать такой запрос, ваш сервер должен передать код аутентификации владельца или редактора проекта скрипта в заголовках. Это необходимо потому, что вызывается url разработчика для тестирования. Т.е. этот адрес нельзя использовать для нормальной работы.

    Самый простой способ вызвать корректно сервис - это опубликовать его. Ссылки очень легко отличаются по своим окончаниям:
    • /dev - адрес для тестирования
    • /exec - текщий адрес приложения


    Подробности Web Apps / Deploying a script as a web app
    Консультации по GS
    Ответ написан
    Комментировать
  • Удобоваримые ответы из google forms в spreadsheet, как правильно?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Не используйте "обработку формулой по триггеру" в данном случае. Вам просто необходим скрипт, который будет преобразовывать текущие данные в подходящие для отчета формы. Без примера сложно сказать, как именно необходимо разделить данные, но в общем случае это будет выглядеть как-то так https://productforums.google.com/forum/#!topic/doc... Там задача несколько сложнее чем у вас, рассчитываются веса вхождений для каждого ответа. Скопируйте Таблицу, в ней появится меню генерации отчета.

    Консультации https://google-apps-script-russian.gitlab.io/page/...
    Ответ написан
    Комментировать
  • Как работать в google script с защищенными листами google таблиц?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вам нужны два скрипта:
    1. Сервер-приемник (запускается от имени владельца защищенного диапазона)
    2. Программа-инициатор (запускается от имени пользователя)


    Вам необходимо будет решить несколько проблем:
    • Определение подлинности программы-инициатора на сервере
    • Контроль команд на выполнение
    • Согласованная разработка и поддержка двух программ
    Ответ написан
    Комментировать
  • Как заставить работать скрипт дистанционно?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Рассмотрите две возможные проблемы:

    Во-первых. Прежде чем отправлять запрос, ваше приложение должно авторизоваться через и получить токен.
    This method requires authorization with an OAuth 2.0 token that includes at least one of the scopes listed in the Authorization section; script projects that do not require authorization cannot be executed through this API.


    Во-вторых: У вас 404 ошибка. Ресурс не найден. Проверьте коррекность адреса
    POST https://script.googleapis.com/v1/scripts/{scriptId}:run

    В данном случае {scriptId} - это уникальный номер самого скрипта, который находится в меню Файл - Свойства проекта - Ключ проекта

    Если вы не хотите авторизовываться, но вам очень надо отправить POST, попробуйте
    function doPost(e) {
      return ContentService.createTextOutput(JSON.stringify({status: 'ok'}))
        .setMimeType(ContentService.MimeType.JSON);
    }


    Но, тогда вам необходимо опубликовать скрипт как веб-приложение и разрешить доступ на выполнение для всех, даже анонимных пользователей.

    Добавлено

    Еще одной частой проблемой является необходимость вручную добавить в файл манифеста области видимости для скрипта, чтобы он мог иметь доступ к Script API.
    Ответ написан
    1 комментарий
  • Как сделать выпадающий список с добавлением новых элементов?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Здравствуйте,

    доступа к манипулированию IU нет. Добавят ли разработчики эту особенность неизвестно. Вопрос был поднят больше года назад на импортном форуме по Диску. Тогда из вариантов был сайдбар, но это неудобно.

    Идея larionov_n похожа на выстрел из пушки по воробьям. Почему бы тогда не использовать нормальную БД вместо кастрированного табличного процессора? Но, в то же время, могу сказать, что манипулирование Таблицей через внешнее приложение с AngularJS на фронте самое популярное в моих заказах. Т.е. на свой вопрос я найти ответа не могу.
    Ответ написан
    Комментировать
  • Как избежать timout в 6 минут при разработке дополнения?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Здравствуйте.

    Никак.
    Script runtime 6 min / execution и добавьте к ним Triggers total runtime 1 hr / day , а еще URL Fetch data received 50MB / day (хотя не понятно, что вы там такое втягиваете). Вам необходимо завести собственный сервер.

    Еще возможно, что страдает логика приложения.

    С уважением.
    Ответ написан
    1 комментарий
  • Как сделать вложенный цикл в google spreadsheet?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Ни при каких обстоятельствах не дразните систему setValue(), да еще и во вложенном цикле. Запись происходит не на ваш ПК, а на сервер. Кому понравится, когда его так дергают?

    Чтобы работало даже с "черновиками", т.е. до 50 000 строк на лист, сначала берите ВСЕ данные, потом формируйте ГОТОВЫЙ массив, потом делайте setValue().

    Примерно так:
    function writeData(){
      //Не факт, что поможет, но вдруг
      SpreadsheetApp.flush();
      
      //Далее как у всех
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      
      var resultSheet = ss.getSheetByName("Result");
      var agentsSheet = ss.getSheetByName("Buyers");
      var productsSheet = ss.getSheetByName("Products");
      
      var aV = agentsSheet.getDataRange().getValues();
      var pV = productsSheet.getDataRange().getValues();
      
      //срезать шапку
      aV.shift();
      //  pV.shift();
      
      var res = [];
      
      for(var i = 0; i< aV.length; i++)
      {
        res.push([].concat([aV[i][0]], pV[0]));    
        for(var j = 1; j < pV.length; j++){      
          res.push([].concat([''], pV[j]));      
        }
      }
      
      res = res.slice(0, 50000);
      resultSheet.getDataRange().clear(); 
      resultSheet.getRange(1, 1, res.length, res[0].length).setValues(res);
    }
    Ответ написан
    1 комментарий
  • Какая формула вернет название Листа в ячейку?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Здравствуйте.

    Можно создать собственную функцию на основе Google Apps Script.
    Code.gs
    function sheetName() {
      return SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
    }
    
    function sheetsName() {
      return SpreadsheetApp.getActiveSpreadsheet().getSheets().map(function(s){return s.getName()});
    }

    В Таблице необходимо указать формулу для получения имени активного листа
    =sheetName(E5:E13)
    Для получения списка всех листов
    =sheetsName(E5:E13)
    где E5:E13 особый, обязательно активный, т.е. зависящий от ввода пользователя, диапазон. Без изменения этого диапазона, толку от функции будет мало.
    Внимание, функции времени (TODAY(), NOW()) нельзя передавать как аргументы.

    Пример Таблицы со скриптом goo.gl/w97FNi

    P.S.: Чтобы исключить гнев и любую неверную интерпретацию, скажу, что этот скрипт стал возможен после публикования вопроса и первых комментариев к этому посту. Пруф https://code.google.com/p/google-apps-script-issue...
    Ответ написан
    Комментировать
  • Система бронирования (резервирования) товаров (заказов) Google spread sheet. Примеры?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Здравствуйте.

    Вот небольшой пример https://youtu.be/9WK1FM2_tDc Таблица https://goo.gl/4u23t7 (необходимо сделать копию)

    Суть в том, чтобы вести запись только в одном листе "История". На "Складе" остатки отображаются через формулу:
    =QUERY('История'!B:E;"select B, C, sum(D) where E=true group by B, C")

    Т.е. если в "Истории" будет запись без TRUE, то ее можно считать "непроведенной".
    Лист "Заказы" служит пояснением для "Истории", возможно, надо как-то расширить связь этих Таблиц, но она все равно будет условной.

    Пояснения к коду. Серверная часть нуждается в LockService, чтобы разрешить очередь записи.
    Можно сделать форму просмотра заказов, с возможностью помечать FALSE в "Истории". Может быть даже "Редактировать", но лучше по-моему, делать копию и записывать новый заказ, помечая прежний как FALSE. Также необходимо продумать регламент, т.к. Таблицы "нерезиновые" и необходимо максимально часто переносить остатки.

    Пояснения к концепту. Ваше представление, которое приведено выше, вряд ли подойдет для реализации в Таблицах, уже только потому, что никаких идентификаторов строк, и тем более связей между Таблицами, не существует. Я предлагаю вообще ничего не удалять. Лучше делать "перенос остатков" и прочих параметров в новые Таблицы (или наоборот), чтобы иметь более полноценную картину действий.

    Пожалуйста, измените теги в топике на Google script и Spreadsheets. При текущем теге ответ на вопрос должен быть примерно таким https://www.google.com/enterprise/marketplace/sear...
    Ответ написан
  • Как получить ссылку на изображение из Google диска по id?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Здравствуйте.

    Берут сомнения меня, что CAJA, даже в режиме IFRAME, предоставляет нам DOM-документ. Все же, с большой вероятностью, это некий фарш LIKE_DOM = CAJA(DOM);

    Поэтому инструкция, чтобы в нее не передавали, не будет "responsive":
    document.getElementByTagName('img').src = data.image;

    Но, это же DOM, в конце концов! Создаем элемент, вместо изменения атрибута:
    index.html
    <div>
    	<input type="button" onclick="showImage()" value="Click Me!" />
    	<div id="app"></div>
    </div>
    <script>
    	function showImage() {
    		google.script.run.withSuccessHandler(doStuff).withFailureHandler(function serverFailureHandler(e) {
    			console.log(JSON.stringify(e))
    		}).getImageId();
    	};
    	function doStuff(id) {
    		var img = document.createElement('img');
    		img.src = 'https://drive.google.com/uc?export=download&id=' + id;
    		document.getElementById('app').appendChild(img);
    	};
    </script>

    Code.gs
    function doGet(e){
      return HtmlService.createHtmlOutputFromFile('index');
    }
    function getImageId(){
      return '0BzStSAirQJ2oREZGYnM5MjBUZTQ';
    }

    Заметьте, нет никакой необходимости вызывать DriveApp. Он вам понадобится, только если изображение будет не в общем доступе. Тогда вам придется брать Blob, кодировать его в base64 и передавать слепок. Размер слепка не может превышать 10Мб.

    И еще я заметил у вас
    <?!= include('JavaScript'); ?>
    Не забудьте, что шаблоны не исполняют асинхронного кода Load data asynchronously, not in templates, поэтому, создавая нечто объемное, постарайтесь выдать HTML как можно быстрее:
    function doGet(e){
      return HtmlService.createHtmlOutputFromFile('index');
    }
    Ответ написан
    Комментировать
  • Как сгенерить md5 строку на Google Script?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Здравствуйте.

    Мне кажется, что у вас неполные представления о методах Google Apps Script, алгоритмах хэширования и системах исчисления.

    Во-первых, computeDigest возвращает массив Byte[], что он и должен делать.
    Во-вторых, вам просто его надо "преобразовать в 16-ричную хэш строку", как сказано выше.

    Попробуйте сделать так:

    function test(){
      Logger.log(toMD5('56'));
    }
    const toMD5 = function(charset, toByte) {
      charset = charset || Utilities.Charset.UTF_8;
      var digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, this, charset);
      if(toByte) return digest;
      var __ = '';
      for (i = 0; i < digest.length; i++) {
        var byte = digest[i];
        if (byte < 0) byte += 256;
        var bStr = byte.toString(16);
        if (bStr.length == 1) bStr = '0' + bStr;
        __ += bStr;
      }
      return __;
    }
    Ответ написан
    Комментировать
  • Как настроить взаимодействие Google Apps и Яндекс.Маркет API?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Основное требование от Яндекс для доступа к API Маркета - это наличие "Партнерского API"- или
    "Контентного API"- ключа доступа в виде oauth_login. Этакий ключ-в-ключе. Рассуждения на тему необходимости, полагаю, неуместны.

    API Маркета
    • Партнерский API Для магазинов, которые размещают товары на Яндекс.Маркете, а также для рекламных агентств.
    • Контентный API Для разработчиков, которым нужны данные о товарах с Маркета: описания моделей, фотографии, отзывы.

    Следует точно определиться с целью задачи. Не знаю, дает ли Яндекс добро на использование данных в масштабах "личного хочу", поэтому, не имея магазина, придется создавать приложение "для всех".

    Опишу вкратце, как использовать API Маркет в Google Apps Script:
    1. Вы должны быть уверены, что у вас есть ваш oauth_login (см. выше)
    2. Пройдите Авторизацию. Для этого ваше приложение должно использовать следующие из возможностей Google Apps Script: HtmlService как опубликованное приложение, HtmlService как возврат StateTokenBuilder, если вы создаете Add-On. Для получения токена и отправки запросов POST (или запросов GET от лица приложения) используйте URLFetch
    3. Следуйте инструкциям Яндекс по Ограничениям для запросов

    Могу добавить только то, что описание API Яндекс и ВКонтакте на данный момент, как мне кажется, являются самыми внятными и, вероятно, прямолинейными. От чего вероятность возникновения ошибок минимальна.

    С уважением.
    Ответ написан
    Комментировать