Skip to content

fix(journal): Corrige falha de atualização de journal_history, causando status "inprogress" e impedindo a publicação - #1052

Open
robertatakenaka wants to merge 15 commits into
scieloorg:mainfrom
robertatakenaka:main_atualiza_com_rc_journal
Open

fix(journal): Corrige falha de atualização de journal_history, causando status "inprogress" e impedindo a publicação#1052
robertatakenaka wants to merge 15 commits into
scieloorg:mainfrom
robertatakenaka:main_atualiza_com_rc_journal

Conversation

@robertatakenaka

Copy link
Copy Markdown
Member

O que este PR faz

Descreva de forma objetiva o que foi alterado e por quê.

Corrige o bug raiz que causava current_status = "inprogress" em periódicos já publicáveis: JournalCollection.create_or_update não tinha return obj no ramo em que o registro já existia, retornando None implicitamente e quebrando a associação de JournalHistory durante o reprocessamento de dados do core.scielo.org. Corrige o mesmo padrão de bug em JournalHistory.create_or_update. Em conjunto, remove redundâncias identificadas durante a investigação: imports locais repetidos de Collection em journal/models.py; funções standalone (create_or_update_journal, create_or_update_issue) que duplicavam a lógica hoje centralizada em BaseDataChecker.get_or_fetch; e o cálculo de event_type/current_status, antes duplicado inline em build_journal, agora centralizado em JournalPayload (translate_status, add_current_status, add_forced_current_status). Também unifica o acesso ao histórico do periódico via JournalProc.journal_history, eliminando um filtro repetido em publish_journal.

Por que essa mudança é necessária

Explique o problema/motivação que originou este PR.

Periódicos com JournalCollection já cadastrada perdiam o vínculo com JournalHistory durante o reprocessamento (retorno None de create_or_update), resultando em status_history vazio e current_status = "inprogress", o que bloqueava a publicação do periódico e, em cascata, de todos os seus artigos. As redundâncias de código identificadas na investigação também aumentavam o risco de a mesma classe de bug reaparecer em pontos similares.

Como foi testado

Descreva os testes realizados (unitários, manuais, etc.).

  • Testes unitários novos com unittest/mock:
    • journal/tests/test_models_get_registered.py: cobre get_registered (match exato, trocado, fallback por ISSNs).
    • proc/tests/test_source_core_api.py: cobre process_journal_result de ponta a ponta com TestCase (banco real), validando que JournalHistory é criado e associado corretamente mesmo quando JournalCollection já existe previamente.
    • publication/tests/test_api_journal.py e test_utils_journal.py: cobrem translate_status, add_current_status, add_forced_current_status e build_journal, garantindo que o histórico processado corretamente resulta em current_status correto.
  • Cenário manual reproduzido: JournalCollection pré-existente + reprocessamento com novo item de journal_history → antes do fix, JournalHistory.create_or_update recebia journal_collection=None; após o fix, recebe o objeto correto e current_status passa a refletir o histórico real.
  • Execução local: python manage.py test journal.tests proc.tests publication.tests.

Closes: Fixes #1051

