Volond Apps
221 subscribers
216 photos
28 videos
22 files
994 links
Программирование для души и от лени
Добро пожаловать! У нас есть отличное сообщество любителей Excel и VBA, Google Sheet и Google Apps Script, а также всех тех, кто готов работать с этим программным обеспечением. Присоединяйтесь к нам, чтобы задавать и от
Download Telegram
Google Workspace Developers
Источник:
••••••••••
Обсудить:
Forwarded from DidacticСardsBot
Svelte Front-end Fun с AppsScripts

Источник:



Мне нравятся инструменты для написания, поэтому я считаю себя скорее «бэкенд-программистом», если бы мне захотелось. На самом деле я действительно хочу подчеркнуть, что действительно важно то, что оба «конца» должны согласовываться и работать вместе, что, возможно, означает, что я программист «полного стека».

В любом случае я должен работать в области JavaScript, учитывая контекст, в котором я работаю и в котором я строю. И JavaScript известен своей привлекательностью внешнего интерфейса: если вы хотите работать над пользовательским интерфейсом в браузере, вы говорите о разработке интерфейса JavaScript, и вместе с этой банкой червей приходит серия библиотек, фреймворков, зависимостей, инструментов варианты ... Я вижу много упоминаний о «хрюканье» и «грант» и смутно знаю, что они собой представляют ... но я не уделяю слишком много внимания этим вещам.

Я много читал о React и заинтересовался Vue, но у меня никогда не получалось. Затем я прочитал приличный обзор этого. Потом посмотрел несколько уроков . А потом я нашел статью, в которой говорилось о том, как использовать Svelte в контексте AppsScripts , в котором я живу.

ПОЧЕМУ SVELTE ТАК ВАЖЕН


Наблюдение, которое привлекло меня к Svelte больше, чем к другим фреймворкам, заключалось в том, насколько он был упрощен и в то же время предоставлял такие мощные абстракции, основанные на его простоте. Большинство вещей, которые я делаю, связаны с относительно простым взаимодействием с пользователем, например, надстройка с боковой панелью или что-то вроде простого щелчка-щелчка. Раньше я использовал инструмент с низким кодом, такой как AppMaker, потому что мне не требовалась тяжелая работа. Платформа, которую я использовал, была довольно простой и сдержанной.

Хотя меня интересовала технология, перспектива использования React и Vue была похожа на бензопилу, чтобы разрезать сыр. Между тем, Svelte в основном был тем, чем, как я думал, должен был быть написан сам HTML / CSS / JavaScript, если бы мы попробовали еще раз:
••••••••••
Обсудить:
Идентификатор родительской папки электронной таблицы приложений Google
Источник:

var spreadsheetId =  SpreadsheetApp.getActiveSpreadsheet().getId();
var spreadsheetFile = DriveApp.getFileById(spreadsheetId);
var folderId = spreadsheetFile.getParents().next().getId();

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

Правильный Код который не подвержен ошибке
Достигнут
конец итератора
Exception: Cannot retrieve the next object: iterator has reached the end.

function getParentFolder(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var file = DriveApp.getFileById(ss.getId());
var folders = file.getParents();
while (folders.hasNext()){
Logger.log('folder name = '+folders.next().getName());
Logger.log('folder name = '+folders.next().getId());
}
}


••••••••••
Обсудить:
#DriveApp #SpreadsheetApp
Копейка рубль бережет!

var num=123457.12546544
// С округлением
console.log(Math.round((num-Math.trunc(num))*100))
// Без округления
console.log(Math.trunc(num % 1 * 100))

#snippet #GAS #JS #customformula
Как создать триггер для запуска программы в назначенное время?

Источник:



Чтобы не терять триггерное время, а оно очень лимитировано для обычных аккаунтов, лучше всего использовать самовоспроизводящийся триггер


/**
* это то, что вы должны запустить один раз при первом запуске вашего триггера.
* Другие настройки не требуются
*/
function runOnce() {
trigger_();
}

