Загрузка данных
const SHEET_RESULTS = 'Результаты';
const CONFIG = {
title: 'Подростки о родителях (ПоР / ADOR)',
description: 'Оцените, насколько каждое утверждение соответствует воспитательным принципам вашей матери и вашего отца. 0 — не относится, 1 — частично, 2 — полностью.',
metaFields: [
{ key: 'name', label: 'ФИО', type: 'text' },
{ key: 'group', label: 'Группа', type: 'list',
options: ['ИС-21', 'ИС-22', 'ПИ-21', 'ПИ-22'] }
],
scales: {
poz_m: { name: 'Позитивный интерес (мать)', norm: { min: 8, max: 14 } },
dir_m: { name: 'Директивность (мать)', norm: { min: 10, max: 16 } },
hos_m: { name: 'Враждебность (мать)', norm: { min: 6, max: 12 } },
aut_m: { name: 'Автономность (мать)', norm: { min: 8, max: 14 } },
ned_m: { name: 'Непоследовательность (мать)', norm: { min: 8, max: 14 } },
poz_f: { name: 'Позитивный интерес (отец)', norm: { min: 8, max: 14 } },
dir_f: { name: 'Директивность (отец)', norm: { min: 10, max: 16 } },
hos_f: { name: 'Враждебность (отец)', norm: { min: 6, max: 12 } },
aut_f: { name: 'Автономность (отец)', norm: { min: 8, max: 14 } },
ned_f: { name: 'Непоследовательность (отец)', norm: { min: 8, max: 14 } }
},
questions: [
{ id: 1, text: 'Очень часто улыбается мне.', keys: { poz_m: 0 } },
{ id: 2, text: 'Категорически требует, чтобы я усвоил(а), что могу делать, а что нет.', keys: { dir_m: 0 } },
{ id: 3, text: 'Недостаточно терпелив(а) по отношению ко мне.', keys: { hos_m: 0 } },
{ id: 4, text: 'Когда я ухожу, сам(а) решает, когда я должен (должна) вернуться.', keys: { aut_m: 0 } },
{ id: 5, text: 'Всегда быстро забывает то, что сам(а) говорит или приказывает.', keys: { ned_m: 0 } },
{ id: 6, text: 'Когда у меня плохое настроение, советует мне успокоиться или развеселиться.', keys: { poz_m: 0 } },
{ id: 7, text: 'Считает, что для меня должно существовать много правил, которые я обязан(а) выполнять.', keys: { dir_m: 0 } },
{ id: 8, text: 'Постоянно на меня кому-то жалуется.', keys: { hos_m: 0 } },
{ id: 9, text: 'Предоставляет мне столько свободы, сколько мне надо.', keys: { aut_m: 0 } },
{ id: 10, text: 'За одно и то же один раз наказывает, а другой – прощает.', keys: { ned_m: 0 } },
{ id: 11, text: 'Очень любит делать что-нибудь вместе.', keys: { poz_m: 0 } },
{ id: 12, text: 'Если поручает мне какую-нибудь работу, то считает, что я должен (должна) делать только ее, пока не закончу.', keys: { dir_m: 0 } },
{ id: 13, text: 'Начинает сердиться и возмущаться по поводу любого пустяка, который я сделал(а).', keys: { hos_m: 0 } },
{ id: 14, text: 'Могу идти куда захочу и не спрашивать у него (нее) разрешения.', keys: { aut_m: 0 } },
{ id: 15, text: 'В зависимости от моего настроения отказывается от многих своих дел.', keys: { ned_m: 0 } },
{ id: 16, text: 'Когда мне грустно, пытается развеселить и воодушевить меня.', keys: { poz_m: 0 } },
{ id: 17, text: 'Всегда настаивает на том, что за все мои проступки я должен (должна) быть наказан(а).', keys: { dir_m: 0 } },
{ id: 18, text: 'Мало интересуется тем, что меня волнует и чего я хочу.', keys: { hos_m: 0 } },
{ id: 19, text: 'Если бы мне захотелось, то я мог(ла) бы идти куда захочу каждый вечер.', keys: { aut_m: 0 } },
{ id: 20, text: 'Имеет определенные правила, но иногда соблюдает их, а иногда – нет.', keys: { ned_m: 0 } },
{ id: 21, text: 'Всегда с пониманием выслушивает мои взгляды и мнения.', keys: { poz_m: 0 } },
{ id: 22, text: 'Следит за тем, чтобы я всегда делал(а) то, что мне сказано.', keys: { dir_m: 0 } },
{ id: 23, text: 'Иногда у меня возникает чувство, что я ему (ей) противен (противна).', keys: { hos_m: 0 } },
{ id: 24, text: 'Позволяет мне делать то, что нравится, и не требует ничего другого.', keys: { aut_m: 0 } },
{ id: 25, text: 'Отменяет свои требования, если я возражаю.', keys: { ned_m: 0 } },
{ id: 26, text: 'Старается меня поддержать, если у меня что-то не получается.', keys: { poz_m: 0 } },
{ id: 27, text: 'Требует от меня беспрекословного подчинения.', keys: { dir_m: 0 } },
{ id: 28, text: 'Считает, что я должен (должна) быть благодарен (благодарна) за все, что он (она) для меня делает.', keys: { hos_m: 0 } },
{ id: 29, text: 'Не вмешивается в мои дела, когда я этого не хочу.', keys: { aut_m: 0 } },
{ id: 30, text: 'Ведет себя по-разному по отношению ко мне в зависимости от настроения.', keys: { ned_m: 0 } },
{ id: 31, text: 'Всегда старается помочь мне в трудной ситуации.', keys: { poz_m: 0 } },
{ id: 32, text: 'Навязывает мне свою волю.', keys: { dir_m: 0 } },
{ id: 33, text: 'Часто бывает недоволен (недовольна) мной, когда я этого не заслуживаю.', keys: { hos_m: 0 } },
{ id: 34, text: 'Разрешает мне то, что другие родители запрещают.', keys: { aut_m: 0 } },
{ id: 35, text: 'Иногда бывает строг(а), а иногда – нет.', keys: { ned_m: 0 } },
{ id: 36, text: 'Умеет выразить свою признательность за то, что я делаю.', keys: { poz_m: 0 } },
{ id: 37, text: 'Контролирует каждый мой шаг.', keys: { dir_m: 0 } },
{ id: 38, text: 'Считает, что я не оправдываю его (ее) ожиданий.', keys: { hos_m: 0 } },
{ id: 39, text: 'Не ограничивает мою самостоятельность.', keys: { aut_m: 0 } },
{ id: 40, text: 'Его (ее) отношение ко мне меняется от случая к случаю.', keys: { ned_m: 0 } },
{ id: 41, text: 'Радуется моим успехам и огорчается неудачам.', keys: { poz_m: 0 } },
{ id: 42, text: 'Требует, чтобы я отчитывался (отчитывалась) перед ним (ней) за каждый поступок.', keys: { dir_m: 0 } },
{ id: 43, text: 'Никогда не хвалит меня.', keys: { hos_m: 0 } },
{ id: 44, text: 'Могу свободно высказывать свое мнение, даже если оно не совпадает с его (ее).', keys: { aut_m: 0 } },
{ id: 45, text: 'Иногда запрещает мне то, что разрешал(а) раньше.', keys: { ned_m: 0 } },
{ id: 46, text: 'Часто говорит мне, что я хороший (хорошая).', keys: { poz_m: 0 } },
{ id: 47, text: 'Устанавливает жесткие правила, которые я должен (должна) соблюдать.', keys: { dir_m: 0 } },
{ id: 48, text: 'Считает меня источником своих проблем.', keys: { hos_m: 0 } },
{ id: 49, text: 'Дает мне возможность самому (самой) решать, что делать.', keys: { aut_m: 0 } },
{ id: 50, text: 'Непредсказуем(а) в своих требованиях.', keys: { ned_m: 0 } },
{ id: 51, text: 'Очень часто улыбается мне.', keys: { poz_f: 0 } },
{ id: 52, text: 'Категорически требует, чтобы я усвоил(а), что могу делать, а что нет.', keys: { dir_f: 0 } },
{ id: 53, text: 'Недостаточно терпелив(а) по отношению ко мне.', keys: { hos_f: 0 } },
{ id: 54, text: 'Когда я ухожу, сам(а) решает, когда я должен (должна) вернуться.', keys: { aut_f: 0 } },
{ id: 55, text: 'Всегда быстро забывает то, что сам(а) говорит или приказывает.', keys: { ned_f: 0 } },
{ id: 56, text: 'Когда у меня плохое настроение, советует мне успокоиться или развеселиться.', keys: { poz_f: 0 } },
{ id: 57, text: 'Считает, что для меня должно существовать много правил, которые я обязан(а) выполнять.', keys: { dir_f: 0 } },
{ id: 58, text: 'Постоянно на меня кому-то жалуется.', keys: { hos_f: 0 } },
{ id: 59, text: 'Предоставляет мне столько свободы, сколько мне надо.', keys: { aut_f: 0 } },
{ id: 60, text: 'За одно и то же один раз наказывает, а другой – прощает.', keys: { ned_f: 0 } },
{ id: 61, text: 'Очень любит делать что-нибудь вместе.', keys: { poz_f: 0 } },
{ id: 62, text: 'Если поручает мне какую-нибудь работу, то считает, что я должен (должна) делать только ее, пока не закончу.', keys: { dir_f: 0 } },
{ id: 63, text: 'Начинает сердиться и возмущаться по поводу любого пустяка, который я сделал(а).', keys: { hos_f: 0 } },
{ id: 64, text: 'Могу идти куда захочу и не спрашивать у него (нее) разрешения.', keys: { aut_f: 0 } },
{ id: 65, text: 'В зависимости от моего настроения отказывается от многих своих дел.', keys: { ned_f: 0 } },
{ id: 66, text: 'Когда мне грустно, пытается развеселить и воодушевить меня.', keys: { poz_f: 0 } },
{ id: 67, text: 'Всегда настаивает на том, что за все мои проступки я должен (должна) быть наказан(а).', keys: { dir_f: 0 } },
{ id: 68, text: 'Мало интересуется тем, что меня волнует и чего я хочу.', keys: { hos_f: 0 } },
{ id: 69, text: 'Если бы мне захотелось, то я мог(ла) бы идти куда захочу каждый вечер.', keys: { aut_f: 0 } },
{ id: 70, text: 'Имеет определенные правила, но иногда соблюдает их, а иногда – нет.', keys: { ned_f: 0 } },
{ id: 71, text: 'Всегда с пониманием выслушивает мои взгляды и мнения.', keys: { poz_f: 0 } },
{ id: 72, text: 'Следит за тем, чтобы я всегда делал(а) то, что мне сказано.', keys: { dir_f: 0 } },
{ id: 73, text: 'Иногда у меня возникает чувство, что я ему (ей) противен (противна).', keys: { hos_f: 0 } },
{ id: 74, text: 'Позволяет мне делать то, что нравится, и не требует ничего другого.', keys: { aut_f: 0 } },
{ id: 75, text: 'Отменяет свои требования, если я возражаю.', keys: { ned_f: 0 } },
{ id: 76, text: 'Старается меня поддержать, если у меня что-то не получается.', keys: { poz_f: 0 } },
{ id: 77, text: 'Требует от меня беспрекословного подчинения.', keys: { dir_f: 0 } },
{ id: 78, text: 'Считает, что я должен (должна) быть благодарен (благодарна) за все, что он (она) для меня делает.', keys: { hos_f: 0 } },
{ id: 79, text: 'Не вмешивается в мои дела, когда я этого не хочу.', keys: { aut_f: 0 } },
{ id: 80, text: 'Ведет себя по-разному по отношению ко мне в зависимости от настроения.', keys: { ned_f: 0 } },
{ id: 81, text: 'Всегда старается помочь мне в трудной ситуации.', keys: { poz_f: 0 } },
{ id: 82, text: 'Навязывает мне свою волю.', keys: { dir_f: 0 } },
{ id: 83, text: 'Часто бывает недоволен (недовольна) мной, когда я этого не заслуживаю.', keys: { hos_f: 0 } },
{ id: 84, text: 'Разрешает мне то, что другие родители запрещают.', keys: { aut_f: 0 } },
{ id: 85, text: 'Иногда бывает строг(а), а иногда – нет.', keys: { ned_f: 0 } },
{ id: 86, text: 'Умеет выразить свою признательность за то, что я делаю.', keys: { poz_f: 0 } },
{ id: 87, text: 'Контролирует каждый мой шаг.', keys: { dir_f: 0 } },
{ id: 88, text: 'Считает, что я не оправдываю его (ее) ожиданий.', keys: { hos_f: 0 } },
{ id: 89, text: 'Не ограничивает мою самостоятельность.', keys: { aut_f: 0 } },
{ id: 90, text: 'Его (ее) отношение ко мне меняется от случая к случаю.', keys: { ned_f: 0 } },
{ id: 91, text: 'Радуется моим успехам и огорчается неудачам.', keys: { poz_f: 0 } },
{ id: 92, text: 'Требует, чтобы я отчитывался (отчитывалась) перед ним (ней) за каждый поступок.', keys: { dir_f: 0 } },
{ id: 93, text: 'Никогда не хвалит меня.', keys: { hos_f: 0 } },
{ id: 94, text: 'Могу свободно высказывать свое мнение, даже если оно не совпадает с его (ее).', keys: { aut_f: 0 } },
{ id: 95, text: 'Иногда запрещает мне то, что разрешал(а) раньше.', keys: { ned_f: 0 } },
{ id: 96, text: 'Часто говорит мне, что я хороший (хорошая).', keys: { poz_f: 0 } },
{ id: 97, text: 'Устанавливает жесткие правила, которые я должен (должна) соблюдать.', keys: { dir_f: 0 } },
{ id: 98, text: 'Считает меня источником своих проблем.', keys: { hos_f: 0 } },
{ id: 99, text: 'Дает мне возможность самому (самой) решать, что делать.', keys: { aut_f: 0 } },
{ id: 100, text: 'Непредсказуем(а) в своих требованиях.', keys: { ned_f: 0 } }
]
};
const ANSWER_OPTIONS = ['0', '1', '2'];
function normalizeConfig() {
if (!Array.isArray(CONFIG.metaFields)) {
CONFIG.metaFields = CONFIG.nameField
? [{ key: 'name', label: CONFIG.nameField, type: 'text' }]
: [];
}
if (!CONFIG.questions) CONFIG.questions = [];
if (!CONFIG.scales) CONFIG.scales = {};
}
function resolveForm_() {
const active = FormApp.getActiveForm();
if (active) {
PropertiesService.getScriptProperties().setProperty('FORM_ID', active.getId());
return active;
}
const props = PropertiesService.getScriptProperties();
const id = props.getProperty('FORM_ID');
if (id) {
try { return FormApp.openById(id); }
catch (e) { props.deleteProperty('FORM_ID'); }
}
throw new Error('Скрипт не привязан к форме. Открой Google Форму → ⋮ → Редактор скриптов и запусти setup().');
}
function setup() {
normalizeConfig();
const form = resolveForm_();
const ss = getOrCreateDestination_(form);
syncForm_(form);
ensureResultsSheet_(ss);
installTrigger_(form);
const info = {
formId: form.getId(),
formEditUrl: form.getEditUrl(),
formLiveUrl: form.getPublishedUrl(),
spreadsheetUrl: ss.getUrl()
};
Logger.log(JSON.stringify(info, null, 2));
return info;
}
function processExistingResponses() {
normalizeConfig();
const form = resolveForm_();
const ss = SpreadsheetApp.openById(form.getDestinationId());
ensureResultsSheet_(ss);
const responses = form.getResponses();
let count = 0;
responses.forEach(function (resp) {
const parsed = parseResponse_(resp.getItemResponses());
if (!parsed.meta.name) return;
const scores = calculateScores(parsed.answers);
const abnormal = checkAbnormal(scores);
writeResultRow_(form, parsed, scores, abnormal);
count++;
});
Logger.log('Обработано ответов: ' + count);
return count;
}
function getOrCreateDestination_(form) {
let destId = null;
try { destId = form.getDestinationId(); } catch (e) { destId = null; }
if (!destId) {
const ss = SpreadsheetApp.create(form.getTitle() + ' — результаты');
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
destId = ss.getId();
}
return SpreadsheetApp.openById(destId);
}
function syncForm_(form) {
form.setTitle(CONFIG.title).setDescription(CONFIG.description);
const expectedTitles = [
...CONFIG.metaFields.map(f => f.label),
...CONFIG.questions.map(q => '№' + q.id + '. ' + q.text)
];
const current = form.getItems();
const matches =
current.length === expectedTitles.length &&
current.every(function (it, i) { return it.getTitle() === expectedTitles[i]; });
if (matches) return false;
for (let i = current.length - 1; i >= 0; i--) form.deleteItem(current[i]);
CONFIG.metaFields.forEach(function (f) {
switch (f.type) {
case 'list':
form.addListItem().setTitle(f.label).setChoiceValues(f.options || []).setRequired(true);
break;
case 'paragraph':
form.addParagraphTextItem().setTitle(f.label).setRequired(true);
break;
case 'date':
form.addDateItem().setTitle(f.label).setRequired(true);
break;
default:
form.addTextItem().setTitle(f.label).setRequired(true);
}
});
CONFIG.questions.forEach(function (q) {
form.addMultipleChoiceItem()
.setTitle('№' + q.id + '. ' + q.text)
.setChoiceValues(ANSWER_OPTIONS)
.setRequired(true);
});
return true;
}
function ensureResultsSheet_(ss) {
let sheet = ss.getSheetByName(SHEET_RESULTS);
if (!sheet) sheet = ss.insertSheet(SHEET_RESULTS);
const scaleKeys = Object.keys(CONFIG.scales);
const header = [
'Дата',
...CONFIG.metaFields.map(f => f.label),
...scaleKeys.map(k => headerLabel_(CONFIG.scales[k])),
...CONFIG.questions.map(q => '№' + q.id)
];
const lastCol = Math.max(header.length, sheet.getLastColumn() || 1);
sheet.getRange(1, 1, 1, lastCol).clearContent();
sheet.getRange(1, 1, 1, header.length).setValues([header]);
sheet.getRange(1, 1, 1, header.length)
.setFontWeight('bold').setBackground('#4472c4').setFontColor('#ffffff')
.setHorizontalAlignment('center').setVerticalAlignment('middle').setWrap(true);
sheet.setFrozenRows(1);
sheet.setFrozenColumns(1 + CONFIG.metaFields.length);
sheet.setRowHeight(1, 40);
sheet.setColumnWidth(1, 130);
CONFIG.metaFields.forEach(function (_, i) { sheet.setColumnWidth(2 + i, 180); });
const scoreCol = 2 + CONFIG.metaFields.length;
scaleKeys.forEach(function (_, i) { sheet.setColumnWidth(scoreCol + i, 200); });
const rawCol = scoreCol + scaleKeys.length;
CONFIG.questions.forEach(function (_, i) { sheet.setColumnWidth(rawCol + i, 60); });
return sheet;
}
function headerLabel_(scale) {
if (scale.norm && scale.norm.min !== undefined && scale.norm.max !== undefined) {
return scale.name + ' (' + scale.norm.min + '–' + scale.norm.max + ')';
}
return scale.name;
}
function calculateScores(answers) {
const scores = {};
Object.keys(CONFIG.scales).forEach(function (k) { scores[k] = 0; });
CONFIG.questions.forEach(function (q) {
const userVal = answers[q.id];
if (userVal === undefined || userVal === null) return;
const points = Number(userVal);
if (isNaN(points)) return;
Object.keys(q.keys).forEach(function (sk) {
if (scores[sk] === undefined) scores[sk] = 0;
scores[sk] += points;
});
});
return scores;
}
function checkAbnormal(scores) {
const res = {};
Object.keys(CONFIG.scales).forEach(function (key) {
const s = CONFIG.scales[key];
const v = scores[key];
if (s.norm && s.norm.min !== undefined && s.norm.max !== undefined) {
res[key] = (v < s.norm.min) || (v > s.norm.max);
} else {
res[key] = false;
}
});
return res;
}
function installTrigger_(form) {
const exists = ScriptApp.getUserTriggers(form)
.some(function (t) { return t.getHandlerFunction() === 'onFormSubmit'; });
if (exists) return;
ScriptApp.newTrigger('onFormSubmit').forForm(form).onFormSubmit().create();
}
function onFormSubmit(e) {
try {
const form = e.source || FormApp.getActiveForm();
const parsed = parseResponse_(e.response.getItemResponses());
if (!parsed.meta.name) return;
const scores = calculateScores(parsed.answers);
const abnormal = checkAbnormal(scores);
writeResultRow_(form, parsed, scores, abnormal);
} catch (err) {
console.error('onFormSubmit error:', err.message, err.stack);
throw err;
}
}
function parseResponse_(responses) {
const out = { meta: {}, answers: {}, raw: {} };
const metaCount = CONFIG.metaFields.length;
responses.forEach(function (ir, i) {
const item = ir.getItem();
const title = item.getTitle();
const value = ir.getResponse();
if (i < metaCount) {
const field = CONFIG.metaFields[i];
if (field) out.meta[field.key] = (value === null || value === undefined)
? '' : String(value).trim();
return;
}
const m = title.match(/^№\s*(\d+)\./);
if (!m) return;
const qid = Number(m[1]);
const q = CONFIG.questions.filter(function (x) { return x.id === qid; })[0];
if (!q) return;
const text = (value === null || value === undefined) ? '' : String(value).trim();
out.answers[qid] = text;
out.raw[qid] = text;
});
return out;
}
function writeResultRow_(form, parsed, scores, abnormal) {
const ss = SpreadsheetApp.openById(form.getDestinationId());
const sheet = ensureResultsSheet_(ss);
const scaleKeys = Object.keys(CONFIG.scales);
const rowValues = [
new Date(),
...CONFIG.metaFields.map(function (f) { return parsed.meta[f.key] || ''; }),
...scaleKeys.map(function (k) { return scores[k]; }),
...CONFIG.questions.map(function (q) {
return parsed.raw[q.id] === undefined ? '' : parsed.raw[q.id];
})
];
sheet.appendRow(rowValues);
const rowIndex = sheet.getLastRow();
sheet.getRange(rowIndex, 1).setNumberFormat('dd.MM.yyyy HH:mm');
sheet.getRange(rowIndex, 1, 1, rowValues.length)
.setHorizontalAlignment('center').setVerticalAlignment('middle');
if (CONFIG.metaFields.length > 0) {
sheet.getRange(rowIndex, 2, 1, CONFIG.metaFields.length)
.setHorizontalAlignment('left');
}
const scoreStart = 2 + CONFIG.metaFields.length;
scaleKeys.forEach(function (key, i) {
const cell = sheet.getRange(rowIndex, scoreStart + i);
if (abnormal[key]) {
cell.setBackground('#ff0000').setFontColor('#ffffff').setFontWeight('bold');
} else {
cell.setBackground('#e8f5e9').setFontColor('#000000').setFontWeight('normal');
}
});
}
function getUrls() {
const form = resolveForm_();
const ss = getOrCreateDestination_(form);
return {
formEdit: form.getEditUrl(),
formLive: form.getPublishedUrl(),
sheetUrl: ss.getUrl()
};
}
function rebuildForm() {
normalizeConfig();
const form = resolveForm_();
syncForm_(form);
return getUrls();
}