Migración a Python/Flask y adaptación para proyecto Infra
- Reemplaza Netlify Functions con servidor Python/Flask (server.py) - Adapta dashboard para contar tickets en lugar de Story Points - Conecta al proyecto IO (Infra) de Jira - Elimina secciones de QA y Code Review - Agrega métricas de solicitudes semanales y reporters - Excluye devs inactivos y unifica nombres duplicados Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+34
-16
@@ -8,7 +8,7 @@
|
||||
const config = {
|
||||
jira: {
|
||||
host: process.env.JIRA_HOST || 'invera.atlassian.net',
|
||||
project: process.env.JIRA_PROJECT || 'LABS',
|
||||
project: process.env.JIRA_PROJECT || 'Infra',
|
||||
email: process.env.JIRA_EMAIL,
|
||||
token: process.env.JIRA_TOKEN,
|
||||
},
|
||||
@@ -30,7 +30,7 @@ const config = {
|
||||
enCursoEntry: 'customfield_10815',
|
||||
enCursoTotalTime: 'customfield_10816',
|
||||
},
|
||||
jql: process.env.JIRA_JQL || 'project = ${PROJECT} AND ${FIELD_SP_ESTIMATE} is not EMPTY ORDER BY updated DESC',
|
||||
jql: process.env.JIRA_JQL || 'project = ${PROJECT} AND issuetype not in (Epic, "Sub-task", Subtask) ORDER BY updated DESC',
|
||||
api: {
|
||||
cacheMsec: parseInt(process.env.CACHE_MS || '3600000'), // 1 hora por defecto
|
||||
},
|
||||
@@ -244,7 +244,37 @@ function processIssues(issues, period = '90d') {
|
||||
}
|
||||
}
|
||||
|
||||
issuesList.push({ id: issue.id, key: issue.key, summary: f.summary, status: statusKey, sp, devs, creMonth });
|
||||
const issueWarnings = [];
|
||||
|
||||
if (isInProgress && f.updated) {
|
||||
const days = (now - new Date(f.updated)) / (1000 * 60 * 60 * 24);
|
||||
if (days > 14) {
|
||||
const w = { type: 'stalled', key: issue.key, summary: f.summary, devs, sp, days: Math.round(days) };
|
||||
warnings.push(w);
|
||||
issueWarnings.push(w);
|
||||
}
|
||||
}
|
||||
if (!isDone && f.duedate) {
|
||||
const dueDate = new Date(f.duedate);
|
||||
if (dueDate < now) {
|
||||
const daysOverdue = Math.floor((now - dueDate) / (1000 * 60 * 60 * 24));
|
||||
const w = { type: 'expired', key: issue.key, summary: f.summary, devs, sp, days: daysOverdue };
|
||||
warnings.push(w);
|
||||
issueWarnings.push(w);
|
||||
}
|
||||
}
|
||||
if (isDone && sp === 0) {
|
||||
const w = { type: 'done_no_sp', key: issue.key, summary: f.summary, devs, sp: 0 };
|
||||
warnings.push(w);
|
||||
issueWarnings.push(w);
|
||||
}
|
||||
if (!isDone && devs.includes('Sin asignar') && sp > 0) {
|
||||
const w = { type: 'unassigned_sp', key: issue.key, summary: f.summary, devs, sp };
|
||||
warnings.push(w);
|
||||
issueWarnings.push(w);
|
||||
}
|
||||
|
||||
issuesList.push({ id: issue.id, key: issue.key, summary: f.summary, status: statusKey, sp, devs, creMonth, warnings: issueWarnings });
|
||||
|
||||
const timeQaMin = f[config.fields.qaTotalTimeQa] ?? 0;
|
||||
const timeRtqaMin = f[config.fields.qaTotalTimeRtqa] ?? 0;
|
||||
@@ -305,19 +335,7 @@ function processIssues(issues, period = '90d') {
|
||||
}
|
||||
}
|
||||
|
||||
if (isInProgress && f.updated) {
|
||||
const days = (now - new Date(f.updated)) / (1000 * 60 * 60 * 24);
|
||||
if (days > 14) warnings.push({ type: 'stalled', key: issue.key, summary: f.summary, devs, sp, days: Math.round(days) });
|
||||
}
|
||||
if (!isDone && f.duedate) {
|
||||
const dueDate = new Date(f.duedate);
|
||||
if (dueDate < now) {
|
||||
const daysOverdue = Math.floor((now - dueDate) / (1000 * 60 * 60 * 24));
|
||||
warnings.push({ type: 'expired', key: issue.key, summary: f.summary, devs, sp, days: daysOverdue });
|
||||
}
|
||||
}
|
||||
if (isDone && sp === 0) warnings.push({ type: 'done_no_sp', key: issue.key, summary: f.summary, devs, sp: 0 });
|
||||
if (!isDone && devs.includes('Sin asignar') && sp > 0) warnings.push({ type: 'unassigned_sp', key: issue.key, summary: f.summary, devs, sp });
|
||||
// Las alertas de salud se generan y empujan arriba (junto con issueWarnings)
|
||||
|
||||
} catch (e) {
|
||||
console.warn(`Error procesando issue ${issue.key}: ${e.message}`);
|
||||
|
||||
Reference in New Issue
Block a user