From 7608e933347162bd74be07f15a80b909b8d51803 Mon Sep 17 00:00:00 2001 From: Gowtham M Date: Thu, 16 Apr 2026 14:08:23 +0530 Subject: [PATCH] IIP calculation corrected : GWM --- app/controllers/ipiDemo.controller.js | 219 ++++++++++++++++++ app/views/ipi-demo.ejs | 308 ++++++++++++++++++++++++++ server.js | 10 + 3 files changed, 537 insertions(+) create mode 100644 app/controllers/ipiDemo.controller.js create mode 100644 app/views/ipi-demo.ejs diff --git a/app/controllers/ipiDemo.controller.js b/app/controllers/ipiDemo.controller.js new file mode 100644 index 0000000..b7862d4 --- /dev/null +++ b/app/controllers/ipiDemo.controller.js @@ -0,0 +1,219 @@ +const db = require("../models"); + +const ManufacturingIpi = db.ManufacturingIpi; +const Isic2DigitIndices = db.Isic2DigitIndices; +const Isic3DigitIndices = db.Isic3DigitIndices; +const Isic4DigitIndices = db.Isic4DigitIndices; +const CalculationLog = db.CalculationLog; + +const formatNumber = (value) => { + if (value === null || value === undefined || value === "") return null; + const parsed = Number(value); + if (Number.isNaN(parsed)) return null; + return parsed.toFixed(2); +}; + +exports.renderPage = async (req, res) => { + const now = new Date(); + const year = Number(req.query.year) || now.getFullYear(); + const month = Number(req.query.month) || now.getMonth() + 1; + + return res.render("ipi-demo", { + title: "IPI Calculation Demo Dashboard", + defaultYear: year, + defaultMonth: month, + }); +}; + +exports.getData = 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 whereClause = { year, month }; + + const [manufacturing, isic2, isic3, isic4, itemCount, logs] = await Promise.all([ + ManufacturingIpi.findOne({ where: whereClause, raw: true }), + Isic2DigitIndices.findAll({ where: whereClause, order: [["isic_2digit_code", "ASC"]], raw: true }), + Isic3DigitIndices.findAll({ where: whereClause, order: [["isic_3digit_code", "ASC"]], raw: true }), + Isic4DigitIndices.findAll({ where: whereClause, order: [["isic_4digit_code", "ASC"]], raw: true }), + db.sequelize.query( + `SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND month = ?`, + { replacements: [year, month], type: db.Sequelize.QueryTypes.SELECT } + ), + CalculationLog.findAll({ + where: { reference_year: year, reference_month: month }, + order: [["started_at", "DESC"]], + raw: true, + }), + db.sequelize.query( + `SELECT product_id, product_hs_code, base_year, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\` + FROM base_year_production + WHERE base_year = 2022 + ORDER BY product_id + LIMIT 25`, + { type: db.Sequelize.QueryTypes.SELECT } + ), + db.sequelize.query( + `SELECT product_id, product_hs_code, year, month, month_name, production_quantity, unit + FROM monthly_production + WHERE year = ? AND month = ? + ORDER BY product_id + LIMIT 50`, + { replacements: [year, month], type: db.Sequelize.QueryTypes.SELECT } + ), + ]); + + return res.json({ + success: true, + year, + month, + summary: { + item_count: Number(itemCount[0]?.total || 0), + isic4_count: isic4.length, + isic3_count: isic3.length, + isic2_count: isic2.length, + }, + manufacturing: manufacturing + ? { + ...manufacturing, + total_weight: formatNumber(manufacturing.total_weight), + weighted_index_sum: formatNumber(manufacturing.weighted_index_sum), + manufacturing_index: formatNumber(manufacturing.manufacturing_index), + mom_change: formatNumber(manufacturing.mom_change), + yoy_change: formatNumber(manufacturing.yoy_change), + } + : null, + isic2: isic2.map((row) => ({ + ...row, + total_weight: formatNumber(row.total_weight), + weighted_index_sum: formatNumber(row.weighted_index_sum), + isic_2digit_index: formatNumber(row.isic_2digit_index), + })), + isic3: isic3.map((row) => ({ + ...row, + total_weight: formatNumber(row.total_weight), + weighted_index_sum: formatNumber(row.weighted_index_sum), + isic_3digit_index: formatNumber(row.isic_3digit_index), + })), + isic4: isic4.map((row) => ({ + ...row, + total_weight: formatNumber(row.total_weight), + weighted_index_sum: formatNumber(row.weighted_index_sum), + isic_4digit_index: formatNumber(row.isic_4digit_index), + })), + logs, + }); + } catch (error) { + return res.status(500).json({ + success: false, + message: "Failed to load IPI demo data", + error: error.message, + }); + } +}; + +exports.getTableData = async (req, res) => { + try { + const table = String(req.query.table || ""); + const year = Number(req.query.year); + const month = Number(req.query.month); + const baseYear = Number(req.query.baseYear) || 2022; + const page = Math.max(1, Number(req.query.page) || 1); + const pageSize = Math.min(200, Math.max(1, Number(req.query.pageSize) || 50)); + const search = String(req.query.search || "").trim(); + const offset = (page - 1) * pageSize; + + const queryConfig = { + monthly: { + whereSql: `year = ? AND month = ?`, + whereParams: [year, month], + searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ? OR month_name LIKE ?)` : "", + searchParams: search ? [`%${search}%`, `%${search}%`, `%${search}%`] : [], + countSql: `SELECT COUNT(*) AS total FROM monthly_production WHERE year = ? AND month = ?`, + dataSql: `SELECT product_id, product_hs_code, year, month, month_name, production_quantity, unit + FROM monthly_production + WHERE year = ? AND month = ?`, + orderSql: ` ORDER BY product_id`, + }, + baseyear: { + whereSql: `base_year = ?`, + whereParams: [baseYear], + searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ?)` : "", + searchParams: search ? [`%${search}%`, `%${search}%`] : [], + countSql: `SELECT COUNT(*) AS total FROM base_year_production WHERE base_year = ?`, + dataSql: `SELECT product_id, product_hs_code, base_year, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\` + FROM base_year_production + WHERE base_year = ?`, + orderSql: ` ORDER BY product_id`, + }, + items: { + whereSql: `year = ? AND month = ?`, + whereParams: [year, month], + searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ?)` : "", + searchParams: search ? [`%${search}%`, `%${search}%`] : [], + countSql: `SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND month = ?`, + dataSql: `SELECT product_id, product_hs_code, current_production, base_year_avg_production, production_relative, item_index + FROM item_level_indices + WHERE year = ? AND month = ?`, + orderSql: ` ORDER BY product_id`, + }, + }; + + if (!queryConfig[table]) { + return res.status(400).json({ success: false, message: "Invalid table parameter" }); + } + + if ((table === "monthly" || table === "items") && (!year || !month || month < 1 || month > 12)) { + return res.status(400).json({ success: false, message: "Valid year and month are required" }); + } + + const cfg = queryConfig[table]; + const whereParams = [...cfg.whereParams, ...cfg.searchParams]; + + const [countResult] = await db.sequelize.query( + cfg.countSql + (search ? cfg.searchSql.replace(" AND ", " AND ") : ""), + { replacements: whereParams, type: db.Sequelize.QueryTypes.SELECT } + ); + + const rows = await db.sequelize.query( + cfg.dataSql + cfg.searchSql + cfg.orderSql + " LIMIT ? OFFSET ?", + { replacements: [...whereParams, pageSize, offset], type: db.Sequelize.QueryTypes.SELECT } + ); + + const formattedRows = rows.map((row) => { + const copy = { ...row }; + Object.keys(copy).forEach((k) => { + if ( + ["production_quantity", "avg_by_production", "current_production", "base_year_avg_production", "production_relative", "item_index", "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"].includes(k) + ) { + copy[k] = formatNumber(copy[k]); + } + }); + return copy; + }); + + return res.json({ + success: true, + table, + page, + pageSize, + total: Number(countResult.total || 0), + rows: formattedRows, + }); + } catch (error) { + return res.status(500).json({ + success: false, + message: "Failed to load table data", + error: error.message, + }); + } +}; + diff --git a/app/views/ipi-demo.ejs b/app/views/ipi-demo.ejs new file mode 100644 index 0000000..f3876a7 --- /dev/null +++ b/app/views/ipi-demo.ejs @@ -0,0 +1,308 @@ + + + + + + <%= title %> + + + +
+
+

IPI Calculation Demo Dashboard

+
Use Swagger to trigger calculation first, then view data here by year/month.
+
+ + + + +
+
+ +
+
+
Item-level rows-
+
ISIC 4-digit rows-
+
ISIC 3-digit rows-
+
ISIC 2-digit rows-
+
+
+ + + +
Manufacturing IndexMoM %YoY %Total WeightWeighted Index Sum
No data loaded.
+
+
+ +
+
+ + + + + + + +
+ + + +
+
+
+
+
+
+
+
+
+ + + + + diff --git a/server.js b/server.js index 7bb0467..3ad69b2 100644 --- a/server.js +++ b/server.js @@ -13,6 +13,7 @@ const verifySignature = require("./app/middleware/app.middleware"); const authController = require("./app/controllers/auth.controller"); const establishmentController = require("./app/controllers/establishment.controller"); const AutomatedSchedulerService = require('./app/services/scheduler.service'); +const ipiDemoController = require("./app/controllers/ipiDemo.controller"); require("dotenv").config(); @@ -299,6 +300,15 @@ app.get("/api/test", (req, res) => { const deploymentController = require("./app/controllers/deployment.controller"); app.post("/deploy", deploymentController.deployment); +/** + * ========================= + * IPI DEMO UI + * ========================= + */ +app.get("/ipi-demo", ipiDemoController.renderPage); +app.get("/ipi-demo/data", ipiDemoController.getData); +app.get("/ipi-demo/table-data", ipiDemoController.getTableData); + /** * =========================