Недавно мы семьей погрузились в мир игротек и настольных игр. С детьми играем, в целом, давно, а вот взрослыми — как-то не приходилось. Ну и плюс, появилось свободное время — было принято решение: почему не попробовать DnD.
Как следствие, через VK нашел группу в телеграме одной известной московской компании, которая эти настолки продает, а так же проводит игротеки.
Записываться на игротеку нужно через гугл форму, что конечно же очень удобно.
Таких игротек в день проводят от одной до трех и они повторяются плюс-минус на еженедельной основе.
Итого максимум 15 форм.
Добрая половина вопросов в телеграм группе — есть ли еще места на игротеку и зарегистрирован ли тот или иной человек.
На вопросы отвечают админы группы — допускаю, что происходит открытие формы, или связанной с ней гугл таблицей с последующим поиском/просмотром того или иного контакта.
И вот тут я подумал — можно ли автоматизировать это всё? Ну например на какой-то периодической основе собирать все ответы из форм, где-то их консолидировать, а потом выводить в веб списком или чем-то подобным.
Предварительное ТЗ
Есть 15 форм, из них надо собирать данные, при условии, что данные могут (и будут) удаляться из формы — кто-то не может приехать, кто был ранее зарегистрирован или по факту окончания недели — надо очищать всю форму ради новой регистрации.
Собранные данные надо где-то хранить и обновлять.
Для вывода в веб данные надо изменять — например выводить не весь номер телефона, а только последние 4 цифры
Формы по игротекам — могут быть в любом количестве. Старые игры уходят, новые появляются, разработка должна быть универсальной.
Формы не должны быть связаны с таблицами, а если и да — то данные по игрокам должны забираться напрямую из формы.
Разработка типовой формы
Форма будет иметь заголовок в котором отражается дата и название игротеки.
Форма будет в себе иметь поля: Имя, email, телефон, согласие на обработку персональных данных.

Так как таких форм может быть сколь угодно, формы будут храниться в специальной папке:

Для тестового примера была взята одна игра и размножена 16 раз. Был изменен заголовок в форме, чтобы в дальнейшем отличать из какой именно формы приходит ответ.
Изначально была идея, что в форме будет скрипт, который будет запускаться при отправке формы, но потом пришлось отказаться от этой идеи — лучше отслеживать работу одного скрипта, чем, пусть и однотипных, но 15+.
В связи с этим был создан отдельный скрипт, в котором происходит обработка форм, сбор данных из них, сохранение данных для публикации в вебе.
Скрипт — получение всех id форм
Чтобы выиграть в лотерею сначала надо купить лотерейный билетик. Чтобы собирать данных из форм, надо знать сколько таких форм вообще есть.
const formType = "application/vnd.google-apps.form" // искомый тип файла, если в папке с формами будут и другие файлы, брать только этот
const consoTableId = "GSheetID" // Id таблицы где хранятся все данные
const folderId = "GFolderID" // Id папки, где лежат все формы
function getAllFormsIDInFolder() {
const currentFolder = DriveApp.getFolderById(folderId)
const allFilesInFolderArr = []
const allFiles = currentFolder.getFiles();
while (allFiles.hasNext()) {
let file = allFiles.next();
if (file.getMimeType() == formType) {
allFilesInFolderArr.push(file.getId())
}
}
return allFilesInFolderArr;
}
Так как теоретически в папке с формами может лежать что угодно еще — нужно получать id только форм. Понять что за тип файла помогает file.getMimeType()
, где у гул форм возвращается — application/vnd.google-apps.form
.
Именно с ним идет сравнение и если MimeType совпал — берется Id файла и заносится в массив:

