altas mudanças
All checks were successful
API and ETL Build / build_etl (push) Successful in 30s
API and ETL Build / build_api (push) Successful in 15s

This commit is contained in:
2025-06-10 20:16:22 -03:00
parent 684a2c0630
commit 23b1f0f14e
10 changed files with 175 additions and 83 deletions

View File

@@ -2,11 +2,14 @@
{
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string value)
public static bool IsNullOrEmpty(this string? value)
{
return string.IsNullOrEmpty(value) ||
value == "#NE" ||
value == "#NULO";
value == "#NULO" ||
value == "#NULO#" ||
value == "NÃO DIVULGÁVEL" ||
value == "-4";
}
}
}

View File

@@ -36,10 +36,10 @@ namespace OpenCand.Parser.Models
public string Apelido { get; set; }
[Name("NR_CPF_CANDIDATO")]
public string CPFCandidato { get; set; }
public string? CPFCandidato { get; set; }
[Name("DS_EMAIL", "NM_EMAIL")]
public string Email { get; set; }
public string? Email { get; set; }
[Name("DT_NASCIMENTO")]
public string DataNascimento { get; set; }
@@ -57,7 +57,7 @@ namespace OpenCand.Parser.Models
public string GrauInstrucao { get; set; }
[Name("DS_SIT_TOT_TURNO")]
public string SituacaoTurno { get; set; }
public string? SituacaoTurno { get; set; }
[Name("NR_PARTIDO")]
public int NumeroPartido { get; set; }

View File

