From 27dcf4df73045209c1c2518cfa6a96282010d03e Mon Sep 17 00:00:00 2001 From: Andrea Date: Mon, 28 Feb 2022 15:18:54 +0100 Subject: [PATCH] [census][pl] aggregates --- locale/pl/config.suml | 50 +++++++++++++++++++++++++++++++++++++++++ server/routes/census.js | 27 +++++++++++++++++++++- src/sets.js | 48 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/sets.js diff --git a/locale/pl/config.suml b/locale/pl/config.suml index d06418ba..d08175ec 100644 --- a/locale/pl/config.suml +++ b/locale/pl/config.suml @@ -1473,6 +1473,24 @@ census: - ['liczba mnoga, rodzaj neutralny', '„byłośmy zmęczone”'] - ['liczba mnoga, rodzaj postpłciowy', '„byłuśmy zmęczone”'] - ['unikanie form nacechowanych płciowo', '„dopadło mnie zmęczenie”'] + aggregates: + binarne: + operation: 'OR' + values: ['rodzaj męski', 'rodzaj żeński'] + binarne_zamiennie: + operation: 'AND' + values: ['rodzaj męski', 'rodzaj żeński'] + wyłącznie_binarne: + operation: 'OR' + values: ['rodzaj męski', 'rodzaj żeński'] + exclusive: true + wyłącznie_niebinarne: + operation: 'OR' + values: ['rodzaj neutralny', 'liczba mnoga, rodzaj męskoosobowy', 'liczba mnoga, rodzaj niemęskoosobowy', 'liczba mnoga, rodzaj neutralny', 'liczba mnoga, rodzaj postpłciowy', 'unikanie form nacechowanych płciowo'] + exclusive: true + mnogie: + operation: 'OR' + values: ['liczba mnoga, rodzaj męskoosobowy', 'liczba mnoga, rodzaj niemęskoosobowy', 'liczba mnoga, rodzaj neutralny', 'liczba mnoga, rodzaj postpłciowy'] - type: 'checkbox' question: 'Jak piszesz o sobie / chcesz, by pisano o Tobie?' @@ -1517,6 +1535,29 @@ census: 'n'/je' - > „'n' jest fajn'”, „lubię je' zwierzątko” + aggregates: + binarne: + operation: 'OR' + values: ['on/jego', 'ona/jej'] + binarne_zamiennie: + operation: 'AND' + values: ['on/jego', 'ona/jej'] + wyłącznie_binarne: + operation: 'OR' + values: ['on/jego', 'ona/jej'] + exclusive: true + neutralne: + operation: 'OR' + values: ['ono/jego', 'ono/jej', 'ono/jejgo', 'ono/jeno', 'ono/jenu', 'vono/vego', 'ono/eno'] + postpłciowe: + operation: 'OR' + values: ['onu/jenu', 'onu/jejo', 'ne/nego'] + mnogie: + operation: 'OR' + values: ['oni/ich', 'one/ich', 'oni/e/ich', 'ona/ich', 'ony/ich', 'onie/ich'] + graficzne: + operation: 'OR' + values: ['onæ/jæ', 'on/a/jego/jej', 'onx/jex', 'on_/je_', 'on*/je*', 'onø/jenø'] - type: 'checkbox' question: 'Jakich form rzeczownikowych używasz wobec siebie?' @@ -1549,6 +1590,11 @@ census: - ['państwo (l. mn.)', '„czy chcieliby państwo…”'] - ['pań'] - ['panu'] + aggregates: + wyłącznie_ty: + operation: 'OR' + values: ['„per ty”'] + exclusive: true - type: 'checkbox' question: > @@ -1632,6 +1678,10 @@ census: optionsLast: - ['nie mam preferencji po angielsku'] - ['bez użycia zaimków (nounself, emojiself, …)'] + aggregates: + neozaimki: + operation: 'OR' + values: ['ae/aer', 'co/cos', 'e/em/eir', 'e/em/es', 'ey/em', 'fae/faer', 'hu/hum', 'ne/nem', 'ne/nir', 'per/per', 's/he/hir', 'thon/thons', 've/ver', 'vi/vir', 'xe/xem', 'ze/hir', 'ze/zir', 'zhe/zher'] - type: 'checkbox' question: 'Jakimi słowami opisujesz swoją płeć?' diff --git a/server/routes/census.js b/server/routes/census.js index 28386af5..216f37ac 100644 --- a/server/routes/census.js +++ b/server/routes/census.js @@ -4,6 +4,7 @@ import sha1 from 'sha1'; import {ulid} from "ulid"; import Papa from 'papaparse'; import {handleErrorAsync} from "../../src/helpers"; +import {intersection, difference} from "../../src/sets"; const getIp = req => { try { @@ -126,6 +127,21 @@ router.get('/census/count', handleErrorAsync(async (req, res) => { }); })); +const calculateAggregate = (config, answer) => { + const expected = new Set(config.values); + if (config.exclusive && difference(answer, expected).size > 0) { + return false; + } + switch (config.operation) { + case 'OR': + return intersection(expected, answer).size > 0; + case 'AND': + return intersection(expected, answer).size === expected.size; + default: + throw new Exception(`Operation "${config.operation} not supported"`); + } +} + router.get('/census/export', handleErrorAsync(async (req, res) => { if (!req.isGranted('census')) { return res.status(401).json({error: 'Unauthorised'}); @@ -146,8 +162,17 @@ router.get('/census/export', handleErrorAsync(async (req, res) => { let i = 0; for (let question of config.census.questions) { if (question.type === 'checkbox') { + const answerForAggregate = new Set(); for (let [option, comment] of question.options) { - answer[`${i}_${option}`] = (answers[i.toString()] || []).includes(option) ? 1 : ''; + const checked = (answers[i.toString()] || []).includes(option); + answer[`${i}_${option}`] = checked ? 1 : ''; + if (checked) { + answerForAggregate.add(option); + } + } + for (let aggr in question.aggregates || {}) { + if (!question.aggregates.hasOwnProperty(aggr)) { continue; } + answer[`${i}_aggr_${aggr}`] = calculateAggregate(question.aggregates[aggr], answerForAggregate) ? 1 : ''; } } else { answer[`${i}_`] = answers[i.toString()] || ''; diff --git a/src/sets.js b/src/sets.js new file mode 100644 index 00000000..7c3488e9 --- /dev/null +++ b/src/sets.js @@ -0,0 +1,48 @@ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set + +module.exports.isSuperset = (set, subset) => { + for (let elem of subset) { + if (!set.has(elem)) { + return false + } + } + return true +} + +module.exports.union = (setA, setB) => { + let _union = new Set(setA) + for (let elem of setB) { + _union.add(elem) + } + return _union +} + +module.exports.intersection = (setA, setB) => { + let _intersection = new Set() + for (let elem of setB) { + if (setA.has(elem)) { + _intersection.add(elem) + } + } + return _intersection +} + +module.exports.symmetricDifference = (setA, setB) => { + let _difference = new Set(setA) + for (let elem of setB) { + if (_difference.has(elem)) { + _difference.delete(elem) + } else { + _difference.add(elem) + } + } + return _difference +} + +module.exports.difference = (setA, setB) => { + let _difference = new Set(setA) + for (let elem of setB) { + _difference.delete(elem) + } + return _difference +}