Скрипт — получение всех ответов из всех форм
function getFormResponses() {
const allForResponsesArr = []
const formsID = getAllFormsIDInFolder()
for (let formID of formsID) {
let form = FormApp.openById(formID);
let formResponses = form.getResponses();
let formTitle = form.getTitle()
let responseArr = []
let itemsArr = []
for (let formResponse of formResponses) {
itemsArr.push(formTitle)
const itemResponses = formResponse.getItemResponses();
for (let itemResponse of itemResponses) {
itemsArr.push(itemResponse.getResponse())
}
responseArr.push(itemsArr)
itemsArr = []
}
allForResponsesArr.push(responseArr)
}
return (allForResponsesArr.flat())
}
let form = FormApp.openById(formID);
— открывается каждая форма
const itemResponses = formResponse.getItemResponses();
— получаю все актуальные на момент сбора данных ответы из формы для дальнейшего занесения данных в массив.
Чтобы отличать формы используется название в ФОРМЕ let formTitle = form.getTitle()
Результат:

В среднем каждая форма обрабатывается до 1 секунды, общее время обработки всех форм колеблется от 8 до 15 секунд в зависимости от воли гугл серверов.
Скрипт — текущая БД
Для хранения собранных выше данных используется гугл таблица
// Передача всех ответов в файл гугл таблицы
function setCurrentDB() {
const table = SpreadsheetApp.openById(consoTableId);
const ws = table.getSheets()[0]
const allPeople = getFormResponses();
if (allPeople.length > 0) {
ws.clear()
ws.getRange(1, 1, allPeople.length, allPeople[0].length).setValues(allPeople)
}
}
Со следующим результатом:

Где первый столбец — это название игротеки в форме
Второй — email зарегистрированного
Третий -Имя
Четвертый — Телефон
Пятый — согласие на обработку данных.
Ну, что, в целом все пользователи есть, можно их выводить в интернет, слегка причесав в целях сохранности персональных данных, а именно телефона.
doGet() — куда же без него
// doGet на API с обрезанными данными телефона
function doGet() {
const allPeople = SpreadsheetApp.openById(consoTableId).getSheets()[0].getDataRange().getValues()
const webArr = []
for (let elem of allPeople) {
let lastFourDigits = elem[3].toString().replace(/\s/g, '')
webArr.push([elem[0].toString(), elem[2].toString(), lastFourDigits.substring(lastFourDigits.length - 4)])
}
const contService = ContentService.createTextOutput((JSON.stringify(webArr)))
contService.setMimeType(ContentService.MimeType.JSON)
return contService
}
let lastFourDigits = elem[3].toString().replace(/\s/g, '')
— убираются возможные пробелы из телефона
lastFourDigits.substring(lastFourDigits.length - 4)
— оставляются последние 4 цифры телефона
Для постоянных читателей этого блога ничего нового не будет. При развернутом webapp наружу выдается массив «причесанных» данных:

Фронтенд
Как быстро и просто решить проблему предоставления данных пользователю?
Конечно через Vue для обработки данных и Bootstrap — для внешнего лоска.
Полный код html-css-js можно посмотреть здесь — https://codepen.io/DmitriiBeattle/full/dyQKoVj


При этом список выпадающего меню зависит от активных игротек и в случае изменения названия или дополнения новыми формами — так же будет меняться (именно для этого и используется title в форме):

Если же в форме удаляется чья-то регистрация, то при следующем сборе данных из всех форм — эти обновления так же учтутся. Как впрочем и учтутся вообще любые изменения формы, в том числе и добавления новых регистраций.
Ну и напоследок: для обновления данных в таблице необходимо использовать триггеры по времени, например запускать каждые 10 минут.
В результате получаем информационный сайт на котором видны все игротеки текущей недели с актуальными регистрациями.
Ваше мнение важно и может улучшить блог
Я хочу услышать ваше мнение и ваши идеи о том, как сделать этот сайт еще лучше. Примите участие в опросе, чтобы поделиться вашими пожеланиями, предложениями и замечаниями. Пройдите опрос сейчас и помогите сделать этот сайт более полезным для вас!
Добрый день! Подскажите, возможно управлять данными в гугл форме при ее заполнении. Например, при выборе в поле ФИО вывести в следующее поле список данных относящийся только к этому ФИО?
Андрей, добрый день.
Непосредственно с гугл формами не сталкивался с такой задачей, делал подобное на чистом вебе (веб апп) через vue.js