/**
 * это и триггер и конфигурация вашего триггера
*/
function trigger_() {
try {
triggerAction();
} catch (error) {
console.error(error.message, error);
} finally {
var hours = 10;
var minutes = 17;
var seconds = 56;
var now = new Date();
var nextTime = new Date();
nextTime.setHours(0, 0, 24 * 3600 + hours * 3600 + minutes * 60 + seconds);
var delta = nextTime.getTime() - now.getTime();
ScriptApp.newTrigger('trigger_')
.timeBased()
.after(delta)
.create();
}
}

/**
* это то, что выполняет ваш скрипт
*/
function triggerAction() {
console.log("I'm fine");
}

triggerAction - это то, что выполняет ваш скрипт
runOnce - это то, что вы должны запустить один раз при первом запуске вашего триггера. Другие настройки не требуются
trigger_ - это и триггер и конфигурация вашего триггера

Такой триггер выполняется с высокой степенью точности, погрешность часто составляет меньше половины секунды.
••••••••••
Обсудить:
#JS #GAS #triggers
Облегчение функции IMPORTRANGE v.2.0

Источник:
При связи двух и более таблиц с помошью формулы importrange - при переходе определенного порога нагрузки (зависит от количества импортируемых ячеек а так же от количества символов в ячейках) мы сталкиваемся с ошибкой импорта, когда данные просто не импортируются или импортируются не всегда. Работать с данными становится проблематично. Для решения этой проблемы можно использовать следующий метод облегчения функции IMPORTRANGE. Метод позволяет импортировать примерно в 2 раза больше данных.
Суть метода состоит в 2-х аспектах:
1. Мы с помощью формулы импортируем ровно столько строк, сколько необходимо для импорта, не более того
2. Мы многократно уменьшаем количество импортируемых ячеек при том же количестве информации - с помощью сжатия данных до одного столбца.

На примере двух вкладок "импортируемые данные" и "экспорт" продемонстируем метод, действия описаны ниже:
#formula
Change queue time of Google spreadsheet app script trigger

Источник:

Ask Question


Если точность имеет первостепенное значение, вы можете вообще отказаться от использования триггеров сценариев приложений и вместо этого использовать сторонний инструмент.

Я бы рекомендовал использовать
cron-job.org . Эта служба может создавать задания cron, которые отправляют запросы POST на указанную вами конечную точку URL, и вы можете планировать время с точностью до минуты. Чтобы использовать его с Apps Script, реализуйте a doPost()для обработки почтовых запросов и развертывания вашего скрипта как веб-приложения. Затем вы создаете задание cron с помощью службы и передаете ему URL-адрес веб-приложения в качестве конечной точки.

Задание cron запускается в запланированное время, и вы можете выполнять любые необходимые операции внутри doPost()в ответ на входящий запрос POST.

Спасибо случайным деталям и Dimu Designs за руководство. На основе экспериментов вот ответы на мои вопросы:

Возможно ли, кроме наблюдения за ежедневной обработкой, определить точное время выполнения триггера, которое было назначено? Ответ: Ни в коем случае, кроме как путем наблюдения случайного времени запуска, назначенного в пределах запрошенного часового окна.
При случайном назначении можно ли создавать и удалять триггеры сценария, пока не будет получено приемлемо точное время выполнения? Ответ: Да. Я скорректировал назначенное мне время выполнения сценария, наблюдая за временем выполнения триггера (через временную метку сообщения электронной почты), а также удаляя, воссоздавая и наблюдая за случайно назначенным временем выполнения триггера, пока не получу приемлемую минуту в пределах запрошенного часового окна.
••••••••••
смотрите также
Обсудить:
#triggers #webapp
Forwarded from DidacticСardsBot
XPath Tester using Web Apps Created by Google Apps Script

Источник:



В этом посте я хотел бы представить тестер xpath, использующий веб-приложения, созданные с помощью скрипта Google Apps.


Применение


1. Подготовьте таблицу Google.
Создайте новую таблицу Google на своем Google Диске.

