FEAT: Titulo da solicitação adicionado dinamicamente

- Adição de Titulo composto dinamicamente "TIPO - ENTIDADE - PROBLEMA"
- Adicionado Fluxo completo da criação do banco de dados
- Adicionado verificação se o arquivo .csv foi modificado nos ultimos 2 minutos, se sim a tabela de mapping é atualizada
This commit is contained in:
Rafael Alves Lopes 2025-09-18 20:52:39 -03:00
parent 07230dd1e4
commit 9df7e62cf5
4 changed files with 129 additions and 7 deletions

View File

@ -47,6 +47,66 @@ class LocationMappingModel {
return null;
}
}
// Buscar entity (id e name) do GLPI baseado no location_id do ServiceNow
static async getGlpiEntity(snLocationId) {
try {
const query = `
SELECT glpi_entity_id, glpi_entity_name
FROM location_mapping
WHERE sn_location_id = $1
`;
const result = await pool.query(query, [snLocationId]);
if (result.rows.length > 0) {
return {
id: result.rows[0].glpi_entity_id,
name: result.rows[0].glpi_entity_name
};
}
return null;
} catch (error) {
logError(error, `Erro ao buscar entity para location_id: ${snLocationId}`);
return null;
}
}
// Buscar entity (id e name) do GLPI baseado no location_name do ServiceNow
static async getGlpiEntityByName(snLocationName) {
try {
const query = `
SELECT glpi_entity_id, glpi_entity_name
FROM location_mapping
WHERE sn_location_name = $1
`;
const result = await pool.query(query, [snLocationName]);
if (result.rows.length > 0) {
return {
id: result.rows[0].glpi_entity_id,
name: result.rows[0].glpi_entity_name
};
}
return null;
} catch (error) {
logError(error, `Erro ao buscar entity para location_name: ${snLocationName}`);
return null;
}
}
}
module.exports = LocationMappingModel;

View File

@ -63,6 +63,36 @@ class TicketGlpiModel {
return 0; // Default se nenhum critério for atendido
}
// Formata o título do ticket
static async formatTitle(ticketData) {
try {
let entityData = null;
if (ticketData.location_id) {
entityData = await LocationMappingModel.getGlpiEntity(ticketData.location_id);
} else if (ticketData.location_name) {
entityData = await LocationMappingModel.getGlpiEntityByName(ticketData.location_name);
}
const entityName = entityData?.name || 'Sem Entidade';
const tipo = ticketData.type === 'requisicao' ? 'REQUISIÇÃO' : 'INCIDENTE';
const tituloSn = ticketData.short_description || 'Sem título';
return {
title: `${tipo} - ${entityName} - ${tituloSn}`,
entityId: entityData?.id || 127 // fallback para entity_id default
};
} catch (error) {
logError(error, 'Erro ao formatar título do ticket');
const tipo = ticketData.type === 'requisicao' ? 'REQUISIÇÃO' : 'INCIDENTE';
return {
title: `${tipo} - ${ticketData.short_description || 'Sem título'}`,
entityId: 127
};
}
}
static formatDescription(ticketData) {
// Função auxiliar para extrair informações usando regex
const extractInfo = (regex, description) => {
@ -129,7 +159,7 @@ class TicketGlpiModel {
await connection.beginTransaction();
// Buscar entity_id com base no location_id ou location_name
let entityId = 1371; // Valor padrão (fallback)
let entityId = 127; // Valor padrão (fallback)
if (ticketData.location_id) {
entityId = await LocationMappingModel.getGlpiEntityId(ticketData.location_id) || entityId;
@ -147,6 +177,8 @@ class TicketGlpiModel {
}
}
// Formata o título do ticket
const { title: formattedTitle } = await TicketGlpiModel.formatTitle(ticketData);
// Formata a descrição para HTML
const formattedDescription = TicketGlpiModel.formatDescription(ticketData);
// Mapeia o tipo do ticket
@ -157,7 +189,7 @@ class TicketGlpiModel {
// Prepara os dados para o GLPI
const glpiTicketData = {
entities_id: entityId, // Usar o entity_id mapeado
name: ticketData.short_description || 'Ticket sem descrição',
name: formattedTitle,
date: ticketData.opened_at || new Date(),
date_mod: ticketData.updated_at || new Date(),
status: TicketGlpiModel.mapStatus(ticketData.state || '1'),

View File

@ -4,6 +4,18 @@
-- OBS: Chamados sempre são abertos pelo ServiceNow
-- =============================================
CREATE DATABASE snglpi
WITH
OWNER = desenvolvimento
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.UTF-8'
LC_CTYPE = 'en_US.UTF-8'
LOCALE_PROVIDER = 'libc'
TABLESPACE = pg_default
CONNECTION LIMIT = -1
IS_TEMPLATE = False;
-- =============================================
-- TABELA: tickets_sn
-- DESCRIÇÃO: Armazena tickets importados do ServiceNow

View File

@ -8,6 +8,7 @@ from datetime import datetime
import os
from dotenv import load_dotenv
import pandas as pd
from datetime import timedelta
# Carrega as variáveis do .env
load_dotenv()
@ -43,6 +44,22 @@ GLPI_DB_CONFIG = {
# Caminho para o arquivo CSV
CSV_PATH = os.getenv('LOCATION_MAPPING_CSV_PATH')
# Função para verificar se o CSV foi modificado recentemente
def csv_recently_modified(minutes=2):
if not os.path.exists(CSV_PATH):
logging.error(f"Arquivo CSV não encontrado: {CSV_PATH}")
return False
last_mod_time = datetime.fromtimestamp(os.path.getmtime(CSV_PATH))
if datetime.now() - last_mod_time <= timedelta(minutes=minutes):
logging.info(f"CSV modificado recentemente ({last_mod_time}), rodando atualização.")
return True
else:
logging.info(f"CSV não foi modificado nos últimos {minutes} minutos (última modificação: {last_mod_time}).")
return False
# Função para conectar ao banco SNGLPI
def get_snglpi_connection():
try:
@ -177,8 +194,9 @@ def update_mapping_table():
if __name__ == "__main__":
logging.info("Iniciando atualização de mapeamento de localizações")
success = update_mapping_table()
if success:
logging.info("Atualização concluída com sucesso")
else:
logging.error("Falha na atualização")
if csv_recently_modified(minutes=2):
success = update_mapping_table()
if success:
logging.info("Atualização concluída com sucesso")
else:
logging.error("Falha na atualização")