Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 317 additions & 1 deletion journal/forms.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,328 @@
from django import forms
from django.utils.translation import gettext_lazy as _

from core.forms import CoreAdminModelForm
from organization.models import Organization
from location.models import Country, Location


class OrganizationMixin:
"""
Mixin para adicionar campos de entrada manual de organização
que não estão presentes na lista padrão de Organization.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Adiciona campos para entrada manual de organização
self.fields["manual_org_name"] = forms.CharField(
label=_("Organization Name (Manual)"),
max_length=255,
required=False,
help_text=_("Enter organization name if not found in the list above"),
widget=forms.TextInput(
attrs={"placeholder": _("Enter standardized organization name")}
),
)

self.fields["manual_city"] = forms.CharField(
label=_("City"),
max_length=100,
required=False,
help_text=_("City where organization is located"),
)

self.fields["manual_state"] = forms.CharField(
label=_("State/Province"),
max_length=100,
required=False,
help_text=_("State or province where organization is located"),
)

self.fields["manual_country"] = forms.ModelChoiceField(
label=_("Country"),
queryset=Country.objects.all(),
required=False,
help_text=_("Country where organization is located"),
empty_label=_("Select country..."),
)

def clean(self):
cleaned_data = super().clean()
organization = cleaned_data.get("organization")
manual_org_name = cleaned_data.get("manual_org_name")

# Validação: deve ter organization OU manual_org_name
if not organization and not manual_org_name:
raise forms.ValidationError(
_(
"Please either select an organization from the list or enter manual organization data."
)
)

# Se tem manual_org_name, deve ter pelo menos o país
if manual_org_name and not cleaned_data.get("manual_country"):
raise forms.ValidationError(
_("When entering manual organization data, country is required.")
)

return cleaned_data

def save(self, commit=True):
instance = super().save(commit=False)

# Se não tem organization mas tem dados manuais, cria/busca organization
if not instance.organization and self.cleaned_data.get("manual_org_name"):
organization = self._create_or_get_organization()
if organization:
instance.organization = organization

if commit:
instance.save()
return instance

def _create_or_get_organization(self):
"""Cria ou busca Organization baseado nos dados manuais."""
manual_name = self.cleaned_data.get("manual_org_name")
manual_city = self.cleaned_data.get("manual_city")
manual_state = self.cleaned_data.get("manual_state")
Comment on lines +88 to +89
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Em _create_or_get_organization, manual_city e manual_state são lidos do cleaned_data mas nunca são utilizados para compor o Location (hoje só usa country). Isso adiciona campos/complexidade sem efeito prático e provavelmente não atende ao objetivo de capturar a localização manual. Ou usar esses valores na criação/lookup do Location (via Location.create_or_update) ou remover os campos/variáveis se não forem necessários.

Suggested change
manual_city = self.cleaned_data.get("manual_city")
manual_state = self.cleaned_data.get("manual_state")

Copilot uses AI. Check for mistakes.
manual_country = self.cleaned_data.get("manual_country")

if not manual_name:
return None

# Busca organization existente primeiro
existing_org = Organization.objects.filter(name=manual_name).first()
Comment on lines +92 to +96
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing-organization lookup uses an exact, case-sensitive match on name and ignores Location. Since Organization is unique by (name, acronym, location) and multiple orgs can share the same name, this can attach the wrong organization. Consider normalizing the input (e.g., trim/extra spaces), using name__iexact, and including location criteria (at least country, and state/city if provided) when reusing an existing org.

Suggested change
if not manual_name:
return None
# Busca organization existente primeiro
existing_org = Organization.objects.filter(name=manual_name).first()
# Normaliza nome (remove espaços extras nas bordas)
if manual_name is not None:
manual_name = manual_name.strip()
if not manual_name:
return None
# Busca organization existente primeiro, usando comparação case-insensitive
# e filtrando por país quando disponível, para respeitar a unicidade (name, location).
org_filters = {"name__iexact": manual_name}
if manual_country:
org_filters["location__country"] = manual_country
existing_org = Organization.objects.filter(**org_filters).first()

Copilot uses AI. Check for mistakes.
if existing_org:
Comment on lines +96 to +97
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O lookup de organização existente usa Organization.objects.filter(name=manual_name).first(), que é case-sensitive e não normaliza espaços; isso pode criar duplicatas que o próprio modelo tenta evitar com name__iexact/remove_extra_spaces. Prefira normalizar o texto e consultar com name__iexact (e idealmente incluir location na chave) para reduzir registros duplicados.

Copilot uses AI. Check for mistakes.
return existing_org

# Cria location se tiver dados geográficos
location = None
if manual_country:
try:
# Busca ou cria location (simplificado)
location_data = {"country": manual_country}
Comment on lines +104 to +105
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manual_city and manual_state are collected but never used when creating the Location (only country is used). This drops user-provided data and also collapses all orgs created for the same country onto the same (country, state=None, city=None) location record. Use the provided city/state values to resolve/create City/State and pass them into Location creation, or remove these fields if they are not intended to be persisted.

Suggested change
# Busca ou cria location (simplificado)
location_data = {"country": manual_country}
# Busca ou cria location (incluindo estado/cidade quando fornecidos)
location_data = {"country": manual_country}
if manual_state:
location_data["state"] = manual_state
if manual_city:
location_data["city"] = manual_city

Copilot uses AI. Check for mistakes.
location, created = Location.objects.get_or_create(**location_data)
except Exception:
location = None

Comment on lines +103 to +109
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O bloco Location.objects.get_or_create(country=manual_country) pode disparar MultipleObjectsReturned porque Location tem unique_together=(country, state, city) e, com state/city nulos, o banco pode conter múltiplos registros para o mesmo país. Como a exceção é capturada com except Exception e ignorada, location vira None silenciosamente. Ajustar o lookup para filtrar explicitamente state__isnull=True/city__isnull=True (ou usar filter(...).first()), e evitar engolir exceções inesperadas.

Copilot uses AI. Check for mistakes.
# Cria nova organization
try:
organization = Organization.objects.create(
name=manual_name,
location=location,
source="user", # Marca como fonte user
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Organization.objects.create(..., source="user") will fail because Organization does not have a source field/constructor argument in organization/models.py. Remove this argument or implement the intended tracking field in the model/migration (and update admin/panels accordingly).

Suggested change
source="user", # Marca como fonte user

Copilot uses AI. Check for mistakes.
)
Comment on lines +110 to +116
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Organization.objects.create(..., source="user") vai falhar com TypeError porque o modelo organization.Organization não possui um campo source (ver organization/models.py). Além disso, a exceção está sendo engolida e a função retorna None, o que impede a criação da organização mesmo quando o usuário preenche os dados manuais. Remover/ajustar esse atributo e usar o método existente Organization.create_or_update(...) (ou adicionar o campo no modelo, se realmente necessário).

Suggested change
# Cria nova organization
try:
organization = Organization.objects.create(
name=manual_name,
location=location,
source="user", # Marca como fonte user
)
# Cria nova organization usando o método auxiliar do model
try:
org_result = Organization.create_or_update(
name=manual_name,
location=location,
)
# Suporta tanto retorno direto da instância quanto tupla (instância, created)
if isinstance(org_result, tuple):
organization, _created = org_result
else:
organization = org_result

Copilot uses AI. Check for mistakes.
return organization
except Exception:
# Se não conseguir criar, retorna None - usuário terá que usar lista padrão
return None
Comment on lines +72 to +120
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_create_or_get_organization swallows all exceptions and returns None. In save(), that means the history item can be saved with organization=None even when the user provided manual_org_name (and passed clean()), silently losing the intended link. Instead of broad except Exception, catch expected errors and add a form error / raise ValidationError when organization/location creation fails so the user can fix the input.

Copilot uses AI. Check for mistakes.


class OwnerHistoryForm(OrganizationMixin, CoreAdminModelForm):
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'OwnerHistoryForm' is unnecessary as it is redefined before this value is used.

Copilot uses AI. Check for mistakes.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import OwnerHistory

self._meta.model = OwnerHistory

Comment on lines +123 to +131
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esses ModelForms (OwnerHistoryForm, PublisherHistoryForm, etc.) não são referenciados em nenhum outro ponto do repositório, e os modelos correspondentes em journal/models.py não definem base_form_class para usá-los. Do jeito que está, a lógica de entrada manual provavelmente não será aplicada na UI/admin. Para que tenha efeito, definir base_form_class nos models/Orderables corretos (ou registrar esses forms no local apropriado do admin).

Copilot uses AI. Check for mistakes.

class PublisherHistoryForm(OrganizationMixin, CoreAdminModelForm):
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'PublisherHistoryForm' is unnecessary as it is redefined before this value is used.

Suggested change
class PublisherHistoryForm(OrganizationMixin, CoreAdminModelForm):
class _PublisherHistoryFormBase(OrganizationMixin, CoreAdminModelForm):

Copilot uses AI. Check for mistakes.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import PublisherHistory

self._meta.model = PublisherHistory


class SponsorHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import SponsorHistory

self._meta.model = SponsorHistory


Comment on lines +143 to +152
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'SponsorHistoryForm' is unnecessary as it is redefined before this value is used.

Suggested change
class SponsorHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import SponsorHistory
self._meta.model = SponsorHistory

Copilot uses AI. Check for mistakes.
class CopyrightHolderHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import CopyrightHolderHistory

self._meta.model = CopyrightHolderHistory


Comment on lines +153 to +162
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'CopyrightHolderHistoryForm' is unnecessary as it is redefined before this value is used.

Suggested change
class CopyrightHolderHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import CopyrightHolderHistory
self._meta.model = CopyrightHolderHistory

Copilot uses AI. Check for mistakes.
Comment on lines +123 to +162
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These form classes are defined twice in this file (OwnerHistoryForm/PublisherHistoryForm/SponsorHistoryForm/CopyrightHolderHistoryForm, and SciELOJournalModelForm). The later definitions silently overwrite the earlier ones, which is error-prone and makes future edits confusing. Please remove the duplicated block and keep a single authoritative definition for each class.

Suggested change
class OwnerHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import OwnerHistory
self._meta.model = OwnerHistory
class PublisherHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import PublisherHistory
self._meta.model = PublisherHistory
class SponsorHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import SponsorHistory
self._meta.model = SponsorHistory
class CopyrightHolderHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import CopyrightHolderHistory
self._meta.model = CopyrightHolderHistory

Copilot uses AI. Check for mistakes.
Comment on lines +123 to +162
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'SponsorHistoryForm' is unnecessary as it is redefined before this value is used.

Suggested change
class OwnerHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import OwnerHistory
self._meta.model = OwnerHistory
class PublisherHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import PublisherHistory
self._meta.model = PublisherHistory
class SponsorHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import SponsorHistory
self._meta.model = SponsorHistory
class CopyrightHolderHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import CopyrightHolderHistory
self._meta.model = CopyrightHolderHistory
# NOTE: Earlier duplicate history form definitions were removed here
# to avoid redefining OwnerHistoryForm, PublisherHistoryForm,
# SponsorHistoryForm and CopyrightHolderHistoryForm.
# The canonical definitions of these forms are declared later
# in this module and are the ones that should be used.

Copilot uses AI. Check for mistakes.
class SciELOJournalModelForm(CoreAdminModelForm):
"""
Mixin para adicionar campos de entrada manual de organização
que não estão presentes na lista padrão de Organization.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Adiciona campos para entrada manual de organização
self.fields["manual_org_name"] = forms.CharField(
label=_("Organization Name (Manual)"),
max_length=255,
required=False,
help_text=_("Enter organization name if not found in the list above"),
widget=forms.TextInput(
attrs={"placeholder": _("Enter standardized organization name")}
),
)

