initial commit
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const sass = require('sass');
|
||||
|
||||
const app = express();
|
||||
const BASE = __dirname;
|
||||
const DATA_FILE = path.join(BASE, 'data', 'responses.json');
|
||||
const CSV_FILE = path.join(BASE, 'data', 'responses.csv');
|
||||
|
||||
// Compile SCSS on startup
|
||||
try {
|
||||
const result = sass.compile(path.join(BASE, 'src', 'styles', 'main.scss'), { style: 'compressed' });
|
||||
fs.writeFileSync(path.join(BASE, 'public', 'css', 'main.css'), result.css);
|
||||
console.log('SCSS compiled successfully');
|
||||
} catch (err) {
|
||||
console.error('SCSS compile error:', err.message);
|
||||
}
|
||||
|
||||
// Ensure data files exist
|
||||
if (!fs.existsSync(DATA_FILE)) fs.writeFileSync(DATA_FILE, JSON.stringify([], null, 2));
|
||||
if (!fs.existsSync(CSV_FILE)) fs.writeFileSync(CSV_FILE, buildCSVHeader() + '\n');
|
||||
|
||||
// ──────────────────────────────────────────
|
||||
// CSV helpers
|
||||
// ──────────────────────────────────────────
|
||||
function csvEscape(val) {
|
||||
const s = String(val ?? '');
|
||||
if (s.includes(',') || s.includes('"') || s.includes('\n')) {
|
||||
return '"' + s.replace(/"/g, '""') + '"';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function buildCSVHeader() {
|
||||
const cols = ['timestamp', 'country', 'age', 'gender'];
|
||||
for (let i = 1; i <= 15; i++) cols.push(`case_${i}_likert`);
|
||||
for (let i = 1; i <= 15; i++) cols.push(`case_${i}_order`);
|
||||
for (let i = 1; i <= 15; i++) cols.push(`case_${i}_ms`);
|
||||
cols.push('ahp_w_stability','ahp_w_revenue','ahp_w_capital','ahp_w_plan','ahp_w_social');
|
||||
cols.push('ahp_lambda_max','ahp_CI','ahp_CR','ahp_cr_warning');
|
||||
for (let i = 1; i <= 15; i++) cols.push(`match_case_${i}`);
|
||||
cols.push('post_q1','post_q2','post_q3','post_q4','open_response');
|
||||
return cols.join(',');
|
||||
}
|
||||
|
||||
function buildCSVRow(payload) {
|
||||
const fields = [];
|
||||
|
||||
fields.push(payload.meta.timestamp, csvEscape(payload.meta.country), csvEscape(payload.meta.age), csvEscape(payload.meta.gender));
|
||||
|
||||
// Case judgments ordered by case_id 1-15
|
||||
const byCase = {};
|
||||
(payload.intuitive_judgments || []).forEach(j => { byCase[j.case_id] = j; });
|
||||
for (let i = 1; i <= 15; i++) fields.push(byCase[i]?.judgment ?? '');
|
||||
for (let i = 1; i <= 15; i++) fields.push(byCase[i]?.presented_order ?? '');
|
||||
for (let i = 1; i <= 15; i++) fields.push(byCase[i]?.response_time_ms ?? '');
|
||||
|
||||
// AHP
|
||||
const ahp = payload.ahp || {};
|
||||
(ahp.weights || [0,0,0,0,0]).forEach(w => fields.push(Number(w).toFixed(4)));
|
||||
fields.push(Number(ahp.lambda_max).toFixed(4));
|
||||
fields.push(Number(ahp.CI).toFixed(4));
|
||||
fields.push(Number(ahp.CR).toFixed(4));
|
||||
fields.push(ahp.cr_warning_shown ? 1 : 0);
|
||||
|
||||
// Match by case_id
|
||||
const matchByCase = {};
|
||||
(payload.comparison || []).forEach(c => { matchByCase[c.case_id] = c.match; });
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
const m = matchByCase[i];
|
||||
fields.push(m === true ? 1 : m === false ? 0 : '');
|
||||
}
|
||||
|
||||
// Post survey
|
||||
const ps = payload.post_survey || {};
|
||||
fields.push(ps.q1 ?? '', ps.q2 ?? '', ps.q3 ?? '', ps.q4 ?? '');
|
||||
fields.push(csvEscape(ps.open_response ?? ''));
|
||||
|
||||
return fields.join(',');
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────
|
||||
// Routes
|
||||
// ──────────────────────────────────────────
|
||||
app.use(express.json({ limit: '1mb' }));
|
||||
app.use(express.static(path.join(BASE, 'public')));
|
||||
|
||||
app.post('/api/submit', (req, res) => {
|
||||
try {
|
||||
// Save JSON
|
||||
const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf8'));
|
||||
const submission = { ...req.body, server_timestamp: Date.now() };
|
||||
data.push(submission);
|
||||
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
|
||||
|
||||
// Append CSV row
|
||||
const row = buildCSVRow(submission);
|
||||
fs.appendFileSync(CSV_FILE, row + '\n');
|
||||
|
||||
res.json({ success: true, id: data.length });
|
||||
} catch (err) {
|
||||
console.error('Submit error:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/responses', (req, res) => {
|
||||
try {
|
||||
res.json(JSON.parse(fs.readFileSync(DATA_FILE, 'utf8')));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
|
||||
Reference in New Issue
Block a user