2. Включите API Таблиц.
Откройте редактор сценариев в созданной новой электронной таблице и включите API таблиц в расширенных службах Google .

3. Подготовьте образец сценария.
Скопируйте и вставьте следующий скрипт в редактор скриптов в созданную новую электронную таблицу и сохраните его.

function doGet(e) {
const url = e.parameter.url;
const xpath = e.parameter.xpath;
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheets()[0];
sheet.clear();
const range = sheet.getRange("A1");
range.setFormula(`=IMPORTXML("${url}";"${xpath}")`);
do {
SpreadsheetApp.flush();
Utilities.sleep(1000);
} while (range.getDisplayValue() == "Loading...");
const values = sheet.getDataRange().getDisplayValues();
const res = { url: url, xpath: xpath, values: values };
if (values[0][0] == "#N/A") {
const obj = Sheets.Spreadsheets.get(ss.getId(), {
ranges: [sheet.getSheetName()],
fields: "sheets",
});
const v = obj.sheets[0].data[0].rowData[0].values[0];
if (
v.hasOwnProperty("effectiveValue") &&
v.effectiveValue.hasOwnProperty("errorValue")
) {
res.error = v.effectiveValue.errorValue;
}
}
sheet.clear();
return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(
ContentService.MimeType.JSON
);
}


4. Разверните веб-приложения


Выберите «Я» для «Выполнить как» .
Пожалуйста , выберите «Любой» для «Кто имеет доступ»

С помощью этого параметра любой может получить доступ к вашим веб-приложениям. В данном случае это для тестирования этого скрипта.
Конечно, вы можете использовать токен доступа в этой ситуации. Но в этом случае в качестве простой настройки я использую ключ доступа вместо токена доступа.

Скопируйте URL-адрес веб-приложения. Это как https://script.google.com/macros/s/###/exec.
После изменения скрипта Google Apps повторно разверните его как новую версию. Таким образом, измененный сценарий отражается в веб-приложениях. Пожалуйста, будьте осторожны.

5. Тестирование.


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


$ curl -L 'https://script.google.com/macros/s/###/exec?url=https://tanaikech.github.io/index.xml&xpath=//channel/sample'


{
"url": "https://tanaikech.github.io/index.xml",
"xpath": "//channel/sample",
"values": [["#N/A"]],
"error": { "type": "N_A", "message": "Imported content is empty." }
}
••••••••••
Обсудить:
Forwarded from volond
https://xfanatical.com/blog/print-google-sheet-as-pdf-using-apps-script/
Возможные улучшения:
Экспорт листов как отделных таблиц Google или xlsx
Оправка через Gmail/Телеграмм/Вайбер
Генерация имени по шаблонным правилам


#GAS #SheetAPI #export #PDF
Объединить несколько PDF-файлов в один PDF-файл

Источник:

Задать вопрос


У меня такая же проблема, и я временно использую RestFul API для слияния PDF-файлов:
https://www.convertapi.com/pdf-to-merge

function merge() {
var folder = DriveApp.getFolderById('<ID FOLDER>'); // folder with files pdf
var files = folder.getFiles(); // get all files pdf

var formData = {};
var index = 0;
while(files.hasNext()) {
var file = files.next();
formData['Files[' + index + ']'] = file.getBlob();
index++;
}

var options = {
'method' : 'post',
'payload' : formData,
'muteHttpExceptions': true
};

var response = UrlFetchApp.fetch('https://v2.convertapi.com/pdf/to/merge?Secret=<YOUR SECRET>', options);

if(response.getResponseCode() == 200) {
var contentText = JSON.parse(response.getContentText());
var blob = Utilities.base64Decode(contentText.Files[0].FileData);
folder.createFile(Utilities.newBlob(blob, 'application/pdf', 'merge.pdf'));
}
}

Так что это больше, чем просто объединение данных из каждого файла. Фактически используемые данные для каждого файла «упакованы» с пометками и другим кодом (аналогично HTML и другим форматам документов). Фактически вам нужно декодировать каждый PDF-файл, объединить необходимые части, а затем перекодировать с новой «упаковкой». Для этого требуется практическое знание спецификаций и структуры PDF-файла, который можно бесплатно получить в Adobe здесь .

