Files
yourpart3/backend/scripts/fix-srs-immediate-due.js
Torsten Schulz (local) 4e33c3cb83
All checks were successful
Deploy to production / deploy (push) Successful in 2m9s
feat: aktualisiere SRS-Logik zur Verwendung von konfigurierbaren Zeitfenstern für die Auswahl und Aktualisierung von Elementen
2026-06-04 13:57:13 +02:00

76 lines
2.9 KiB
JavaScript

#!/usr/bin/env node
import { sequelize } from '../utils/sequelize.js';
/**
* Fix SRS items that were created with next_due_at equal-or-immediately-before created_at
* - Detect items created recently whose next_due_at is within a small window of created_at
* - Move next_due_at to the next calendar day at 08:00 local (safe default)
*
* Run on the server after a backup. Prints counts and example rows before/after.
*/
const DRY_RUN = process.env.DRY_RUN !== 'false';
const LOOKBACK_DAYS = parseInt(process.env.LOOKBACK_DAYS || '365', 10);
const WINDOW_SECONDS = parseInt(process.env.WINDOW_SECONDS || '10', 10);
const run = async () => {
try {
await sequelize.authenticate();
console.log('✅ DB connection OK');
// Selector: items created in the last LOOKBACK_DAYS where next_due_at is within WINDOW_SECONDS of created_at
const selector = `
SELECT id, item_key, course_id, stage, next_due_at, created_at
FROM community.vocab_srs_item
WHERE created_at >= now() - interval '${LOOKBACK_DAYS} days'
AND (
next_due_at = created_at
OR abs(extract(epoch from (next_due_at - created_at))) <= ${WINDOW_SECONDS}
)
ORDER BY created_at ASC
LIMIT 1000
`;
const recent = await sequelize.query(selector, { type: sequelize.QueryTypes.SELECT });
console.log('\nFound items matching heuristic: ', recent.length);
console.table(recent.slice(0, 20));
if (recent.length === 0) {
console.log(`\nNo candidate rows found for lookback ${LOOKBACK_DAYS} days and window ${WINDOW_SECONDS}s — nothing to do.`);
return;
}
// Compute update target: next calendar day at 08:00 based on created_at
// Use an UPDATE with an expression so each row gets next_due_at = date_trunc('day', created_at + '1 day') + '08:00'
const updateQuery = `
UPDATE community.vocab_srs_item
SET next_due_at = date_trunc('day', created_at + interval '1 day') + interval '8 hours'
WHERE created_at >= now() - interval '${LOOKBACK_DAYS} days'
AND (
next_due_at = created_at
OR abs(extract(epoch from (next_due_at - created_at))) <= ${WINDOW_SECONDS}
)
RETURNING id, item_key, course_id, stage, next_due_at, created_at;
`;
if (DRY_RUN) {
console.log('\nDRY RUN: to apply changes set ENV var DRY_RUN=false');
console.log('Preview of UPDATE SQL:\n', updateQuery);
} else {
console.log('\nApplying update...');
const updated = await sequelize.query(updateQuery, { type: sequelize.QueryTypes.SELECT });
console.log('\nUpdated rows: ', updated.length);
console.table(updated.slice(0, 20));
}
console.log('\nRecommendation: rerun `backend/scripts/diag-srs-stats.js` to verify totals afterwards.');
} catch (err) {
console.error('Error running fix script:', err);
process.exitCode = 2;
} finally {
try { await sequelize.close(); } catch (_) {}
}
};
run();