self.fields["manual_city"] = forms.CharField(
label=_("City"),
max_length=100,
required=False,
help_text=_("City where organization is located"),
)

self.fields["manual_state"] = forms.CharField(
label=_("State/Province"),
max_length=100,
required=False,
help_text=_("State or province where organization is located"),
)

self.fields["manual_country"] = forms.ModelChoiceField(
label=_("Country"),
queryset=Country.objects.all(),
required=False,
help_text=_("Country where organization is located"),
empty_label=_("Select country..."),
)

def clean(self):
Comment on lines +161 to +205
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'SciELOJournalModelForm' is unnecessary as it is redefined before this value is used.

Suggested change
class SciELOJournalModelForm(CoreAdminModelForm):
"""
Mixin para adicionar campos de entrada manual de organização
que não estão presentes na lista padrão de Organization.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Adiciona campos para entrada manual de organização
self.fields["manual_org_name"] = forms.CharField(
label=_("Organization Name (Manual)"),
max_length=255,
required=False,
help_text=_("Enter organization name if not found in the list above"),
widget=forms.TextInput(
attrs={"placeholder": _("Enter standardized organization name")}
),
)
self.fields["manual_city"] = forms.CharField(
label=_("City"),
max_length=100,
required=False,
help_text=_("City where organization is located"),
)
self.fields["manual_state"] = forms.CharField(
label=_("State/Province"),
max_length=100,
required=False,
help_text=_("State or province where organization is located"),
)
self.fields["manual_country"] = forms.ModelChoiceField(
label=_("Country"),
queryset=Country.objects.all(),
required=False,
help_text=_("Country where organization is located"),
empty_label=_("Select country..."),
)
def clean(self):

Copilot uses AI. Check for mistakes.
cleaned_data = super().clean()
organization = cleaned_data.get("organization")
manual_org_name = cleaned_data.get("manual_org_name")

# Validação: deve ter organization OU manual_org_name
if not organization and not manual_org_name:
raise forms.ValidationError(
_(
"Please either select an organization from the list or enter manual organization data."
)
)

# Se tem manual_org_name, deve ter pelo menos o país
if manual_org_name and not cleaned_data.get("manual_country"):
raise forms.ValidationError(
_("When entering manual organization data, country is required.")
)

return cleaned_data

def save(self, commit=True):
instance = super().save(commit=False)

# Se não tem organization mas tem dados manuais, cria/busca organization
if not instance.organization and self.cleaned_data.get("manual_org_name"):
organization = self._create_or_get_organization()
if organization:
instance.organization = organization

if commit:
instance.save()
return instance

def _create_or_get_organization(self):
"""Cria ou busca Organization baseado nos dados manuais."""
manual_name = self.cleaned_data.get("manual_org_name")
manual_city = self.cleaned_data.get("manual_city")
manual_state = self.cleaned_data.get("manual_state")
Comment on lines +242 to +243
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable manual_city is not used.

Suggested change
manual_city = self.cleaned_data.get("manual_city")
manual_state = self.cleaned_data.get("manual_state")

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable manual_state is not used.

Suggested change
manual_state = self.cleaned_data.get("manual_state")

Copilot uses AI. Check for mistakes.
manual_country = self.cleaned_data.get("manual_country")

if not manual_name:
return None

# Busca organization existente primeiro
existing_org = Organization.objects.filter(name=manual_name).first()
if existing_org:
return existing_org

# Cria location se tiver dados geográficos
location = None
if manual_country:
try:
# Busca ou cria location (simplificado)
location_data = {"country": manual_country}
location, created = Location.objects.get_or_create(**location_data)
except Exception:
location = None

# Cria nova organization
try:
organization = Organization.objects.create(
name=manual_name,
location=location,
source="user", # Marca como fonte user
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: Organization.objects.create(..., source="user") will raise because Organization has no source field/kwarg. Once the duplicate block is removed, ensure the remaining creation call does not pass unsupported kwargs.

Suggested change
source="user", # Marca como fonte user

Copilot uses AI. Check for mistakes.
)
return organization
except Exception:
# Se não conseguir criar, retorna None - usuário terá que usar lista padrão
return None


class OwnerHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import OwnerHistory

self._meta.model = OwnerHistory


class PublisherHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import PublisherHistory

self._meta.model = PublisherHistory


class SponsorHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import SponsorHistory

self._meta.model = SponsorHistory


class CopyrightHolderHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import CopyrightHolderHistory

self._meta.model = CopyrightHolderHistory


Comment on lines +277 to 316
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Os formulários OwnerHistoryForm/PublisherHistoryForm/SponsorHistoryForm/CopyrightHolderHistoryForm estão declarados duas vezes no mesmo módulo (ex.: OwnerHistoryForm na linha 123 e novamente na 277). A segunda declaração sobrescreve a primeira e é um forte indício de código duplicado acidental; manter apenas uma definição para evitar confusão e erros de manutenção.

Suggested change
class OwnerHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import OwnerHistory
self._meta.model = OwnerHistory
class PublisherHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import PublisherHistory
self._meta.model = PublisherHistory
class SponsorHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import SponsorHistory
self._meta.model = SponsorHistory
class CopyrightHolderHistoryForm(OrganizationMixin, CoreAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define o model dinamicamente para evitar importação circular
if not hasattr(self._meta, "model") or self._meta.model is None:
from journal.models import CopyrightHolderHistory
self._meta.model = CopyrightHolderHistory

Copilot uses AI. Check for mistakes.
class SciELOJournalModelForm(CoreAdminModelForm):
def save_all(self, user):
instance_model = super().save_all(user)

if self.instance.issn_scielo is None:
self.instance.issn_scielo = instance_model.journal.official.issn_electronic or instance_model.journal.official.issn_print
self.instance.issn_scielo = (
instance_model.journal.official.issn_electronic
or instance_model.journal.official.issn_print
)

self.save()

Expand Down
12 changes: 8 additions & 4 deletions journal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
)
from journal.forms import (
SciELOJournalModelForm,
OwnerHistoryForm,
PublisherHistoryForm,
SponsorHistoryForm,
CopyrightHolderHistoryForm
Comment on lines +62 to +65
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SciELOJournalModelForm is still referenced later in this module (e.g., SciELOJournal.base_form_class = SciELOJournalModelForm), but it is no longer imported here. This will raise NameError on import. Also, OwnerHistoryForm is imported twice in this block—please remove the duplicate and add back SciELOJournalModelForm (and any other required forms).

Suggested change
OwnerHistoryForm,
PublisherHistoryForm,
SponsorHistoryForm,
CopyrightHolderHistoryForm
PublisherHistoryForm,
SponsorHistoryForm,
CopyrightHolderHistoryForm,
SciELOJournalModelForm,

Copilot uses AI. Check for mistakes.
)
from location.models import Country, Location
from organization.dynamic_models import (
Expand Down Expand Up @@ -1306,7 +1310,7 @@ class OwnerHistory(Orderable, ClusterableModel, BaseHistoryItem):
help_text=HELP_TEXT_ORGANIZATION,
)

base_form_class = SciELOJournalModelForm
base_form_class = OwnerHistoryForm

panels = BaseHistoryItem.panels + [
AutocompletePanel("institution", read_only=True),
Expand Down Expand Up @@ -1340,7 +1344,7 @@ class PublisherHistory(Orderable, ClusterableModel, BaseHistoryItem):
help_text=HELP_TEXT_ORGANIZATION,
)

base_form_class = SciELOJournalModelForm
base_form_class = PublisherHistoryForm

panels = BaseHistoryItem.panels + [
AutocompletePanel("institution", read_only=True),
Expand Down Expand Up @@ -1374,7 +1378,7 @@ class SponsorHistory(Orderable, ClusterableModel, BaseHistoryItem):
help_text=HELP_TEXT_ORGANIZATION,
)

base_form_class = SciELOJournalModelForm
base_form_class = SponsorHistoryForm

panels = BaseHistoryItem.panels + [
AutocompletePanel("institution", read_only=True),
Expand Down Expand Up @@ -1411,7 +1415,7 @@ class CopyrightHolderHistory(Orderable, ClusterableModel, BaseHistoryItem):
help_text=HELP_TEXT_ORGANIZATION,
)

base_form_class = SciELOJournalModelForm
base_form_class = CopyrightHolderHistoryForm

panels = BaseHistoryItem.panels + [
AutocompletePanel("institution", read_only=True),
Expand Down
Loading