Checklist NSI.04 – Segurança (baseado no diff)

  • Esta alteração manipula dados sensíveis de usuários (dados pessoais, credenciais, tokens)?

    • Sim
    • Não
    • Alterações tratam apenas de metadados de periódico (ISSN, título, histórico, status), sem dados pessoais ou credenciais.
  • Esta alteração expõe novos endpoints, views ou campos de API?

    • Sim
    • Não
    • Adiciona JournalCollectionViewSet ao Wagtail admin (journal/wagtail_hooks.py), expondo uma nova view de snippet (journal, collection, creator, updated, created, updated_by) restrita a usuários autenticados do admin. Não é um endpoint público de API REST.
  • Esta alteração modifica regras de autenticação ou autorização?

    • Sim
    • Não
    • Nenhuma alteração em permissões, grupos ou regras de acesso. O novo ViewSet usa a mesma infraestrutura de permissões padrão do Wagtail já aplicada aos demais snippets.
  • Esta alteração introduz ou modifica chamadas a serviços externos (ex.: core.scielo.org)?

    • Sim
    • Não
    • fetch_and_create_journal/fetch_journal_data_with_pagination tiveram a assinatura e o fluxo de chamada alterados (remoção do bloco de retry com collection_acron=None); JournalDataChecker.get_or_fetch/ensure_proc_exists agora decidem chamar o Core com base em is_updated, podendo gerar mais chamadas ao Core do que antes em cenários de dado local incompleto.
  • Esta alteração manipula entrada de dados externos (payload do core.scielo.org) sem validação/sanitização?

    • Sim
    • Não
    • A validação anterior em process_journal_result (block_unregistered_collection, que descartava o registro caso a Collection do payload não existisse localmente) foi removida. Agora, se Collection.objects.get(acron=...) falhar e houver apenas um item em scielo_journal, a exceção Collection.DoesNotExist é propagada (raise) em vez de ser silenciosamente ignorada — mudança de comportamento de validação que precisa ser avaliada quanto a impacto em dados de collections ainda não sincronizadas localmente.
  • Esta alteração pode causar exposição de logs com dados sensíveis?

    • Sim
    • Não
    • logging.info(f"publish_journal {journal_proc}") e logging.info(f"journal_history.event_type: ...") foram removidos (não adicionados); nenhum log novo com dados sensíveis foi introduzido.
  • Esta alteração foi revisada quanto a riscos de injeção (SQL/NoSQL/comando)?

    • Sim
    • Não
    • Todas as consultas usam Django ORM (Journal.objects.get/filter, Q() objects), sem SQL raw, extra() ou concatenação de strings em queries. Nenhum uso de eval, exec ou chamadas de shell.
  • Esta alteração requer nova configuração de permissões, variáveis de ambiente ou segredos?

    • Sim
    • Não
    • Nenhuma nova variável de ambiente, credencial ou configuração de permissão é introduzida; JournalCollectionViewSet reutiliza a infraestrutura existente de SnippetViewSet/get_menu_order.

…lectronic trocado

- Adiciona fallback para busca com issn_electronic/issn_print invertidos
- Adiciona fallback final via Q(OR) sobre o conjunto de ISSNs informados
- MultipleObjectsReturned agora usa AND (issn_electronic + issn_print) em vez de OR, ordenando por -updated
- Adiciona property is_complete (missing_fields + core_synchronized)
- missing_fields passa a considerar journal_acron, ausência de Official Journal, histórico por collection e ausência de collection
- JournalCollection: painel usa InlinePanel('journal_history') em vez de AutocompletePanel('journal'/'collection')
- JournalCollection.create_or_update: não sobrescreve updated_by ao encontrar registro existente
- JournalHistory.create_or_update: retorna o objeto atualizado
Registra novo ViewSet para JournalCollection (journal, collection, creator,
updated, created, updated_by) no grupo JournalViewSetGroup, com busca por
título do journal, nome e acrônimo da collection.
Cobre match exato, match trocado (issn_electronic/issn_print invertidos),
fallback final via Q(OR) e o comportamento atual quando falta ISSN no
ramo trocado.
Filtra JournalHistory pela collection e journal do próprio JournalProc,
centralizando lógica antes duplicada em publication/api/journal.py.
…ecker

- Introduz método abstrato is_updated(obj); get_or_fetch passa a reconsultar
  o core quando o registro local existe mas está desatualizado
- ensure_proc_exists deixa de ser staticmethod e passa a delegar para get_or_fetch
- JournalDataChecker.is_updated verifica is_complete + existência de JournalProc
- IssueDataChecker.is_updated verifica existência de IssueProc
- Remove funções standalone create_or_update_journal/create_or_update_issue,
  substituídas pelos DataCheckers
- process_journal_result: usa Journal.get_registered antes de criar novo,
  limpa M2M (subject/publisher/owner/sponsor) antes de reatribuir, ignora
  nomes de instituição vazios, levanta ValueError quando não há scielo_journal
  e consolida journal_acron/collection ao final do loop
…pdated

ensure_journal_proc_exists/ensure_issue_proc_exists passam a instanciar
JournalDataChecker/IssueDataChecker e chamar ensure_proc_exists(force_update),
alinhado com a nova API de proc/source_core_api.py.
Cobre BaseDataChecker.get_or_fetch/refresh, JournalDataChecker e
IssueDataChecker (is_updated/ensure_proc_exists/fetch_from_core),
paginação de busca de journals e process_journal_result/process_issue_result.
- translate_status(event_type, interruption_reason) centraliza o mapeamento
  de reason/event_type para status (REASON_MAP/EVENT_MAP), com fallback 'inprogress'
- publish_journal: usa journal.is_complete (em vez de core_synchronized) para
  decidir se sincroniza com o core; usa proc.journal_history; propaga force_update
