Skip to content

O OPACHarvester passa a usar o counter_dict v2 do OPAC, que filtra pela data e hora completas de atualização - #1494

Open
robertatakenaka wants to merge 11 commits into
scieloorg:mainfrom
robertatakenaka:melhora_harvest_counter_dict_2
Open

robertatakenaka wants to merge 11 commits into
scieloorg:mainfrom
robertatakenaka:melhora_harvest_counter_dict_2

Conversation

@robertatakenaka

@robertatakenaka robertatakenaka commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

O que esse PR faz?

Permite a coleta incremental de artigos do OPAC sem perder documentos atualizados no mesmo dia da coleta.

O OPACHarvester passa a usar o counter_dict v2 do OPAC, que filtra pela data e hora completas de atualização (updated, em UTC). Essa data e hora passa a ser guardada em ArticleSource.source_date, de modo que o ponto de corte de uma coleta pode ser usado como início (from_date) da seguinte.

Principais mudanças:

  • core/utils/harvesters.py: coleta via /api/v2/counter_dict, com URL montada por urlencode; until_date padrão com hora (truncada em segundos); leitura de documents como lista; novo _parse_iso_datetime no lugar de _parse_gmt_date; novos campos origin_date (data) e updated (data e hora, com fallback para created); is_public lido de is_public (v2) ou status (v1).
  • article/models.py: ArticleSource.source_date com max_length 32 para aceitar ISO 8601 completo; request_pid continua enviando somente a data (YYYY-MM-DD) como origin_date ao PidProviderXML.
  • article/migrations/0052_alter_articlesource_source_date.py: migration da alteração acima.
  • article/controller.py: source_date passa a usar processing_date, depois updated, depois origin_date.
  • article/wagtail_hooks.py: listagem de ArticleSource sem as colunas url e pid_provider_xml; busca sem url.
  • Testes para o harvester v2, o ArticleIteratorBuilder.from_harvest e o request_pid.

Onde a revisão poderia começar?

core/utils/harvesters.py, em OPACHarvester.__init__ (comentários sobre datas e corte) e OPACHarvester.harvest_documents.

Como este poderia ser testado manualmente?

  1. Aplicar a migration: python manage.py migrate article.
  2. Conferir a resposta da API v2 diretamente, por exemplo:
    https://www.scielo.br/api/v2/counter_dict?begin_date=2026-10-27T00:00:00&end_date=2026-10-28T00:00:00&limit=10
  3. Executar a coleta de artigos do OPAC para uma coleção/periódico com from_date recente.
  4. No admin, em ArticleSource, verificar que source_date foi gravado com data e hora completas (ex.: 2026-10-28T02:42:17.464000).
  5. Executar nova coleta usando como from_date o maior source_date obtido: o documento do limite deve aparecer novamente (limites inclusivos) e nenhum documento atualizado depois dele deve faltar.
  6. Verificar no PidProviderXML correspondente que origin_date contém somente a data.
  7. Verificar a listagem e a busca de ArticleSource no admin (busca por pid e por v3).
  8. Rodar os testes:
    python manage.py test article.tests.test_article_iterator_builder article.tests.test_models

Algum cenário de contexto que queira dar?

O counter_dict v1 retornava datas no formato GMT e a coleta usava somente a data (YYYY-MM-DD) como end_date. Como uma data sem hora equivale à meia-noite, os documentos atualizados no próprio dia da coleta ficavam de fora, e não havia como retomar a coleta a partir de um instante preciso.

No v2, o OPAC filtra por begin_date <= updated <= end_date, em UTC, com precisão de milissegundos (BSON), e retorna updated com microssegundos. Reutilizar o source_date de uma coleta como from_date da seguinte repete o documento do limite, mas não perde documentos. Documentos atualizados na fração final do segundo de until_date ficam para a próxima coleta.

Screenshots

Captura de Tela 2026-10-29 às 17 21 49 Captura de Tela 2026-10-29 às 17 21 28

Quais são os tickets relevantes?

#1491

Referências

  • Endpoint counter_dict v2 do OPAC (opac_5)
  • ISO 8601 / datetime.fromisoformat

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

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

  • Sim — descreva os controles de proteção aplicados (criptografia, mascaramento, anonimização, etc.):
  • Não

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:
  • 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):

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

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

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)

…no mesmo dia

Passa a coletar do counter_dict v2 do OPAC, que filtra pela data e hora
completas de atualização (UTC). Assim o ponto de corte de uma coleta
pode servir de início exato da seguinte, e os documentos atualizados no
próprio dia da coleta não são mais excluídos.

Detalhes de implementação:
- URL alterada de /api/v1/counter_dict para /api/v2/counter_dict,
  montada com urlencode (begin_date, end_date, limit, journal)
- until_date padrão passa a ter hora, truncada em segundos
  (datetime.utcnow().isoformat(timespec="seconds")); antes era só a
  data, equivalente à meia-noite
