Skip to content
Merged
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
205 changes: 171 additions & 34 deletions journal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@
TitleInDatabaseCreationOrUpdateError,
WosdbCreationOrUpdateError,
)
from journal.forms import SciELOJournalModelForm
from journal.forms import (
SciELOJournalModelForm,
)
from location.models import Country, Location
from organization.dynamic_models import (
OrgLevelCopyrightHolder,
Expand All @@ -70,7 +72,6 @@
from vocabulary.models import Vocabulary
from tracker.models import UnexpectedEvent


HELP_TEXT_INSTITUTION = _(
"Institution data originally provided. This field is for reference only."
)
Expand Down Expand Up @@ -758,9 +759,11 @@ def owner_names(self):
@property
def owner_data(self):
owner_data = {}
owners = list(self.owner_history.select_related(
'institution__institution', 'institution__institution__location'
).all())
owners = list(
self.owner_history.select_related(
"institution__institution", "institution__institution__location"
).all()
)
for p in owners:
owner_data["country_acronym"] = p.institution_country_acronym
owner_data["state_acronym"] = p.institution_state_acronym
Expand All @@ -772,19 +775,19 @@ def owner_data(self):
def publisher_names(self):
items = []
for item in self.publisher_history.select_related(
'institution__institution', 'institution__institution__location'
"institution__institution", "institution__institution__location"
).all():
if item.organization:
items.append(item.organization.name)
else:
items.append(item.institution_name)
return items

@property
def sponsors(self):
items = []
for item in self.sponsor_history.select_related(
'institution__institution', 'institution__institution__location'
"institution__institution", "institution__institution__location"
).all():
if item.organization:
items.append(item.organization.name)
Expand All @@ -796,7 +799,7 @@ def sponsors(self):
def copyright_holders(self):
items = []
for item in self.copyright_holder_history.select_related(
'institution__institution', 'institution__institution__location'
"institution__institution", "institution__institution__location"
).all():
if item.organization:
items.append(item.organization.name)
Expand Down Expand Up @@ -965,7 +968,7 @@ def select_items(
},
)
return queryset

@classmethod
def get_journal_issns(
cls,
Expand All @@ -977,19 +980,19 @@ def get_journal_issns(
):
"""
Versão alternativa que segue o padrão do select_items() com suporte a datas.

Similar ao método get_issn_list() existente, mas:
- Retorna tuplas (issn_print, issn_electronic) ao invés de dicionário
- Mantém a associação entre ISSNs do mesmo periódico
- Suporta filtros de data como select_items()

Args:
collection_acron_list (list): Lista de acrônimos de coleções
journal_acron_list (list): Lista de acrônimos de periódicos
from_date (str): Data inicial para filtro de atualização
until_date (str): Data final para filtro de atualização
days_to_go_back (int): Dias para voltar a partir de hoje

Returns:
QuerySet: Tuplas de (issn_print, issn_electronic)
"""
Expand All @@ -1000,21 +1003,22 @@ def get_journal_issns(
until_date,
days_to_go_back,
)
return qs.select_related("official").values_list(
"official__issn_print",
"official__issn_electronic"
).distinct()
return (
qs.select_related("official")
.values_list("official__issn_print", "official__issn_electronic")
.distinct()
)

@classmethod
def get_issn_list(cls, collection_acron_list=None, journal_acron_list=None):
qs = cls.select_items(collection_acron_list, journal_acron_list)
return {
"issn_print_list": qs.select_related('official').values_list(
"official__issn_print", flat=True
).distinct(),
"issn_electronic_list": qs.select_related('official').values_list(
"official__issn_electronic", flat=True
).distinct(),
"issn_print_list": qs.select_related("official")
.values_list("official__issn_print", flat=True)
.distinct(),
"issn_electronic_list": qs.select_related("official")
.values_list("official__issn_electronic", flat=True)
.distinct(),
}

@property
Expand Down Expand Up @@ -1061,6 +1065,134 @@ def get_legacy_keys(self, collection_acron_list=None, is_active=None):
)
return list(data.values())

