| |
| |
| |
| |
| |
| |
|
|
| const express = require('express'); |
| const router = express.Router(); |
| const ml = require('../utils/alwas-ml-client'); |
| const Block = require('../models/Block'); |
| const { isAuthenticated, isManager } = require('../middleware/auth'); |
|
|
| |
| |
| |
| |
| |
| router.post('/estimate', isAuthenticated, isManager, async (req, res) => { |
| try { |
| const estimate = await ml.estimateBlock(req.body); |
| res.json(estimate); |
| } catch (error) { |
| console.error('ML estimation error:', error.message); |
| res.status(500).json({ error: 'ML estimation failed', details: error.message }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| |
| router.get('/block/:id/risk', isAuthenticated, async (req, res) => { |
| try { |
| const block = await Block.findById(req.params.id); |
| if (!block) return res.status(404).json({ error: 'Block not found' }); |
|
|
| |
| const lastTransition = block.transitions?.[block.transitions.length - 1]; |
| const daysSince = lastTransition |
| ? (Date.now() - new Date(lastTransition.timestamp)) / (1000 * 60 * 60 * 24) |
| : 0; |
|
|
| const riskData = ml.constructor.formatForBottleneck(block, daysSince); |
| const risk = await ml.predictBottleneck(riskData); |
| |
| res.json({ blockId: block._id, blockName: block.name, ...risk }); |
| } catch (error) { |
| console.error('ML risk prediction error:', error.message); |
| res.status(500).json({ error: 'Risk prediction failed', details: error.message }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| |
| router.get('/block/:id/eta', isAuthenticated, async (req, res) => { |
| try { |
| const block = await Block.findById(req.params.id); |
| if (!block) return res.status(404).json({ error: 'Block not found' }); |
| if (block.status === 'Completed') { |
| return res.json({ remaining_hours: 0, progress_percent: 100, status: 'completed' }); |
| } |
|
|
| const completionData = ml.constructor.formatForCompletion(block); |
| const eta = await ml.predictCompletion(completionData); |
| |
| res.json({ blockId: block._id, blockName: block.name, ...eta }); |
| } catch (error) { |
| console.error('ML completion prediction error:', error.message); |
| res.status(500).json({ error: 'Completion prediction failed', details: error.message }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| |
| router.post('/bulk-estimate', isAuthenticated, isManager, async (req, res) => { |
| try { |
| const { blocks } = req.body; |
| if (!blocks || !Array.isArray(blocks)) { |
| return res.status(400).json({ error: 'blocks array required' }); |
| } |
| if (blocks.length > 200) { |
| return res.status(400).json({ error: 'Maximum 200 blocks per request' }); |
| } |
| const estimates = await ml.bulkEstimate(blocks); |
| res.json(estimates); |
| } catch (error) { |
| console.error('ML bulk estimation error:', error.message); |
| res.status(500).json({ error: 'Bulk estimation failed', details: error.message }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| |
| router.get('/scan/bottlenecks', isAuthenticated, isManager, async (req, res) => { |
| try { |
| const blocks = await Block.find({ status: { $nin: ['Not Started', 'Completed'] } }); |
| const results = []; |
|
|
| for (const block of blocks) { |
| const lastTransition = block.transitions?.[block.transitions.length - 1]; |
| const daysSince = lastTransition |
| ? (Date.now() - new Date(lastTransition.timestamp)) / (1000 * 60 * 60 * 24) |
| : 0; |
|
|
| const riskData = ml.constructor.formatForBottleneck(block, daysSince); |
| const risk = await ml.predictBottleneck(riskData); |
|
|
| if (risk.risk_level !== 'Low') { |
| results.push({ |
| blockId: block._id, |
| blockName: block.name, |
| stage: block.status, |
| engineer: block.assignedTo, |
| ...risk, |
| }); |
| } |
| } |
|
|
| |
| results.sort((a, b) => { |
| const order = { High: 0, Medium: 1, Low: 2 }; |
| return (order[a.risk_level] || 2) - (order[b.risk_level] || 2); |
| }); |
|
|
| res.json({ |
| total_scanned: blocks.length, |
| at_risk: results.length, |
| high_risk: results.filter(r => r.risk_level === 'High').length, |
| medium_risk: results.filter(r => r.risk_level === 'Medium').length, |
| blocks: results |
| }); |
| } catch (error) { |
| console.error('ML bottleneck scan error:', error.message); |
| res.status(500).json({ error: 'Bottleneck scan failed', details: error.message }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| router.get('/health', async (req, res) => { |
| try { |
| const health = await ml.healthCheck(); |
| res.json(health); |
| } catch (error) { |
| res.status(503).json({ status: 'unhealthy', error: error.message }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| |
| router.get('/metrics', isAuthenticated, isManager, async (req, res) => { |
| try { |
| const metrics = await ml.getMetrics(); |
| res.json(metrics); |
| } catch (error) { |
| res.status(500).json({ error: 'Failed to fetch metrics', details: error.message }); |
| } |
| }); |
|
|
| module.exports = router; |
|
|