Skip to content

Garante que o XML de um mesmo artigo receba o mesmo PID v3, independentemente da coleção de origem - #1483

Open
robertatakenaka wants to merge 39 commits into
scieloorg:mainfrom
robertatakenaka:multi_collection_article_tk1009
Open

robertatakenaka wants to merge 39 commits into
scieloorg:mainfrom
robertatakenaka:multi_collection_article_tk1009

Conversation

@robertatakenaka

Copy link
Copy Markdown
Member

O que esse PR faz?

Fale sobre o propósito do pull request, como por exemplo: quais problemas ele soluciona ou quais features ele adiciona.

Passa a registrar o PID v2 e a versão do XML de cada coleção para documentos de periódicos que estão em mais de uma coleção.

Um mesmo periódico pode estar em coleções diferentes com issn_scielo e/ou acrônimos diferentes. Nesse caso, o mesmo artigo tem PID v2 e XML diferentes em cada coleção. Até então, o PidProviderXML guardava um único v2 e uma única versão do XML, e cada recebimento de outra coleção sobrescrevia os dados anteriores ou era tratado como documento distinto.

Principais mudanças:

  • Coleções: novo campo network_classification (scielonetwork, thematic, independent), que permite identificar a coleção principal (nacional) do periódico. Também adiciona o status "migrated" em platform_status.
  • CollectionPidV2: novo modelo com PID v2, acrônimo do periódico e versão do XML por coleção. PidProviderXML.v2 e PidProviderXML.current_version passam a refletir somente a coleção principal.
  • Busca e comparação: o documento é reconhecido por qualquer um de seus PIDs v2, e compare_pid_v2 compara o PID de entrada com a lista de PIDs registrados. O filtro v2__endswith por order foi removido.
  • Fornecimento de PID: o parâmetro collection_acron é repassado desde ArticleSource e do harvesting OPAC/ArticleMeta até o registro.
  • Dependência: atualiza o packtools.

Onde a revisão poderia começar?

Indique o caminho do arquivo e o arquivo onde o revisor deve iniciar a leitura do código.

  1. collection/models.py: network_classification e is_national_journal_collection
  2. pid_provider/multi_collection.py: get_xml_collections
  3. pid_provider/models.py: CollectionPidV2 e PidProviderXML.add_collections
  4. pid_provider/query_params.py: identifier_queries, compare_pid_v2 e compare_items
  5. pid_provider/base_pid_provider.py: parâmetro collection_acron

Como este poderia ser testado manualmente?

Estabeleça os passos necessários para que a funcionalidade seja testada manualmente pelo revisor.

  1. Aplicar as migrations: python manage.py migrate
  2. No admin, em Coleções, definir network_classification: scl como SciELO Network e psi como Thematic. Conferir a coluna e o filtro na listagem.
  3. Executar o fornecimento de PID para um artigo de Psicologia USP a partir das duas coleções, por exemplo:
    • scl: S0103-65642009000300003
    • psi: S1678-51772009000300003
  4. No admin do PidProviderXML, verificar que:
    • existe um único registro para o artigo;
    • o inline "PID v2 by collection" lista as duas coleções, cada uma com seu PID v2 e sua versão do XML;
    • v2 e current_version correspondem à coleção scl.
  5. Reenviar o mesmo XML de psi e confirmar que o registro não é atualizado (skip por igualdade).
  6. Executar os testes:
python manage.py test pid_provider.tests

Algum 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.

Exemplo real: Psicologia USP está nas coleções scl e psi com ISSNs SciELO diferentes, de modo que o mesmo artigo tem PIDs v2 diferentes (S0103-6564... e S1678-5177...). Sem esta mudança, o recebimento do XML de uma coleção sobrescreve o v2 e a versão do XML da outra, ou gera registros duplicados.

A coleção principal é aquela com network_classification exclusivamente ["scielonetwork"]. Se o periódico não estiver em nenhuma coleção principal, PidProviderXML.v2 e current_version ficam vazios, e os dados permanecem somente em CollectionPidV2.

Pontos de atenção para a revisão:

  • Comportamento de XML recebido sem coleção de origem (por exemplo, fluxo do Upload).
  • CollectionPidV2.create_or_update exige journal_acron.
  • O packtools está fixado em um commit. Deve ser substituído pela tag assim que ela for publicada.

Screenshots

Quando aplicável e se fizer possível, adicione screenshots que remetem à situação gráfica do problema que o pull request resolve.

N/A

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.


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

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)

