Repository navigation
Adiciona uma área de relatórios públicos ao site, acessível em /report/, e o primeiro relatório dessa área: **Disponibilidade de dados** - #1488
Conversation
Concentra a lógica do relatório de disponibilidade de dados em um módulo próprio, separado da camada web, para que possa ser reutilizado e testado de forma isolada. Detalhes de implementação: - filter_articles: filtra Article por coleção, periódico e intervalo de ano de publicação online - build_report: agrupa por ano (do fascículo ou de publicação online) e por data_availability_status, com contagens, porcentagens e linha de totais - journals_for, pub_years_for e filter_options: alimentam os filtros dependentes - GROUP_BY_ISSUE_YEAR e GROUP_BY_OPTIONS: opções de agrupamento usadas pelo formulário
… dados Adiciona testes para o módulo report/data_availability.py, cobrindo filtros, agrupamento por ano, cálculo de totais e porcentagens e opções dos filtros dependentes.
Define o template base compartilhado pelas páginas públicas de relatórios, para que novos relatórios mantenham a mesma estrutura e aparência.
Lista os relatórios públicos disponíveis com título e descrição, a partir de PUBLIC_REPORTS em report/views.py. Estende report/public_base.html.
Apresenta a tabela de artigos por status de disponibilidade de dados e por ano, com filtros e exportação em CSV. Detalhes de implementação: - Filtros por coleção, periódico, intervalo de ano de publicação online e tipo de agrupamento - Periódicos e anos atualizados conforme a coleção/periódico escolhidos, via endpoint JSON de opções - Link 'Baixar CSV' preserva os filtros aplicados (query_string sem o parâmetro format) - Legenda dos status e mensagem quando não há artigos para os filtros - Estende report/public_base.html
Registra as URLs da página inicial de relatórios, do relatório de disponibilidade de dados e do endpoint de opções dos filtros dependentes, incluídas em config/urls.py sob /report/.
Adiciona o formulário de filtros do relatório de disponibilidade de dados, tolerante a valores inválidos para que um filtro mal preenchido não impeça a exibição do relatório. Detalhes de implementação: - DataAvailabilityFilterForm com os campos collection, journal, year_from, year_to e group_by - year_from e year_to validados como ano de 4 dígitos (RegexField) - group_by usa GROUP_BY_OPTIONS de report/data_availability.py - filters(): retorna apenas os valores válidos, ignorando campos inválidos, com group_by padrão GROUP_BY_ISSUE_YEAR
Cria as views da página inicial de relatórios públicos e do relatório de disponibilidade de dados, com exportação em CSV e cache para evitar recalcular sobre toda a tabela Article a cada acesso. Detalhes de implementação: - PUBLIC_REPORTS: registro dos relatórios listados em /report/ - report_index_view: renderiza report/index.html - data_availability_view: aplica os filtros, monta o relatório e responde em HTML ou CSV (format=csv) - data_availability_options_view: retorna em JSON os periódicos e anos disponíveis para os filtros dependentes - _data_availability_csv: CSV com separador ';', colunas de contagem e porcentagem por status e linha de total - cache_page de 1 hora (DATA_AVAILABILITY_CACHE_SECONDS) nas views do relatório e de opções
Inclui report.urls em i18n_patterns, antes das rotas do allauth e do Wagtail, para que as páginas de relatórios sejam servidas com prefixo de idioma.
Adiciona as mensagens da página inicial de relatórios e do relatório de disponibilidade de dados (títulos, descrições, filtros, legenda e mensagens de estado vazio).
Recompila locale/en/LC_MESSAGES/django.po com as mensagens dos relatórios públicos.
Adiciona as mensagens da página inicial de relatórios e do relatório de disponibilidade de dados (títulos, descrições, filtros, legenda e mensagens de estado vazio).
Recompila locale/es/LC_MESSAGES/django.po com as mensagens dos relatórios públicos.
Adiciona as mensagens da página inicial de relatórios e do relatório de disponibilidade de dados (títulos, descrições, filtros, legenda e mensagens de estado vazio).
Recompila locale/pt_BR/LC_MESSAGES/django.po com as mensagens dos relatórios públicos.
Rossi-Luciano
left a comment
There was a problem hiding this comment.
Rodei os 33 testes de report.test_data_availability no ambiente local: todos passam. Testei também na interface (índice /report/, relatório, os dois agrupamentos, filtros e CSV) e está funcionando.
Deixei um ponto bloqueante (cache, em views.py) e dois não bloqueantes, comentados nas linhas correspondentes.
Na descrição do PR ficaram pendentes:
- o item da NSI.04 sobre o pipeline de segurança (SonarQube / Trivy), sem nenhuma opção marcada;
- screenshots;
- a issue relacionada.
| DATA_AVAILABILITY_CACHE_SECONDS = 60 * 60 | ||
|
|
||
|
|
||
| @cache_page(DATA_AVAILABILITY_CACHE_SECONDS) |
There was a problem hiding this comment.
Bloqueante: a chave do cache_page inclui a query string inteira. Parâmetros que o form ignora, ou apenas os mesmos parâmetros em outra ordem, geram uma nova entrada e refazem as agregações sobre Article.
Medi localmente (cache Redis, como em production.py), contando as consultas SQL por requisição:
| requisição | consultas em article_article |
|---|---|
| URL A | 5 |
| URL A, repetida | 0 (cache) |
A + &x=1 |
5 |
A + &x=2 |
5 |
| A com os parâmetros reordenados | 5 |
Como o relatório é público, basta variar um parâmetro qualquer para ir sempre ao banco. Também afeta o uso normal: links com os mesmos filtros em ordem diferente não compartilham o cache. O mesmo vale para data_availability_options_view.
Sugestão: em vez de cache_page, guardar em cache o resultado de build_report / pub_years_for com uma chave montada só a partir dos filtros válidos (form.filters()), por exemplo:
key = "data_availability:{}:{}:{}:{}:{}".format(
getattr(filters["collection"], "pk", ""),
getattr(filters["journal"], "pk", ""),
filters["year_from"] or "",
filters["year_to"] or "",
filters["group_by"],
)
report = cache.get_or_set(
key,
lambda: data_availability.build_report(queryset, group_by=filters["group_by"]),
DATA_AVAILABILITY_CACHE_SECONDS,
)Assim, parâmetros estranhos e a ordem deixam de importar.
| return { | ||
| "collection": data.get("collection"), | ||
| "journal": data.get("journal"), | ||
| "year_from": data.get("year_from") or None, | ||
| "year_to": data.get("year_to") or None, | ||
| "group_by": data.get("group_by") or GROUP_BY_ISSUE_YEAR, | ||
| } |
There was a problem hiding this comment.
Não bloqueante: com year_from maior que year_to (ex.: 2025 a 2020), o intervalo passa sem aviso e o relatório sai vazio ("Nenhum artigo encontrado..."). Uma opção é inverter os dois. A comparação como string é segura porque o RegexField garante 4 dígitos:
| return { | |
| "collection": data.get("collection"), | |
| "journal": data.get("journal"), | |
| "year_from": data.get("year_from") or None, | |
| "year_to": data.get("year_to") or None, | |
| "group_by": data.get("group_by") or GROUP_BY_ISSUE_YEAR, | |
| } | |
| year_from = data.get("year_from") or None | |
| year_to = data.get("year_to") or None | |
| if year_from and year_to and year_from > year_to: | |
| year_from, year_to = year_to, year_from | |
| return { | |
| "collection": data.get("collection"), | |
| "journal": data.get("journal"), | |
| "year_from": year_from, | |
| "year_to": year_to, | |
| "group_by": data.get("group_by") or GROUP_BY_ISSUE_YEAR, | |
| } |
| <tr> | ||
| <th class="year" rowspan="2">{% translate "Year" %}</th> | ||
| {% for status, label in report.columns %} | ||
| <th colspan="2" title="{{ label }}">{{ status }}</th> |
There was a problem hiding this comment.
nit (opcional): o cabeçalho da tabela (e o CSV) mostra o código do status (data-available, not-processed...), e o rótulo traduzido aparece só no tooltip e na legenda. Para quem acessa o relatório público, talvez fosse mais legível exibir o rótulo no cabeçalho.
O que esse PR faz?
Adiciona uma área de relatórios públicos ao site, acessível em
/report/, e o primeiro relatório dessa área: Disponibilidade de dados, que mostra a quantidade de artigos por status de disponibilidade de dados (Article.data_availability_status), por ano, com totais e porcentagens.Funcionalidades:
PUBLIC_REPORTS)ArticleOnde a revisão poderia começar?
Indique o caminho do arquivo e o arquivo onde o revisor deve iniciar a leitura do código.
report/data_availability.py— lógica do relatório (filtros, agrupamento, totais e porcentagens)report/test_data_availability.py— testes do móduloreport/forms.py—DataAvailabilityFilterFormreport/views.py—report_index_view,data_availability_view,data_availability_options_viewe exportação CSVreport/urls.pyeconfig/urls.py— rotasreport/templates/report/—public_base.html,index.htmledata_availability.htmlComo este poderia ser testado manualmente?
Estabeleça os passos necessários para que a funcionalidade seja testada manualmente pelo revisor.
python manage.py test report.test_data_availabilitypython manage.py compilemessages/report/sem estar autenticado e verificar se o relatório "Disponibilidade de dados" aparece na lista20a5) e verificar se o relatório continua sendo exibido, ignorando apenas esse filtro;como separadorAlgum cenário de contexto que queira dar?
Indique um contexto onde as modificações se fazem necessárias ou passe informações que contextualizam o revisor a fim de facilitar o entendimento da funcionalidade.
O relatório responde à necessidade de acompanhar a adoção da declaração de disponibilidade de dados nos artigos publicados, por coleção e por periódico, sem exigir acesso à área administrativa.
O ano do fascículo (legenda bibliográfica) e o ano de publicação online podem divergir, por exemplo em publicação contínua ou ahead of print. Por isso o filtro de intervalo se aplica sempre ao ano de publicação online, enquanto o agrupamento das linhas pode usar qualquer um dos dois anos. Essa distinção está explicada na própria página do relatório.
Para adicionar novos relatórios públicos, basta incluir a rota em
report/urls.pye uma entrada emPUBLIC_REPORTSemreport/views.py.Como o relatório é público e consulta toda a tabela
Article, as views usamcache_pagecom validade de 1 hora; alterações recentes nos artigos podem levar até esse tempo para aparecer.Screenshots
Quando aplicável e se fizer possível, adicione screenshots que remetem à situação gráfica do problema que o pull request resolve.
Quais são os tickets relevantes?
Indique uma issue à qual o pull request faz relacionamento.
Referências
Indique as referências utilizadas para a elaboração do pull request.
cache_page): https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cachei18n_patterns): https://docs.djangoproject.com/en/stable/topics/i18n/translation/#language-prefix-in-url-patternsSegurança da informação (NSI.04)
Este PR manipula dados sensíveis ou pessoais (LGPD)?
Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?
Este PR introduz, atualiza ou remove dependências de terceiros?
Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?
Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?
Este PR expõe novos endpoints, telas ou serviços?
/report/e do relatório de disponibilidade de dados, além do endpoint JSON de opções dos filtros. São públicos por definição, somente leitura (GET) e retornam apenas contagens agregadas de artigos e listas de periódicos/anos, sem dados pessoais. Os parâmetros de entrada são validados pelo formulário e usados apenas via ORM. Servidos sob o mesmo HTTPS do site.Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?