40236-vm/backend/src/routes/voterscope.js
Flatlogic Bot d9a7426dfa 1.1.1.0
2026-06-09 15:12:38 +00:00

131 lines
3.7 KiB
JavaScript

const express = require('express');
const db = require('../db/models');
const { wrapAsync } = require('../helpers');
const router = express.Router();
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
const searchableFields = [
'voter_number',
'national_id_number',
'name_bn',
'name_en',
'father_name',
'mother_name',
'spouse_name',
'address_line',
'district',
'upazila',
'union_ward',
'village_mohalla',
'polling_center',
'raw_text_snippet',
];
function cleanText(value, maxLength = 120) {
if (!value || typeof value !== 'string') return '';
return value.trim().slice(0, maxLength);
}
function buildSearchWhere(query) {
const q = cleanText(query.q);
const mode = ['all', 'name', 'voter'].includes(query.mode) ? query.mode : 'all';
const where = {};
if (q) {
if (mode === 'name') {
where[Op.or] = [
{ name_bn: { [Op.iLike]: `%${q}%` } },
{ name_en: { [Op.iLike]: `%${q}%` } },
{ father_name: { [Op.iLike]: `%${q}%` } },
{ mother_name: { [Op.iLike]: `%${q}%` } },
{ spouse_name: { [Op.iLike]: `%${q}%` } },
];
} else if (mode === 'voter') {
where[Op.or] = [
{ voter_number: { [Op.iLike]: `%${q}%` } },
{ national_id_number: { [Op.iLike]: `%${q}%` } },
];
} else {
where[Op.or] = searchableFields.map((field) => ({
[field]: { [Op.iLike]: `%${q}%` },
}));
}
}
const district = cleanText(query.district, 80);
const upazila = cleanText(query.upazila, 80);
const gender = cleanText(query.gender, 20);
if (district) where.district = { [Op.iLike]: `%${district}%` };
if (upazila) where.upazila = { [Op.iLike]: `%${upazila}%` };
if (['male', 'female', 'other', 'unknown'].includes(gender)) where.gender = gender;
return where;
}
router.get('/summary', wrapAsync(async (req, res) => {
const [totalRecords, totalPdfs, maleRecords, femaleRecords, areaRows, latestPdfs] = await Promise.all([
db.voter_records.count(),
db.pdf_documents.count(),
db.voter_records.count({ where: { gender: 'male' } }),
db.voter_records.count({ where: { gender: 'female' } }),
db.voter_records.findAll({
attributes: [
[Sequelize.fn('COALESCE', Sequelize.col('district'), 'অনির্ধারিত'), 'area'],
[Sequelize.fn('COUNT', Sequelize.col('voter_records.id')), 'count'],
],
group: [Sequelize.fn('COALESCE', Sequelize.col('district'), 'অনির্ধারিত')],
order: [[Sequelize.literal('count'), 'DESC']],
limit: 5,
raw: true,
}),
db.pdf_documents.findAll({
attributes: ['id', 'document_name', 'processing_status', 'uploaded_at', 'createdAt'],
order: [['createdAt', 'DESC']],
limit: 6,
raw: true,
}),
]);
res.status(200).send({
totals: {
voters: totalRecords,
pdfs: totalPdfs,
male: maleRecords,
female: femaleRecords,
},
areas: areaRows.map((row) => ({ area: row.area, count: Number(row.count) })),
latestPdfs,
});
}));
router.get('/search', wrapAsync(async (req, res) => {
const limit = Math.min(Number(req.query.limit) || 25, 100);
const page = Math.max(Number(req.query.page) || 0, 0);
const where = buildSearchWhere(req.query);
const payload = await db.voter_records.findAndCountAll({
where,
include: [
{
model: db.pdf_documents,
as: 'source_document',
attributes: ['id', 'document_name', 'processing_status'],
required: false,
},
],
distinct: true,
order: [['createdAt', 'DESC']],
limit,
offset: page * limit,
});
res.status(200).send({ rows: payload.rows, count: payload.count });
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;