Comment thread collection/wagtail_hooks.py Outdated
)
list_filter = (
"platform_status",
"network_classification",

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.

Adicionar network_classification ao list_filter quebra a listagem de Coleções. Ao acessar /admin/snippets/collection/collection/, o django-filter lança AssertionError porque não reconhece MultipleChoiceArrayField para o lookup exact.

Image

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.

O problema continua na versão atual. O django-filter ainda lança AssertionError porque não reconhece MultipleChoiceArrayField para o lookup exact. Um solução seria configurar um filtro específico para esse campo ou retirá-lo de list_filter enquanto não houver suporte.

Comment thread collection/models.py
from collection import choices


class MultipleChoiceArrayField(ArrayField):

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.

Precisa alinhar com migração 0008 (alterar para ChoiceArrayField).

Comment thread pid_provider/models.py
Comment on lines +1473 to +1479
registered_changed.append(
{
"pid_type": "pid_v2",
"pid_in_xml": xml_with_pre.v2,
"registered": self.v2,
}
)

@pitangainnovare pitangainnovare Sep 30, 2026 •

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.

Faltou incluir "version": item.current_version, pois version é algo requerido no OtherPid.get_or_create (chamado por _add_other_pid).

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.

Continua faltando version nos dados encaminhados a _add_other_pid.

@pitangainnovare pitangainnovare left a comment

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.

Corrigir.

…eção

Substitui a tag 4.17.6 pelo commit c43041178e3c53d8e3b5df399181c301d686da6a
do packtools. Substituir pela tag correspondente assim que for publicada.
…uída

- Adiciona o status "migrated" em PLATFORM_STATUS
- Cria NETWORK_CLASSIFICATION com as opções scielonetwork, thematic
  e independent
…cação de rede

Um periódico pode estar em mais de uma coleção. A classificação de rede
permite distinguir a coleção nacional (scielonetwork) das coleções
temáticas e independentes, servindo de referência para eleger os dados
principais de um documento.

Detalhes de implementação:
- Cria ChoiceArrayField: ArrayField cujo formulário apresenta as opções
  do base_field como checkboxes, em vez de texto separado por vírgula
- Cria normalize_network_classification para converter str em lista
- Adiciona Collection.network_classification e o respectivo FieldPanel
- Adiciona Collection.get_national_journal_collections e
  Collection.is_national_journal_collection (classificação
  exclusivamente scielonetwork)
Cria o campo Collection.network_classification e inclui a opção "migrated" em platform_status.
- Inclui network_classification em list_display e list_filter
- Inclui platform_status e network_classification na exportação
…da uma

Fornece get_xml_collections, que lista as coleções do periódico do XML
indicando o acrônimo do periódico e se é a coleção principal, e
get_journal_pid_from_v2, que obtém o ISSN adotado como PID do periódico
(issn_scielo) a partir do PID v2.
Um periódico presente em mais de uma coleção pode ter issn_scielo e
acrônimo diferentes em cada uma, de modo que o mesmo artigo recebe PIDs
v2 distintos (ex.: Psicologia USP, S0103-65642009000300003 em scl e
S1678-51772009000300003 em psi). A busca e a comparação de registros
passam a considerar todos os PIDs v2 do documento.

Detalhes de implementação:
- identifier_queries busca também por collection_pids_v2__pid_v2
- Cria compare_pid_v2: score 1 se o PID v2 de entrada está entre os
  registrados; 0 caso contrário (sem similaridade parcial)
- compare_items avalia igualdade exata antes do tratamento de listas e
  delega "pid_v2" a compare_pid_v2
- Remove o filtro v2__endswith por order de article_location_params,
  pois o sufixo do PID v2 não é confiável entre coleções
…e uma coleção

O mesmo documento pode ser recebido de coleções diferentes, com PID v2 e
XML distintos. Passa-se a guardar o PID v2 e a versão do XML de cada
coleção, mantendo em PidProviderXML.v2 e PidProviderXML.current_version
os dados da coleção principal (scielonetwork).

Detalhes de implementação:
- Cria CollectionPidV2 (pid_provider_xml, collection, pid_v2,
  journal_acron, current_version), com unique_together
  (pid_provider_xml, collection) e índice em pid_v2
- Adiciona InlinePanel "PID v2 by collection" em PidProviderXML
- add_collections passa a usar get_xml_collections e registra
  CollectionPidV2 para a coleção de origem do XML; somente a coleção
  principal atualiza v2 e current_version
- _update_data deixa de atribuir v2 diretamente
- Cria get_current_version(collection, collection_acron) e o usa em
  get_xml_with_pre, fix_pid_v2 e demais pontos que acessavam
  current_version
- is_equal_to compara com a versão da mesma coleção
- check_registered_pids_changed compara o PID v2 da coleção de origem e
  aceita ignore_v2; remove "version" dos itens retornados
- Cria pid_v2_list e collection_pids_v2_data; data inclui
  collection_pids_v2 e data_to_compare usa pid_v2 como lista
- Busca por v2 inclui collection_pids_v2__pid_v2; aplica distinct() nas
  consultas que fazem join com collection_pids_v2
- public_items considera documentos com versão apenas em alguma coleção
- delete_queryset remove também os CollectionPidV2
- Respostas de register e is_registered incluem o v2 do XML de entrada
Cria o modelo CollectionPidV2 com unique_together (pid_provider_xml, collection) e índice em pid_v2.
Adiciona o parâmetro collection_acron a provide_pid_for_xml_with_pre,
provide_pid_for_xml_zip e provide_pid_for_xml_uri. Quando informado e
diferente do custom-meta do XML, sobrescreve xml_with_pre.collection.
… e ArticleMeta

Repassa collection_acron para provide_pid_for_xml_uri em provide_pid_for_opac_and_am_xml.
Repassa collection.acron3 como collection_acron no registro do PID.
…gistro

- Cria ComparePidV2Tests
- Ajusta identifier_queries esperado para incluir collection_pids_v2
- Verifica ausência de v2__endswith em article_location_params
Inclui collection em xml_with_pre de CheckRegisteredPidsChangedTests e espera pid_v2 como lista em data_to_compare.
Testa o modelo CollectionPidV2: criação e atualização por
(pid_provider_xml, collection), obtenção da versão atual por coleção
ou da coleção principal e comparação do XML com a versão da mesma
coleção.
Testa o registro do mesmo documento vindo de coleções diferentes, com
PIDs v2 distintos: v2 e current_version do PidProviderXML refletem a
coleção principal (scielonetwork) e cada coleção mantém seu próprio
PID v2 e versão do XML em CollectionPidV2.
…egistros duplicados

Testa que XMLs de coleções diferentes com identificadores em comum
(v3, DOI ou PID v2 de outra coleção) são associados ao mesmo
PidProviderXML, sem criar novo registro nem conflito de PID.
…cação de rede

Um periódico pode estar em mais de uma coleção. A classificação de rede
permite distinguir a coleção nacional (scielonetwork) das coleções
temáticas e independentes, servindo de referência para eleger os dados
principais de um documento.

Detalhes de implementação:
- Cria ChoiceArrayField: ArrayField cujo formulário apresenta as opções
  do base_field como checkboxes, em vez de texto separado por vírgula
- Cria normalize_network_classification para converter str em lista
- Adiciona Collection.network_classification e o respectivo FieldPanel
- Adiciona Collection.get_national_journal_collections e
  Collection.is_national_journal_collection (classificação
  exclusivamente scielonetwork)
@robertatakenaka
robertatakenaka force-pushed the multi_collection_article_tk1009 branch from b61da3b to c9ce912 Compare September 30, 2026 23:42
…a possuem

Coleções cadastradas sem network_classification ficavam sem esse dado mesmo quando ele está disponível no ArticleMeta. Agora é possível preenchê-lo em lote, preservando os valores já existentes, e a normalização do campo passa a ser a mesma em todos os pontos de entrada.

Detalhes de implementação:
- Extrai a URL do ArticleMeta para a constante ARTICLEMETA_COLLECTIONS_URL, reutilizada em load e no novo método
- Adiciona Collection.complete_network_classification, que seleciona coleções com network_classification nulo ou vazio, busca os dados no ArticleMeta somente se houver coleções a completar, e atualiza via bulk_update
- Define updated manualmente, pois bulk_update não aplica auto_now
- Retorna um resumo com as coleções atualizadas (updated) e as ausentes na fonte (not_found)
- normalize_network_classification passa a descartar itens vazios da lista
- create_or_update passa a usar normalize_network_classification em vez de repetir a lógica
…ões já classificadas

Cobre o comportamento de Collection.complete_network_classification com testes automatizados.

Detalhes de implementação:
- Verifica que apenas coleções com network_classification nulo ou vazio são atualizadas e que as já preenchidas mantêm o valor
- Verifica a normalização de valor em string para lista
- Verifica o relato de coleções ausentes na fonte (not_found)
- Verifica que o ArticleMeta não é consultado quando todas as coleções já estão completas
- Verifica que o ArticleMeta é consultado quando collections_data não é informado
- Verifica o registro de updated_by
… assíncrona

Disponibiliza o preenchimento em lote de network_classification para ser disparado pelo Celery, como já ocorre com a carga de coleções.

Detalhes de implementação:
- Extrai a obtenção do usuário por user_id ou username para a função _get_user, reutilizada pelas tarefas
- Adiciona task_complete_network_classification, que retorna o resumo produzido por Collection.complete_network_classification
O filtro por network_classification não funcionava na listagem do admin porque o django-filter não gera filtro automaticamente para ArrayField. Agora é possível selecionar uma ou mais classificações e listar as coleções que possuem ao menos uma delas.

Detalhes de implementação:
- Substitui list_filter por filterset_class com CollectionFilterSet (WagtailFilterSet)
- Declara network_classification como MultipleChoiceFilter com choices.NETWORK_CLASSIFICATION e widget CheckboxSelectMultiple
- Filtra com o lookup __overlap do ArrayField do Postgres
- Mantém os demais filtros existentes no Meta.fields
…or de testes

Sem o __init__.py, o diretório collection/tests não era tratado como pacote e seus testes não eram encontrados pelo test runner do Django.
…or linha de comando

Oferece uma forma de disparar manualmente o preenchimento em lote de network_classification das coleções que ainda não possuem esse dado, sem depender do agendamento de tarefas.

Detalhes de implementação:
- Adiciona script em collection/scripts que aciona o preenchimento a partir do usuário informado
Cobre com testes automatizados o comportamento de normalize_network_classification.

Detalhes de implementação:
- Verifica a conversão de string para lista
- Verifica o descarte de itens vazios
- Verifica o retorno None para valores nulos ou listas vazias
Cobre com testes automatizados as tarefas do Celery do app collection.

Detalhes de implementação:
- Verifica a obtenção do usuário por user_id ou username e o erro quando nenhum é informado
- Verifica que task_complete_network_classification aciona Collection.complete_network_classification e retorna seu resumo
…em de coleções

Cobre com testes automatizados o CollectionFilterSet usado no admin de coleções.

Detalhes de implementação:
- Verifica que a seleção de uma ou mais classificações retorna as coleções que possuem ao menos uma delas (__overlap)
- Verifica que, sem seleção, o queryset não é filtrado
… das coleções

Adiciona a configuração COLLECTION_ENSURE_NETWORK_CLASSIFICATION (padrão True,
lida da variável de ambiente de mesmo nome), que habilita ou desabilita a consulta
automática ao articlemeta para completar network_classification das coleções antes
de PidProviderXML.register.
Define COLLECTION_ENSURE_NETWORK_CLASSIFICATION = False no ambiente de testes.
Os testes que precisam do comportamento o habilitam explicitamente com
override_settings.
As coleções sem network_classification impedem a identificação da coleção
principal de artigos publicados em mais de uma coleção. Passa a ser possível
completar esse dado sob demanda, de forma segura e sem sobrecarregar o articlemeta.

Detalhes de implementação:
- adiciona Collection.get_collections_without_network_classification, que retorna
  as coleções com network_classification nulo ou vazio; complete_network_classification
  passa a reutilizá-lo
- adiciona Collection.ensure_network_classification(user), que:
  - não faz nada se COLLECTION_ENSURE_NETWORK_CLASSIFICATION estiver desabilitado
  - consulta o articlemeta somente se houver coleções sem o dado
  - usa cache.add (atômico) com a chave ENSURE_NETWORK_CLASSIFICATION_CACHE_KEY para
    consultar no máximo uma vez a cada ENSURE_NETWORK_CLASSIFICATION_INTERVAL (600s),
    evitando uma consulta por registro quando o articlemeta está indisponível ou não
    tem a coleção
  - registra falhas no log sem interromper quem chama
…icoleção

Chama Collection.ensure_network_classification(user) no início de
PidProviderXML.register, antes de identificar a coleção principal
(add_collections), para que coleções sem network_classification não
comprometam essa identificação.
…ão de rede

Adiciona RegisterCompletesNetworkClassificationTest, com
COLLECTION_ENSURE_NETWORK_CLASSIFICATION habilitado:
- remove network_classification das coleções e limpa a chave de cache
- simula a resposta do articlemeta (fetch_data) com scl como scielonetwork e psi
  como thematic
- verifica que o articlemeta é consultado uma única vez, que scl é identificada
  como coleção nacional e psi não, e que o artigo é registrado uma única vez com
  o pid v2 e a versão corrente da coleção scl
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.

3 participants