• Как автоматически выполнять скрипт каждые 5 минут на Apps Script?

    Fzero0
    @Fzero0
    Вечный студент
    Привет, тригерры можно создать двумя способами или в ручную
    или программно
    function myFunction() {
      Logger.log('1')
    }
    
    function createTimeDrivenTriggers() {
      ScriptApp.newTrigger('myFunction')
            .timeBased()
            .everyMinutes(1)
            .create();
    }

    6202b62c49f96736861078.png
    Одна проблема что у них есть ограничения. Общая продолжительность работы функций, запущенных триггером, не может превышать одного часа в день: это один из лимитов системы Apps Scripts. Если вызывать каждые 5 минут, получается 288 запусков в день, т.е. 12 секунд на каждый (в среднем). Предполагаю, что Ваш скрипт не укладывается в это время. Надо или оптимизировать, или запускать не так часто.
    Ответ написан
    1 комментарий
  • Как в apps script при вставке через setvalues Не перезаписывать данные ячейки, если в массиве значение пусто?

    ProgrammerForever
    @ProgrammerForever Куратор тега Google Apps Script
    Учитель, автоэлектрик, программист, музыкант
    1) Получить значения
    2) Заменить пустые значения в заменяемых данных на старые значения
    3) Записать значения
    oldValues = range.getValues();
    arr = arr.map((row,ri)=>row.map((val, ci)=>val===undefined?oldValues[ri][ci]:val));
    range.setValues(arr);
    Ответ написан
    Комментировать
  • Как создать таблицу, чтобы была взаимосвязь цена—кол-во—стоимость?

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

    Класс

    class Recalculator {
      constructor(calcs) {
        this.calcs = calcs;
      }
    
      calc(params, fix) {
        if (params.filter((p) => p === '' || isNaN(p)).length > 1)
          return params.map((p) => (isNaN(p) || p === '' ? '' : Number(p)));
        const index = params.map((p) => (isNaN(p) || p === '' ? '' : Number(p))).findIndex((p) => p === '');
        if (index === -1) return params.map((v, i) => (i === fix ? v : ''));
        const res = this.calcs[index](params);
        const out = [...params];
        out[index] = Number.isInteger(res) ? res : Number(res).toFixed(2);
        return out;
      }
    }


    Применение
    const recalc = new Recalculator([([_, b, c]) => c - b, ([a, _, c]) => c - a, ([a, b, _]) => a + b]);
        const range = sheet.getRange('B3:F3');
        const [a, _, b, __, c] = range.getValues()[0];
        const params = [a, b, c];
    
        const [a1, b1, c1] = recalc.calc(params, fix);
        range.setValues([[a1, _, b1, __, c1]]);


    64b19aeb57f0b767372525.gif

    Пример в Таблице https://docs.google.com/spreadsheets/d/1KTNtoZqQBp...
    Ответ написан
    Комментировать
  • Как в гугл таблицах сделать пример типа 2+3=5, что бы при изменении любого из 3 значений остальные подстраивались?

    BasiC2k
    @BasiC2k
    .NET developer (open to job offers)
    Скриптом.
    Скрипт отслеживает через onEdit изменение значения в 2 или 3 ячейке.
    Если данные изменены в 2 ячейке, они берутся для расчёта и результат вставляется в 3 ячейку.
    Если данные изменены в 3 ячейке, они берутся для расчёта и результат вставляется в 2 ячейку.
    В обоих случаях, для расчёта также используются значение из 1 ячейки.
    Ответ написан
    1 комментарий
  • Ajax в OpenCart 3 — как?

    ThunderCat
    @ThunderCat Куратор тега PHP
    {PHP, MySql, HTML, JS, CSS} developer
    $('#btn-account-edit-popup').on('click', function(e) {
    e.preventDefault();
    ...

    И по уму обработчик вешается на субмит формы, а не на клик кнопки. Почему - подумайте самостоятельно.
    Ответ написан
    1 комментарий
  • Как запустить триггер при редактировании определенной ячейки, определенного листа?

    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 комментарий
  • Как добавить список имён в диапазоны ячеек, которые находятся в разных местах Таблицы?

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

    function run() {
      const book = SpreadsheetApp.getActive();
      const data = book.getSheetByName('dataBase').getRange('A:A').getValues();
      const main = book.getSheetByName('main');
      ['E9:E13', 'J9:J13', 'E23:E27', 'J23:J27'].forEach(addr => {
        console.log(addr);
        const range = main.getRange(addr);
        range.setValues(data.splice(0, range.getNumRows()));
      });
    }


    Важно, чтобы число строк басы всегда было больше или равно заполняемым диапазонам.

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

    633737ff92e3c161305303.png
    Ответ написан
    5 комментариев
  • Как подружить VS Code + Remote SSH + WSL?

    bingo347
    @bingo347
    Crazy on performance...
    Ваша проблема в том, то Вы абсолютно не понимаете как работает WSL. Давайте немного объясню.

    Начиная с WSL2 подсистема Linux крутится в полноценной виртуальной машине. И винда кстати тоже. Это работает следующим образом: когда Вы запускаете свой компьютер первой загружается на самом деле не винда, а гипервизор HyperV. Для пользователя происходит все прозрачно, так как HyperV настроен сразу запускать 2 виртуальные машины - с виндой и с линем. В машину с виндой HyperV сразу прокидывает все Ваши устройства (видеокарту, USB и прочее). Так же HyperV поднимает виртуальную сеть между этими двумя виртуалками. В машине с линем еще не Ваша Ubuntu, там легковесная ОС состоящая только из ядра и оркестратора LXC контейнерами (с ней кстати можно по взаимодействовать по сети, как это делает Docker Desktop например, ну или утилита wsl.exe). Ваша Ubuntu запускается в контейнере, так попросту быстрее ее запускать и останавливать, ибо ядро всегда висит в памяти. Опционально в линь монтируются папки (диски) из винды, делается это посредством патча ядра Linux от Microsoft. Обратный доступ предоставляется из винды посредством сетевого ресурса wsl$.
    Главное что тут стоит понять - винда и линь в WSL по сути работают на разных компах, пусть и виртуальных.

    Теперь ответьте на вопрос, на каком из этих компов работает Ваш VSCode?
    Правильный ответ на винде. А значит и взаимодействовать он будет с виндой. И искать ключи для подключения к ssh будет в домашней папке юзера в винде.

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если вам не надо чтобы каждый человек одинаково часто работал с каждым другим, то подойдет обощение решения, предложенного в комментариях Alexandroppolus и Сергей Сергей.

    Допустим количество работ (N) и количество людей (M) взаимно просты.

    Тогда сгенерируйте M строчек беря людей подряд:
    1, 2, 3
    4, 5, 1
    2, 3, 4
    5, 1, 2
    3, 4, 5

    Тут каждый человек одинаковое количество раз (по одному разу) будет на каждой работе. И дни, когда он будет работать будут максимально равномерно распределены (минимальное расстояние и максимальное между соседними работами будут различаться максимум на 1 и равны floor(N/M) и ceil(N/M)). Это идеальное с точки зрения равенства расписание. Но у него минус - частоты пар работников будут не одинаковыми. 1 гораздо чаще будет работать с 2 и 5, чем с 3 и 4.

    Теперь, если N и M не взяимно просты. Пусть D = GCD(N,M) - наибольший общий делитель.

    Разбейте всех людей на D групп по N'=N/D человек. N' и M взяимно просты, поэтому можно применить алгоритм выше к каждой группе.

    Дальше эти D расписаний надо перемешать. Для максимальной равномерности - сначала взять все первые строки всех расписаний, потом все вторые, и т.д.

    На i-ом месте будет день i / N' из расписания i % N' (если индексация с 0).

    Так, например, решение для 2 работ и 6 людей:

    N' = 3. 2 группы.
    В первой:
    1 2
    3 1
    2 3

    Во второй:
    4 5
    6 4
    5 6

    В итоге:
    1 2
    4 5
    3 1
    6 4
    2 3
    5 6
    Ответ написан
    Комментировать
  • Как составить все возможные сочетания из элементов одного массива, но размер сочетания = N?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    const combinations = (arr, num) => {
      const result = [];
      const comb = (arr, n, idx, cur) => {
        for (let i = idx; i <= arr.length - n; i += 1) {
          cur.push(arr[i]);
          if (n === 1) {
            result.push([...cur]);
          } else {
            comb(arr, n - 1, i + 1, cur);
          }
          cur.pop();
        }
      };
      comb(arr, num, 0, []);
      return result;
    }
    console.log(combinations([1, 2, 3, 4], 3));
    // Array(4) [ (3) […], (3) […], (3) […], (3) […] ]
    ​//   0: Array(3) [ 1, 2, 3 ]
    //​   1: Array(3) [ 1, 2, 4 ]
    ​//   2: Array(3) [ 1, 3, 4 ]
    ​//   3: Array(3) [ 2, 3, 4 ]
    /​/   length: 4
    Ответ написан
    4 комментария
  • Как перемешать строки так, чтобы их значения повторялись не менее чем через 4 строки?

    ProgrammerForever
    @ProgrammerForever Куратор тега Google Sheets
    Учитель, автоэлектрик, программист, музыкант
    1) Отсортировать
    2) Взять в порядке 1-5-10...2-6-11...3-7-12...
    const a = [1,2,2,3,34,54,3,4,45,34,53,45,4,1,23,12,3,235,2,5,1,2,6,76,54,6,84,5,23,2,34,6,735];
    
      let outData = [];
      const n = 5; // Ширина блока
      for(let offset=0; offset<n; offset++){
        for(let i=offset; i<a.length; i+=n){
          outData.push(a[i]);
        };
      };
    
      Logger.log(JSON.stringify(outData));
    //Проверка на то, что все элементы исходного массива включены
      Logger.log(JSON.stringify(outData.sort()));
      Logger.log(JSON.stringify(a.sort()));
    Ответ написан
    2 комментария
  • Как в modx revo получить context_key текущего ресурса через PHP?

    DanArst
    @DanArst
    Гриффиндор в моде при любой погоде!
    $modx->context->key;
    Ответ написан
    2 комментария
  • Как прикрепить файл к письму о заказе в Opencart?

    @VVCh
    в библиотеке mail есть метод
    public function addAttachment($filename) {
    Ответ написан
    2 комментария
  • Как частично перенести данные из одной таблицы в другую mySQL?

    rozhnev
    @rozhnev Куратор тега MySQL
    Fullstack programmer, DBA, медленно, дорого
    The next query should to solve your problem.
    INSERT INTO blue_table (
        country_id,
        zone_id,
        geo_zone.id,
        date_added,
        date_modified
    ) SELECT
        country_id,
        zone_id,
        0 as geo_zone.id,
        now() as date_added,
        '0000-00-00 00:00:00' as date_modified
    FROM green_table;
    Ответ написан
    Комментировать
  • Как правильно передавать файл в контроллер в OpenCart 2.3?

    @amfetamine
    В исходниках все давным-давно реализовано
    $('button[id^=\'button-upload\']').on('click', function() {
    	var node = this;
    
    	$('#form-upload').remove();
    
    	$('body').prepend('<form enctype="multipart/form-data" id="form-upload" style="display: none;"><input type="file" name="file" /></form>');
    
    	$('#form-upload input[name=\'file\']').trigger('click');
    
    	if (typeof timer != 'undefined') {
        	clearInterval(timer);
    	}
    
    	timer = setInterval(function() {
    		if ($('#form-upload input[name=\'file\']').val() != '') {
    			clearInterval(timer);
    
    			$.ajax({
    				url: 'index.php?route=tool/upload',
    				type: 'post',
    				dataType: 'json',
    				data: new FormData($('#form-upload')[0]),
    				cache: false,
    				contentType: false,
    				processData: false,
    				beforeSend: function() {
    					$(node).button('loading');
    				},
    				complete: function() {
    					$(node).button('reset');
    				},
    				success: function(json) {
    					$('.text-danger').remove();
    
    					if (json['error']) {
    						$(node).parent().find('input').after('<div class="text-danger">' + json['error'] + '</div>');
    					}
    
    					if (json['success']) {
    						alert(json['success']);
    
    						$(node).parent().find('input').val(json['code']);
    					}
    				},
    				error: function(xhr, ajaxOptions, thrownError) {
    					alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
    				}
    			});
    		}
    	}, 500);
    });
    Ответ написан
    3 комментария
  • Как сделать админку MODX на двух языках?

    ruslan_aleev
    @ruslan_aleev
    MODX с cat-Art.ru
    Можно сделать 2 пользователя, и для каждого пользователя задать нужный язык, переопределяя системную настройку manager_language в разделе пользователя ("Пользователь" -> "Настройки"). И тогда переключать ничего не нужно будет.
    Ответ написан
    Комментировать
  • Как проверить содержит ли переменная любое значение из массива?

    @MaksPaverov
    $arr = array('141241241', '5463263', '124124124', '4254512');
    if (in_array("$id", $arr)) {
        echo "Найден ID - ".$id."<br>";
    }
    Ответ написан
    Комментировать
  • Элементы flex-box сжимаются даже с заданной шириной. Это нормальное поведение?

    Amirez
    @Amirez
    В чем секрет кота Бориса?
    Да, это нормально поведение флекс-элементов. Для переноса flex-wrap, как говорили выше(почти тоже что white-space). min-width тебе в помощь. Прочти для чего нужны flex-basis, flex-grow и flex-shrink(пригодится, если будешь делать липкий футер)
    Ответ написан
    Комментировать