Я использовал эту информацию, чтобы написать сценарий, достаточный для моих нужд. Однако он не учитывает все возможности, поэтому, в частности, слияние любых документов, требующих PDF-1.4 и выше, потребует довольно большой работы.
https://pastiebin.com/601c57a966040
••••••••••
Обсудить:
Forwarded from DidacticСardsBot
Номер телефона

Источник:



Регулярное выражение для валидации номера телефона:


^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$

Ориентировано на российские мобильные + городские с кодом из 3 цифр (например, Москва).

Зеленый свет для:
+79261234567
89261234567
79261234567
+7 926 123 45 67
8(926)123-45-67
123-45-67
9261234567
79261234567
(495)1234567
(495) 123 45 67
89261234567
8-926-123-45-67
8 927 1234 234
8 927 12 12 888
8 927 12 555 12
8 927 123 8 123

••••••••••
Обсудить:
Forwarded from DidacticСardsBot
Отображение данных из GitHub API в Google Таблицах с помощью Apps Script и Oauth

Источник:



В этом посте показано, как подключить Google Sheet к API GitHub с помощью Oauth и Apps Script. Цель состоит в том, чтобы получить данные и информацию из GitHub и отобразить их в вашей таблице Google для дальнейшего анализа и визуализации.

Если вы управляете командой разработчиков или являетесь техническим менеджером проекта, то это может быть действительно полезным способом анализа и визуализации статистики кодирования вашей команды или проекта в сравнении с целями, такими как количество коммитов, языки, вовлеченные люди и т. Д. С течением времени. .
••••••••••
Обсудить:
function ins(){
var ss= SpreadsheetApp.getActive()
var sheet=ss.getActiveSheet()
var data=sheet.getDataRange().getValues()
for (i=data.length;i>1;i--){
console.log(i)
sheet.insertRowsAfter(i, 1);

}
}
function insAndCopy(){
var ss= SpreadsheetApp.getActive()
var sheet=ss.getActiveSheet()
var data=sheet.getDataRange().getValues()
for (i=data.length;i>1;i--){

var range = sheet.getRange(i,1,1,data[0].length);
sheet.insertRowsAfter(i, 1);
range.copyTo(sheet.getRange(i+1, 1, 1, data[0].length), {contentsOnly:false});
range.copyFormatToRange(sheet.getRange(i+1, 1, 1, data[0].length), {contentsOnly:false});

}
}
JavaScript-массивы: пересечение, разность и объединение в ES6

Источник:
Пересечение даст нам элементы, которые объединяют оба массива, в этом случае результат должен быть [1,5].

let intersection = arrA.filter(x => arrB.includes(x));


Разность будет выводить элементы из массива A, которых нет в массиве B. Результат будет [3,4].


let difference = arrA.filter(x => !arrB.includes(x));


В этом случае вы получите массив, содержащий все элементы arrA, которых нет в arrB, и наоборот, так что результат должен быть [2,3,4,6,7].


let difference = arrA.filter(x => !arrB.includes(x)).concat(arrB.filter(x => !arrA.includes(x)));


Объединение должно быть самым простым из них всех, в конце концов, результатом должны быть все элементы из A, все из B или оба, как это [1,2,3,4,5,6,7].

let union = [...arrA, ...arrB];


Но есть проблема в том, что мы получим дублированные элементы, так что теоретически это не объединение. Для этого мы можем использовать new Set():