def _add_institution_history(
self,
institution_class,
history_class,
user,
original_data=None,
organization=None,
initial_date=None,
final_date=None,
location=None,
):
"""Adiciona instituição usando InstitutionHistory genérico."""
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 docstring says "Adiciona instituição usando InstitutionHistory genérico" (Adds institution using generic InstitutionHistory) but this is vague and doesn't explain the parameters, return value, or the behavior when only organization vs only original_data is provided. Consider adding more detailed documentation about the parameters, expected behavior, and return value to help future maintainers understand this generic helper method.

Suggested change
"""Adiciona instituição usando InstitutionHistory genérico."""
"""
Add an institution history entry for this journal using generic models.
This helper creates or retrieves an institution instance (via
``institution_class``) based on the ``original_data`` provided and then
creates or retrieves a corresponding history record (via
``history_class``). The history record is associated with this
:class:`Journal` instance and, optionally, with an
:class:`organization.models.Organization`.
At least one of ``original_data`` or ``organization`` must be provided.
If only ``original_data`` is provided, an institution is created/located
and linked to the history, with ``organization`` left unset.
If only ``organization`` is provided, the history is created/located
with ``institution`` set to ``None`` and only the ``organization``
field is populated.
Parameters
----------
institution_class :
Model or manager that exposes a ``get_or_create`` method with the
signature used below to obtain an institution instance.
history_class :
Model or manager that exposes a ``get_or_create`` method accepting
``institution``, ``initial_date``, ``final_date`` and ``user`` to
obtain an institution history instance.
user :
User instance associated with the creation/update of the records.
original_data : str, optional
Original textual representation of the institution name. When
provided, it is used to create or retrieve an institution via
``institution_class``.
organization : Organization, optional
Organization instance to associate with the created history record.
initial_date : datetime.date, optional
Initial date for the institution history period.
final_date : datetime.date, optional
Final date for the institution history period.
location : Location, optional
Location to associate with the institution when it is created from
``original_data``.
Returns
-------
history_class instance
The created or retrieved institution history object, already linked
to this journal and saved.
Raises
------
ValueError
If neither ``original_data`` nor ``organization`` is provided.
"""

Copilot uses AI. Check for mistakes.
if not original_data and not organization:
raise ValueError("Either original_data or organization must be provided")

created_institution = None
if original_data:
# Cria/busca a Institution baseado nos dados originais
created_institution = institution_class.get_or_create(
name=original_data,
acronym=None,
level_1=None,
level_2=None,
level_3=None,
user=user,
location=location,
official=None,
is_official=None,
url=None,
institution_type=None,
)

# Cria/busca o InstitutionHistory
institution_history = history_class.get_or_create(
institution=created_institution,
initial_date=initial_date,
final_date=final_date,
user=user,
)
institution_history.journal = self
institution_history.organization = organization
institution_history.save()
return institution_history

def add_publisher(
self,
user,
organization=None,
original_data=None,
initial_date=None,
final_date=None,
location=None,
):
"""Adiciona publisher usando PublisherHistory."""
return self._add_institution_history(
institution_class=Publisher,
history_class=PublisherHistory,
user=user,
organization=organization,
original_data=original_data,
initial_date=initial_date,
final_date=final_date,
location=location,
)

def add_owner(
self,
user,
organization=None,
original_data=None,
initial_date=None,
final_date=None,
location=None,
):
"""Adiciona owner usando OwnerHistory."""
return self._add_institution_history(
institution_class=Owner,
history_class=OwnerHistory,
user=user,
organization=organization,
original_data=original_data,
initial_date=initial_date,
final_date=final_date,
location=location,
)

def add_sponsor(
self,
user,
organization=None,
original_data=None,
initial_date=None,
final_date=None,
location=None,
):
"""Adiciona sponsor usando SponsorHistory."""
return self._add_institution_history(
institution_class=Sponsor,
history_class=SponsorHistory,
user=user,
organization=organization,
original_data=original_data,
initial_date=initial_date,
final_date=final_date,
location=location,
)

