- Substituído `Console.ReadLine()` por `Thread.Sleep(3000)` após erro de autenticação para pausar execução. - Tornada obrigatória a propriedade `Acao` na classe `Record`. - Criada tabela `tableContReunioes` para exibir dados de reuniões. - Adicionados contadores para reuniões agendadas, realizadas e canceladas. - Implementada lógica para preencher `tableContReunioes` com dados calculados. - Melhorado tratamento de exceções com `try-catch` para capturar erros. - Alterada lógica de ordenação para priorizar data, ação e usuário. - Adicionado painel com cabeçalho "Reuniões Agendadas e Realizadas". - Ajustado valor padrão de `Acao` para "Desconhecida" em novos registros.
332 lines
15 KiB
C#
332 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Numerics;
|
|
using System.Threading.Tasks;
|
|
using Npgsql;
|
|
using Spectre.Console;
|
|
|
|
class Program
|
|
{
|
|
static DateTime _endTime;
|
|
static bool _stop = false;
|
|
static bool _isAuthenticated = false;
|
|
static readonly string _connectionString = "Server=192.168.10.248;Port=5432;Database=pipefy_move_cards;User Id=postgres;Password=gds21;";
|
|
static readonly string _windowsID = Environment.UserName;
|
|
static readonly List<(string actionId, string actionName)> _actionIds = // Tuplas de IDs e nomes das fases que queremos monitorar
|
|
[
|
|
new ("318642783-318906957", "Agendou reunião 1"),
|
|
new ("322784034-332649256", "Agendou reunião 2"),
|
|
new ("332659066-332653733", "Agendou reunião 3"),
|
|
new ("332659083-339300210", "Agendou reunião 4"),
|
|
new ("339300218-339300231", "Agendou reunião 5"),
|
|
new ("318906957-322784034", "Realizou reunião 1"),
|
|
new ("332649256-332659066", "Realizou reunião 2"),
|
|
new ("332653733-332659083", "Realizou reunião 3"),
|
|
new ("339300210-339300218", "Realizou reunião 4"),
|
|
new ("339300231-339300232", "Realizou reunião 5"),
|
|
new ("318906957-318642783", "Reunião cancelada"),
|
|
new ("332649256-322784034", "Reunião cancelada"),
|
|
new ("332653733-332659066", "Reunião cancelada"),
|
|
new ("339300210-332659083", "Reunião cancelada"),
|
|
new ("339300231-339300218", "Reunião cancelada"),
|
|
new ("325103205-325103208", "Agendou reunião 1"),
|
|
new ("325103212-332710299", "Agendou reunião 2"),
|
|
new ("332710321-332710322", "Agendou reunião 3"),
|
|
new ("332710362-339304168", "Agendou reunião 4"),
|
|
new ("339304193-339304215", "Agendou reunião 5"),
|
|
new ("325103208-325103212", "Realizou reunião 1"),
|
|
new ("332710299-332710321", "Realizou reunião 2"),
|
|
new ("332710322-332710362", "Realizou reunião 3"),
|
|
new ("339304168-339304193", "Realizou reunião 4"),
|
|
new ("339304215-339304218", "Realizou reunião 5"),
|
|
new ("325103208-325103205", "Reunião cancelada"),
|
|
new ("332710299-325103212", "Reunião cancelada"),
|
|
new ("332710322-332710321", "Reunião cancelada"),
|
|
new ("339304168-332710362", "Reunião cancelada"),
|
|
new ("339304215-339304193", "Reunião cancelada")
|
|
];
|
|
// Agora guardamos uma lista de tuplas (UserID, Nome)
|
|
static readonly List<(BigInteger UserId, string Nome)> _pipeUsers = [];
|
|
|
|
static void Main()
|
|
{
|
|
AnsiConsole.Clear();
|
|
|
|
// 1) Autenticação (pode retornar múltiplos usuários)
|
|
Autenticacao();
|
|
|
|
if (!_isAuthenticated)
|
|
{
|
|
AnsiConsole.MarkupLine("[bold red]Nenhum usuário encontrado ou erro na autenticação. Saindo...[/]");
|
|
Thread.Sleep(3000);
|
|
return;
|
|
}
|
|
|
|
// 2) Loop principal: atualizar dados e aguardar entrada
|
|
while (!_stop)
|
|
{
|
|
AtualizarDados();
|
|
AguardarEntrada();
|
|
}
|
|
|
|
AnsiConsole.MarkupLine("[bold yellow]Aplicação encerrada.[/]");
|
|
}
|
|
|
|
public class Record
|
|
{
|
|
public int? Id { get; set; }
|
|
public string? Action { get; set; }
|
|
public string? User { get; set; }
|
|
public string? FieldID { get; set; }
|
|
public int? From { get; set; }
|
|
public int? To { get; set; }
|
|
public int? CardID { get; set; }
|
|
public string? PipeSUID { get; set; }
|
|
public DateTime MovedAt { get; set; }
|
|
public required string Title { get; set; }
|
|
public string? AcaoID { get; set; }
|
|
public required string Acao { get; set; }
|
|
}
|
|
static void AtualizarDados()
|
|
{
|
|
var records = new List<Record>();
|
|
AnsiConsole.Clear();
|
|
|
|
// Painel de cabeçalho que mostra quantos usuários foram carregados
|
|
|
|
var status = new Align(new Panel($"[bold]WindowsID[/]: {_windowsID} | [bold]Usuários encontrados[/]: {_pipeUsers.Count}"), HorizontalAlignment.Center, VerticalAlignment.Top);
|
|
var header = new Panel(status)
|
|
.Header("[yellow]Resumo Diário por Usuário[/]",Justify.Center)
|
|
.Expand();
|
|
AnsiConsole.Write(new Align(header, HorizontalAlignment.Center, VerticalAlignment.Top));
|
|
|
|
// Montamos uma tabela com: Nome do usuário | UserID | Registros Hoje
|
|
var tableContLigacoes = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
|
|
tableContLigacoes.Border(TableBorder.Rounded);
|
|
tableContLigacoes.AddColumn(new TableColumn("[green]Nome[/]").Centered());
|
|
tableContLigacoes.AddColumn(new TableColumn("[green]UserID[/]").Centered());
|
|
tableContLigacoes.AddColumn(new TableColumn("[green]Registros Hoje[/]").Centered());
|
|
|
|
// Montamos uma tabela com: Nome do usuário | UserID | Registros Hoje
|
|
var tableContReunioes = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
|
|
tableContReunioes.Border(TableBorder.Rounded);
|
|
tableContReunioes.AddColumn(new TableColumn("[green]Nome[/]").Centered());
|
|
tableContReunioes.AddColumn(new TableColumn("[green]UserID[/]").Centered());
|
|
tableContReunioes.AddColumn(new TableColumn("[green]Agendadas[/]").Centered());
|
|
tableContReunioes.AddColumn(new TableColumn("[green]Realizadas[/]").Centered());
|
|
tableContReunioes.AddColumn(new TableColumn("[green]Canceladas[/]").Centered());
|
|
|
|
// Para cada usuário, rodar uma query COUNT(*) e adicionar linha na tabela
|
|
foreach (var (userId, nome) in _pipeUsers)
|
|
{
|
|
var userRecords = new List<Record>();
|
|
int count = 0;
|
|
int countR = 0;
|
|
int countA = 0;
|
|
int countC = 0;
|
|
AnsiConsole.Status()
|
|
.Spinner(Spinner.Known.Line)
|
|
.Start($"[blue]Obtendo registros de [yellow]{nome}[/]...[/]", ctx =>
|
|
{
|
|
const string query = @"
|
|
SELECT * FROM public.""ActionsHistory""
|
|
WHERE ""UserID"" = @pipeUser AND
|
|
""MovedAt"" >= date_trunc('month', CURRENT_DATE)";
|
|
try
|
|
{
|
|
using var conn = new NpgsqlConnection(_connectionString);
|
|
conn.Open();
|
|
using var cmd = new NpgsqlCommand(query, conn);
|
|
cmd.Parameters.AddWithValue("@pipeUser", userId);
|
|
var reader = cmd.ExecuteReader();
|
|
using (reader)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
var temp = new Record
|
|
{
|
|
Id = ToIntOrNull(reader, "Id"),
|
|
Action = ToStringOrNull(reader, "Action"),
|
|
User = _pipeUsers.Find(u => u.UserId == ToIntOrNull(reader, "UserID")).Nome,
|
|
FieldID = ToStringOrNull(reader, "FieldID"),
|
|
From = ToIntOrNull(reader, "From"),
|
|
To = ToIntOrNull(reader, "To"),
|
|
CardID = ToIntOrNull(reader, "CardID"),
|
|
PipeSUID = ToStringOrNull(reader, "PipeSUID"),
|
|
MovedAt = (DateTime)(reader["MovedAt"] ?? DateTime.MinValue),
|
|
Title = ToStringOrNull(reader, "Title") ?? "-",
|
|
Acao = "Desconhecida"
|
|
};
|
|
temp.AcaoID = temp.From + "-" + temp.To;
|
|
temp.Acao = _actionIds.Find(p => p.actionId == temp.AcaoID).actionName;
|
|
|
|
userRecords.Add(temp);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Em caso de erro, exibimos zero e continuamos
|
|
count = 0;
|
|
AnsiConsole.MarkupLine($"\n[red]Erro ao consultar {nome}:[/] {ex.Message}");
|
|
}
|
|
});
|
|
count = userRecords.Where(x => (x.MovedAt > DateTime.Today && x.FieldID is not null)).ToList().Count;
|
|
countR = userRecords.Where(x => ((x.Acao ?? "").StartsWith("Realizou"))).ToList().Count;
|
|
countA = userRecords.Where(x => ((x.Acao ?? "").StartsWith("Agendou"))).ToList().Count;
|
|
countC = userRecords.Where(x => ((x.Acao ?? "").EndsWith("cancelada"))).ToList().Count;
|
|
tableContLigacoes.AddRow(nome, userId.ToString(), $"[bold yellow]{count}[/]");
|
|
tableContReunioes.AddRow(nome, userId.ToString(), $"[bold yellow]{countA}[/]", $"[bold yellow]{countR}[/]", $"[bold yellow]{countC}[/]");
|
|
records.AddRange(userRecords);
|
|
}
|
|
|
|
var panel = new Panel(new Align(tableContLigacoes, HorizontalAlignment.Center, VerticalAlignment.Top))
|
|
.Header("[blue bold] Ligações [/]", Justify.Center)
|
|
.Border(BoxBorder.Double)
|
|
.Expand();
|
|
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
|
|
|
|
panel = new Panel(new Align(tableContReunioes, HorizontalAlignment.Center, VerticalAlignment.Top))
|
|
.Header("[blue bold] Reuniões Agendadas e Realizadas [/]", Justify.Center)
|
|
.Border(BoxBorder.Double)
|
|
.Expand();
|
|
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
|
|
// Montamos uma tabela com: Nome do usuário | UserID | Registros Hoje
|
|
var tableReunioes = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
|
|
tableReunioes.AddColumn(new TableColumn("[green]Data[/]").Centered());
|
|
tableReunioes.AddColumn(new TableColumn("[green]Status[/]").Centered());
|
|
tableReunioes.AddColumn(new TableColumn("[green]Empresa[/]").Centered());
|
|
tableReunioes.AddColumn(new TableColumn("[green]Card ID\n(Ctrl + click)[/]").Centered());
|
|
tableReunioes.AddColumn(new TableColumn("[green]Prospectante[/]").Centered());
|
|
|
|
records = [.. records.OrderBy(x => x.MovedAt.Date).OrderBy(x => x.Acao).OrderBy(y => y.User)];
|
|
|
|
// Para cada usuário, rodar uma query COUNT(*) e adicionar linha na tabela
|
|
foreach (var record in records)
|
|
{
|
|
if (record.Acao is not null)
|
|
{
|
|
tableReunioes.AddRow(
|
|
record.MovedAt.ToString("d"),
|
|
record.Acao ?? "Ação desconhecida",
|
|
record.Title ?? "",
|
|
$"[link=https://app.pipefy.com/open-cards/{record.CardID}]{record.CardID}[/]",
|
|
record.User ?? ""
|
|
);
|
|
}
|
|
}
|
|
panel = new Panel(new Align(tableReunioes, HorizontalAlignment.Center, VerticalAlignment.Top))
|
|
.Header("[blue bold] Reuniões no Mês [/]", Justify.Center)
|
|
.Border(BoxBorder.Double)
|
|
.Expand();
|
|
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
|
|
|
|
AnsiConsole.MarkupLine("\n[gray]Pressione [green]ENTER[/] para atualizar agora ou qualquer outra tecla para sair.[/]");
|
|
}
|
|
|
|
static void AguardarEntrada()
|
|
{
|
|
// Tempo de espera em segundos (ex.: 10 minutos = 600s)
|
|
int intervalSeconds = 600;
|
|
_endTime = DateTime.Now.AddSeconds(intervalSeconds);
|
|
|
|
// Escreve uma linha em branco para “reservar” o lugar do contador
|
|
AnsiConsole.WriteLine("");
|
|
|
|
// Começa o loop que vai atualizar a mesma linha a cada segundo
|
|
while (!_stop)
|
|
{
|
|
var remaining = _endTime - DateTime.Now;
|
|
|
|
if (remaining.TotalMilliseconds <= 0)
|
|
{
|
|
// Quando chegar a zero, exibe a mensagem de atualização automática
|
|
AnsiConsole.MarkupLine("\r[red]Atualização automática agora![/]");
|
|
Thread.Sleep(1000);
|
|
break;
|
|
}
|
|
|
|
// Monta o texto de “Próxima atualização em MM:SS”
|
|
AnsiConsole.Markup($"\r[blue]Próxima atualização em {remaining.Minutes:00}:{remaining.Seconds:00}[/]");
|
|
|
|
// Pequena pausa de 1 segundo
|
|
Thread.Sleep(1000);
|
|
|
|
// Se o usuário apertar qualquer tecla…
|
|
if (Console.KeyAvailable)
|
|
{
|
|
var key = Console.ReadKey(true);
|
|
// …e não for ENTER, sinalizamos para parar o loop e sair
|
|
if (key.Key != ConsoleKey.Enter)
|
|
_stop = true;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Garante que, ao sair do loop, o cursor fique embaixo do texto
|
|
Console.WriteLine("");
|
|
}
|
|
|
|
static void Autenticacao()
|
|
{
|
|
AnsiConsole.Status()
|
|
.Spinner(Spinner.Known.Dots)
|
|
.Start("[green]Autenticando usuário...[/]", ctx =>
|
|
{
|
|
const string query = @"
|
|
SELECT ""UserID"", ""Nome""
|
|
FROM public.""usuarios""
|
|
WHERE ""windowsID"" = @WindowsID";
|
|
|
|
try
|
|
{
|
|
using var conn = new NpgsqlConnection(_connectionString);
|
|
conn.Open();
|
|
using var cmd = new NpgsqlCommand(query, conn);
|
|
cmd.Parameters.AddWithValue("@WindowsID", _windowsID);
|
|
|
|
using var reader = cmd.ExecuteReader();
|
|
while (reader.Read())
|
|
{
|
|
var userId = BigInteger.Parse(reader["UserID"].ToString()!);
|
|
var nome = reader["Nome"].ToString()!;
|
|
_pipeUsers.Add((userId, nome));
|
|
}
|
|
|
|
if (_pipeUsers.Count > 0)
|
|
{
|
|
_isAuthenticated = true;
|
|
}
|
|
else
|
|
{
|
|
AnsiConsole.MarkupLine("[bold red]Nenhum usuário encontrado para este WindowsID.[/]");
|
|
_isAuthenticated = false;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AnsiConsole.MarkupLine($"\n[red]Erro na autenticação:[/] {ex.Message}");
|
|
_isAuthenticated = false;
|
|
}
|
|
});
|
|
}
|
|
#region Helpers
|
|
|
|
static int? ToIntOrNull(DbDataReader r, string col)
|
|
{
|
|
return r.IsDBNull(r.GetOrdinal(col))
|
|
? null
|
|
: Convert.ToInt32(r[col]); ;
|
|
}
|
|
|
|
static string? ToStringOrNull(DbDataReader r, string col)
|
|
{
|
|
return r.IsDBNull(r.GetOrdinal(col))
|
|
? null
|
|
: r.GetString(r.GetOrdinal(col));
|
|
}
|
|
#endregion
|
|
}
|