feat(sync): add academic data reset tool, configurable sheets URL and fix sync timeout
This commit is contained in:
+114
-26
@@ -16,27 +16,57 @@ const MOCK_RECENT_SUBJECTS = [
|
||||
// 1. Google Sheets Import
|
||||
const handleGoogleSheetsImport = async (req, res, justSynced = false) => {
|
||||
try {
|
||||
let totalClassrooms = 31;
|
||||
let totalReservations = 19;
|
||||
let totalClassrooms = 0;
|
||||
let totalReservations = 0;
|
||||
let totalCareers = 0;
|
||||
let totalSubjects = 0;
|
||||
let totalCommissions = 0;
|
||||
let recentSubjects = [];
|
||||
let sheetsConfig = {
|
||||
active_url: 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSc_T_BQjbn3uPelioCgx52UM5Py-qNhJN0TYPd1kmsN5jdb3Q8rAaIvNMF_2ZTzQt6bH--yIWQKrKR/pub?single=true&output=csv',
|
||||
default_url: 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSc_T_BQjbn3uPelioCgx52UM5Py-qNhJN0TYPd1kmsN5jdb3Q8rAaIvNMF_2ZTzQt6bH--yIWQKrKR/pub?single=true&output=csv',
|
||||
source: 'default'
|
||||
};
|
||||
|
||||
try {
|
||||
const classRes = await req.apiClient.get('/classrooms');
|
||||
if (classRes.data && classRes.data.classrooms) {
|
||||
totalClassrooms = classRes.data.classrooms.length;
|
||||
const [classRes, resRes, carRes, subjRes, commRes, cfgRes] = await Promise.allSettled([
|
||||
req.apiClient.get('/classrooms'),
|
||||
req.apiClient.get('/reservations'),
|
||||
req.apiClient.get('/careers'),
|
||||
req.apiClient.get('/subjects'),
|
||||
req.apiClient.get('/commissions'),
|
||||
req.apiClient.get('/admin/sheets-config')
|
||||
]);
|
||||
|
||||
if (classRes.status === 'fulfilled' && classRes.value.data?.classrooms) {
|
||||
totalClassrooms = classRes.value.data.classrooms.length;
|
||||
}
|
||||
const resRes = await req.apiClient.get('/reservations');
|
||||
if (resRes.data && resRes.data.reservations) {
|
||||
totalReservations = resRes.data.reservations.length;
|
||||
if (resRes.status === 'fulfilled' && resRes.value.data?.reservations) {
|
||||
totalReservations = resRes.value.data.reservations.length;
|
||||
}
|
||||
if (carRes.status === 'fulfilled' && carRes.value.data?.careers) {
|
||||
totalCareers = carRes.value.data.careers.length;
|
||||
}
|
||||
if (subjRes.status === 'fulfilled' && subjRes.value.data?.subjects) {
|
||||
const subjs = subjRes.value.data.subjects;
|
||||
totalSubjects = subjs.length;
|
||||
recentSubjects = subjs.slice(0, 15);
|
||||
}
|
||||
if (commRes.status === 'fulfilled' && commRes.value.data?.commissions) {
|
||||
totalCommissions = commRes.value.data.commissions.length;
|
||||
}
|
||||
if (cfgRes.status === 'fulfilled' && cfgRes.value.data?.config) {
|
||||
sheetsConfig = cfgRes.value.data.config;
|
||||
}
|
||||
} catch (apiErr) {
|
||||
console.warn('Google Sheets import live stats fallback:', apiErr.message);
|
||||
}
|
||||
|
||||
const metrics = {
|
||||
total_careers: 5,
|
||||
total_subjects: 18,
|
||||
total_careers: totalCareers,
|
||||
total_subjects: totalSubjects,
|
||||
total_classrooms: totalClassrooms,
|
||||
total_commissions: 24,
|
||||
total_commissions: totalCommissions,
|
||||
total_reservations: totalReservations,
|
||||
sheets: [
|
||||
{ name: 'LUNES' },
|
||||
@@ -49,34 +79,53 @@ const handleGoogleSheetsImport = async (req, res, justSynced = false) => {
|
||||
};
|
||||
|
||||
let stats = null;
|
||||
let syncError = null;
|
||||
if (justSynced) {
|
||||
try {
|
||||
const syncRes = await req.apiClient.post('/dashboard/sync-sheets');
|
||||
const syncPayload = {};
|
||||
if (req.body && req.body.sheets_url) {
|
||||
syncPayload.sheets_url = req.body.sheets_url;
|
||||
}
|
||||
const syncRes = await req.apiClient.post('/dashboard/sync-sheets', syncPayload, { timeout: 90000 });
|
||||
if (syncRes.data && syncRes.data.stats) {
|
||||
stats = syncRes.data.stats;
|
||||
}
|
||||
// Refresh counts after sync
|
||||
try {
|
||||
const [cR, rR, crR, sR, cmR] = await Promise.allSettled([
|
||||
req.apiClient.get('/classrooms'),
|
||||
req.apiClient.get('/reservations'),
|
||||
req.apiClient.get('/careers'),
|
||||
req.apiClient.get('/subjects'),
|
||||
req.apiClient.get('/commissions')
|
||||
]);
|
||||
if (cR.status === 'fulfilled') metrics.total_classrooms = cR.value.data?.classrooms?.length || 0;
|
||||
if (rR.status === 'fulfilled') metrics.total_reservations = rR.value.data?.reservations?.length || 0;
|
||||
if (crR.status === 'fulfilled') metrics.total_careers = crR.value.data?.careers?.length || 0;
|
||||
if (sR.status === 'fulfilled') {
|
||||
metrics.total_subjects = sR.value.data?.subjects?.length || 0;
|
||||
recentSubjects = (sR.value.data?.subjects || []).slice(0, 15);
|
||||
}
|
||||
if (cmR.status === 'fulfilled') metrics.total_commissions = cmR.value.data?.commissions?.length || 0;
|
||||
} catch (e) {}
|
||||
} catch (err) {
|
||||
console.warn('Backend sync-sheets API notice:', err.message);
|
||||
stats = {
|
||||
total_rows_processed: 239,
|
||||
careers_created: 0,
|
||||
classrooms_created: 0,
|
||||
subjects_created: 0,
|
||||
commissions_created: 0,
|
||||
reservations_created: 0,
|
||||
errors: []
|
||||
};
|
||||
}
|
||||
if (stats && (!stats.errors || stats.errors.length === 0)) {
|
||||
stats.errors = null;
|
||||
console.error('Backend sync-sheets API error:', err.response?.data || err.message);
|
||||
syncError = err.response?.data?.message || err.message;
|
||||
}
|
||||
}
|
||||
|
||||
const flashMsg = req.query.msg || null;
|
||||
const flashType = req.query.type || 'info';
|
||||
|
||||
res.render('admin/import', {
|
||||
title: 'Import from Google Sheets - Edu-Space Admin',
|
||||
metrics,
|
||||
stats,
|
||||
recent_subjects: MOCK_RECENT_SUBJECTS
|
||||
sync_error: syncError,
|
||||
sheets_config: sheetsConfig,
|
||||
recent_subjects: recentSubjects.length > 0 ? recentSubjects : MOCK_RECENT_SUBJECTS,
|
||||
flash_msg: flashMsg,
|
||||
flash_type: flashType
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error rendering google sheets import:', error.message);
|
||||
@@ -87,6 +136,45 @@ const handleGoogleSheetsImport = async (req, res, justSynced = false) => {
|
||||
router.get(['/google_sheets_import', '/import'], (req, res) => handleGoogleSheetsImport(req, res, false));
|
||||
router.post(['/google_sheets_import', '/import'], (req, res) => handleGoogleSheetsImport(req, res, true));
|
||||
|
||||
// Guardar configuración del enlace de Google Sheets
|
||||
router.post('/google_sheets_config', async (req, res) => {
|
||||
try {
|
||||
const url = (req.body.google_sheets_url || req.body.url || '').trim();
|
||||
if (!url) {
|
||||
return res.redirect('/admin/google_sheets_import?msg=La+URL+no+puede+estar+vac%C3%ADa&type=danger');
|
||||
}
|
||||
await req.apiClient.post('/admin/sheets-config', { url });
|
||||
res.redirect('/admin/google_sheets_import?msg=Configuraci%C3%B3n+de+Google+Sheets+actualizada+correctamente&type=success');
|
||||
} catch (err) {
|
||||
console.error('Error updating sheets config:', err.message);
|
||||
const errMsg = encodeURIComponent(err.response?.data?.message || 'Error al guardar la configuración');
|
||||
res.redirect(`/admin/google_sheets_import?msg=${errMsg}&type=danger`);
|
||||
}
|
||||
});
|
||||
|
||||
// Purga / Reinicio de datos académicos
|
||||
router.post('/reset_academic_data', async (req, res) => {
|
||||
try {
|
||||
const payload = {
|
||||
all: req.body.reset_all === 'true' || req.body.reset_all === 'on',
|
||||
reservations: req.body.reset_reservations === 'true' || req.body.reset_reservations === 'on',
|
||||
commissions: req.body.reset_commissions === 'true' || req.body.reset_commissions === 'on',
|
||||
subjects: req.body.reset_subjects === 'true' || req.body.reset_subjects === 'on',
|
||||
careers: req.body.reset_careers === 'true' || req.body.reset_careers === 'on',
|
||||
classrooms: req.body.reset_classrooms === 'true' || req.body.reset_classrooms === 'on'
|
||||
};
|
||||
|
||||
const purgeRes = await req.apiClient.post('/admin/reset-academic-data', payload);
|
||||
const del = purgeRes.data?.deleted || {};
|
||||
const summary = encodeURIComponent(`Datos reiniciados: ${del.reservations || 0} reservas, ${del.commissions || 0} comisiones, ${del.subjects || 0} materias, ${del.careers || 0} carreras, ${del.classrooms || 0} aulas.`);
|
||||
res.redirect(`/admin/google_sheets_import?msg=${summary}&type=warning`);
|
||||
} catch (err) {
|
||||
console.error('Error resetting academic data:', err.message);
|
||||
const errMsg = encodeURIComponent(err.response?.data?.message || 'Error al purgar los datos');
|
||||
res.redirect(`/admin/google_sheets_import?msg=${errMsg}&type=danger`);
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Occupancy Metrics
|
||||
const handleOccupancyMetrics = async (req, res) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user