Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6e54b65
feat(web): indicador visual de carregamento ao validar pacote
Rossi-Luciano Jul 30, 2026
3953a4e
feat(web): identidade visual e layout mais cuidados na tela principal
Rossi-Luciano Jul 30, 2026
c7741fc
style(web): envolve tabela de historico em table-wrap e usa badge de …
Rossi-Luciano Jul 30, 2026
09bb877
feat(web): calcula rotulo curto para cada PDF do historico
Rossi-Luciano Jul 30, 2026
61dd18c
style(web): estilo de "chip" para os links de PDF do historico
Rossi-Luciano Jul 30, 2026
9877da7
feat(web): renderiza PDFs do historico como chips com rotulo curto
Rossi-Luciano Jul 30, 2026
b8bfaa0
fix(web): remove microssegundos da data exibida no historico
Rossi-Luciano Jul 30, 2026
09fabd3
fix(web): preserva q/status/page_size/page ao redirecionar apos validar
Rossi-Luciano Jul 30, 2026
6f2e385
fix(web): formulario de validar envia a querystring atual da pagina
Rossi-Luciano Jul 30, 2026
39878c2
feat(web): calcula janela de paginas pra paginacao numerada
Rossi-Luciano Jul 30, 2026
033ede2
feat(web): paginacao numerada no historico, estilo Django
Rossi-Luciano Jul 30, 2026
bd9af36
style(web): estilo visual da paginacao numerada
Rossi-Luciano Jul 30, 2026
5a70a79
refactor(version): extrai is_running_from_source() reutilizavel
Rossi-Luciano Jul 30, 2026
0fc9052
fix(web): rodape para de confiar em build_info.py rodando do codigo-f…
Rossi-Luciano Jul 30, 2026
0a085f1
feat(web): abas pra "Artigos Considerados" e "Pacotes validados"
Rossi-Luciano Jul 30, 2026
66a311b
style(web): generaliza classes de chip pra reuso em CSV/HTML/PDF
Rossi-Luciano Jul 30, 2026
4727f45
feat(web): consolida CSV/HTML/PDF numa unica coluna "Ações"
Rossi-Luciano Jul 30, 2026
6088de2
i18n(web): traduz as strings novas da issue #36 para en/es
Rossi-Luciano Jul 30, 2026
c00a5a3
Merge branch 'main' into feature/issue-36-melhorias-ui-ux
Rossi-Luciano Jul 31, 2026
20fce97
feat(web): reconstroi o relatorio HTML com a identidade visual do SciELO
Rossi-Luciano Jul 31, 2026
180d49b
i18n(web): traduz as strings novas do relatorio redesenhado para en/es
Rossi-Luciano Jul 31, 2026
fcc8075
fix(packaging): gera build_info.py real no build Linux
Rossi-Luciano Jul 31, 2026
5cc3028
fix(web): corrige rotulo de PDF por prefixo e formato de data no hist…
Rossi-Luciano Jul 31, 2026
21d5816
i18n(web): usa msgid em pt-BR pra coluna de excecoes do historico
Rossi-Luciano Jul 31, 2026
4510e7a
style(web): encurta e reordena as abas de Artigos/Pacotes
Rossi-Luciano Jul 31, 2026
dd6afbd
i18n(web): atualiza catalogo pt pros msgids revisados no PR #37
Rossi-Luciano Jul 31, 2026
32941ed
i18n(web): traduz os msgids revisados no PR #37 pro catalogo en
Rossi-Luciano Jul 31, 2026
0943824
i18n(web): traduz os msgids revisados no PR #37 pro catalogo es
Rossi-Luciano Jul 31, 2026
133b292
fix(web): adiciona checagem defensiva platform.system() no rodape
Rossi-Luciano Aug 2, 2026
36406fd
feat(packaging): cria generate_build_info.ps1 pro build Windows
Rossi-Luciano Aug 2, 2026
c43a083
fix(packaging): chama generate_build_info.ps1 no build Windows
Rossi-Luciano Aug 2, 2026
24fd5f2
test(web): cobre get_footer_build_label com build_info desatualizado
Rossi-Luciano Aug 2, 2026
94fc638
feat(db): adiciona list_articles/count_articles/get_package_name
Rossi-Luciano Aug 2, 2026
43b0294
test(db): cobre list_articles/count_articles
Rossi-Luciano Aug 2, 2026
17c172a
feat(web): adiciona /articles-list e desacopla a aba Artigos de lates…
Rossi-Luciano Aug 2, 2026
5aadd55
feat(web): cria partial da listagem de artigos
Rossi-Luciano Aug 2, 2026
b6c2e54
feat(web): abas Pacotes/Artigos sempre visiveis com filtro proprio de…
Rossi-Luciano Aug 2, 2026
1c2fab8
Merge remote-tracking branch 'origin/main' into feature/issue-36-melh…
Rossi-Luciano Aug 3, 2026
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
4 changes: 3 additions & 1 deletion spsvalidator/src/spsvalidator/build_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from flask_babel import gettext