- JournalPayload.add_event_to_timeline passa a chamar translate_status internamente
- Adiciona add_current_status() e add_forced_current_status(force_update), que
  força status 'current' com data de fallback quando não há status_history
- Inicializa institution_responsible_for em reset_lists
…load

build_journal deixa de calcular event_type/current_status inline e passa a
delegar para builder.add_event_to_timeline (com translate_status),
builder.add_current_status() e builder.add_forced_current_status(force_update).
Aceita novo parâmetro force_update (default False) e corrige duplicidade em
add_publisher (agora usa union de owner+publisher via set 'names').
…oad e publish_journal

Cobre mapeamento de reason/event_type, setters de JournalPayload, clean_br_tags,
add_current_status/add_forced_current_status e o fluxo de publish_journal
(sincronização condicional via is_complete, tratamento silencioso de exceção
no fetch e montagem do payload).
Cobre campos básicos, mission/timeline, current_status/force_update, ISSNs
e títulos com fallback para official_journal, fallback de logo_url,
related journals, sponsors/owners/publishers (união sem duplicar) e
escopos temáticos/visibilidade.
@robertatakenaka robertatakenaka changed the title fix(journal): JournalCollection.create_or_update não retornava objeto existente, causando status "inprogress" na publicação fix(journal): Corrige falha de atualização de journal_history, causando status "inprogress" e impedindo a publicação Aug 10, 2026
Comment thread journal/wagtail_hooks.py Outdated
search_fields = (
"journal__title", # ajuste para o campo textual real de Journal
"collection__name", # ajuste para o campo textual real de Collection
"collection__acron3", # se existir, ajuda muito na busca por sigla

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 correto é collection__acron e não collection__acron3. Ver modelo Collection

Comment thread journal/models.py

@property
def is_complete(self):
if self.missing_fields:

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.

Num teste local com a RAE eletrônica, carregada no Core a partir do ArticleMeta, is_complete continuou False mesmo após uma sincronização bem-sucedida. Na fonte, o periódico está identificado como eletrônico (v35=ONLIN), não possui ISSN impresso nem URL de submissão. A localização está registrada no Core e é retornada pela API, mas ainda não é processada por process_journal_result. Consequentemente, o Upload mantém Print ISSN, Submission Online URL e Contact Location como ausentes e consulta novamente o Core em toda publicação com force_update=True, embora uma nova sincronização não resolva esses campos. Podemos restringir a completude aos dados realmente obrigatórios e que esse fluxo consegue preencher?

Comment thread proc/source_core_api.py
for item in result.get("scielo_journal") or []:
journal_acron = journal.journal_acron

scielo_journals = result.get("scielo_journal") or []

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.

Neste ponto, o journal já foi salvo e seus relacionamentos de e-mail, assunto, publisher, owner e sponsor já foram removidos/recriados. Se scielo_journal estiver ausente, nenhuma coleção local for encontrada ou ocorrer uma exceção posterior, fetch_and_create_journal apenas registra o erro e essas alterações parciais permanecem no banco. Podemos validar scielo_journal/coleções antes das mutações e executar process_journal_result em uma transação atômica para evitar deixar o periódico parcialmente sincronizado?

Comment thread proc/source_core_api.py
raise IssueProc.DoesNotExist(f"IssueProc does not exist: {issue}")


def create_or_update_issue(

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.

Esta função ainda é importada por upload/models.py:42 e utilizada em PidV2Generator.get_issue_pid (upload/models.py:2409). Confirmei que python manage.py shell -c 'import upload.models' falha com ImportError: cannot import name 'create_or_update_issue'.

Comment thread proc/source_core_api.py
title=result.get("title"),
short_title=result.get("short_title"),
)
journal.core_synchronized = False

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.

Existe a possibilidade de o periódico permanecer associado ao registro com os ISSNs trocados. Quando get_registered encontra o journal pelo fallback de ISSNs invertidos, o official_journal correto criado ou atualizado anteriormente pode ser diferente daquele atualmente associado ao journal. Não encontrei a reatribuição dessa relação antes do save(). Nesse caso, o processamento pode terminar com core_synchronized=True, mas mantendo os ISSNs invertidos e deixando o novo OfficialJournal sem uso. Uma solução cabível seria atribuir explicitamente journal.official_journal = official_journal antes de salvar.

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.

Journal e artigos não publicados por status "inprogress"

2 participants