IIP calculation corrected : GWM
This commit is contained in:
parent
7608e93334
commit
c3cac46cce
@ -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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -39,8 +49,18 @@
|
||||
<label>Year <input id="year" type="number" value="<%= defaultYear %>" min="2000" max="2100" /></label>
|
||||
<label>Month <input id="month" type="number" value="<%= defaultMonth %>" min="1" max="12" /></label>
|
||||
<button id="loadBtn">Load Data</button>
|
||||
<button id="runBtn">Run Month Calculation</button>
|
||||
<span id="status" class="muted"></span>
|
||||
</div>
|
||||
|
||||
<div class="run-monitor">
|
||||
<div class="row" style="justify-content: space-between;">
|
||||
<b>Live Run Monitor</b>
|
||||
<span id="runState" class="muted">Idle</span>
|
||||
</div>
|
||||
<div class="progress"><div id="runProgress"></div></div>
|
||||
<div id="stepGrid" class="step-grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
@ -96,6 +116,7 @@
|
||||
items: { page: 1, pageSize: 50, total: 0, search: "" },
|
||||
};
|
||||
let activeTab = "isic4";
|
||||
let pollTimer = null;
|
||||
|
||||
function tableHtml(columns, rows) {
|
||||
if (!rows || rows.length === 0) return '<div class="muted">No rows found.</div>';
|
||||
@ -179,6 +200,52 @@
|
||||
refreshTableControls();
|
||||
}
|
||||
|
||||
function statusBadge(status) {
|
||||
const s = String(status || "Pending");
|
||||
const lower = s.toLowerCase();
|
||||
if (lower === "completed") return `<span class="badge badge-completed">Completed</span>`;
|
||||
if (lower === "started") return `<span class="badge badge-started">Started</span>`;
|
||||
if (lower === "failed") return `<span class="badge badge-failed">Failed</span>`;
|
||||
return `<span class="badge badge-pending">Pending</span>`;
|
||||
}
|
||||
|
||||
function renderRunStatus(data) {
|
||||
document.getElementById("runProgress").style.width = `${data.progressPercent || 0}%`;
|
||||
const state = data.isFinished
|
||||
? (data.failedStep ? `Failed at ${data.failedStep}` : "Completed")
|
||||
: (data.started ? "Running..." : "Idle");
|
||||
document.getElementById("runState").textContent = `${state} (${data.completedCount || 0}/${data.totalSteps || 6})`;
|
||||
|
||||
const html = (data.steps || []).map((s) => `
|
||||
<div class="step-card">
|
||||
<div><b>${s.step}</b> ${statusBadge(s.status)}</div>
|
||||
<div class="muted">Records: ${s.records_processed ?? 0}</div>
|
||||
<div class="muted">Started: ${s.started_at || "-"}</div>
|
||||
<div class="muted">Completed: ${s.completed_at || "-"}</div>
|
||||
${s.error_message ? `<div class="err">Error: ${s.error_message}</div>` : ""}
|
||||
</div>
|
||||
`).join("");
|
||||
document.getElementById("stepGrid").innerHTML = html || '<div class="muted">No run status yet.</div>';
|
||||
}
|
||||
|
||||
async function fetchRunStatus() {
|
||||
const year = document.getElementById("year").value;
|
||||
const month = document.getElementById("month").value;
|
||||
try {
|
||||
const resp = await fetch(`/ipi-demo/run-status?year=${encodeURIComponent(year)}&month=${encodeURIComponent(month)}`);
|
||||
const data = await resp.json();
|
||||
if (!resp.ok || !data.success) throw new Error(data.message || "Status fetch failed");
|
||||
renderRunStatus(data);
|
||||
if (data.isFinished && pollTimer) {
|
||||
clearInterval(pollTimer);
|
||||
pollTimer = null;
|
||||
loadData();
|
||||
}
|
||||
} catch (err) {
|
||||
document.getElementById("runState").textContent = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
function getColumnsForTable(tab) {
|
||||
if (tab === "monthly") {
|
||||
return [
|
||||
@ -264,6 +331,30 @@
|
||||
}
|
||||
|
||||
document.getElementById("loadBtn").addEventListener("click", loadData);
|
||||
document.getElementById("runBtn").addEventListener("click", async () => {
|
||||
const year = Number(document.getElementById("year").value);
|
||||
const month = Number(document.getElementById("month").value);
|
||||
statusEl.textContent = "Starting run...";
|
||||
statusEl.className = "muted";
|
||||
if (pollTimer) clearInterval(pollTimer);
|
||||
pollTimer = setInterval(fetchRunStatus, 2500);
|
||||
try {
|
||||
const resp = await fetch("/ipi-demo/run-month", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ year, month }),
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!resp.ok || !data.success) throw new Error(data.message || "Run failed");
|
||||
statusEl.textContent = "Run completed";
|
||||
statusEl.className = "ok";
|
||||
await fetchRunStatus();
|
||||
await loadData();
|
||||
} catch (err) {
|
||||
statusEl.textContent = err.message;
|
||||
statusEl.className = "err";
|
||||
}
|
||||
});
|
||||
document.querySelectorAll(".tab").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
document.querySelectorAll(".tab").forEach((b) => b.classList.remove("active"));
|
||||
@ -302,6 +393,8 @@
|
||||
});
|
||||
|
||||
loadData();
|
||||
fetchRunStatus();
|
||||
pollTimer = setInterval(fetchRunStatus, 2500);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -308,6 +308,8 @@ app.post("/deploy", deploymentController.deployment);
|
||||
app.get("/ipi-demo", ipiDemoController.renderPage);
|
||||
app.get("/ipi-demo/data", ipiDemoController.getData);
|
||||
app.get("/ipi-demo/table-data", ipiDemoController.getTableData);
|
||||
app.get("/ipi-demo/run-status", ipiDemoController.getRunStatus);
|
||||
app.post("/ipi-demo/run-month", ipiDemoController.runMonthCalculation);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user