- documents passa a ser lista (v2) em vez de dict indexado por pid_v3;
  pid_v3 vem de "aid" ou "pid_v3"
- _parse_gmt_date substituído por _parse_iso_datetime, que valida e
  normaliza ISO 8601 com microssegundos
- novos campos origin_date (YYYY-MM-DD) e updated (data e hora
  completas), com fallback de updated para created
- is_public lido de "is_public" (v2) ou "status" (v1)
- limites begin_date/end_date são inclusivos: reutilizar o source_date
  como from_date repete o documento do limite, sem perda
ArticleSource.source_date passa a aceitar data e hora em ISO 8601
(ex.: updated do OPAC), mantendo compatibilidade com datas YYYY-MM-DD.
PidProviderXML.origin_date continua recebendo somente a data.

Detalhes de implementação:
- source_date: max_length de 10 para 32
- request_pid envia origin_date=self.source_date[:10]
…oleta

ArticleIteratorBuilder passa a preencher source_date com o updated do
OPAC quando não há processing_date, antes de recorrer a origin_date.
Com isso, o source_date guardado serve de from_date (inclusivo) da
coleta seguinte.
Remove url e pid_provider_xml das colunas da listagem e url dos campos
de busca, que passa a usar apenas pid_provider_xml__v3 e pid.
Garante que request_pid envia "2026-10-28" quando source_date contém
data e hora completas ("2026-10-28T02:42:17").
Cobre a montagem da URL v2, until_date padrão com hora, a leitura da
lista de documentos com updated completo e fallback para created,
is_public (v2) e status (v1), datas inválidas e o uso de updated como
source_date em ArticleIteratorBuilder.from_harvest.
@robertatakenaka robertatakenaka changed the title Melhora harvest counter dict 2 O OPACHarvester passa a usar o counter_dict v2 do OPAC, que filtra pela data e hora completas de atualização Oct 29, 2026
…ico opcional

Antes, a coleta dependia dos periódicos registrados em SciELOJournal:
uma coleção sem periódicos cadastrados não era coletada. Agora a
iteração parte das coleções, e o filtro por periódico só é aplicado
quando informado. Sem filtro, cada coleção é coletada por um único
harvester. O parâmetro opac_url passa a direcionar qualquer coleção
para o OPAC, não apenas a scl.

Detalhes de implementação:
- from_harvest itera sobre collection_acron_list ou, se ausente, sobre
  todos os acron3 de Collection
- _build_harvester foi substituído pelo gerador _build_harvesters, que
  produz um ou mais harvesters por coleção
- OPAC (scl ou opac_url informado): um OPACHarvester por acrônimo de
  journal_acron_list ou um único para a coleção
- ArticleMeta: um AMHarvester por issn_scielo dos periódicos filtrados
  (excluindo issn_scielo nulo) ou um único para a coleção
- kwargs com valor None (from_date, until_date, limit, timeout) não
  são mais repassados aos harvesters
Atualiza os testes de from_harvest para a nova forma de montar os
harvesters e adiciona cobertura para a seleção entre OPAC e ArticleMeta
com e sem filtro de periódico.

Detalhes de implementação:
- mocks de SciELOJournal deixam de usar select_related e passam a
  encadear filter().order_by().values_list().distinct()
- remove test_builds_harvester_per_item_with_positional_args
- testes de from_harvest fazem patch de _iter_harvesters
- nova classe TestIterHarvesters: OPAC com um harvester por coleção,
  OPAC com um harvester por periódico, opac_url aplicado a qualquer
  coleção e ArticleMeta com um harvester por issn_scielo
…entes na origem

Ordena a listagem de ArticleSource pela data da fonte em vez da data
de atualização local do registro.

Detalhes de implementação:
- ArticleSourceSnippetViewSet.ordering: "-updated" → "-source_date"
Os testes passam a refletir a implementação atual: a iteração parte
das coleções (informadas ou todas as cadastradas) e os harvesters de
cada coleção são produzidos por _build_harvesters. A seleção entre OPAC
e ArticleMeta, com e sem filtro de periódico, é testada diretamente.

Detalhes de implementação:
- from_harvest: testes deixam de mockar SciELOJournal e passam a
  verificar o uso de collection_acron_list ou, na ausência dela, de
  Collection.objects.values_list("acron3", flat=True)
- testes de from_harvest fazem patch de _build_harvesters (lista de
  harvesters) em vez de _iter_harvesters (tuplas)
- TestIterHarvesters e TestBuildHarvester substituídas por
  TestBuildHarvesters
- novos casos: repasse de stop ao OPAC, ausência de consulta a
  SciELOJournal sem journal_acron_list e um AMHarvester por
  issn_scielo via filter(collection__acron3=..., journal_acron__in=...)
  .exclude(...)
- verifica que kwargs com valor None não são repassados aos harvesters
- bloco if __name__ == "__main__" movido para o final do arquivo
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.

2 participants