Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions consts.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
__author__ = "Rosetta Reatherford"
__license__ = "AGPL v3"
__maintainer__ = "The Public Library of Science (PLOS)"

import os

from django.conf import settings
Expand Down
3 changes: 2 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
hypothesis==6.138.7
pytest==8.4.1
pytest==8.4.1
python-magic==0.4.27
Empty file added enums/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions enums/transfer_log_message_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
A file for tracking the transfer log message types.
"""
__author__ = "Rosetta Reatherford"
__license__ = "AGPL v3"
__maintainer__ = "The Public Library of Science (PLOS)"

import django.db.models as models
from django.utils.translation import gettext_lazy as _

class TransferLogMessageType(models.TextChoices):
EXPORT = "EX", _("Export Message")
IMPORT = "IM", _("Import Message")
100 changes: 72 additions & 28 deletions file_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import plugins.editorial_manager_transfer_service.logger_messages as logger_messages
from core.models import File
from journal.models import Journal
from plugins.editorial_manager_transfer_service.enums.transfer_log_message_type import TransferLogMessageType
from plugins.editorial_manager_transfer_service.models import TransferLogs
from submission.models import Article
from utils import setting_handler
from utils.logger import get_logger
Expand All @@ -42,47 +44,32 @@ class ExportFileCreation:
A class for managing the export file creation process.
"""

def __init__(self, journal_code: str, article_id: str):
def __init__(self, janeway_journal_code: str, article_id: int | None) -> None:
self.zip_filepath: str | None = None
self.go_filepath: str | None = None
self.in_error_state: bool = False
self.__license_code: str | None = None
self.__journal_code: str | None = None
self.__submission_partner_code: str | None = None
self.article_id: str | None = article_id.strip() if article_id else None
self.article_id: int | None = article_id
self.article: Article | None = None
self.journal: Journal | None = None
self.export_folder: str | None = None

# If no article ID, return an error.
if not self.article_id or len(self.article_id) <= 0:
logger.error(logger_messages.process_failed_no_article_id_provided())
self.in_error_state = True
return

# Attempt to get the journal.
try:
self.journal: Journal = Journal.objects.get(code=journal_code)
except Journal.DoesNotExist:
logger.error(logger_messages.process_failed_fetching_journal(article_id))
self.in_error_state = True
# Gets the journal
self.journal: Journal | None = self.__fetch_journal(janeway_journal_code)
if self.in_error_state:
return

# Get the article based upon the given article ID.
logger.info(logger_messages.process_fetching_article(article_id))
try:
self.article: Article = self.__fetch_article(self.journal, article_id)
if not self.article:
raise Article.DoesNotExist
except Article.DoesNotExist:
logger.error(logger_messages.process_failed_fetching_article(article_id))
self.in_error_state = True
self.article: Article | None = self.__fetch_article(self.journal, article_id)
if self.in_error_state:
return

# Get the export folder.
export_folders: str = get_article_export_folders()
if len(export_folders) <= 0:
logger.error(logger_messages.export_process_failed_no_export_folder())
self.log_error(logger_messages.export_process_failed_no_export_folder())
self.in_error_state = True
return
self.export_folder = export_folders
Expand Down Expand Up @@ -131,7 +118,7 @@ def __create_export_file(self):
# Attempt to fetch the article files.
article_files: Sequence[File] = self.__fetch_article_files(self.article)
if len(article_files) <= 0:
logger.error(logger_messages.process_failed_fetching_article_files(self.article_id))
self.log_error(logger_messages.process_failed_fetching_article_files(self.article_id))
self.in_error_state = True
return

Expand Down Expand Up @@ -199,7 +186,7 @@ def get_setting(self, setting_name: str) -> str:
return setting_handler.get_setting(setting_group_name=consts.PLUGIN_SETTINGS_GROUP_NAME,
setting_name=setting_name, journal=self.journal, ).processed_value
except ObjectDoesNotExist:
logger.error("Could not get the following setting, '{0}'".format(setting_name))
self.log_error("Could not get the following setting, '{0}'".format(setting_name))
self.in_error_state = True
return ""

