partido + melhorias
This commit is contained in:
parent
146495c07b
commit
a3d67198af
@ -58,6 +58,17 @@ namespace OpenCand.Repository
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Partido?> GetPartidoBySigla(string sigla)
|
||||
{
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
{
|
||||
var query = @"
|
||||
SELECT * FROM partido
|
||||
WHERE sigla = @sigla";
|
||||
return await connection.QueryFirstOrDefaultAsync<Partido>(query, new { sigla });
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<RedeSocial>?> GetCandidatoRedeSocialById(Guid idcandidato)
|
||||
{
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
|
@ -21,7 +21,7 @@ namespace OpenCand.API.Repository
|
||||
(SELECT COUNT(*) FROM bem_candidato) AS TotalBemCandidatos,
|
||||
(SELECT SUM(valor) FROM bem_candidato) AS TotalValorBemCandidatos,
|
||||
(SELECT COUNT(*) FROM rede_social) AS TotalRedesSociais,
|
||||
(SELECT COUNT(DISTINCT ano) FROM bem_candidato) AS TotalEleicoes;");
|
||||
(SELECT COUNT(DISTINCT ano) FROM candidato_mapping) AS TotalEleicoes;");
|
||||
return stats ?? new OpenCandStats();
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,11 @@ namespace OpenCand.API.Services
|
||||
var result = await candidatoRepository.GetCandidatoAsync(idcandidato);
|
||||
var eleicoes = await candidatoRepository.GetCandidatoMappingById(idcandidato);
|
||||
|
||||
foreach (var eleicao in eleicoes)
|
||||
{
|
||||
eleicao.Partido = await candidatoRepository.GetPartidoBySigla(eleicao.Sgpartido);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Candidato with ID {idcandidato} not found.");
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System.IO.Pipes;
|
||||
|
||||
namespace OpenCand.Core.Models
|
||||
{
|
||||
public class Candidato
|
||||
@ -41,6 +43,9 @@ namespace OpenCand.Core.Models
|
||||
public string Cargo { get; set; }
|
||||
public string NrCandidato { get; set; }
|
||||
public string Resultado { get; set; }
|
||||
public string Sgpartido { get; set; }
|
||||
|
||||
public Partido? Partido { get; set; } // Nullable to allow for candidates without a party
|
||||
}
|
||||
|
||||
public class RedeSocial
|
||||
|
9
OpenCand.Core/Models/Partido.cs
Normal file
9
OpenCand.Core/Models/Partido.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace OpenCand.Core.Models
|
||||
{
|
||||
public class Partido
|
||||
{
|
||||
public string Sigla { get; set; }
|
||||
public string Nome { get; set; }
|
||||
public int Numero { get; set; }
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="33.0.1" />
|
||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
|
||||
<PackageReference Include="Npgsql" Version="8.0.2" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.5" />
|
||||
|
@ -4,30 +4,9 @@ namespace OpenCand.Parser.Models
|
||||
{
|
||||
public class BemCandidatoCSV
|
||||
{
|
||||
[Name("DT_GERACAO")]
|
||||
public string DataGeracao { get; set; }
|
||||
|
||||
[Name("HH_GERACAO")]
|
||||
public string HoraGeracao { get; set; }
|
||||
|
||||
[Name("ANO_ELEICAO")]
|
||||
public int AnoEleicao { get; set; }
|
||||
|
||||
[Name("CD_TIPO_ELEICAO")]
|
||||
public int CodigoTipoEleicao { get; set; }
|
||||
|
||||
[Name("NM_TIPO_ELEICAO")]
|
||||
public string NomeTipoEleicao { get; set; }
|
||||
|
||||
[Name("CD_ELEICAO")]
|
||||
public int CodigoEleicao { get; set; }
|
||||
|
||||
[Name("DS_ELEICAO")]
|
||||
public string DescricaoEleicao { get; set; }
|
||||
|
||||
[Name("DT_ELEICAO")]
|
||||
public string DataEleicao { get; set; }
|
||||
|
||||
[Name("SG_UF")]
|
||||
public string SiglaUF { get; set; }
|
||||
|
||||
@ -40,12 +19,9 @@ namespace OpenCand.Parser.Models
|
||||
[Name("SQ_CANDIDATO")]
|
||||
public string SequencialCandidato { get; set; }
|
||||
|
||||
[Name("NR_ORDEM_BEM_CANDIDATO")]
|
||||
[Name("NR_ORDEM_BEM_CANDIDATO", "NR_ORDEM_CANDIDATO")]
|
||||
public int NumeroOrdemBemCandidato { get; set; }
|
||||
|
||||
[Name("CD_TIPO_BEM_CANDIDATO")]
|
||||
public int CodigoTipoBemCandidato { get; set; }
|
||||
|
||||
[Name("DS_TIPO_BEM_CANDIDATO")]
|
||||
public string DescricaoTipoBemCandidato { get; set; }
|
||||
|
||||
|
@ -5,48 +5,18 @@ namespace OpenCand.Parser.Models
|
||||
{
|
||||
public class CandidatoCSV
|
||||
{
|
||||
[Name("DT_GERACAO")]
|
||||
public string DataGeracao { get; set; }
|
||||
|
||||
[Name("HH_GERACAO")]
|
||||
public string HoraGeracao { get; set; }
|
||||
|
||||
[Name("ANO_ELEICAO")]
|
||||
public int AnoEleicao { get; set; }
|
||||
|
||||
[Name("CD_TIPO_ELEICAO")]
|
||||
public int CodigoTipoEleicao { get; set; }
|
||||
|
||||
[Name("NM_TIPO_ELEICAO")]
|
||||
public string NomeTipoEleicao { get; set; }
|
||||
|
||||
[Name("NR_TURNO")]
|
||||
public int NumeroTurno { get; set; }
|
||||
|
||||
[Name("CD_ELEICAO")]
|
||||
public int CodigoEleicao { get; set; }
|
||||
|
||||
[Name("DS_ELEICAO")]
|
||||
public string DescricaoEleicao { get; set; }
|
||||
|
||||
[Name("DT_ELEICAO")]
|
||||
public string DataEleicao { get; set; }
|
||||
|
||||
[Name("TP_ABRANGENCIA")]
|
||||
public string TipoAbrangencia { get; set; }
|
||||
|
||||
[Name("SG_UF")]
|
||||
public string SiglaUF { get; set; }
|
||||
|
||||
[Name("SG_UE")]
|
||||
public string SiglaUE { get; set; }
|
||||
|
||||
[Name("NM_UE")]
|
||||
public string NomeUE { get; set; }
|
||||
|
||||
[Name("CD_CARGO")]
|
||||
public int CodigoCargo { get; set; }
|
||||
|
||||
[Name("DS_CARGO")]
|
||||
public string DescricaoCargo { get; set; }
|
||||
|
||||
@ -59,21 +29,12 @@ namespace OpenCand.Parser.Models
|
||||
[Name("NM_CANDIDATO")]
|
||||
public string NomeCandidato { get; set; }
|
||||
|
||||
[Name("NM_URNA_CANDIDATO")]
|
||||
public string NomeUrnaCandidato { get; set; }
|
||||
|
||||
[Name("NM_SOCIAL_CANDIDATO")]
|
||||
public string NomeSocialCandidato { get; set; }
|
||||
|
||||
[Name("NR_CPF_CANDIDATO")]
|
||||
public string CPFCandidato { get; set; }
|
||||
|
||||
[Name("DS_EMAIL", "NM_EMAIL")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Name("SG_UF_NASCIMENTO")]
|
||||
public string SiglaUFNascimento { get; set; }
|
||||
|
||||
[Name("DT_NASCIMENTO")]
|
||||
public string DataNascimento { get; set; }
|
||||
|
||||
@ -91,5 +52,14 @@ namespace OpenCand.Parser.Models
|
||||
|
||||
[Name("DS_SIT_TOT_TURNO")]
|
||||
public string SituacaoTurno { get; set; }
|
||||
|
||||
[Name("NR_PARTIDO")]
|
||||
public int NumeroPartido { get; set; }
|
||||
|
||||
[Name("SG_PARTIDO")]
|
||||
public string SiglaPartido { get; set; }
|
||||
|
||||
[Name("NM_PARTIDO")]
|
||||
public string NomePartido { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace OpenCand.Parser.Services
|
||||
using var csv = new CsvReader(reader, parserConfig);
|
||||
var po = new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = 100
|
||||
MaxDegreeOfParallelism = 25
|
||||
};
|
||||
|
||||
csv.Context.RegisterClassMap<CandidatoMap>();
|
||||
@ -111,6 +111,12 @@ namespace OpenCand.Parser.Services
|
||||
Cargo = record.DescricaoCargo,
|
||||
NrCandidato = record.NumeroCandidato,
|
||||
Resultado = record.SituacaoTurno,
|
||||
Partido = new Partido
|
||||
{
|
||||
Sigla = record.SiglaPartido,
|
||||
Nome = record.NomePartido,
|
||||
Numero = record.NumeroPartido,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenCand.Config;
|
||||
using OpenCand.ETL.Repository;
|
||||
using OpenCand.Parser;
|
||||
using OpenCand.Parser.Services;
|
||||
using OpenCand.Repository;
|
||||
@ -64,6 +65,7 @@ namespace OpenCand
|
||||
services.AddTransient<CandidatoRepository>();
|
||||
services.AddTransient<BemCandidatoRepository>();
|
||||
services.AddTransient<RedeSocialRepository>();
|
||||
services.AddTransient<PartidoRepository>();
|
||||
services.AddTransient<CsvFixerService>();
|
||||
});
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ namespace OpenCand.Repository
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
{
|
||||
await connection.ExecuteAsync(@"
|
||||
INSERT INTO candidato_mapping (idcandidato, cpf, nome, sqcandidato, ano, tipoeleicao, siglauf, nomeue, cargo, nrcandidato, resultado)
|
||||
VALUES (@idcandidato, @cpf, @nome, @sqcandidato, @ano, @tipoeleicao, @siglauf, @nomeue, @cargo, @nrcandidato, @resultado);",
|
||||
INSERT INTO candidato_mapping (idcandidato, cpf, nome, sqcandidato, ano, tipoeleicao, siglauf, nomeue, cargo, nrcandidato, sgpartido, resultado)
|
||||
VALUES (@idcandidato, @cpf, @nome, @sqcandidato, @ano, @tipoeleicao, @siglauf, @nomeue, @cargo, @nrcandidato, @sgpartido, @resultado);",
|
||||
new
|
||||
{
|
||||
idcandidato = candidatoMapping.IdCandidato,
|
||||
@ -61,6 +61,7 @@ namespace OpenCand.Repository
|
||||
nomeue = candidatoMapping.NomeUE,
|
||||
nrcandidato = candidatoMapping.NrCandidato,
|
||||
cargo = candidatoMapping.Cargo,
|
||||
sgpartido = candidatoMapping.Partido?.Sigla,
|
||||
resultado = candidatoMapping.Resultado
|
||||
});
|
||||
}
|
||||
@ -78,15 +79,15 @@ namespace OpenCand.Repository
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<CandidatoMapping>?> GetCandidatoMappingByNome(string nome)
|
||||
public async Task<List<CandidatoMapping>?> GetCandidatoMappingByNome(string nome, string sqcandidato, int ano, string siglauf, string nomeue, string nrcandidato)
|
||||
{
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
{
|
||||
var query = @"
|
||||
SELECT idcandidato, cpf, nome, sqcandidato, ano, tipoeleicao, siglauf, nomeue, cargo, nrcandidato, resultado
|
||||
FROM candidato_mapping
|
||||
WHERE nome = @nome";
|
||||
return (await connection.QueryAsync<CandidatoMapping>(query, new { nome })).AsList();
|
||||
WHERE nome = @nome AND sqcandidato = @sqcandidato AND ano = @ano AND siglauf = @siglauf AND nomeue = @nomeue AND nrcandidato = @nrcandidato";
|
||||
return (await connection.QueryAsync<CandidatoMapping>(query, new { nome, sqcandidato, ano, siglauf, nomeue, nrcandidato })).AsList();
|
||||
}
|
||||
}
|
||||
|
||||
|
49
OpenCand.ETL/Repository/PartidoRepository.cs
Normal file
49
OpenCand.ETL/Repository/PartidoRepository.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using OpenCand.Core.Models;
|
||||
using OpenCand.Repository;
|
||||
|
||||
namespace OpenCand.ETL.Repository
|
||||
{
|
||||
public class PartidoRepository : BaseRepository
|
||||
{
|
||||
// Memory cache for partido data
|
||||
private readonly MemoryCache partidoCache = new MemoryCache(new MemoryCacheOptions());
|
||||
|
||||
public PartidoRepository(IConfiguration configuration) : base(configuration)
|
||||
{
|
||||
// Initialize the cache with a sliding expiration of 5 minutes
|
||||
partidoCache = new MemoryCache(new MemoryCacheOptions
|
||||
{
|
||||
ExpirationScanFrequency = TimeSpan.FromMinutes(5)
|
||||
});
|
||||
}
|
||||
|
||||
public async Task AddPartidoAsync(Partido partido)
|
||||
{
|
||||
// Check if partido is already cached
|
||||
if (partidoCache.TryGetValue(partido.Sigla, out Partido? cachedPartido))
|
||||
{
|
||||
// If partido is already cached, no need to insert again
|
||||
return;
|
||||
}
|
||||
|
||||
using (var connection = new Npgsql.NpgsqlConnection(ConnectionString))
|
||||
{
|
||||
await connection.ExecuteAsync(@"
|
||||
INSERT INTO partido (sigla, nome, numero)
|
||||
VALUES (@sigla, @nome, @numero)
|
||||
ON CONFLICT DO NOTHING;",
|
||||
new
|
||||
{
|
||||
sigla = partido.Sigla,
|
||||
nome = partido.Nome,
|
||||
numero = partido.Numero
|
||||
});
|
||||
|
||||
partidoCache.Set(partido.Sigla, partido, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using OpenCand.Core.Models;
|
||||
using OpenCand.ETL.Repository;
|
||||
using OpenCand.Repository;
|
||||
|
||||
namespace OpenCand.Services
|
||||
@ -6,10 +7,12 @@ namespace OpenCand.Services
|
||||
public class CandidatoService
|
||||
{
|
||||
private readonly CandidatoRepository candidatoRepository;
|
||||
private readonly PartidoRepository partidoRepository;
|
||||
|
||||
public CandidatoService(CandidatoRepository candidatoRepository)
|
||||
public CandidatoService(CandidatoRepository candidatoRepository, PartidoRepository partidoRepository)
|
||||
{
|
||||
this.candidatoRepository = candidatoRepository;
|
||||
this.partidoRepository = partidoRepository;
|
||||
}
|
||||
|
||||
public async Task AddCandidatoAsync(Candidato candidato)
|
||||
@ -26,11 +29,23 @@ namespace OpenCand.Services
|
||||
|
||||
var candidatoMapping = candidato.Eleicoes.First();
|
||||
|
||||
// Add partido data
|
||||
if (candidatoMapping.Partido != null)
|
||||
{
|
||||
await partidoRepository.AddPartidoAsync(candidatoMapping.Partido);
|
||||
}
|
||||
|
||||
List<CandidatoMapping>? mappings = null;
|
||||
CandidatoMapping? existingMapping = null;
|
||||
if (candidato.Cpf == null || candidato.Cpf.Length != 11)
|
||||
{
|
||||
mappings = await candidatoRepository.GetCandidatoMappingByNome(candidato.Nome);
|
||||
// If CPF is not provided or invalid, we STRICTLY search by name and other properties
|
||||
mappings = await candidatoRepository.GetCandidatoMappingByNome(candidato.Nome,
|
||||
candidato.SqCandidato,
|
||||
candidatoMapping.Ano,
|
||||
candidatoMapping.SiglaUF,
|
||||
candidatoMapping.NomeUE,
|
||||
candidatoMapping.NrCandidato);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
15
db/db.sql
15
db/db.sql
@ -2,6 +2,7 @@ DROP TABLE IF EXISTS bem_candidato CASCADE;
|
||||
DROP TABLE IF EXISTS candidato_mapping CASCADE;
|
||||
DROP TABLE IF EXISTS rede_social CASCADE;
|
||||
DROP TABLE IF EXISTS candidato CASCADE;
|
||||
DROP TABLE IF EXISTS partido CASCADE;
|
||||
|
||||
CREATE TABLE candidato (
|
||||
idcandidato UUID NOT NULL PRIMARY KEY,
|
||||
@ -27,6 +28,7 @@ CREATE TABLE candidato_mapping (
|
||||
siglauf VARCHAR(2),
|
||||
nomeue VARCHAR(100),
|
||||
cargo VARCHAR(50),
|
||||
sgpartido VARCHAR(10),
|
||||
nrcandidato VARCHAR(20),
|
||||
resultado VARCHAR(50),
|
||||
CONSTRAINT pk_candidato_mapping PRIMARY KEY (idcandidato, ano, siglauf, nomeue, cargo, nrcandidato, resultado),
|
||||
@ -37,6 +39,7 @@ CREATE INDEX idx_candidato_mapping_nome ON candidato_mapping (nome);
|
||||
CREATE INDEX idx_candidato_mapping_ano ON candidato_mapping (ano);
|
||||
CREATE INDEX idx_candidato_mapping_sqcandidato ON candidato_mapping (sqcandidato);
|
||||
|
||||
---- Table for storing assets of candidates
|
||||
CREATE TABLE bem_candidato (
|
||||
idcandidato UUID NOT NULL,
|
||||
ano INT NOT NULL,
|
||||
@ -50,6 +53,7 @@ ALTER TABLE bem_candidato ADD CONSTRAINT pk_bem_candidato PRIMARY KEY (idcandida
|
||||
CREATE INDEX idx_bem_candidato_idcandidato ON bem_candidato (idcandidato);
|
||||
CREATE INDEX idx_bem_candidato_valor ON bem_candidato (valor);
|
||||
|
||||
---- Table for storing social media links of candidates
|
||||
CREATE TABLE rede_social (
|
||||
idcandidato UUID NOT NULL,
|
||||
rede VARCHAR(50) NOT NULL,
|
||||
@ -59,4 +63,13 @@ CREATE TABLE rede_social (
|
||||
CONSTRAINT pk_rede_social PRIMARY KEY (idcandidato, rede, siglauf, ano),
|
||||
CONSTRAINT fk_rede_social_candidato FOREIGN KEY (idcandidato) REFERENCES candidato(idcandidato) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
CREATE INDEX idx_rede_social_idcandidato ON rede_social (idcandidato);
|
||||
CREATE INDEX idx_rede_social_idcandidato ON rede_social (idcandidato);
|
||||
|
||||
---- Table for storing party information
|
||||
CREATE TABLE partido (
|
||||
sigla VARCHAR(10) NOT NULL PRIMARY KEY,
|
||||
nome VARCHAR(255) NOT NULL,
|
||||
numero INT NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_partido_nome ON partido (nome);
|
||||
CREATE INDEX idx_partido_numero ON partido (numero);
|
Loading…
x
Reference in New Issue
Block a user