from spsvalidator import build_info
from spsvalidator.version import is_running_from_source


def get_footer_build_label() -> str:
if (
build_info.BUILD_MACOS_VERSION != "development"
not is_running_from_source()
and build_info.BUILD_MACOS_VERSION != "development"
and build_info.BUILD_PLATFORM == "macOS"
):
return gettext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ msgstr "Select a .zip file to validate."
msgid "Apenas arquivos .zip SPS são suportados."
msgstr "Only SPS .zip files are supported."

#: src/spsvalidator/web/templates/index.html:414
msgid "Validando..."
msgstr "Validating..."

#: src/spsvalidator/web/templates/_history_list.html:15
msgid "Ações"
msgstr "Actions"

#: src/spsvalidator/web/templates/_history_list.html:37
#, python-format
msgid "Baixar CSV de %(name)s"
msgstr "Download CSV for %(name)s"

#: src/spsvalidator/web/templates/_history_list.html:38
msgid "Baixar CSV"
msgstr "Download CSV"

#: src/spsvalidator/web/templates/_history_list.html:44
msgid "CSV"
msgstr "CSV"

#: src/spsvalidator/web/templates/_history_list.html:56
msgid "Pré-visualização HTML"
msgstr "HTML preview"

#: src/spsvalidator/web/routes.py:83
msgid "PDF principal"
msgstr "Main PDF"

#: src/spsvalidator/web/templates/_history_list.html:105
msgid "Paginação"
msgstr "Pagination"

#: src/spsvalidator/web/templates/index.html:79
msgid "Validação de pacotes SPS"
msgstr "SPS package validation"
Expand Down Expand Up @@ -80,11 +113,6 @@ msgstr ""
msgid "Exceptions"
msgstr ""

#: src/spsvalidator/web/templates/index.html:109
#: src/spsvalidator/web/templates/index.html:129
msgid "Relatório"
msgstr "Report"

#: src/spsvalidator/web/templates/index.html:136
msgid "Nenhum pacote validado ainda."
msgstr "No packages validated yet."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ msgstr "Seleccione un archivo .zip para validar."
msgid "Apenas arquivos .zip SPS são suportados."
msgstr "Solo se admiten archivos .zip SPS."

#: src/spsvalidator/web/templates/index.html:414
msgid "Validando..."
msgstr "Validando..."

#: src/spsvalidator/web/templates/_history_list.html:15
msgid "Ações"
msgstr "Acciones"

#: src/spsvalidator/web/templates/_history_list.html:37
#, python-format
msgid "Baixar CSV de %(name)s"
msgstr "Descargar CSV de %(name)s"

#: src/spsvalidator/web/templates/_history_list.html:38
msgid "Baixar CSV"
msgstr "Descargar CSV"

#: src/spsvalidator/web/templates/_history_list.html:44
msgid "CSV"
msgstr "CSV"

#: src/spsvalidator/web/templates/_history_list.html:56
msgid "Pré-visualização HTML"
msgstr "Vista previa HTML"

#: src/spsvalidator/web/routes.py:83
msgid "PDF principal"
msgstr "PDF principal"

#: src/spsvalidator/web/templates/_history_list.html:105
msgid "Paginação"
msgstr "Paginación"

#: src/spsvalidator/web/templates/index.html:79
msgid "Validação de pacotes SPS"
msgstr "Validación de paquetes SPS"
Expand Down Expand Up @@ -80,11 +113,6 @@ msgstr ""
msgid "Exceptions"
msgstr ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vamos traduzir esse termo também?


#: src/spsvalidator/web/templates/index.html:109
#: src/spsvalidator/web/templates/index.html:129
msgid "Relatório"
msgstr "Informe"

#: src/spsvalidator/web/templates/index.html:136
msgid "Nenhum pacote validado ainda."
msgstr "Ningún paquete validado todavía."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ msgstr ""
msgid "Apenas arquivos .zip SPS são suportados."
msgstr ""

#: src/spsvalidator/web/templates/index.html:414
msgid "Validando..."
msgstr ""

#: src/spsvalidator/web/templates/_history_list.html:15
msgid "Ações"
msgstr ""

#: src/spsvalidator/web/templates/_history_list.html:37
#, python-format
msgid "Baixar CSV de %(name)s"
msgstr ""

#: src/spsvalidator/web/templates/_history_list.html:38
msgid "Baixar CSV"
msgstr ""

#: src/spsvalidator/web/templates/_history_list.html:44
msgid "CSV"
msgstr ""

#: src/spsvalidator/web/templates/_history_list.html:56
msgid "Pré-visualização HTML"
msgstr ""

#: src/spsvalidator/web/routes.py:83
msgid "PDF principal"
msgstr ""

#: src/spsvalidator/web/templates/_history_list.html:105
msgid "Paginação"
msgstr ""

#: src/spsvalidator/web/templates/index.html:79
msgid "Validação de pacotes SPS"
msgstr ""
Expand Down Expand Up @@ -80,11 +113,6 @@ msgstr ""
msgid "Exceptions"
msgstr ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vamos traduzir esse termo também?


#: src/spsvalidator/web/templates/index.html:109
#: src/spsvalidator/web/templates/index.html:129
msgid "Relatório"
msgstr ""

#: src/spsvalidator/web/templates/index.html:136
msgid "Nenhum pacote validado ainda."
msgstr ""
Expand Down
20 changes: 17 additions & 3 deletions spsvalidator/src/spsvalidator/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,24 @@
from pathlib import Path


def _pyproject_path() -> Path:
return Path(__file__).resolve().parents[2] / "pyproject.toml"


def is_running_from_source() -> bool:
"""True quando rodando a partir do repositorio (pip install -e .).

Nesse caso pyproject.toml esta presente ao lado do pacote; num build
empacotado (PyInstaller) ele nao existe, e so ai os metadados
gravados em build_info.py (gerados pra aquele build especifico) sao
confiaveis.
"""
return _pyproject_path().is_file()


def _load_version() -> str:
pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
if pyproject_path.is_file():
with pyproject_path.open("rb") as file_pointer:
if is_running_from_source():
with _pyproject_path().open("rb") as file_pointer:
data = tomllib.load(file_pointer)
return str(data["project"]["version"])
from spsvalidator import build_info
Expand Down
58 changes: 56 additions & 2 deletions spsvalidator/src/spsvalidator/web/routes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from datetime import datetime
from pathlib import Path

from flask import (
Expand Down Expand Up @@ -69,6 +70,24 @@ def _html_previews_by_article(package_sha256: str) -> list[dict]:
return groups


def _short_pdf_label(xml_stem: str, filename: str) -> str:
"""Rótulo curto pra diferenciar PDFs de um mesmo artigo na listagem.

Os nomes de arquivo compartilham o prefixo `xml_stem` (ex.:
"artigo.pdf", "artigo-suppl1.pdf"), o que faz uma lista inteira de
nomes completos parecer repetitiva; aqui extraímos só a parte que
diferencia cada arquivo.
"""
stem = filename[:-4] if filename.lower().endswith(".pdf") else filename
if stem == xml_stem:
return gettext("PDF principal")
if stem.startswith(xml_stem):
suffix = stem[len(xml_stem):].lstrip("-_")
if suffix:
return suffix
return filename


def _pdf_previews_by_article(package_sha256: str) -> list[dict]:
"""PDFs extraídos para um pacote, agrupados por artigo (xml_stem).

Expand All @@ -82,19 +101,43 @@ def _pdf_previews_by_article(package_sha256: str) -> list[dict]:
for article_dir in sorted(base_dir.iterdir()):
if not article_dir.is_dir():
continue
xml_stem = article_dir.name
pdf_names = sorted(p.name for p in (article_dir / "assets").glob("*.pdf"))
if pdf_names:
groups.append({"xml_stem": article_dir.name, "pdf_names": pdf_names})
pdfs = [
{"filename": name, "label": _short_pdf_label(xml_stem, name)}
for name in pdf_names
]
groups.append({"xml_stem": xml_stem, "pdfs": pdfs})
return groups


def _format_validated_at(value: str) -> str:
"""Trunca os microssegundos de "validated_at" pra exibicao na tabela.

O valor e gravado com datetime.now(UTC).isoformat(), que inclui
microssegundos; isso e ruido pra quem esta lendo a lista de historico.
"""
try:
return datetime.fromisoformat(value).isoformat(timespec="seconds")
except ValueError:
return value


def _parse_int(value, default: int) -> int:
try:
return int(value)
except (TypeError, ValueError):
return default


def _page_range(page: int, total_pages: int, window: int = 2) -> list[int]:
"""Janela de numeros de pagina ao redor da pagina atual, tipo Django."""
start = max(1, page - window)
end = min(total_pages, page + window)
return list(range(start, end + 1))


def _paginated_history() -> dict:
db_path = current_app.config["DB_PATH"]
name_query = request.args.get("q", "").strip()
Expand All @@ -115,6 +158,7 @@ def _paginated_history() -> dict:
offset=(page - 1) * page_size,
)
for item in history_items:
item["validated_at"] = _format_validated_at(item["validated_at"])
item["html_previews"] = _html_previews_by_article(item["package_sha256"])
item["pdf_previews"] = _pdf_previews_by_article(item["package_sha256"])

Expand All @@ -126,6 +170,7 @@ def _paginated_history() -> dict:
"page_size": page_size,
"total": total,
"total_pages": total_pages,
"page_range": _page_range(page, total_pages),
}


Expand Down Expand Up @@ -171,7 +216,16 @@ def validate():
except Exception as exc:
return _render_index(latest_result=None, error_message=str(exc))

return redirect(url_for("web.index", history_id=result["history_id"]))
return redirect(
url_for(
"web.index",
history_id=result["history_id"],
q=request.args.get("q") or None,
status=request.args.get("status") or None,
page_size=request.args.get("page_size") or None,
page=request.args.get("page") or None,
)
)


@web_blueprint.get("/validation/<history_id>/report.csv")
Expand Down
Loading