def add_copyright_holder(
self,
user,
organization=None,
original_data=None,
initial_date=None,
final_date=None,
location=None,
):
"""Adiciona copyright_holder usando CopyrightHolderHistory."""
return self._add_institution_history(
institution_class=CopyrightHolder,
history_class=CopyrightHolderHistory,
user=user,
organization=organization,
original_data=original_data,
initial_date=initial_date,
final_date=final_date,
location=location,
)


class FileOpenScience(Orderable, FileWithLang, CommonControlField):
journal = ParentalKey(
Expand Down Expand Up @@ -1174,6 +1306,8 @@ class OwnerHistory(Orderable, ClusterableModel, BaseHistoryItem):
help_text=HELP_TEXT_ORGANIZATION,
)

base_form_class = SciELOJournalModelForm

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

base_form_class = SciELOJournalModelForm

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

base_form_class = SciELOJournalModelForm

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

base_form_class = SciELOJournalModelForm

panels = BaseHistoryItem.panels + [
AutocompletePanel("institution", read_only=True),
AutocompletePanel("organization"),
Expand Down Expand Up @@ -1383,11 +1523,9 @@ class Copyright(Orderable, RichTextWithLanguage, CommonControlField):
rich_text = RichTextField(
null=True,
blank=True,
help_text=_(
"""Describe the policy used by the journal on copyright issues.
help_text=_("""Describe the policy used by the journal on copyright issues.
We recommend that this section be in accordance with the recommendations of the SciELO criteria,
item 5.2.10.1.2. - Copyright"""
),
item 5.2.10.1.2. - Copyright"""),
Comment on lines +1526 to +1528
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 formatting change compresses a multi-line help_text string into a single very long line (spanning lines 1562-1564), making it harder to read. The previous format with properly indented lines was more readable. Consider reverting this change to maintain code readability, especially since similar help_text strings in the codebase use multiple lines (see lines 1515-1516, 1549-1551, 1576-1577).

Copilot uses AI. Check for mistakes.
)
journal = ParentalKey(
Journal, on_delete=models.SET_NULL, related_name="copyright", null=True
Expand Down Expand Up @@ -1580,14 +1718,12 @@ class AuthorsContributions(Orderable, RichTextWithLanguage, CommonControlField):
null=True,
blank=True,
help_text=mark_safe(
_(
"""Description of how authors contributions should be specified.
_("""Description of how authors contributions should be specified.
Does it use any taxonomy? If yes, which one?
Does the article text explicitly state the authors contributions?
Preferably, use the CREDiT taxonomy structure: <a target='_blank'
href='https://casrai.org/credit/'>https://casrai.org/credit/</a>
"""
)
""")
Comment on lines +1721 to +1726
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 formatting change compresses a multi-line help_text string into a single long line, making it harder to read. The previous format was more readable. Consider reverting this change for consistency and readability.

Copilot uses AI. Check for mistakes.
),
)

Expand Down Expand Up @@ -2983,7 +3119,7 @@ class JournalTableOfContents(CharFieldLangMixin, CommonControlField):
FieldPanel("text"),
]
base_form_class = CoreAdminModelForm

class Meta:
unique_together = [
("journal", "collection", "language", "text", "code"),
Expand Down Expand Up @@ -3011,7 +3147,7 @@ def autocomplete_custom_queryset_filter(search_term):
return JournalTableOfContents.objects.filter(
text__icontains=search_term
).distinct()

@classmethod
def get(cls, journal, collection, language, text, code):
if not journal or not language or not text or not collection:
Expand All @@ -3031,7 +3167,7 @@ def get(cls, journal, collection, language, text, code):
return cls.objects.get(**filters)
except cls.MultipleObjectsReturned:
return cls.objects.filter(**filters).first()

@classmethod
def create(
cls,
Expand Down Expand Up @@ -3062,6 +3198,7 @@ def create(
text=text,
code=code,
)

@classmethod
def create_or_update(
cls,
Expand Down
Loading
Loading