From 93985725141509a4ba3ca0c912a504c661ffd92c Mon Sep 17 00:00:00 2001 From: Edvaldo Szymonek Date: Thu, 26 Feb 2026 13:34:29 -0300 Subject: [PATCH 01/10] cria tag e release automaticamente quando algo vai para master (#363) --- .github/workflows/release.yml | 65 +++++++++++++++++++++++++ script/database-sync/database-sync.sh | 22 ++++----- script/database-sync/docker-compose.yml | 6 +-- script/database-sync/entrypoint.sh | 2 +- 4 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..cbcb85f7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Date Release + +on: + push: + branches: + - master + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # important to fetch all tags + + - name: Generate version based on date + id: version + run: | + BASE_VERSION=$(date -u +'%Y.%-m.%-d') + echo "Base version: $BASE_VERSION" + + # Fetch tags + git fetch --tags + + # Find existing tags for today + MATCHING_TAGS=$(git tag -l "${BASE_VERSION}*") + + if [ -z "$MATCHING_TAGS" ]; then + FINAL_VERSION=$BASE_VERSION + else + # Extract numeric suffixes + MAX_SUFFIX=$(echo "$MATCHING_TAGS" \ + | grep -E "^${BASE_VERSION}-[0-9]+$" \ + | sed -E "s/^${BASE_VERSION}-//" \ + | sort -n \ + | tail -n 1) + + if [ -z "$MAX_SUFFIX" ]; then + FINAL_VERSION="${BASE_VERSION}-1" + else + NEXT=$((MAX_SUFFIX + 1)) + FINAL_VERSION="${BASE_VERSION}-${NEXT}" + fi + fi + + echo "Final version: $FINAL_VERSION" + echo "version=$FINAL_VERSION" >> $GITHUB_OUTPUT + + - name: Create tag + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git tag ${{ steps.version.outputs.version }} + git push origin ${{ steps.version.outputs.version }} + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.version.outputs.version }} + name: ${{ steps.version.outputs.version }} diff --git a/script/database-sync/database-sync.sh b/script/database-sync/database-sync.sh index 866b6bf6..a6b26e5a 100755 --- a/script/database-sync/database-sync.sh +++ b/script/database-sync/database-sync.sh @@ -1,7 +1,6 @@ #!/bin/bash -set -e -set -o pipefail +set -euo pipefail EXCLUDE_PARAMS="" if [ -n "$SYNC_EXCLUDE_TABLES" ]; then @@ -9,7 +8,7 @@ if [ -n "$SYNC_EXCLUDE_TABLES" ]; then for table in "${TABLES[@]}"; do table=$(echo "$table" | xargs) if [ -n "$table" ]; then - EXCLUDE_PARAMS="$EXCLUDE_PARAMS --exclude-table=$table" + EXCLUDE_PARAMS="$EXCLUDE_PARAMS --exclude-table-data=$table" fi done echo "Excluding tables: $SYNC_EXCLUDE_TABLES" @@ -21,18 +20,17 @@ PGPASSWORD="$SYNC_SOURCE_PASSWORD" pg_dump \ -h "$SYNC_SOURCE_HOST" \ -p "$SYNC_SOURCE_PORT" \ -U "$SYNC_SOURCE_USER" \ - --clean \ - --if-exists \ - --no-owner \ - --no-acl \ - --format plain \ - $EXCLUDE_PARAMS \ - "$SYNC_SOURCE_DATABASE" | \ -PGPASSWORD="$SYNC_DEST_PASSWORD" psql \ + -d "$SYNC_SOURCE_DATABASE" \ + -Fc \ + $EXCLUDE_PARAMS | \ +PGPASSWORD="$SYNC_DEST_PASSWORD" pg_restore \ -h "$SYNC_DEST_HOST" \ -p "$SYNC_DEST_PORT" \ -U "$SYNC_DEST_USER" \ -d "$SYNC_DEST_DATABASE" \ - --single-transaction + --clean \ + --if-exists \ + --no-owner \ + --no-privileges echo "Synchronization completed successfully: ${SYNC_SOURCE_DATABASE} -> ${SYNC_DEST_DATABASE}" diff --git a/script/database-sync/docker-compose.yml b/script/database-sync/docker-compose.yml index 2aa8e08c..fbea10e4 100644 --- a/script/database-sync/docker-compose.yml +++ b/script/database-sync/docker-compose.yml @@ -4,16 +4,16 @@ services: container_name: database-sync build: . environment: - SYNC_SOURCE_HOST: "127.0.0.1" + SYNC_SOURCE_HOST: "172.26.136.2" SYNC_SOURCE_PORT: "5432" SYNC_SOURCE_USER: "postgres" SYNC_SOURCE_PASSWORD: "masterkey" SYNC_SOURCE_DATABASE: "herbario_prod" - SYNC_DEST_HOST: "127.0.0.1" + SYNC_DEST_HOST: "172.26.136.2" SYNC_DEST_PORT: "5432" SYNC_DEST_USER: "postgres" SYNC_DEST_PASSWORD: "masterkey" SYNC_DEST_DATABASE: "herbario_dev" - SYNC_EXCLUDE_TABLES: "" + SYNC_EXCLUDE_TABLES: "public.usuarios" CRON_SCHEDULE: "*/2 * * * *" TZ: "America/Sao_Paulo" diff --git a/script/database-sync/entrypoint.sh b/script/database-sync/entrypoint.sh index 69adea0f..52ec2108 100755 --- a/script/database-sync/entrypoint.sh +++ b/script/database-sync/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/sh -echo "Configuring cron job with schedule: $CRON_SCHEDULE (timezone: $TZ)" +echo "Configuring cron job with schedule: $CRON_SCHEDULE ($TZ)" # ensure environment variables are passed to the cron job printenv | grep -E "^SYNC_" >> /etc/environment From 58e44fa455b29aa221e0df7d0972a42f06f2b69f Mon Sep 17 00:00:00 2001 From: Moran Date: Mon, 2 Mar 2026 14:25:35 -0300 Subject: [PATCH 02/10] =?UTF-8?q?incremento=20do=20numero=20da=20coleta=20?= =?UTF-8?q?agora=20=C3=A9=20feito=20na=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/tombos-controller.js | 15 ++++++++++----- src/routes/tombos.js | 18 +++++++----------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/controllers/tombos-controller.js b/src/controllers/tombos-controller.js index 60597178..52293f2b 100644 --- a/src/controllers/tombos-controller.js +++ b/src/controllers/tombos-controller.js @@ -17,7 +17,7 @@ import { aprovarPendencia } from './pendencias-controller'; const { Solo, Relevo, Cidade, Estado, Vegetacao, FaseSucessional, Pais, Tipo, LocalColeta, Familia, sequelize, Genero, Subfamilia, Autor, Coletor, Variedade, Subespecie, TomboFoto, Identificador, - ColecaoAnexa, Especie, Herbario, Tombo, Alteracao, TomboIdentificador, ColetorComplementar, Sequelize: { Op }, + ColecaoAnexa, Especie, Herbario, Tombo, Alteracao, TomboIdentificador, ColetorComplementar, Sequelize: { Op, fn, col }, } = models; function parseDataTombo(valor) { @@ -1551,15 +1551,20 @@ export const getNumeroColetor = (request, response, next) => { Promise.resolve() .then(() => - Tombo.findAll({ + Tombo.findOne({ where: { coletor_id: idColetor, }, - attributes: ['hcf', 'numero_coleta'], + attributes: [ + [fn('MAX', col('numero_coleta')), 'max_numero_coleta'], + ], + raw: true, }), ) - .then(tombos => { - response.status(codigos.BUSCAR_UM_ITEM).json(tombos); + .then(resultado => { + const maxNumero = resultado?.max_numero_coleta; + const proximoNumero = maxNumero ? Number(maxNumero) + 1 : 1; + response.status(codigos.BUSCAR_UM_ITEM).json({ proximo_numero_coleta: proximoNumero }); }) .catch(next); }; diff --git a/src/routes/tombos.js b/src/routes/tombos.js index e1006ab1..114938d4 100644 --- a/src/routes/tombos.js +++ b/src/routes/tombos.js @@ -76,7 +76,7 @@ export default app => { * @swagger * /tombos/numeroColetor/{idColetor}: * get: - * summary: Obtém o número do coletor pelo ID + * summary: Obtém o próximo número de coleta do coletor * tags: [Tombos] * parameters: * - in: path @@ -87,19 +87,15 @@ export default app => { * description: ID do coletor * responses: * 200: - * description: Lista de HCF e números de coleta retornada com sucesso + * description: Próximo número de coleta retornado com sucesso * content: * application/json: * schema: - * type: array - * items: - * type: object - * properties: - * hcf: - * type: integer - * numero_coleta: - * type: integer - * nullable: true + * type: object + * properties: + * proximo_numero_coleta: + * type: integer + * description: Próximo número de coleta disponível (MAX + 1) * '404': * $ref: '#/components/responses/NotFound' * '500': From 49d90cca119a35b366314ad4b1efda3f53edd39a Mon Sep 17 00:00:00 2001 From: Edvaldo Szymonek Date: Wed, 4 Mar 2026 19:42:42 -0300 Subject: [PATCH 03/10] corrige ordem da subespecie e variedade na ficha tombo --- src/views/ficha-tombo.ejs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/views/ficha-tombo.ejs b/src/views/ficha-tombo.ejs index 28615bcd..7fa8e1ea 100644 --- a/src/views/ficha-tombo.ejs +++ b/src/views/ficha-tombo.ejs @@ -141,7 +141,7 @@
Herbário da Universidade Tecnológica Federal do Paraná -
Campus Campo Mourão
- +
HCF <%- tombo.hcf %> @@ -156,7 +156,7 @@
<% if (familia && familia.nome) { %>
- Família: + Família: <% if (familia && familia.nome.toLowerCase() !== 'indeterminada') { %> <%- tombo.familia.nome %> <% } %> @@ -177,18 +177,18 @@ <% if (especie && especie.autor) { %> <%- especie.autor.nome %> <% } %> - <% if (variedade && variedade.nome) { %> - var. <%- variedade.nome %> - <% } %> - <% if (variedade && variedade.autor) { %> - <%- variedade.autor.nome %> - <% } %> <% if (subespecie && subespecie.nome) { %> subsp. <%- subespecie.nome %> <% } %> <% if (subespecie && subespecie.autor) { %> <%- subespecie.autor.nome %> <% } %> + <% if (variedade && variedade.nome) { %> + var. <%- variedade.nome %> + <% } %> + <% if (variedade && variedade.autor) { %> + <%- variedade.autor.nome %> + <% } %>
@@ -275,7 +275,7 @@
nº: <%- tombo.numero_coleta %>
- +
Data: <%- romano_data_coleta %>
From 7e643fa6f439d8cf7e204b811d696941f4eb2873 Mon Sep 17 00:00:00 2001 From: Moran <105233020+feliperm17@users.noreply.github.com> Date: Thu, 5 Mar 2026 09:10:17 -0300 Subject: [PATCH 04/10] Fix/ Complemento Local de Coleta (#366) --- src/controllers/locais-coleta-controller.js | 4 ++-- src/controllers/relatorios-controller.js | 4 ++-- src/helpers/formata-dados-relatorio.js | 4 +--- src/models/LocalColeta.js | 5 +---- src/validators/localColeta-cadastro.js | 6 +----- src/views/ficha-tombo.ejs | 4 +--- 6 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/controllers/locais-coleta-controller.js b/src/controllers/locais-coleta-controller.js index 9c000201..7ca28e0e 100644 --- a/src/controllers/locais-coleta-controller.js +++ b/src/controllers/locais-coleta-controller.js @@ -208,7 +208,7 @@ export const buscarFasesSucessionais = (request, response, next) => { export const cadastrarLocalColeta = async (request, response, next) => { try { - const dados = pick(request.body, ['descricao', 'complemento', 'cidade_id', 'fase_sucessional_id']); + const dados = pick(request.body, ['descricao', 'cidade_id', 'fase_sucessional_id']); const localColeta = await LocalColeta.create(dados); response.status(201).json(localColeta); } catch (error) { @@ -321,7 +321,7 @@ export const buscarLocalColetaPorId = async (request, response, next) => { export const atualizarLocalColeta = async (request, response, next) => { try { const { id } = request.params; - const dados = pick(request.body, ['descricao', 'complemento', 'cidade_id', 'fase_sucessional_id']); + const dados = pick(request.body, ['descricao', 'cidade_id', 'fase_sucessional_id']); const [updated] = await LocalColeta.update(dados, { where: { id }, }); diff --git a/src/controllers/relatorios-controller.js b/src/controllers/relatorios-controller.js index c019c380..ee4cc3b6 100644 --- a/src/controllers/relatorios-controller.js +++ b/src/controllers/relatorios-controller.js @@ -213,7 +213,7 @@ export const obtemDadosDoRelatorioDeColetaPorLocalEIntervaloDeData = async (req, }, { model: LocalColeta, - attributes: ['id', 'descricao', 'complemento'], + attributes: ['id', 'descricao'], where: whereLocal, required: true, }, @@ -578,7 +578,7 @@ export const obtemDadosDoRelatorioDeLocalDeColeta = async (req, res, next) => { }, { model: LocalColeta, - attributes: ['id', 'descricao', 'complemento'], + attributes: ['id', 'descricao'], where: whereLocal, required: true, include: { diff --git a/src/helpers/formata-dados-relatorio.js b/src/helpers/formata-dados-relatorio.js index 5a011564..da688bf0 100644 --- a/src/helpers/formata-dados-relatorio.js +++ b/src/helpers/formata-dados-relatorio.js @@ -58,9 +58,7 @@ const defineNomeCientifico = dado => { export const formatarDadosParaRelatorioDeColetaPorLocalEIntervaloDeData = dados => { const romanos = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII']; const dadosFormatados = dados.map(dado => ({ - local: dado.locais_coletum?.complemento - ? `${dado.locais_coletum.descricao} ${dado.locais_coletum.complemento}` - : dado.locais_coletum?.descricao, + local: dado.locais_coletum?.descricao, data: `${String(dado.data_coleta_dia).padStart(2, '0')}/${romanos[dado.data_coleta_mes - 1]}/${dado.data_coleta_ano}`, tombo: dado?.hcf, numeroColeta: dado.numero_coleta || '-', diff --git a/src/models/LocalColeta.js b/src/models/LocalColeta.js index 62c7589a..d9652c44 100644 --- a/src/models/LocalColeta.js +++ b/src/models/LocalColeta.js @@ -35,10 +35,7 @@ export default (Sequelize, DataTypes) => { type: DataTypes.TEXT, allowNull: true, }, - complemento: { - type: DataTypes.TEXT, - allowNull: true, - }, + cidade_id: { type: DataTypes.INTEGER, allowNull: true, diff --git a/src/validators/localColeta-cadastro.js b/src/validators/localColeta-cadastro.js index 6f1c8530..c88a8a33 100644 --- a/src/validators/localColeta-cadastro.js +++ b/src/validators/localColeta-cadastro.js @@ -5,11 +5,7 @@ export default { notEmpty: true, errorMessage: 'Descrição é obrigatória.', }, - complemento: { - in: ['body'], - isString: true, - optional: true, - }, + cidade_id: { in: ['body'], isInt: true, diff --git a/src/views/ficha-tombo.ejs b/src/views/ficha-tombo.ejs index 7fa8e1ea..acced25c 100644 --- a/src/views/ficha-tombo.ejs +++ b/src/views/ficha-tombo.ejs @@ -213,9 +213,7 @@
Local de Coleta: - <% if (localColeta && localColeta.complemento) { %> - <%- localColeta.complemento %> - <% } %> + <% if (localColeta && localColeta.descricao) { %> <%- localColeta.descricao %> <% } %> From ac7ea7132fa7c77552f80fdad5af1f68d2b96620 Mon Sep 17 00:00:00 2001 From: Edvaldo Szymonek Date: Thu, 5 Mar 2026 09:54:53 -0300 Subject: [PATCH 05/10] =?UTF-8?q?atualiza=20vers=C3=A3o=20do=20trivy-actio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull_request.yml | 2 +- README.md | 15 +++++++++++++++ script/database-sync/docker-compose.yml | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index ed3da3be..a11d89a2 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -45,7 +45,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - name: Run scanner - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@0.34.2 with: scan-type: filesystem scan-ref: . diff --git a/README.md b/README.md index e2e75687..a67e7c08 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,18 @@ Using "development" environment Master 18385 is running Worker 18385 started on port 3000 ``` + +--- + +- adicionar busca pelo código de barras da foto +- colocar um alerta no painel quando estiver em ambiente de desenvolvimento +- issue para corrigir o DarwinCore (problema com o botão, quando clica não está funcionando) +- issue para o incremento do hcf na api ao invés do painel +- relatorio de quantidade de tombos por periodo + - relatorio tem que ter um grafico, mostrando quantos tombos foram cadastrados por periodo, somente quantitativo + +- alteração na ficha tombo + +nome cientifico = genero, especie, subespecie (se tiver), variedade (se tiver) +um unico tombo tem variedade e subespecie. como devemos exibir no nome cientifico? +inverter a ordem, colocar subespecie e depois variedade diff --git a/script/database-sync/docker-compose.yml b/script/database-sync/docker-compose.yml index fbea10e4..35b44824 100644 --- a/script/database-sync/docker-compose.yml +++ b/script/database-sync/docker-compose.yml @@ -4,12 +4,12 @@ services: container_name: database-sync build: . environment: - SYNC_SOURCE_HOST: "172.26.136.2" + SYNC_SOURCE_HOST: "10.0.10.80" SYNC_SOURCE_PORT: "5432" SYNC_SOURCE_USER: "postgres" SYNC_SOURCE_PASSWORD: "masterkey" SYNC_SOURCE_DATABASE: "herbario_prod" - SYNC_DEST_HOST: "172.26.136.2" + SYNC_DEST_HOST: "10.0.10.80" SYNC_DEST_PORT: "5432" SYNC_DEST_USER: "postgres" SYNC_DEST_PASSWORD: "masterkey" From fc02332d73eb3562e612c6120cd29d7c1c569f2e Mon Sep 17 00:00:00 2001 From: Edvaldo Szymonek Date: Thu, 5 Mar 2026 22:53:55 -0300 Subject: [PATCH 06/10] =?UTF-8?q?remove=20op=C3=A7=C3=A3o=20para=20exclus?= =?UTF-8?q?=C3=A3o=20de=20tabelas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/database-sync/database-sync.sh | 15 +-------------- script/database-sync/docker-compose.yml | 1 - 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/script/database-sync/database-sync.sh b/script/database-sync/database-sync.sh index a6b26e5a..32881b1a 100755 --- a/script/database-sync/database-sync.sh +++ b/script/database-sync/database-sync.sh @@ -2,18 +2,6 @@ set -euo pipefail -EXCLUDE_PARAMS="" -if [ -n "$SYNC_EXCLUDE_TABLES" ]; then - IFS=',' read -ra TABLES <<< "$SYNC_EXCLUDE_TABLES" - for table in "${TABLES[@]}"; do - table=$(echo "$table" | xargs) - if [ -n "$table" ]; then - EXCLUDE_PARAMS="$EXCLUDE_PARAMS --exclude-table-data=$table" - fi - done - echo "Excluding tables: $SYNC_EXCLUDE_TABLES" -fi - echo "Starting synchronization process..." PGPASSWORD="$SYNC_SOURCE_PASSWORD" pg_dump \ @@ -21,8 +9,7 @@ PGPASSWORD="$SYNC_SOURCE_PASSWORD" pg_dump \ -p "$SYNC_SOURCE_PORT" \ -U "$SYNC_SOURCE_USER" \ -d "$SYNC_SOURCE_DATABASE" \ - -Fc \ - $EXCLUDE_PARAMS | \ + -Fc | \ PGPASSWORD="$SYNC_DEST_PASSWORD" pg_restore \ -h "$SYNC_DEST_HOST" \ -p "$SYNC_DEST_PORT" \ diff --git a/script/database-sync/docker-compose.yml b/script/database-sync/docker-compose.yml index 35b44824..b88a7a65 100644 --- a/script/database-sync/docker-compose.yml +++ b/script/database-sync/docker-compose.yml @@ -14,6 +14,5 @@ services: SYNC_DEST_USER: "postgres" SYNC_DEST_PASSWORD: "masterkey" SYNC_DEST_DATABASE: "herbario_dev" - SYNC_EXCLUDE_TABLES: "public.usuarios" CRON_SCHEDULE: "*/2 * * * *" TZ: "America/Sao_Paulo" From 38863934f9b2a71ffe6770bb93c70bddb8b48cd3 Mon Sep 17 00:00:00 2001 From: Moran <105233020+feliperm17@users.noreply.github.com> Date: Sat, 7 Mar 2026 14:27:31 -0300 Subject: [PATCH 07/10] Converter colunas ativo e identificacao de smallint para boolean na tabela alteracoes (#368) --- src/controllers/fichas-tombos-controller.js | 2 +- src/controllers/pendencias-controller.js | 12 ++++---- src/controllers/tombos-controller.js | 14 ++++----- ...60304120000_convert_smallint_to_boolean.ts | 29 +++++++++++++++++++ src/herbarium/herbariumdatabase.js | 2 +- src/models/Alteracao.js | 4 +-- 6 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 src/database/migration/20260304120000_convert_smallint_to_boolean.ts diff --git a/src/controllers/fichas-tombos-controller.js b/src/controllers/fichas-tombos-controller.js index 6ecdb7d8..0addb97c 100644 --- a/src/controllers/fichas-tombos-controller.js +++ b/src/controllers/fichas-tombos-controller.js @@ -166,7 +166,7 @@ export default function fichaTomboController(request, response, next) { }; const where = { - identificacao: 1, + identificacao: true, status: 'APROVADO', tombo_hcf: tombo.hcf, }; diff --git a/src/controllers/pendencias-controller.js b/src/controllers/pendencias-controller.js index 520a3fa9..993ac8f6 100644 --- a/src/controllers/pendencias-controller.js +++ b/src/controllers/pendencias-controller.js @@ -42,7 +42,7 @@ export const listagem = (request, response, next) => { resultado: {}, }; let where = { - ativo: 1, + ativo: true, }; let whereUsuario = {}; if (status) { @@ -113,7 +113,7 @@ export const desativar = (request, response, next) => { const callback = transaction => Promise.resolve() .then(() => Alteracao.findOne({ where: { - ativo: 1, + ativo: true, id, }, transaction, @@ -123,7 +123,7 @@ export const desativar = (request, response, next) => { throw new BadRequestExeption(800); } return Alteracao.update({ - ativo: 0, + ativo: false, }, { where: { id, @@ -1738,7 +1738,7 @@ export async function visualizar(request, response, next) { try { const id = request.params.pendencia_id; const alteracao = await Alteracao.findOne({ - where: { ativo: 1, id }, + where: { ativo: true, id }, }); if (!alteracao) { @@ -2151,14 +2151,14 @@ export function aceitarPendencia(request, response, next) { status, }, { where: { - ativo: 1, + ativo: true, id, }, transaction, })) .then(() => Alteracao.findOne({ where: { - ativo: 1, + ativo: true, id, }, transaction, diff --git a/src/controllers/tombos-controller.js b/src/controllers/tombos-controller.js index 52293f2b..671ac3fc 100644 --- a/src/controllers/tombos-controller.js +++ b/src/controllers/tombos-controller.js @@ -397,8 +397,8 @@ export const cadastro = (request, response, next) => { usuario_id: request.usuario.id, status, tombo_json: JSON.stringify(tomboData), - ativo: 1, - identificacao: 1, + ativo: true, + identificacao: true, }; tomboCriado = tombo; @@ -486,8 +486,8 @@ function alteracaoIdentificador(request, transaction) { usuario_id: request.usuario.id, status: 'ESPERANDO', tombo_json: JSON.stringify(update), - ativo: 1, - identificacao: 1, + ativo: true, + identificacao: true, }, { transaction })) .then(alteracaoIdent => { if (request.usuario.tipo_usuario_id === 3) { @@ -612,8 +612,8 @@ function alteracaoCuradorouOperador(request, response, transaction) { usuario_id: request.usuario.id, status: 'ESPERANDO', tombo_json: JSON.stringify(update), - ativo: 1, - identificacao: 1, + ativo: true, + identificacao: true, }, { transaction }) .then(alteracaoCriada => { if (request.usuario.tipo_usuario_id === 1) { @@ -1472,7 +1472,7 @@ export const obterTombo = async (request, response, next) => { where: { tombo_hcf: dadosTombo.hcf, status: 'APROVADO', - identificacao: 1, + identificacao: true, }, order: [['created_at', 'DESC']], }), diff --git a/src/database/migration/20260304120000_convert_smallint_to_boolean.ts b/src/database/migration/20260304120000_convert_smallint_to_boolean.ts new file mode 100644 index 00000000..c99cbd64 --- /dev/null +++ b/src/database/migration/20260304120000_convert_smallint_to_boolean.ts @@ -0,0 +1,29 @@ +import { Knex } from 'knex' + +export async function run(knex: Knex): Promise { + await knex.transaction(async trx => { + await trx.schema.alterTable('alteracoes', table => { + table.smallint('ativo_backup').nullable() + table.smallint('identificacao_backup').nullable() + }) + + await trx.raw('UPDATE alteracoes SET ativo_backup = ativo, identificacao_backup = identificacao') + + await trx.schema.alterTable('alteracoes', table => { + table.dropColumn('ativo') + table.dropColumn('identificacao') + }) + + await trx.schema.alterTable('alteracoes', table => { + table.boolean('ativo').nullable() + table.boolean('identificacao').nullable() + }) + + await trx.raw('UPDATE alteracoes SET ativo = (ativo_backup = 1), identificacao = (identificacao_backup = 1)') + + await trx.schema.alterTable('alteracoes', table => { + table.dropColumn('ativo_backup') + table.dropColumn('identificacao_backup') + }) + }) +} diff --git a/src/herbarium/herbariumdatabase.js b/src/herbarium/herbariumdatabase.js index b87f3fd3..fe0a9855 100644 --- a/src/herbarium/herbariumdatabase.js +++ b/src/herbarium/herbariumdatabase.js @@ -796,7 +796,7 @@ export function insereAlteracaoSugerida(idUsuario, statusAlteracao, idTombo, tom status: statusAlteracao, tombo_hcf: idTombo, tombo_json: tomboJson, - identificacao: 1, + identificacao: true, data_identificacao_dia: diaIdentificacao, data_identificacao_mes: mesIdentificacao, data_identificacao_ano: anoIdentificacao, diff --git a/src/models/Alteracao.js b/src/models/Alteracao.js index dc9af655..a9ca6974 100644 --- a/src/models/Alteracao.js +++ b/src/models/Alteracao.js @@ -41,11 +41,11 @@ export default (Sequelize, DataTypes) => { allowNull: false, }, ativo: { - type: DataTypes.SMALLINT, + type: DataTypes.BOOLEAN, allowNull: true, }, identificacao: { - type: DataTypes.SMALLINT, + type: DataTypes.BOOLEAN, allowNull: true, }, }; From f3b28011113cabd85c31f1bfcc088fb7abb41e4f Mon Sep 17 00:00:00 2001 From: Lucas Dos Santos Vaz <52181258+luscas18@users.noreply.github.com> Date: Sat, 7 Mar 2026 14:28:56 -0300 Subject: [PATCH 08/10] =?UTF-8?q?remo=C3=A7=C3=A3o=20de=20duplicidade=20de?= =?UTF-8?q?=20autores=20da=20base=20de=20dados=20(#338)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/taxonomias-controller.js | 14 +-- ...211011159_fix_remove_autores_duplicados.ts | 105 ++++++++++++++++++ src/models/Autor.js | 4 +- src/routes/taxonomias.js | 12 +- src/validators/autor-atualiza.js | 9 +- src/validators/autor-cadastro.js | 2 +- 6 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 src/database/migration/20260211011159_fix_remove_autores_duplicados.ts diff --git a/src/controllers/taxonomias-controller.js b/src/controllers/taxonomias-controller.js index 7040b725..253eab92 100644 --- a/src/controllers/taxonomias-controller.js +++ b/src/controllers/taxonomias-controller.js @@ -1341,9 +1341,9 @@ export const editarVariedade = (request, response, next) => { // //////////////////AUTORES////////////////// export const cadastrarAutores = (request, response, next) => { - let { nome, iniciais } = request.body; + let { nome, observacao } = request.body; nome = limparEspacos(nome); - iniciais = limparEspacos(iniciais); + observacao = limparEspacos(observacao); const callback = transaction => Promise.resolve() .then(() => Autor.findOne({ where: { @@ -1356,7 +1356,7 @@ export const cadastrarAutores = (request, response, next) => { throw new BadRequestExeption(513); } }) - .then(() => Autor.create({ nome, iniciais }, transaction)); + .then(() => Autor.create({ nome, observacao }, transaction)); sequelize.transaction(callback) .then(autorCriado => { if (!autorCriado) { @@ -1409,7 +1409,7 @@ export const buscarAutores = async (request, response, next) => { ]; const result = await Autor.findAndCountAll({ - attributes: ['id', 'nome', 'iniciais'], + attributes: ['id', 'nome', 'observacao'], order, limit: limite, offset, @@ -1476,9 +1476,9 @@ export const excluirAutores = (request, response, next) => { }; export const editarAutores = (request, response, next) => { - let { nome, iniciais } = request.body; + let { nome, observacao } = request.body; nome = limparEspacos(nome); - iniciais = limparEspacos(iniciais); + observacao = limparEspacos(observacao); const autorId = parseInt(request.params.autor_id); const callback = transaction => Promise.resolve() @@ -1493,7 +1493,7 @@ export const editarAutores = (request, response, next) => { throw new BadRequestExeption(517); } }) - .then(() => Autor.update({ nome, iniciais }, { + .then(() => Autor.update({ nome, observacao }, { where: { id: autorId, }, diff --git a/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts b/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts new file mode 100644 index 00000000..80ded0b6 --- /dev/null +++ b/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts @@ -0,0 +1,105 @@ +import { Knex } from 'knex' + +type DupGroupRow = { + nome: string + observacao: string | null + keep_id: number + ids: number[] + qtde: number +} + +export async function run(knex: Knex): Promise { + await knex.transaction(async trx => { + const hasIniciais = await trx.schema.hasColumn('autores', 'iniciais') + const hasObservacao = await trx.schema.hasColumn('autores', 'observacao') + + if (hasIniciais && !hasObservacao) { + await trx.schema.alterTable('autores', table => { + table.renameColumn('iniciais', 'observacao') + }) + } + + const hasObservacaoNow = await trx.schema.hasColumn('autores', 'observacao') + + if (hasObservacaoNow) { + await trx.schema.alterTable('autores', table => { + table.string('observacao', 500).nullable().alter() + }) + } + + const dupGroups = (await trx('autores') + .select([ + 'nome', + 'observacao', + trx.raw('MIN(id)::int as keep_id'), + trx.raw('ARRAY_AGG(id ORDER BY id) as ids'), + trx.raw('COUNT(*)::int as qtde') + ]) + .groupBy(['nome', 'observacao']) + .havingRaw('COUNT(*) > 1')) as unknown as DupGroupRow[] + + if (!dupGroups.length) return + + const pairs: Array<{ keep_id: number; drop_id: number }> = [] + + for (const g of dupGroups) { + const keepId = Number(g.keep_id) + const ids = (g.ids ?? []).map(n => Number(n)).filter(Number.isFinite) + + for (const id of ids) { + if (id !== keepId) pairs.push({ keep_id: keepId, drop_id: id }) + } + } + + if (!pairs.length) return + + await trx.raw('DROP TABLE IF EXISTS autor_merge') + + await trx.schema.createTable('autor_merge', table => { + table.integer('keep_id').notNullable() + table.integer('drop_id').notNullable().primary() + }) + + await trx('autor_merge').insert(pairs) + + await trx.raw(` + UPDATE especies e + SET autor_id = m.keep_id + FROM autor_merge m + WHERE e.autor_id = m.drop_id + `) + + await trx.raw(` + UPDATE sub_especies se + SET autor_id = m.keep_id + FROM autor_merge m + WHERE se.autor_id = m.drop_id + `) + + await trx.raw(` + UPDATE variedades v + SET autor_id = m.keep_id + FROM autor_merge m + WHERE v.autor_id = m.drop_id + `) + + const hasSubFamiliasAutorId = await trx.schema.hasColumn('sub_familias', 'autor_id') + + if (hasSubFamiliasAutorId) { + await trx.raw(` + UPDATE sub_familias sf + SET autor_id = m.keep_id + FROM autor_merge m + WHERE sf.autor_id = m.drop_id + `) + } + + await trx.raw(` + DELETE FROM autores a + USING autor_merge m + WHERE a.id = m.drop_id + `) + + await trx.schema.dropTable('autor_merge') + }) +} diff --git a/src/models/Autor.js b/src/models/Autor.js index 0287ed97..7848efab 100644 --- a/src/models/Autor.js +++ b/src/models/Autor.js @@ -24,8 +24,8 @@ export default (Sequelize, DataTypes) => { type: DataTypes.STRING(200), allowNull: false, }, - iniciais: { - type: DataTypes.STRING(200), + observacao: { + type: DataTypes.STRING(500), allowNull: true, }, }; diff --git a/src/routes/taxonomias.js b/src/routes/taxonomias.js index 3c88bee1..8aa16c57 100644 --- a/src/routes/taxonomias.js +++ b/src/routes/taxonomias.js @@ -38,8 +38,7 @@ const generosOrdenacaoMiddleware = criaOrdenacaoMiddleware(['genero', 'familia', const especiesOrdenacaoMiddleware = criaOrdenacaoMiddleware(['especie', 'reino', 'familia', 'genero', 'familia', 'autor'], 'nome', 'asc'); const subEspeciesOrdenacaoMiddleware = criaOrdenacaoMiddleware(['subespecie', 'reino', 'familia', 'genero', 'especie', 'autor'], 'nome', 'asc'); const variedadesOrdenacaoMiddleware = criaOrdenacaoMiddleware(['variedade', 'reino', 'familia', 'genero', 'especie', 'autor'], 'nome', 'asc'); -const autorOrdenacaoMiddleware = criaOrdenacaoMiddleware(['autor', 'iniciais'], 'nome', 'asc'); - +const autorOrdenacaoMiddleware = criaOrdenacaoMiddleware(['autor', 'observacao'], 'nome', 'asc'); /** * @swagger * tags: @@ -1534,10 +1533,15 @@ export default app => { * properties: * nome: * type: string + * observacao: + * type: string + * nullable: true + * maxLength: 500 * required: * - nome * example: * nome: "A. Author" + * observacao: "Observação opcional sobre o autor" * responses: * 201: * description: Autor cadastrado com sucesso @@ -1548,6 +1552,7 @@ export default app => { * example: * id: 1 * nome: "A. Author" + * observacao: "Observação opcional sobre o autor" * '400': * $ref: '#/components/responses/BadRequest' * '401': @@ -1600,9 +1605,10 @@ export default app => { * type: integer * nome: * type: string - * iniciais: + * observacao: * type: string * nullable: true + * maxLength: 500 * '400': * $ref: '#/components/responses/BadRequest' * '401': diff --git a/src/validators/autor-atualiza.js b/src/validators/autor-atualiza.js index 87247546..d85fb1ec 100644 --- a/src/validators/autor-atualiza.js +++ b/src/validators/autor-atualiza.js @@ -2,19 +2,22 @@ export default { nome: { in: 'body', isString: true, - isEmpty: false, + notEmpty: true, isLength: { options: [{ min: 3 }], }, }, - iniciais: { + observacao: { in: 'body', isString: true, optional: true, + isLength: { + options: [{ max: 500 }], + }, }, autor_id: { in: 'params', isInt: true, - isEmpty: false, + notEmpty: true, }, }; diff --git a/src/validators/autor-cadastro.js b/src/validators/autor-cadastro.js index 89e82436..5f0554c2 100644 --- a/src/validators/autor-cadastro.js +++ b/src/validators/autor-cadastro.js @@ -7,7 +7,7 @@ export default { options: [{ min: 3 }], }, }, - iniciais: { + observacao: { in: 'body', isString: true, optional: true, From 035c878bb01e510aa86ab9e056b2fd0504294c8c Mon Sep 17 00:00:00 2001 From: Edvaldo Szymonek Date: Sat, 7 Mar 2026 14:53:59 -0300 Subject: [PATCH 09/10] =?UTF-8?q?faz=20backup=20sem=20dono=20ou=20privil?= =?UTF-8?q?=C3=A9gios,=20adiciona=20comando=20para=20cria=20objetos=20se?= =?UTF-8?q?=20n=C3=A3o=20existirem=20(#370)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 ++-- script/database-sync/database-sync.sh | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1beb67df..993aee35 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: postgres: - image: postgres:18 + image: docker.io/postgis/postgis:18-3.6 container_name: herbario_postgresql environment: POSTGRES_DB: $PG_DATABASE @@ -25,7 +25,7 @@ services: nginx: image: nginx:1.17-alpine - container_name: berbario_nginx + container_name: herbario_nginx volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./nginx/conf.d:/etc/nginx/conf.d diff --git a/script/database-sync/database-sync.sh b/script/database-sync/database-sync.sh index 32881b1a..431a01bd 100755 --- a/script/database-sync/database-sync.sh +++ b/script/database-sync/database-sync.sh @@ -9,15 +9,17 @@ PGPASSWORD="$SYNC_SOURCE_PASSWORD" pg_dump \ -p "$SYNC_SOURCE_PORT" \ -U "$SYNC_SOURCE_USER" \ -d "$SYNC_SOURCE_DATABASE" \ - -Fc | \ + --no-owner \ + --no-privileges \ + --clean \ + --if-exists \ + --format=c | \ PGPASSWORD="$SYNC_DEST_PASSWORD" pg_restore \ -h "$SYNC_DEST_HOST" \ -p "$SYNC_DEST_PORT" \ -U "$SYNC_DEST_USER" \ -d "$SYNC_DEST_DATABASE" \ --clean \ - --if-exists \ - --no-owner \ - --no-privileges + --if-exists echo "Synchronization completed successfully: ${SYNC_SOURCE_DATABASE} -> ${SYNC_DEST_DATABASE}" From 5e091f32f694babb3b9e9e161d84db78750c88f3 Mon Sep 17 00:00:00 2001 From: Edvaldo Szymonek Date: Sat, 7 Mar 2026 14:56:54 -0300 Subject: [PATCH 10/10] =?UTF-8?q?remove=20migra=C3=A7=C3=A3o=20desnecess?= =?UTF-8?q?=C3=A1ria?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20251215003403_altera-tipos-data-configuracao.ts | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/database/migration/20251215003403_altera-tipos-data-configuracao.ts diff --git a/src/database/migration/20251215003403_altera-tipos-data-configuracao.ts b/src/database/migration/20251215003403_altera-tipos-data-configuracao.ts deleted file mode 100644 index 79332d52..00000000 --- a/src/database/migration/20251215003403_altera-tipos-data-configuracao.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Knex } from 'knex' - -export async function run(knex: Knex): Promise { - await knex.raw(` - ALTER TABLE configuracao - MODIFY hora_inicio TIME NOT NULL, - MODIFY hora_fim TIME NULL, - MODIFY data_proxima_atualizacao DATETIME NULL; - `) -}