diff --git a/app/controllers/ipiDemo.controller.js b/app/controllers/ipiDemo.controller.js index b7862d4..dc563fc 100644 --- a/app/controllers/ipiDemo.controller.js +++ b/app/controllers/ipiDemo.controller.js @@ -1,4 +1,5 @@ const db = require("../models"); +const IPICalculationService = require("../services/ipi_calculation_service"); const ManufacturingIpi = db.ManufacturingIpi; const Isic2DigitIndices = db.Isic2DigitIndices; @@ -6,6 +7,29 @@ const Isic3DigitIndices = db.Isic3DigitIndices; const Isic4DigitIndices = db.Isic4DigitIndices; const CalculationLog = db.CalculationLog; +const dbConfig = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + dialect: process.env.DB_DIALECT || "mysql", + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000, + }, +}; + +const CALCULATION_STEPS = [ + "monthly_production", + "item_level_indices", + "isic_4digit_indices", + "isic_3digit_indices", + "isic_2digit_indices", + "manufacturing_ipi", +]; + const formatNumber = (value) => { if (value === null || value === undefined || value === "") return null; const parsed = Number(value); @@ -217,3 +241,93 @@ exports.getTableData = async (req, res) => { } }; +exports.runMonthCalculation = async (req, res) => { + try { + const year = Number(req.body.year); + const month = Number(req.body.month); + + if (!year || !month || month < 1 || month > 12) { + return res.status(400).json({ + success: false, + message: "Valid year and month are required", + }); + } + + const service = new IPICalculationService(dbConfig); + const result = await service.runCompleteCalculation(year, month); + await service.close(); + + return res.json({ + success: true, + message: `Calculation completed for ${year}-${String(month).padStart(2, "0")}`, + data: result, + }); + } catch (error) { + return res.status(500).json({ + success: false, + message: "Failed to run month calculation", + error: error.message, + }); + } +}; + +exports.getRunStatus = async (req, res) => { + try { + const year = Number(req.query.year); + const month = Number(req.query.month); + + if (!year || !month || month < 1 || month > 12) { + return res.status(400).json({ + success: false, + message: "Valid year and month are required", + }); + } + + const logs = await CalculationLog.findAll({ + where: { + reference_year: year, + reference_month: month, + }, + order: [["started_at", "ASC"]], + raw: true, + }); + + const map = new Map(logs.map((l) => [l.calculation_type, l])); + const steps = CALCULATION_STEPS.map((step) => { + const row = map.get(step); + return { + step, + status: row?.status || "Pending", + records_processed: row?.records_processed ?? 0, + started_at: row?.started_at || null, + completed_at: row?.completed_at || null, + error_message: row?.error_message || null, + }; + }); + + const completedCount = steps.filter((s) => s.status === "Completed").length; + const failedStep = steps.find((s) => s.status === "Failed"); + const startedAny = steps.some((s) => s.status !== "Pending"); + const isFinished = completedCount === CALCULATION_STEPS.length || Boolean(failedStep); + + return res.json({ + success: true, + year, + month, + started: startedAny, + isFinished, + completedCount, + totalSteps: CALCULATION_STEPS.length, + progressPercent: Math.round((completedCount / CALCULATION_STEPS.length) * 100), + failedStep: failedStep?.step || null, + steps, + }); + } catch (error) { + return res.status(500).json({ + success: false, + message: "Failed to fetch run status", + error: error.message, + }); + } +}; + diff --git a/app/views/ipi-demo.ejs b/app/views/ipi-demo.ejs index f3876a7..471c108 100644 --- a/app/views/ipi-demo.ejs +++ b/app/views/ipi-demo.ejs @@ -28,6 +28,16 @@ .panel.active { display: block; } .pager { display: flex; gap: 8px; align-items: center; margin: 10px 0; flex-wrap: wrap; } .pager input { width: 220px; } + .run-monitor { margin-top: 12px; border: 1px solid #e5e7eb; border-radius: 8px; padding: 12px; background: #f8fafc; } + .progress { width: 100%; height: 10px; background: #e5e7eb; border-radius: 999px; overflow: hidden; margin: 10px 0; } + .progress > div { height: 100%; background: #2563eb; width: 0%; transition: width 0.3s ease; } + .step-grid { display: grid; grid-template-columns: repeat(3, minmax(220px, 1fr)); gap: 8px; } + .step-card { border: 1px solid #d1d5db; border-radius: 6px; padding: 8px; background: #fff; } + .badge { display: inline-block; font-size: 11px; padding: 2px 6px; border-radius: 999px; } + .badge-pending { background: #f3f4f6; color: #374151; } + .badge-started { background: #dbeafe; color: #1d4ed8; } + .badge-completed { background: #dcfce7; color: #166534; } + .badge-failed { background: #fee2e2; color: #991b1b; }
@@ -39,8 +49,18 @@ + + +