Expand Down Expand Up @@ -260,15 +247,72 @@ def __create_metadata_file(self, article: Article) -> File | None:
"""
pass

@staticmethod
def __fetch_article(journal: Journal, article_id: str) -> Article:
def __fetch_article(self, journal: Journal | None, article_id: int | None) -> Article | None:
"""
Gets the article object for the given article ID.
:param journal: The journal to fetch the article from.
:param article_id: The ID of the article.
:return: The article object with the given article ID.
"""
return Article.get_article(journal, "id", article_id)
# If no article ID or journal, return an error.
if not article_id or article_id <= 0:
self.log_error(logger_messages.process_failed_no_article_id_provided())
self.in_error_state = True
return None

article: Article | None = None

logger.debug(logger_messages.process_fetching_article(article_id))
try:
article = Article.get_article(journal, "id", article_id)
logger.debug(logger_messages.process_finished_fetching_article(article_id))
except Article.DoesNotExist:
self.log_error(logger_messages.process_failed_fetching_article(article_id))
self.in_error_state = True

return article

def __fetch_journal(self, janeway_journal_code: str | None) -> Journal | None:
"""
Gets the journal from the database given the Janeway journal code.
:param janeway_journal_code: The code of the Janeway journal to fetch.
:return: The journal object with the given Janeway journal code, if there is one. None otherwise.
"""
# If no journal code, return an error.
if not janeway_journal_code or len(janeway_journal_code) <= 0:
self.log_error(logger_messages.process_failed_no_janeway_journal_code_provided())
self.in_error_state = True
return None

journal: Journal | None = None

# Attempt to get the journal.
logger.debug(logger_messages.process_fetching_journal(janeway_journal_code))
try:
journal = Journal.objects.get(code=janeway_journal_code)
logger.debug(logger_messages.process_finished_fetching_journal(janeway_journal_code))
except Journal.DoesNotExist:
self.log_error(logger_messages.process_failed_fetching_journal(janeway_journal_code))
self.in_error_state = True

return journal

def log_error(self, message: str) -> None:
"""
Logs the given error message in both the database and plaintext logs.
:param message: The message to log.
"""
logger.error(message)
TransferLogs.objects.create(journal=self.journal, article=self.article, message=message,
message_type=TransferLogMessageType.EXPORT, success=False)

def log_success(self) -> None:
"""
Logs a success message in both the database and plaintext logs.
"""
TransferLogs.objects.create(journal=self.journal, article=self.article,
message=logger_messages.export_process_succeeded(self.article_id),
message_type=TransferLogMessageType.EXPORT, success=True)

@staticmethod
def __fetch_article_files(article: Article) -> List[File]:
Expand Down
39 changes: 39 additions & 0 deletions file_transfer_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import os
from typing import List

from plugins.editorial_manager_transfer_service import logger_messages
from plugins.editorial_manager_transfer_service.file_exporter import ExportFileCreation
from utils.logger import get_logger

Expand Down Expand Up @@ -53,6 +54,12 @@ def get_export_file_creator(self, journal_code: str, article_id: str) -> ExportF

@staticmethod
def __get_dictionary_identifier(journal_code: str, article_id: str) -> str:
"""
Gets the dictionary identifier for the given article.
:param journal_code: The journal code of the journal where the article lives.
:param article_id: The article id.
:return: The dictionary identifier.
"""
return f"{journal_code}-{article_id}"

def get_export_zip_filepath(self, journal_code: str, article_id: str) -> str | None:
Expand All @@ -75,7 +82,32 @@ def get_export_go_filepath(self, journal_code: str, article_id: str) -> str | No
file_export_creator = self.get_export_file_creator(journal_code, article_id)
return file_export_creator.get_go_filepath() if file_export_creator else None

def log_export_error(self, journal_code: str, article_id: str) -> None:
"""
Logs an error message in both the database and plaintext logs.
:param journal_code: The journal code of the journal where the article lives.
:param article_id: The article id.
"""
file_export_creator = self.get_export_file_creator(journal_code, article_id)
if file_export_creator:
file_export_creator.log_error(logger_messages.export_process_failed_ingest(article_id))

def log_export_success(self, journal_code: str, article_id: str) -> None:
"""
Logs the success message for when an article has completed a journey to Editorial Manager.
:param journal_code: The journal code of the journal where the article lives.
:param article_id: The article id.
"""
file_export_creator = self.get_export_file_creator(journal_code, article_id)
if file_export_creator:
file_export_creator.log_success()

def delete_export_files(self, journal_code: str, article_id: str) -> None:
"""
Deletes the export files for the given article.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
"""
dictionary_identifier: str = self.__get_dictionary_identifier(journal_code, article_id)
if dictionary_identifier not in self.exports:
return
Expand All @@ -93,6 +125,11 @@ def __delete_files(self) -> None:

@staticmethod
def __delete_file(filepath: str) -> bool:
"""
Deletes the given file.
:param filepath: The file path of the file to delete.
:return: True if the file was deleted, false otherwise.
"""
if not os.path.exists(filepath):
return True
try:
Expand Down Expand Up @@ -129,6 +166,7 @@ def export_success_callback(journal_code: str, article_id: str) -> None:
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
"""
FileTransferService().log_export_success(journal_code, article_id)
FileTransferService().delete_export_files(journal_code, article_id)


