Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/controllers/relatorios-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ export const obtemDadosDoRelatorioDeColetaPorColetorEIntervaloDeData = async (re
export const obtemDadosDoRelatorioDeLocalDeColeta = async (req, res, next) => {
const { paginacao } = req;
const { limite, pagina, offset } = paginacao;
const { local, dataInicio, dataFim } = req.query;
const { local, dataInicio, dataFim, showCoord } = req.query;

let whereLocal = {};
let whereData = {};
Expand Down Expand Up @@ -533,7 +533,13 @@ export const obtemDadosDoRelatorioDeLocalDeColeta = async (req, res, next) => {
{
model: Especie,
attributes: ['id', 'nome'],
// required: true,
include: [
{
model: Autor,
attributes: ['id', 'nome'],
as: 'autor',
},
],
},
{
model: LocalColeta,
Expand Down Expand Up @@ -582,6 +588,7 @@ export const obtemDadosDoRelatorioDeLocalDeColeta = async (req, res, next) => {
dados: dadosFormatados.locais,
total: dadosFormatados?.quantidadeTotal || 0,
textoFiltro: formataTextFilter(local, dataInicio, dataFim || new Date()),
showCoord: showCoord === 'true',
});
const readable = new Readable();

Expand Down
6 changes: 6 additions & 0 deletions src/controllers/tombos-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,12 @@ export const obterTombo = async (request, response, next) => {
colecaoInicial: tombo.colecoes_anexa !== null ? tombo.colecoes_anexa?.tipo : '',
complementoInicial: tombo.localizacao !== null && tombo.localizacao !== undefined ? tombo.localizacao?.complemento : '',
hcf: tombo.hcf,
coletor: tombo.coletore
? {
id: tombo.coletore?.id,
nome: tombo.coletore?.nome,
}
: null,
situacao: tombo.situacao,
data_tombo: tombo.data_tombo,
observacao: tombo.observacao !== null ? tombo.observacao : '',
Expand Down
67 changes: 53 additions & 14 deletions src/helpers/formata-dados-relatorio.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,33 +218,33 @@ export function agruparPorFamiliaGeneroEspecie(dados) {
return resultado;
}

function formatarCoordenadas(lat, lon) {
const latDir = lat >= 0 ? 'N' : 'S';
const lonDir = lon >= 0 ? 'E' : 'W';
return `${Math.abs(lat).toFixed(4)}° ${latDir}, ${Math.abs(lon).toFixed(4)}° ${lonDir}`;
}

export function agruparPorLocal(dados) {
const agrupado = {};
let quantidadeTotal = 0;

dados.sort((a, b) => a?.familia?.nome.localeCompare(b?.familia?.nome)).forEach(entradaOriginal => {
dados.sort((a, b) => {
const familia = a?.familia?.nome.localeCompare(b?.familia?.nome);
if (familia !== 0) return familia;

const genero = a?.genero?.nome.localeCompare(b?.genero?.nome);
if (genero !== 0) return genero;

return a?.especy?.nome.localeCompare(b?.especy?.nome);
}).forEach(entradaOriginal => {
const locaisColetum = entradaOriginal.locais_coletum;
const cidade = locaisColetum?.cidade;
const estado = cidade?.estado?.nome || 'Desconhecido';
const estadoSigla = cidade?.estado?.sigla || '-';
const municipio = cidade?.nome || 'Desconhecido';
const local = locaisColetum?.descricao?.trim() || 'Local não informado';

const coordenadasFormatadas = cidade
? formatarCoordenadas(cidade.latitude, cidade.longitude)
: 'Coordenadas indisponíveis';

const chave = `${estado} > ${municipio} > ${local}`;

const entrada = {
...entradaOriginal,
coordenadasFormatadas,
latitude: cidade?.latitude || null,
longitude: cidade?.longitude || null,
autor: entradaOriginal.especy?.autor?.nome || 'Não Informado',
};

if (!agrupado[chave]) {
Expand All @@ -253,7 +253,8 @@ export function agruparPorLocal(dados) {
estadoSigla,
municipio,
local,
coordenadas: coordenadasFormatadas,
latitude: cidade?.latitude || null,
longitude: cidade?.longitude || null,
quantidadeRegistros: 0,
registros: [],
};
Expand All @@ -262,17 +263,55 @@ export function agruparPorLocal(dados) {
agrupado[chave].registros.push(entrada);
agrupado[chave].quantidadeRegistros += 1;
quantidadeTotal += 1;

});

// Transforma o objeto em um array
const locais = Object.values(agrupado);

const locaisComResumo = adicionarResumoTaxonomicoPorLocal(locais);

return {
locais, // agora é um array
locais: locaisComResumo,
quantidadeTotal,
};
}

export function adicionarResumoTaxonomicoPorLocal(locaisAgrupados) {
return locaisAgrupados.map(local => {
const familias = new Set();
const generos = new Set();
const especies = new Set();

for (const reg of local.registros) {
// família
const familiaId = reg.familia_id ?? reg.familia?.id;
if (familiaId != null) {
familias.add(familiaId);
}

// gênero
const generoId = reg.genero_id ?? reg.genero?.id;
if (generoId != null) {
generos.add(generoId);
}

// espécie
const especieId = reg.especie_id ?? reg.especy?.id;
if (especieId != null) {
especies.add(especieId);
}
}

return {
...local,
quantidadeFamilias: familias.size,
quantidadeGeneros: generos.size,
quantidadeEspecies: especies.size,
};
});
}

export function agruparPorGenero(dados) {
return dados.map(familia => {
const generosMap = new Map();
Expand Down
12 changes: 9 additions & 3 deletions src/reports/assets/styles/root.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,17 @@ tfoot {
.grupoLocalColeta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}

.grupoLocalColeta div {
width: calc(33%); /* 33% menos 2.5rem de espaçamento das margens */
.grupoLocalColeta div:first-child {
display: flex;
gap: 0.5rem;
width: calc(40);
}

.grupoLocalColeta div:last-child {
width: calc(60);
}

.grupoLocalColeta div h1 {
Expand Down
58 changes: 41 additions & 17 deletions src/reports/templates/LocaisColeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ interface Registro {
genero: {
nome: string;
}
coordenadasFormatadas: string;
latitude: number | null;
longitude: number | null;
autor?: string;
}

interface LocaisColeta {
Expand All @@ -31,19 +33,23 @@ interface LocaisColeta {
local: string;
registros: Registro[];
quantidadeRegistros: number;
quantidadeEspecies?: number;
quantidadeGeneros?: number;
quantidadeFamilias?: number;
}

interface RelacaoLocaisColetaProps {
dados: LocaisColeta[];
total?: number;
textoFiltro?: string;
showCoord?: boolean;
}

function RelacaoLocaisColeta({ dados, total, textoFiltro }: RelacaoLocaisColetaProps) {
const renderTotalizador = (geral: boolean, qtd?: number) => {
function RelacaoLocaisColeta({ dados, total, textoFiltro, showCoord = false }: RelacaoLocaisColetaProps) {
const renderTotalizador = (geral: boolean, qtd?: number, qtdEspecies?: number, qtdGeneros?: number, qtdFamilias?: number) => {
return (
<div style={{ marginTop: '1em', borderTop: '1px solid #000', paddingTop: '0.5em' }}>
Total {geral ? 'geral' : 'do local'}: {geral ? total : qtd}
Total {geral ? 'geral' : 'do local'}: {geral ? total : qtd} {!geral ? `(Famílias: ${qtdFamilias || 0}, Gêneros: ${qtdGeneros || 0}, Espécies: ${qtdEspecies || 0})`: ''}
</div>
)
}
Expand All @@ -57,26 +63,45 @@ function RelacaoLocaisColeta({ dados, total, textoFiltro }: RelacaoLocaisColetaP
return `${data_coleta_dia}/${romanoMeses[data_coleta_mes - 1]}/${data_coleta_ano}`;
}

const converteDecimalParaDMS = (decimal: number | null, isLatitude = true) => {
if (decimal === null || decimal === undefined) {
return '';
}

const abs = Math.abs(decimal);
const graus = Math.floor(abs);
const minutosDecimal = (abs - graus) * 60;
const minutos = Math.floor(minutosDecimal);
const segundos = ((minutosDecimal - minutos) * 60).toFixed(2);

let hemisferio;
if (isLatitude) {
hemisferio = decimal >= 0 ? 'N' : 'S';
} else {
hemisferio = decimal >= 0 ? 'E' : 'W';
}

return `${graus}° ${minutos}' ${segundos}" ${hemisferio}`;
};

const obtemCordenadas = (registro: Registro) => {
const { coordenadasFormatadas } = registro;
if (!coordenadasFormatadas) return { latitude: '', longitude: '' };
const [latitude, longitude] = coordenadasFormatadas.split(',');
return {
latitude: latitude ? latitude.trim() : '',
longitude: longitude ? longitude.trim() : ''
latitude: registro.latitude ? converteDecimalParaDMS(registro.latitude, true) : '',
longitude: registro.longitude ? converteDecimalParaDMS(registro.longitude, false) : ''
};
}

const renderTable = (registros: Registro[]) => {
return (
<table>
<table style={{ fontSize: showCoord ? '0.9em' : '1.1em' }}>
<thead>
<tr>
<th>Data Coleta</th>
<th>Família</th>
<th>Espécie</th>
<th>Latitude</th>
<th>Longitude</th>
<th>Autor</th>
{showCoord && <th>Latitude</th>}
{showCoord && <th>Longitude</th>}
<th style={{ textAlign: 'right' }}>Nº do Tombo</th>
</tr>
</thead>
Expand All @@ -89,8 +114,9 @@ function RelacaoLocaisColeta({ dados, total, textoFiltro }: RelacaoLocaisColetaP
<td>{criaData(item)}</td>
<td>{familia?.nome}</td>
<td style={{ fontStyle: 'italic' }}>{genero?.nome} {especy?.nome}</td>
<td>{cordenadas.latitude}</td>
<td>{cordenadas.longitude}</td>
<td>{item.autor}</td>
{showCoord && <td>{cordenadas.latitude}</td>}
{showCoord && <td>{cordenadas.longitude}</td>}
<td style={{ textAlign: 'right' }}>{item.hcf}</td>
</tr>
)
Expand All @@ -106,16 +132,14 @@ function RelacaoLocaisColeta({ dados, total, textoFiltro }: RelacaoLocaisColetaP
<div key={item.local} className="grupoLocalColeta">
<div>
<h1>UF.: {item.estadoSigla}</h1>
</div>
<div>
<h1>Município: {item.municipio}</h1>
</div>
<div>
<h1>Local: {item.local}</h1>
</div>
</div>
{renderTable(item.registros)}
{renderTotalizador(false, item.quantidadeRegistros)}
{renderTotalizador(false, item.quantidadeRegistros, item.quantidadeEspecies, item.quantidadeGeneros, item.quantidadeFamilias)}
</div>
)
}
Expand Down
Loading