diff --git a/scipost_django/journals/models/publication.py b/scipost_django/journals/models/publication.py index 6ee178057277b07efe3a0e5dcc8b0a2690911d9b..5ef34f4d994a7b89bb5a7d4d74a371ace71d8653 100644 --- a/scipost_django/journals/models/publication.py +++ b/scipost_django/journals/models/publication.py @@ -5,12 +5,14 @@ __license__ = "AGPL v3" from decimal import Decimal from string import Template as string_Template import re +from typing import TYPE_CHECKING from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError from django.db import models from django.db.models import Min, Sum from django.urls import reverse +from django.utils.functional import cached_property from ..constants import ( STATUS_DRAFT, @@ -31,6 +33,10 @@ from scipost.constants import SCIPOST_APPROACHES from scipost.fields import ChoiceArrayField +if TYPE_CHECKING: + from production.models import ProofsRepository + + class PublicationAuthorsTable(models.Model): """ PublicationAuthorsTable represents an author of a Publication. @@ -67,7 +73,7 @@ class PublicationAuthorsTable(models.Model): def is_empty(self) -> bool: """Check if object is a temporary placeholder.""" return self.profile is None - + @property def is_registered(self) -> bool: """Check if author is registered at SciPost.""" @@ -476,7 +482,7 @@ class Publication(models.Model): return self.pubfracs.aggregate(Sum("fraction"))["fraction__sum"] == 1 @property - def proofs_repository(self): + def proofs_repository(self) -> "ProofsRepository": """Return the proofs repository for the publication.""" return self.accepted_submission.production_stream.proofs_repository @@ -606,14 +612,29 @@ class Publication(models.Model): This list is used to render the affiliation list in the add_author view. """ - tex_contents = self.proofs_repository.fetch_publication_tex() - return Publication.extract_affiliations_from_tex(tex_contents) + if self.tex_contents: + return Publication.extract_affiliations_from_tex(self.tex_contents) + return [] + + @cached_property + def tex_contents(self) -> str | None: + return self.proofs_repository.fetch_tex() def construct_tex_author_info(self) -> tuple[list[str], list[list[int]]]: - """ Puts together information for each author from the TeX file of the publication. """ - - tex_contents = self.proofs_repository.fetch_publication_tex() - author_field = re.findall("%%%%%%%%%% TODO: AUTHORS(.*?)%%%%%%%%%% END TODO: AUTHORS", tex_contents, re.DOTALL)[0] + """Puts together information for each author from the TeX file of the publication.""" + + tex_contents = self.tex_contents or "" + author_field_match = re.search( + "%%%%%%%%%% TODO: AUTHORS(.*?)%%%%%%%%%% END TODO: AUTHORS", + tex_contents, + re.DOTALL, + ) + if author_field_match is None: + # If matching against the tex file fails, we use the author_list of the submission. + original_authors_as_list = [author.strip() for author in self.author_list.split(",")] + return original_authors_as_list, [[] for _ in original_authors_as_list] + else: + author_field = author_field_match.group(1) author_texts = author_field.strip().replace("\,", "").split("\n") # Remove any trailing or leading "and"s. diff --git a/scipost_django/journals/views.py b/scipost_django/journals/views.py index 94feb2d5bd21bf9f5d35983df15548ba48320a1c..6f712561fb8436845534e5b36214ca5f79c514a4 100644 --- a/scipost_django/journals/views.py +++ b/scipost_django/journals/views.py @@ -987,8 +987,9 @@ def add_author(request, doi_label: str) -> HttpResponse: ) # Update and save the author_list in the publication object. - publication.author_list = ", ".join(author_string_list) - publication.save() + if len(author_string_list) > 0: + publication.author_list = ", ".join(author_string_list) + publication.save() context = { "publication": publication, diff --git a/scipost_django/production/models.py b/scipost_django/production/models.py index 48ce75866e657de11c0e8a4a04c16677ec0b6f32..ae05406e9a7eb2c40cc24496d0a7c24b7380a712 100644 --- a/scipost_django/production/models.py +++ b/scipost_django/production/models.py @@ -536,19 +536,54 @@ class ProofsRepository(models.Model): return paths - def fetch_publication_tex(self) -> str: - """ Fetches the TeX file of the publication. """ - gl = gitlab.Gitlab("https://" + settings.GITLAB_URL, private_token = settings.GITLAB_KEY) - gl.auth() - - project = gl.projects.get(self.git_path) + def fetch_tex(self) -> str | None: + """ + Fetches the TeX file of the publication, or of the proofs if not available. + If both fail, returns None. + """ + # Attempt to authenticate with GitLab + try: + gl = gitlab.Gitlab( + "https://" + settings.GITLAB_URL, + private_token=settings.GITLAB_KEY, + ) + gl.auth() + except: + return None + publication: "Publication" = self.stream.submission.publications.first() - - main_file_path = publication.doi_label.replace(".", "_") +".tex" - publication_file = project.files.get(file_path = main_file_path, ref = "main") - tex_contents = publication_file.decode().decode("utf-8") - - return tex_contents + publication_filename = publication.doi_label.replace(".", "_") + ".tex" + + # Attempt to fetch the project from GitLab + try: + project = gl.projects.get(self.git_path) + except: + return None + + # Attempt to fetch the publication file + try: + publication_file = project.files.get( + file_path=publication_filename, ref="main" + ) + tex_contents = publication_file.decode().decode("utf-8") + return tex_contents + except: + pass + + # Fall back to the submission's main file + proofs_file = None + for project_file in project.repository_tree(ref="main"): + project_filename = project_file["name"] + if project_filename.endswith(".tex") and self.name in project_filename: + proofs_file = project.files.get(file_path=project_filename, ref="main") + break + + if proofs_file is not None: + tex_contents = proofs_file.decode().decode("utf-8") + return tex_contents + + # If all else fails, return None + return None def __str__(self) -> str: return f"Proofs repo for {self.stream}"