@@ -2,6 +2,7 @@
using Microsoft.Extensions.Logging;
using OpenCand.Core.Models;
using OpenCand.ETL.Contracts;
using OpenCand.ETL.Extensions;
using OpenCand.Parser.Models;
using OpenCand.Services;
@@ -22,62 +23,80 @@ namespace OpenCand.ETL.Parser.ParserServices
public async Task ParseObject(CandidatoCSV record)
{
if (string.IsNullOrWhiteSpace(record.CPFCandidato) || record.CPFCandidato.Length <= 3)
if (record.CPFCandidato?.Length <= 3 || record.CPFCandidato.IsNullOrEmpty())
{
record.CPFCandidato = null; // Handle null/empty/whitespace CPF
}
else
{
record.CPFCandidato = record.CPFCandidato.Trim();
}
if (record.NomeCandidato == "NÃO DIVULGÁVEL" ||
string.IsNullOrEmpty(record.NomeCandidato) ||
record.NomeCandidato == "#NULO")
if (record.Apelido.IsNullOrEmpty())
{
record.Apelido = null;
}
if (record.NomeCandidato.IsNullOrEmpty())
{
logger.LogCritical($"ParseCandidatosAsync - Candidate with id {record.SequencialCandidato} with invalid name, skipping...");
return; // Skip candidates with invalid name
}
if (string.IsNullOrWhiteSpace(record.Apelido) ||
record.Apelido.Contains("#NUL") ||
record.Apelido.Contains("NULO#") ||
record.Apelido.Contains("#NE"))
if (record.Apelido.IsNullOrEmpty())
{
record.Apelido = null;
}
if (record.SituacaoTurno.IsNullOrEmpty())
{
record.SituacaoTurno = null;
}
var candidato = new Candidato
{
Cpf = record.CPFCandidato,
SqCandidato = record.SequencialCandidato,
Nome = record.NomeCandidato,
Apelido = record.Apelido,
Email = record.Email.Contains("@") ? record.Email : null,
Sexo = record.Genero,
EstadoCivil = record.EstadoCivil,
Escolaridade = record.GrauInstrucao,
Ocupacao = record.Ocupacao,
Nome = record.NomeCandidato.Trim(),
Apelido = record.Apelido?.Trim(),
Sexo = record.Genero.Trim(),
Localidade = record.NomeUE.Trim(),
Ultimoano = record.AnoEleicao,
Eleicoes = new List<CandidatoMapping>()
{
new CandidatoMapping
{
Cpf = record.CPFCandidato,
Nome = record.NomeCandidato,
Apelido = record.Apelido,
SqCandidato = record.SequencialCandidato,
Ano = record.AnoEleicao,
Turno = record.Turno,
TipoEleicao = record.TipoAbrangencia,
NomeUE = record.NomeUE,
SiglaUF = record.SiglaUF,
Cargo = record.DescricaoCargo,
NrCandidato = record.NumeroCandidato,
Resultado = record.SituacaoTurno,
Partido = new Partido
{
Sigla = record.SiglaPartido,
Nome = record.NomePartido,
Numero = record.NumeroPartido,
}
}
}
{
new CandidatoMapping
{
Cpf = record.CPFCandidato,
Nome = record.NomeCandidato.Trim(),
SqCandidato = record.SequencialCandidato.Trim(),
Ano = record.AnoEleicao,
Turno = record.Turno.Trim(),
TipoEleicao = record.TipoAbrangencia.Trim(),
NomeUE = record.NomeUE.Trim(),
SiglaUF = record.SiglaUF.Trim(),
Cargo = record.DescricaoCargo.Trim(),
NrCandidato = record.NumeroCandidato.Trim(),
Resultado = record.SituacaoTurno?.Trim() ?? "-",
Partido = new Partido
{
Sigla = record.SiglaPartido.Trim(),
Nome = record.NomePartido.Trim(),
Numero = record.NumeroPartido,
}
}
},
CandidatoExt = new List<CandidatoExt>()
{
new CandidatoExt
{
Apelido = record.Apelido?.Trim(),
EstadoCivil = record.EstadoCivil.Trim(),
Escolaridade = record.GrauInstrucao.Trim(),
Ocupacao = record.Ocupacao.Trim(),
Ano = record.AnoEleicao,
Email = record.Email.IsNullOrEmpty() ? null : record.Email.Trim()
}
}
};
if (!string.IsNullOrEmpty(record.DataNascimento) &&

View File

@@ -16,18 +16,17 @@ namespace OpenCand.Repository
using (var connection = new NpgsqlConnection(ConnectionString))
{
await connection.ExecuteAsync(@"
INSERT INTO candidato (idcandidato, cpf, nome, apelido, datanascimento, email, sexo, estadocivil, escolaridade, ocupacao)
VALUES (@idcandidato, @cpf, @nome, @apelido, @datanascimento, @email, @sexo, @estadocivil, @escolaridade, @ocupacao)
INSERT INTO candidato (idcandidato, cpf, nome, apelido, datanascimento, sexo, ultimoano, localidade)
VALUES (@idcandidato, @cpf, @nome, @apelido, @datanascimento, @sexo, @ultimoano, @localidade)
ON CONFLICT (idcandidato) DO UPDATE SET
cpf = EXCLUDED.cpf,
nome = EXCLUDED.nome,
apelido = EXCLUDED.apelido,
datanascimento = EXCLUDED.datanascimento,
email = EXCLUDED.email,
sexo = EXCLUDED.sexo,
estadocivil = EXCLUDED.estadocivil,
escolaridade = EXCLUDED.escolaridade,
ocupacao = EXCLUDED.ocupacao,
apelido = EXCLUDED.apelido;",
localidade = EXCLUDED.localidade,
ultimoano = EXCLUDED.ultimoano
WHERE candidato.ultimoano IS NULL OR EXCLUDED.ultimoano > candidato.ultimoano;",
new
{
idcandidato = candidato.IdCandidato,
@@ -35,11 +34,9 @@ namespace OpenCand.Repository
nome = candidato.Nome,
apelido = candidato.Apelido,
datanascimento = candidato.DataNascimento,
email = candidato.Email,
sexo = candidato.Sexo,
estadocivil = candidato.EstadoCivil,
escolaridade = candidato.Escolaridade,
ocupacao = candidato.Ocupacao
localidade = candidato.Localidade,
ultimoano = candidato.Ultimoano
});
}
}
@@ -49,15 +46,14 @@ namespace OpenCand.Repository
using (var connection = new NpgsqlConnection(ConnectionString))
{
await connection.ExecuteAsync(@"
INSERT INTO candidato_mapping (idcandidato, cpf, nome, apelido, sqcandidato, ano, turno, tipoeleicao, siglauf, nomeue, cargo, nrcandidato, sgpartido, resultado)
VALUES (@idcandidato, @cpf, @nome, @apelido, @sqcandidato, @ano, @turno, @tipoeleicao, @siglauf, @nomeue, @cargo, @nrcandidato, @sgpartido, @resultado)
INSERT INTO candidato_mapping (idcandidato, cpf, nome, sqcandidato, ano, turno, tipoeleicao, siglauf, nomeue, cargo, nrcandidato, sgpartido, resultado)
VALUES (@idcandidato, @cpf, @nome, @sqcandidato, @ano, @turno, @tipoeleicao, @siglauf, @nomeue, @cargo, @nrcandidato, @sgpartido, @resultado)
ON CONFLICT DO NOTHING;",
new
{
idcandidato = candidatoMapping.IdCandidato,
cpf = candidatoMapping.Cpf,
nome = candidatoMapping.Nome,
apelido = candidatoMapping.Apelido,
sqcandidato = candidatoMapping.SqCandidato,
ano = candidatoMapping.Ano,
turno = candidatoMapping.Turno,
@@ -72,6 +68,32 @@ namespace OpenCand.Repository
}
}
public async Task AddCandidatoExtAsync(CandidatoExt candidatoExt)
{
using (var connection = new NpgsqlConnection(ConnectionString))
{
await connection.ExecuteAsync(@"
INSERT INTO candidato_ext (idcandidato, ano, apelido, email, estadocivil, escolaridade, ocupacao)
VALUES (@idcandidato, @ano, @apelido, @email, @estadocivil, @escolaridade, @ocupacao)
ON CONFLICT (idcandidato, ano) DO UPDATE SET
apelido = EXCLUDED.apelido,
email = EXCLUDED.email,
estadocivil = EXCLUDED.estadocivil,
escolaridade = EXCLUDED.escolaridade,
ocupacao = EXCLUDED.ocupacao;",
new
{
idcandidato = candidatoExt.IdCandidato,
ano = candidatoExt.Ano,
apelido = candidatoExt.Apelido,
email = candidatoExt.Email,
estadocivil = candidatoExt.EstadoCivil,
escolaridade = candidatoExt.Escolaridade,
ocupacao = candidatoExt.Ocupacao
});
}
}
public async Task<Candidato?> GetCandidatoByCpf(string cpf)
{
using (var connection = new NpgsqlConnection(ConnectionString))

View File

@@ -17,7 +17,7 @@ namespace OpenCand.Services
public async Task AddCandidatoAsync(Candidato candidato)
{
if (candidato == null)
if (candidato == null || candidato.CandidatoExt == null || candidato.Eleicoes == null)
{
throw new ArgumentNullException(nameof(candidato), "Candidato cannot be null");
}
@@ -28,6 +28,7 @@ namespace OpenCand.Services
}
var candidatoMapping = candidato.Eleicoes.First();
var candidatoExt = candidato.CandidatoExt.First();
// Add partido data
if (candidatoMapping.Partido != null)
@@ -52,18 +53,18 @@ namespace OpenCand.Services
candidato.IdCandidato = existingCandidato.IdCandidato;
candidato.Cpf = GetNonEmptyString(existingCandidato.Cpf, candidato.Cpf);
candidato.Email = GetNonEmptyString(existingCandidato.Email, candidato.Email);
candidato.EstadoCivil = GetNonEmptyString(existingCandidato.EstadoCivil, candidato.EstadoCivil);
candidato.Apelido = GetNonEmptyString(existingCandidato.Apelido, candidato.Apelido);
candidato.Escolaridade = GetNonEmptyString(existingCandidato.Escolaridade, candidato.Escolaridade);
candidato.Ocupacao = GetNonEmptyString(existingCandidato.Ocupacao, candidato.Ocupacao);
candidato.Sexo = GetNonEmptyString(existingCandidato.Sexo, candidato.Sexo);
candidatoMapping.IdCandidato = candidato.IdCandidato;
candidatoMapping.Cpf = GetNonEmptyString(candidato.Cpf, candidatoMapping.Cpf);
candidatoExt.IdCandidato = candidato.IdCandidato;
// Update the entries for the existing candidate
await candidatoRepository.AddCandidatoAsync(candidato);
await candidatoRepository.AddCandidatoMappingAsync(candidatoMapping);
await candidatoRepository.AddCandidatoExtAsync(candidatoExt);
return;
}
@@ -79,10 +80,12 @@ namespace OpenCand.Services
if (existingMapping != null)
{
candidato.IdCandidato = existingMapping.IdCandidato;
candidatoExt.IdCandidato = existingMapping.IdCandidato;
candidato.Cpf = GetNonEmptyString(existingMapping.Cpf, candidato.Cpf);
candidato.Apelido = GetNonEmptyString(existingMapping.Apelido, candidato.Apelido);
await candidatoRepository.AddCandidatoAsync(candidato);
await candidatoRepository.AddCandidatoExtAsync(candidatoExt);
return;
}
@@ -94,8 +97,11 @@ namespace OpenCand.Services
candidatoMapping.Cpf = candidato.Cpf;
candidatoMapping.Nome = candidato.Nome;
candidatoExt.IdCandidato = candidato.IdCandidato;
await candidatoRepository.AddCandidatoAsync(candidato);
await candidatoRepository.AddCandidatoMappingAsync(candidatoMapping);
await candidatoRepository.AddCandidatoExtAsync(candidatoExt);
}
public string GetNonEmptyString(string? value1, string? value2)

View File

@@ -30,7 +30,7 @@ namespace OpenCand.Services
}
redeSocial.IdCandidato = candidato.IdCandidato;
redeSocial.Rede = GetRedeSocialType(redeSocial.Link);
redeSocial.Rede = GetRedeSocialType(redeSocial.Link.Trim());
await redeSocialRepository.AddRedeSocialAsync(redeSocial);
}