Expand All @@ -138,4 +176,5 @@ def export_failure_callback(journal_code: str, article_id: str) -> None:
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
"""
FileTransferService().log_export_error(journal_code, article_id)
FileTransferService().delete_export_files(journal_code, article_id)
4 changes: 4 additions & 0 deletions forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
__author__ = "Rosetta Reatherford"
__license__ = "AGPL v3"
__maintainer__ = "The Public Library of Science (PLOS)"

from django import forms


Expand Down
65 changes: 63 additions & 2 deletions logger_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def import_folder_created() -> str:
return '{0} import folder already exists.'.format(PLUGIN_NAME)


def process_fetching_article(article_id: str) -> str:
def process_fetching_article(article_id: int) -> str:
"""
Gets the log message for when an article is being fetched from the database.
:param: article_id: The ID of the article being fetched.
Expand All @@ -73,7 +73,16 @@ def process_fetching_article(article_id: str) -> str:
return "Fetching article from database (ID: {0})...".format(article_id)


def process_failed_fetching_article(article_id: str) -> str:
def process_finished_fetching_article(article_id: int) -> str:
"""
Gets the log message for when an article is being fetched from the database.
:param: article_id: The ID of the article being fetched.
:return: The logger message.
"""
return "Completed fetching article from database (ID: {0})...".format(article_id)


def process_failed_fetching_article(article_id: int) -> str:
"""
Gets the log message for when an article failed to be fetched.
:param: article_id: The ID of the article being fetched.
Expand All @@ -82,6 +91,34 @@ def process_failed_fetching_article(article_id: str) -> str:
return "Fetching article from database (ID: {0}) failed. Discontinuing export process.".format(article_id)


def process_fetching_journal(janway_journal_code: str) -> str:
"""
Gets the log message for when a journal is being fetched from the database.
:param: janway_journal_code: The code of the journal being fetched.
:return: The logger message.
"""
return "Fetching journal from database (Code: {0})...".format(janway_journal_code)


def process_finished_fetching_journal(janway_journal_code: str) -> str:
"""
Gets the log message for when a journal is being fetched from the database.
:param: janway_journal_code: The code of the journal being fetched.
:return: The logger message.
"""
return "Completed fetching journal from database (Code: {0})...".format(janway_journal_code)


def process_failed_fetching_journal(janway_journal_code: str) -> str:
"""
Gets the log message for when a journal failed to be fetched.
:param: janway_journal_code: The code of the journal being fetched.
:return: The logger message.
"""
return "Fetching journal from database (Code: {0}) failed. Discontinuing export process.".format(
janway_journal_code)


def process_failed_fetching_metadata(article_id) -> str:
"""
Gets the log message for when an article's metadata failed to be fetched.
Expand Down Expand Up @@ -117,9 +154,33 @@ def process_failed_no_article_id_provided() -> str:
return "No article ID provided. Discontinuing export process."


def process_failed_no_janeway_journal_code_provided() -> str:
"""
Gets the log message for when no journal code was provided.
:return: The logger message.
"""
return "No Janeway journal code was provided. Discontinuing export process."


def export_process_failed_no_export_folder() -> str:
"""
Gets the log message for when an export folder was not created.
:return: The logger message.
"""
return "No export folder provided. Discontinuing export process."


def export_process_failed_ingest(article_id: int) -> str:
"""
Gets the log message for when the article failed to be ingested into Editorial Manager.
:return: The logger message.
"""
return "Export process failed during ingest to Editorial Manager for article (ID: {0}).".format(article_id)


def export_process_succeeded(article_id: int) -> str:
"""
Gets the log message for when the export process was successful.
:return: The logger message.
"""
return "Export process succeeded for article (ID: {0}).".format(article_id)
4 changes: 4 additions & 0 deletions logic.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
__author__ = "Rosetta Reatherford"
__license__ = "AGPL v3"
__maintainer__ = "The Public Library of Science (PLOS)"

import plugins.editorial_manager_transfer_service.consts as consts
from journal.models import Journal
from utils import setting_handler
Expand Down
Loading
Loading