let union = [...new Set([...arrA, ...arrB)];

••••••••••
Google Apps Script - новый день - новая функция
Источник:
Наряду с сервисом контента, теперь вы можете получить закодированную jSon версию скрипта любого модуля.


function doGet(e) {
return ContentService
.createTextOutput(JSON.stringify(mcpher.getMySource ( e )))
.setMimeType(ContentService.MimeType.JSON); ;
}

Код , который на это реагирует:


/**
* Returns a modules source code
* @param {parameters} e the argument to doGet(e). should have module parameter specified
* @param {scriptappinstance} sap an instance of scriptapp
* @return {object} The result.
*/
function getMySource(e) {
var results = {error : "missing module parameter"};
if (e.hasOwnProperty("parameter")) {
if (e.parameter.module) {
try {
var results =
{ code :
{ module : e.parameter.module, code : ScriptApp.getResource(e.parameter.module).getDataAsString() }
};
}
catch (err) {
var results = { error : "could not open module " + e.parameter.module} ;
}
}
}
return results;
}

function testGetSource() {
Logger.log( getMySource ( {parameter: { module: "cRest" }}));
}



Попробуйте и убедитесь. Итак, теперь вы можете получить исходный код любого модуля, создав doGet (), как указано выше, и скопировав код getMySource.

Я реализую это для сайтов Google и в других местах и опубликую здесь позже с результатами. Больше не нужно поддерживать исходный код в виде Gist и Apps Script!
••••••••••
Обсудить:
#GAS #ScriptApp
Forwarded from DidacticСardsBot
Function.caller

Источник:



Не стандартно
Эта возможность не является стандартной и стандартизировать её пока никто не собирается. Не используйте её на сайтах, смотрящих во внешний мир: она будет работать не у всех пользователей. Также могут присутствовать большие несовместимости между реализациями и её поведение может в будущем измениться.

function myFunc() {
if (myFunc.caller == null) {
return 'Эта функция была вызвана из верхнего уровня!';
} else {
return 'Эта функция была вызвана из ' + myFunc.caller;
}
}

••••••••••
Обсудить:
Object.assign VS Object Spread
Источник:

var assingDestruction(){
const obj = { foo: 1, bar: 1 };
console.log({ ...obj, baz: 1 })
}


Оператор распространения объекта {...obj}
похож на оператор Object.assign(), так какой из них следует использовать?
Оказывается, ответ немного более подробный, чем вы могли ожидать.


Краткий обзор Object Spread
Различия в сравнении Object.assign()



Операторы покоя / распространения объекта синтаксически аккуратны и предлагают преимущества в производительности Object.assign(). Если вы используете Node.js 8 или выше, попробуйте эти новые операторы и сделайте свой код более лаконичным.

••••••••••
Обсудить:
Краткий обзор Object Spread

Источник:



Основная идея оператора распространения объекта - создать новый простой объект,
используя
собственные свойства существующего объекта.
Таким образом
{...obj}
создается новый объект с теми же свойствами и значениями, что и obj.
Для
простых старых объектов JavaScript вы по сути создаете копию obj.


const obj = { foo: 'bar' };
const clone = { ...obj }; // `{ foo: 'bar' }`
obj.foo = 'baz';
clone.foo; // 'bar'

Например Object.assign(), оператор распространения объекта не копирует унаследованные свойства или информацию о классе.
Это
делает копирование символов ES6 .


class BaseClass {
foo() { return 1; }
}

class MyClass extends BaseClass {
bar() { return 2; }
}

const obj = new MyClass();
obj.baz = function() { return 3; };
obj[Symbol.for('test')] = 4;

// Does _not_ copy any properties from `MyClass` or `BaseClass`
const clone = { ...obj };

console.log(clone); // { baz: [Function], [Symbol(test)]: 4 }
console.log(clone.constructor.name); // Object
console.log(clone instanceof MyClass); // false

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


const obj = { a: 'a', b: 'b', c: 'c' };
{ a: 1, b: null, c: void 0, ...obj }; // { a: 'a', b: 'b', c: 'c' }
{ a: 1, b: null, ...obj, c: void 0 }; // { a: 'a', b: 'b', c: undefined }
{ a: 1, ...obj, b: null, c: void 0 }; // { a: 'a', b: null, c: undefined }
{ ...obj, a: 1, b: null, c: void 0 }; // { a: 1, b: null, c: undefined }
••••••••••
Обсудить: