Skip to content

feat: adiciona status NOT_PUBLIC para evitar requisições a documentos despublicados#1453

Open
robertatakenaka wants to merge 14 commits into
scieloorg:mainfrom
robertatakenaka:criacao_article_via_opac_xml_3
Open

feat: adiciona status NOT_PUBLIC para evitar requisições a documentos despublicados#1453
robertatakenaka wants to merge 14 commits into
scieloorg:mainfrom
robertatakenaka:criacao_article_via_opac_xml_3

Conversation

@robertatakenaka

@robertatakenaka robertatakenaka commented Jul 22, 2026

Copy link
Copy Markdown
Member

O que esse PR faz?

Implementa suporte a filtragem por periódico (journal_acron) na coleta de artigos e adiciona tratamento de artigos não públicos (is_public), além de expandir a cobertura de testes unitários do app article.

Principais mudanças:

  • ArticleIteratorBuilder (article/controller.py): a iteração por coleção passou a ser feita via SciELOJournal (join com Collection), permitindo filtrar por journal_acron_list além de collection_acron_list. O harvester agora recebe o periódico (journal_acron/journal_id) e um novo parâmetro stop (limite de páginas). O item retornado pelo iterator passou a incluir is_public.
  • ArticleSource (article/models.py): novo status NOT_PUBLIC no StatusChoices. create/create_or_update agora recebem is_public e ajustam o status de acordo (define NOT_PUBLIC quando is_public=False; retorna a PENDING se um artigo antes não-público se tornar público). O método de processamento passa a interromper artigos marcados como NOT_PUBLIC, a menos que force_update=True seja passado. Correção: complete() não fazia mais self.save() redundante.
  • Renomeação: add_normalized_affiliationset_normalized na chamada feita por ContribPerson.
  • task_dispatch_articles/task_process_article_pipeline (article/tasks.py): novos parâmetros verify, stop e is_public, propagados até ArticleSource.create_or_update.
  • Harvesters (core/utils/harvesters.py):
    • AMHarvester: novo parâmetro journal, incluído como issn na query da API do ArticleMeta.
    • OPACHarvester: novos parâmetros journal e stop; extração do campo is_public a partir de item.get("status") retornado pela API do OPAC; paginação agora respeita stop; URL base passou a usar https:// explicitamente.
  • Migração 0049_alter_articlesource_status.py: adiciona a choice NOT_PUBLIC ao campo status de ArticleSource.
  • Testes: novos arquivos article/tests/test_mixins.py, test_models.py e test_tasks.py.

Onde a revisão poderia começar?

Sugiro começar por article/models.py (lógica de is_public/NOT_PUBLIC em ArticleSource), seguido de article/controller.py (mudança na fonte de iteração de coleções para SciELOJournal) e core/utils/harvesters.py (novos parâmetros journal/stop e extração de is_public).

Como este poderia ser testado manualmente?

  1. Rodar a suíte de testes do app article:
    python manage.py test article.tests
    
  2. Disparar task_dispatch_articles informando collection_acron_list e journal_acron_list para validar o filtro por periódico via SciELOJournal.
  3. Simular um item vindo do OPAC com status indicando não-público e verificar que o ArticleSource correspondente recebe status=not_public e que o processamento é interrompido (sem force_update).
  4. Testar reprocessamento com force_update=True sobre um ArticleSource em NOT_PUBLIC para confirmar que ele volta a ser processado.
  5. Validar a paginação do OPACHarvester com stop definido (ex.: stop=2) para confirmar que a coleta é interrompida na página esperada.
django@d472b88177fe:/app$ python manage.py test article.tests --keepdb
WARNING 2026-07-22 21:44:21,031 profiling_tools 16349 281473848352064 PROFILING_ENABLED=True
WARNING 2026-07-22 21:44:21,031 profiling_tools 16349 281473848352064 PROFILING_LOG_ALL=True
WARNING 2026-07-22 21:44:21,031 profiling_tools 16349 281473848352064 PROFILING_LOG_SLOW_REQUESTS=0.2
WARNING 2026-07-22 21:44:21,031 profiling_tools 16349 281473848352064 PROFILING_LOG_HIGH_MEMORY=20
/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py:98: RuntimeWarning: Accessing the database during app initialization is discouraged. To fix this warning, avoid executing queries in AppConfig.ready() or when your app modules are imported.
  warnings.warn(self.APPS_NOT_READY_WARNING_MSG, category=RuntimeWarning)
Found 52 test(s).
Using existing test database for alias 'default'...
System check identified some issues:

WARNINGS:
?: (urls.W005) URL namespace 'pid_provider' isn't unique. You may not be able to reverse all URLs in this namespace
?: (wagtailadmin.W003) The WAGTAILADMIN_BASE_URL setting is not defined
	HINT: This should be the base URL used to access the Wagtail admin site. Without this, admin URLs outside of the admin (e.g. notification emails and the user bar) will not display correctly.
?: settings.ACCOUNT_AUTHENTICATION_METHOD is deprecated, use: settings.ACCOUNT_LOGIN_METHODS = {'username'}
?: settings.ACCOUNT_EMAIL_REQUIRED is deprecated, use: settings.ACCOUNT_SIGNUP_FIELDS = ['email*', 'username*', 'password1*', 'password2*']
pid_provider.FixPidV2.pid_provider_xml: (fields.W342) Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.
	HINT: ForeignKey(unique=True) is usually better served by a OneToOneField.

System check identified 5 issues (0 silenced).
....................................................
----------------------------------------------------------------------
Ran 52 tests in 2.288s

OK

Conferir as datas de atualização de Pid Provider XML e Article

Algum cenário de contexto que queira dar?

Este PR resolve a necessidade de: (1) coletar artigos filtrando por periódico específico dentro de uma coleção (antes só era possível por coleção inteira); (2) tratar corretamente artigos que a fonte (OPAC) sinaliza como não públicos, evitando reprocessamento desnecessário até que se tornem públicos ou até um force_update explícito. A migração adiciona apenas uma nova choice ao campo status, sem impacto em dados existentes.

Screenshots

Não aplicável (mudança de backend/lógica de processamento).

Quais são os tickets relevantes?

Relacionado com #1449

Referências

A preencher, se houver documentação ou discussão relacionada (ex. issue do OPAC sobre campo status/visibilidade).


Segurança da informação (NSI.04)

Seção obrigatória. Marque as opções aplicáveis e justifique quando necessário. Referência: NSI.04 - Norma de Desenvolvimento Seguro.

Este PR manipula dados sensíveis ou pessoais (LGPD)?

  • Sim — descreva os controles de proteção aplicados (criptografia, mascaramento, anonimização, etc.):
  • Não — o PR não introduz nova coleta/exposição de dados pessoais. O sinalizador is_public/NOT_PUBLIC refere-se à visibilidade editorial do artigo (status de publicação), não a dado pessoal de indivíduo. Os testes em test_models.py e test_tasks.py que envolvem nomes, e-mails e ORCID de ContribPerson/ArticleAffiliation/ResearcherIdentifier usam exclusivamente dados fictícios/de exemplo e cobrem funcionalidade já existente, não introduzida por este PR.

Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?

  • Sim — descreva o que mudou e por quê:
  • Não

Este PR introduz, atualiza ou remove dependências de terceiros?

  • Sim — as novas dependências foram verificadas no SBOM/Trivy sem vulnerabilidades críticas/altas em aberto?
    • Verificado e aprovado
    • Pendente / vulnerabilidade aceita com justificativa: test_models.py passa a importar freezegun (usado para congelar datas na criação de artigos em testes de deduplicação). Se ainda não constar no requirements/SBOM do projeto, é necessário adicioná-la e submetê-la à verificação de vulnerabilidades antes do merge.
  • Não

Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?

  • Sim — link do job:
  • Não aplicável a este PR (justifique): pipeline ainda não executado nesta branch/PR — aguardando execução do CI antes do merge.

Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?

  • Sim — confirme que há sanitização/parametrização (prepared statements, escaping, etc.):
  • Não — todas as consultas usam o ORM do Django (filter, values_list, select_related) com parâmetros tipados; as chamadas HTTP feitas pelos harvesters usam urlencode para montar a query string, sem concatenação direta de entrada externa em SQL/HTML/JS.

Este PR expõe novos endpoints, telas ou serviços?

  • Sim — HTTPS obrigatório está garantido e o acesso segue o princípio de menor privilégio?
  • Não — as mudanças são internas (models, tasks, harvesters, migração); nenhuma view, API ou tela nova foi criada. Observação: a URL padrão do OPACHarvester foi corrigida de www.scielo.br (sem esquema) para https://www.scielo.br, reforçando o uso de HTTPS nas requisições já existentes. Recomenda-se confirmar que verify=True (verificação de certificado SSL) permanece o padrão em produção, já que AMHarvester/OPACHarvester aceitam verify=False.

Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?

  • Não, nenhum segredo foi commitado
  • Sim (bloquear merge e corrigir antes de prosseguir)

Propósito:
Evitar requisição HTTP desnecessária para documentos que estão
temporária ou efetivamente despublicados, identificados via is_public.

Solução técnica:
- Novo status NOT_PUBLIC em ArticleSource.StatusChoices.
- create() e create_or_update() passam a aceitar is_public: quando
  False, o status é definido como NOT_PUBLIC em vez de PENDING.
- create_or_update(): quando is_public volta a True e o status atual
  é NOT_PUBLIC, o status retorna para PENDING, reabrindo o pipeline.
  obj.save() só é chamado quando algo de fato mudou (changed).
- add_pid_provider(): adiciona guarda no início — se o status é
  NOT_PUBLIC e force_update não foi passado, retorna sem executar
  request_xml nem request_pid.

Notas adicionais (fora do escopo de is_public, incluídas neste diff):
- is_completed deixa de chamar self.save() ao marcar COMPLETED.
- ContribPerson passa a usar affiliation.set_normalized(...) em vez
  de add_normalized_affiliation(...).
Propósito:
Fornecer o dado de publicação/despublicação já na coleta (harvest),
para que o pipeline possa decidir sem precisar de requisição extra.

Solução técnica:
Inclui "is_public": item.get("status") no dict retornado por
OPACHarvester. Nome do campo de origem manteve-se status (herdado da
fonte); mapeado para is_public no dict de saída.
Propósito:
Propagar o sinal de publicação/despublicação coletado pelo harvester
até o ponto de despacho da task, para que chegue a
task_process_article_pipeline.

Solução técnica:
Adiciona "is_public": document.get("is_public") ao dict yield de
_iter_from_harvest.
Propósito:
Fechar o fluxo do sinal is_public até ArticleSource.create_or_update,
permitindo que o pipeline marque/reabra o status NOT_PUBLIC.

Solução técnica:
Novo parâmetro is_public em task_process_article_pipeline, repassado
para ArticleSource.create_or_update(...).
Propósito:
Criar o pacote de testes do app article.

Solução técnica:
Arquivo __init__.py vazio, necessário para o Django/pytest reconhecer
o diretório como pacote de testes.
Propósito:
Fornecer mixins/utilitários de teste reutilizáveis pelas demais
suítes do app article (setup de dados, mocks comuns, supressão de
logging do OpenSearch em ambiente de teste).

Solução técnica:
Novo arquivo test_mixins.py.
Propósito:
Cobrir com testes unitários os models do app article (Article,
ArticleSource, ContribPerson, etc.).

Solução técnica:
Novo arquivo test_models.py, usando os mixins de test_mixins.py e
unittest.mock para isolar dependências externas.
Propósito:
Cobrir com testes unitários as tasks do app article
(task_dispatch_articles, task_process_article_pipeline, etc.) e o
ArticleIteratorBuilder.

Solução técnica:
Novo arquivo test_tasks.py, usando mock-based testing (unittest.mock)
e os mixins de test_mixins.py.
Propósito:
Persistir no banco a nova choice not_public do campo status de
ArticleSource, introduzida para suportar documentos temporária ou
efetivamente despublicados.

Solução técnica:
Migração gerada automaticamente (AlterField) a partir da alteração em
ArticleSource.StatusChoices, adicionando ("not_public", "Not public")
à lista de choices do campo status.
@robertatakenaka robertatakenaka changed the title Criacao article via opac xml 3 feat: adiciona status NOT_PUBLIC para evitar requisições a documentos despublicados Jul 22, 2026
…Harvester

Propósito:
Permitir coletar documentos de um periódico específico, em vez de
sempre buscar a coleção inteira.

Solução técnica:
- AMHarvester: novo parâmetro journal (ISSN); quando informado, é
  incluído como issn nos params da requisição.
- OPACHarvester: novo parâmetro journal (acrônimo); quando informado,
  é incluído como journal na querystring. URL base (main_url) é
  montada uma única vez fora do loop de paginação, e cada página
  apenas concatena &page={page}, evitando reconstruir toda a URL a
  cada iteração.
Propósito:
Usar o novo suporte a filtro por periódico dos harvesters, permitindo
respeitar journal_acron_list e reduzir o volume de cada requisição de
harvest (por periódico em vez de coleção inteira).

Solução técnica:
- Import de SciELOJournal em journal.models.
- _iter_from_harvest passa a consultar SciELOJournal (com
  select_related('collection')) filtrando por collection_acron_list e
  journal_acron_list, obtendo tuplas (collection__acron3,
  journal_acron, issn_scielo) distintas.
- Para cada tupla, _build_harvester é chamado passando journal_acron
  (OPAC) ou issn_scielo (ArticleMeta), instanciando um harvester por
  periódico em vez de um único harvester por coleção.
- _build_harvester ganha os parâmetros journal_acron e journal_id,
  repassados ao harvester correspondente (OPACHarvester ou
  AMHarvester) via kwargs['journal'].
- Remove logging.info residual (collection_acron, harvester) do loop
  anterior.
…CHarvester

Propósito:
Permitir interromper a coleta de documentos do OPAC após um número
máximo de páginas, útil para testes e execuções controladas sem
depender apenas de total_pages.

Solução técnica:
Novo parâmetro stop em OPACHarvester.__init__. Dentro do loop de
paginação de harvest_documents, após incrementar page, encerra o
loop (break) se stop for informado e page ultrapassar esse limite —
checagem feita antes da verificação de total_pages.
Propósito:
Expor o novo limite de páginas (stop) do OPACHarvester através do
ArticleIteratorBuilder, para uso em _iter_from_harvest.

Solução técnica:
Novo parâmetro stop em ArticleIteratorBuilder.__init__, armazenado
em self.stop. Em _build_harvester, quando a coleção é 'scl' e
self.stop está definido, o valor é incluído em kwargs['stop'] antes
de instanciar o OPACHarvester. Corrige também a URL padrão do OPAC
(www.scielo.br -> https://www.scielo.br).
…Builder

Propósito:
Permitir configurar, a partir da task orquestradora, o limite de
páginas (stop) usado no harvest via OPAC.

Solução técnica:
Novos parâmetros verify e stop na assinatura de
task_dispatch_articles; stop é repassado à instanciação de
ArticleIteratorBuilder. Nota: verify é aceito na assinatura mas não é
utilizado em nenhum ponto deste diff — confirmar se é uso futuro
intencional ou parâmetro remanescente antes de mergear.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant