Ответы пользователя по тегу Google Apps Script
  • Как сохранить один лист из Гугл Таблицы в формат xlsx и отправить его по email?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    На самом деле эта задача легко гуглится.

    Например, требуется сохранить лист "Мой супер лист".

    /**
     *
     */
    function runSheet() {
      var spec = ['Мой супер лист'];
    
      var spreadsheet = SpreadsheetApp.openById(
        'ABCD1234'
      ).copy('tmp');
    
      spec.forEach(function(sheetName) {
        var dr = spreadsheet.getSheetByName(sheetName).getDataRange();
        dr.setValues(dr.getValues());
      });
      spreadsheet.getSheets().forEach(function(sheet) {
        if (spec.indexOf(sheet.getName()) < 0) spreadsheet.deleteSheet(sheet);
      });
      var spreadsheetId = spreadsheet.getId();
      var file = exportSpreadsheetToFile_(spreadsheetId, 'xlsx');
      DriveApp.getFileById(spreadsheetId).setTrashed(true);
      return file;
    }


    Листинг exportSpreadsheetToFile_ можно найти тут.

    Ну, а полученный файл вы можете отправить вот так

    MailApp.sendEmail({
      to: "recipient@example.com",
      subject: "Logos",
      attachments: [runSheet().getBlob()]
    });
    Ответ написан
  • Как исправить ошибку при использовании onEdit, дать разрешения?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Триггеры делятся на простые (Simple) и устанавливаемые (Installable).

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

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вероятно, вот так должно сработать

    /**
     * @param {GoogleAppsScript.Events.SheetsOnEdit} e
     */
    function onEdit(e) {
      if (
        e.range.getSheet().getName() === 'Sheet1' &&
        e.range.getA1Notation() === 'M2'
      )
        CopyList();
    }
    
    /**
     *
     */
    function CopyList() {
      var sss = SpreadsheetApp.openById('ID-ТАБЛИЦЫ1');
      var ss = sss.getSheetByName('Sheet1');
    
      var from = ss;
      var fromValues = from.getDataRange().getValues();
      var fromData = fromValues;
    
      var tss = SpreadsheetApp.openById('ID-ТАБЛИЦЫ2');
      var ts = tss.getSheetByName('Sheet2');
    
      ts.getRange(
        ts.getLastRow() + 1,
        1,
        fromData.length,
        fromData[0].length
      ).setValues(fromData);
    }
    Ответ написан
    Комментировать
  • Как запустить onEdit из редактора или программным способом?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Насколько я понимаю, вы пробуете вызвать функцию onEdit из другой функции или из редактора.

    Разбор ошибки

    Ошибка
    TypeError: Cannot read property 'range' of undefined (строка 3, файл se)

    Означает, что в некотором файле se в 3й строке есть некоторая переменная со значением undefined, свойство range которой прочитать невозможно. Тут все очевидно - у undefined нет свойств.

    Если посмотреть на код, то становится ясно, что имя этой переменной e. И мы ее получаем в системную функцию onEdit. Это означает, что система сама передает контекст в эту функцию.

    ОК. Значит, чтобы протестировать эту функцию, нужно передать параметр самостоятельно.

    Например,

    /**
     * Тестирование триггера для события EDIT
     */
    function runOnEdit() {
      var source = SpreadsheetApp.getActive();
      var range = source.getRangeByName('Sheet!!B26');
      /**
       * @type {GoogleAppsScript.Events.SheetsOnEdit}
       */
      var e = {
        authMode: ScriptApp.AuthMode.LIMITED,
        oldValue: undefined, // ну или что хотите
        range: range,
        value: range.getValue(),
        source: source,
        triggerUid: 0,
        user: Session.getActiveUser(),
      };
    
      onEdit(e);
    }
    
    /**
     *
     * @param {GoogleAppsScript.Events.SheetsOnEdit} e
     */
    function onEdit(e) {
      // Работает простой триггер
    }


    Проверьте - этот код будет работать точно так же, как если бы пользователь внес изменения в Таблице.
    Ответ написан
    1 комментарий
  • Как заставить работать триггер "по изменению" в таблице с применением Google script App?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вы должны установить триггер отправки Формы. Простой триггер для этого не подходит.
    Ответ написан
    Комментировать
  • Почему получается неверная кодировка при парсинге html?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Обычно, всегда при fetch нужно указывать кодировку. Но так сложилось, что все привыкли к UTF-8.

    Укажите кодировку вашего контента при извлечении

    const data = UrlFetchApp.fetch('https://classinform.ru/fkko-2017.html.');
    console.log(data.getContentText('windows-1251'));
    Ответ написан
    2 комментария
  • Как скриптом запустить действие триггера?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Триггеры на изменение и редактирование срабатывают только для действий пользователя.
    Ответ написан
    Комментировать
  • Как скриптом запускать другие скрипты?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вам нужна точка входа. И когда запускаем скрипты из редактора то все кажется очевидным. Но это не так. Валидны следующие точки входа:

    • Endpoint (webapp)
    • Меню
    • Редактор скриптов
    • Триггер


    Ситуация, когда весь код находится в одном проекте не рассматривается.

    Соответственно, теперь вам необходимо выбрать, какой путь выбрать. Очевидно вызов из меню и редактор скриптов отпадает. Остается либо для каждого скрипта опубликовать веб-приложение и вызывать их из одного места, либо настроить для каждого проекта триггер, который будет инициализироваться при схождении определенных условий.
    Ответ написан
    Комментировать
  • Как спуститься на одну ячейку вниз после нахождения последней заполненной?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Попробуйте offset

    ...
    spreadsheet.getCurrentCell().offset(1, 0).activate();
    Ответ написан
  • Как в скрипте присвоить кнопкам функций из других скриптов?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Никак.

    Необходимо либо копировать и поддерживать копии кода, либо использовать внешние компоновщики.
    Ответ написан
    Комментировать
  • Как скопировать скриптом формулу?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вам нужно

    /**
     * User action. Runs the snippet
     */
    function run2() {
      const sheet = SpreadsheetApp.getActiveSheet();
      const base = sheet.getRange('J3:J');
      const colFormula = sheet.getRange('J3');
      draggDownOneFormula_(base, colFormula);
    }
    
    /**
     * @param {GoogleAppsScript.Spreadsheet.Range} base
     * @param {GoogleAppsScript.Spreadsheet.Range} colFormula
     */
    function draggDownOneFormula_(base, colFormula) {
      const baseValues = base.getValues();
      const lastBase =
        baseValues.length - baseValues.reverse().findIndex(row => row[0] !== '');
      const colFormulaFormula = colFormula.getFormula();
      colFormula
        .getSheet()
        .getRange(base.getRow(), colFormula.getColumn(), lastBase + 1)
        .setFormula(colFormulaFormula);
    }


    Сниппет
    Ответ написан
  • Как протянуть скриптом формулу по столбцу "J:J", до последней заполненной ячейки ориентируясь на столбец "C:C"?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вам нужно что-то типа такого

    /**
     * @file Dragg down a formula
     * @url https://qna.habr.com/q/709715
     * */
    
    /**
     * User action. Runs the snippet
     */
    function run() {
      const sheet = SpreadsheetApp.getActiveSheet();
      const base = sheet.getRange('C3:C');
      const colFormula = sheet.getRange('J3');
      draggDownFormulas_(base, colFormula);
    }
    
    /**
     * @param {GoogleAppsScript.Spreadsheet.Range} base
     * @param {GoogleAppsScript.Spreadsheet.Range} colFormula
     */
    function draggDownFormulas_(base, colFormula) {
      const baseValues = base.getValues();
      const lastBase =
        baseValues.length - baseValues.reverse().findIndex(row => row[0] !== '');
      const colFormulaFormula = colFormula.getFormula();
      colFormula
        .getSheet()
        .getRange(base.getRow(), colFormula.getColumn(), lastBase)
        .setFormula(colFormulaFormula);
    }


    5e3c613d3eabc476526073.png

    Обратите внимание
    • проверка начинается с 3й строки
    • колонка C заполнена на одну строку ниже, чем массив данных
    • копируется формула из J3
    • чтобы код работал, вам нужно включить V8


    Сниппет
    Ответ написан
    2 комментария
  • Как вырезать данные из Таблицы со второй строки и вставить их в другой лист?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Ну, есть прям несколько способов. Какой выбрать - дело непростое. Начните со слайса

    function copyTabList() {
      var from = SpreadsheetApp.getActiveSheet();
      var fromValues = from.getDataRange().getValues();
      var fromData = fromValues.slice(1);
    
      var tss = SpreadsheetApp.openById('Id');
      var ts = tss.getSheetByName('Sheet3');
    
      ts.getDataRange()
        .offset(1, 0)
        .clearContent()
        .getRange(2, 1, fromData.length, fromData[0].length)
        .setValues(fromData);
    }


    А еще есть вариант с offset (я стал к нему склоняться из-за скорость работы)

    function copyTabList() {
      var from = SpreadsheetApp.getActiveSheet();
      var fromValues = from
        .getDataRange()
        .offset(1, 0)
        .getValues();
      var fromData = fromValues;
    
      var tss = SpreadsheetApp.openById('Id');
      var ts = tss.getSheetByName('Sheet3');
    
      ts.getDataRange()
        .offset(1, 0)
        .clearContent()
        .getSheet()
        .getRange(2, 1, fromData.length, fromData[0].length)
        .setValues(fromData);
    }
    Ответ написан
  • Как запустить скрипт по условию?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Никак. Такого события нет.

    У вас есть два-три выхода.
    • Например, вызывать после первой функции вторую
    • Или создавать мгновенный триггер, который будет вызван после его создания
    • Гонять вторую функцию в триггере, проверяя, появились ли изменения

    Если выйти за рамки скриптов
    • Проверять Drive Activity
    • Подписаться на Pub/Sub
    • Создать вотчер (это, наверное, не надо)
    Ответ написан
    Комментировать
  • Скопировать данные из одного листа таблицы в другую таблицу на лист в последнюю не заполненную строку?

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

    В данном случае необходимо создать два скрипта:
    1. Скрип клиента. Он так и будет отправлятьинформацию, которую вы получаете в onEdit
    2. Скрипт-сервер. Он должен получать данные от клиента и запускать функцию userActionsCopyToLogs уже у себя.


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

    В любом случае, копайте в сторону Google Apps Script Web Apps. Это должно помочь. Никакого лишенего кода, кроме отправки запроса на сервер и получения данных на сервере вам не нужно. Все остальное уже есть.

    С уважением.
    Ответ написан
  • Скопировать данные из одного листа на другой последнюю не заполненную строку?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Я бы сделал так

    /**
     *
     */
    function onOpen() {
      SpreadsheetApp.getUi()
        .createMenu('Custom menu')
        .addItem('Copy to logs', 'userActionsCopyToLogs')
        .addToUi();
    }
    
    /**
     *
     */
    function userActionsCopyToLogs() {
      var headers = ['Name', 'Number'];
    
      var from = SpreadsheetApp.getActiveSheet();
    
      if (from.getName() !== 'Sheet1') {
        SpreadsheetApp.getActive().toast('Activate a range on "Sheet1"');
        return;
      }
    
      var fromValues = from.getDataRange().getValues();
    
      var fromHeaders = fromValues[0].map(function(h) {
        return headers.indexOf(h);
      });
    
      var activeRange = SpreadsheetApp.getActiveRange();
      var rowStart = activeRange.getRow();
      var rowEnd = activeRange.getLastRow();
      var fromData = fromValues
        .filter(function(row, i) {
          return i >= rowStart - 1 && i <= rowEnd - 1;
        })
        .map(function(row) {
          return row.filter(function(_, j) {
            return fromHeaders[j] > -1;
          });
        });
    
      var to =
        SpreadsheetApp.getActive().getSheetByName('Logs') ||
        SpreadsheetApp.getActive().insertSheet('Logs');
    
      to.getRange(to.getLastRow() + 1, 1, fromData.length, fromData[0].length)
        .setValues(fromData)
        .activate();
    }


    screenrecord.gif

    Измените строку var headers = ['Name', 'Number']; так, чтобы копировались данные из нужных вам колонок.

    Полный код
    Ответ написан
    Комментировать
  • Как отправить заявку с сайта на Битриксе в Google sheets?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Опубликуйте ваш скрипт от своего имени для всех в интернете.

    5e03728fc004c946730977.png

    Не забудьте, что при внесении изменений в скрипт вам придется публиковать его заново.
    Ответ написан
    1 комментарий
  • Как сделать проверку по определенному столбцу?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Если без объяснений кода, то

    function onEdit() {
       run2();
    }
    
    function run2() {
      /* Remove dash */
      var sheet = SpreadsheetApp.getActiveSheet();
      if (sheet.getName() === 'Журнал вода данных') return;
      var archive = SpreadsheetApp.getActive().getSheetByName('Журнал вода данных');
    
      var action = function(values, i, i2) {
        var data = values.slice(i, i + i2);
        archive
          .getRange(archive.getLastRow() + 1, 1, data.length, data[0].length)
          .setValues(data);
      };
    
      var condition = function(values, i) {
        var row = values[i];
        return (
          i > 0 && row[0] !== '' && row[1] !== '' && row[3] !== '' && row[5] !== ''
        );
      };
    
      deleteRowsByConditional_(sheet, condition, action);
    }
    
    function deleteRowsByConditional_(sheet, condition, action) {
      sheet
        .getDataRange()
        .getValues()
        .forEach(
          function(_, i, arr) {
            var j = arr.length - i - 1;
            if (this.condition.apply(null, [arr, j])) {
              this.isContinue++;
              if (j > 0) return;
            }
            if (this.isContinue > 0) {
              var prevPos = j + 1; // It's reversed
              if (action) action(arr, prevPos, this.isContinue);
              this.sheet.deleteRows(prevPos + 1, this.isContinue);
              this.isContinue = 0;
              return;
            }
            return;
          },
          { sheet: sheet, condition: condition, isContinue: 0 }
        );
    }


    Будет работать на любом листе вашей Таблицы. Можно подключить run2() к меню.

    Ссылка на видео https://www.facebook.com/oshliaer/videos/258791787...

    Ссылка на сниппет https://github.com/contributorpw/google-apps-scrip...
    Ответ написан
    Комментировать
  • Ошибка TypeError: Не удается прочитать свойство "postData" объекта undefined. (строка 3, файл Код)?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    В doPost(e) передается запрос от внешнего источника только тогда, когда ваш сервис опубликован и отправлен запрос POST.

    Чтобы тестировать эту чать, вам необходимо отправлять запросы на ваш эндпоинт, например, вот этим https://www.getpostman.com/

    Ту ошибку, которую вы наблюдаете, можно воспроизвести только одним способом - вызвать doPost из редактора кода.
    Ответ написан
    Комментировать
  • Как добавить вложения в письмо при рассылка писем из Таблицы?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Самое простое, это добавить body с сылкой на Диск

    MailApp.sendEmail({
      ...
      body: 'https://drive...',
      ...
    });


    Файл подтянется автоматически. Другой вопрос - это подключение файлов через аттачмент.

    Пример для PDF

    var file = DriveApp.getFilesByName('test123.pdf');
    if (file.hasNext()) {
        MailApp.sendEmail(emailAddress, subject, message, {
        attachments: [file.next().getAs(MimeType.PDF)],
        name: 'Automatic Emailer Script'
    }


    Мой код согласно комментариям

    function sendEmail() {
      var sheet = SpreadsheetApp.getActiveSheet();
      var activeRange = sheet.getActiveRange();
      var data = sheet
        .getRange(
          activeRange.getRow(),
          1,
          activeRange.getLastRow() - activeRange.getRow() + 1,
          6
        )
        .getValues();
    
      data.forEach(function(row) {
        var file = DriveApp.getFilesByName(row[5]);
        if (file.hasNext()) {
          var namesender = row[0];
          var emailAddress = row[1];
          var hiddencopy = row[2];
          var subject = row[3];
          var message = row[5];
          var information = row[4];
          var emailQuotaRemaining = MailApp.getRemainingDailyQuota();
          Logger.log('Remaining email quota: ' + emailQuotaRemaining);
    
          MailApp.sendEmail({
            to: emailAddress,
            bcc: hiddencopy,
            replyTo: 'supportmp@test.ru',
            name: namesender,
            subject: subject + ' ' + information,
            htmlBody: message + ' ' + https,
            attachments: [file.next().getAs(MimeType.PDF)],
          });
        } else {
          Logger.log('Файл для отправки не найден');
        }
      });
    }
    Ответ написан
    7 комментариев