-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfile_transfer_service.py
More file actions
217 lines (185 loc) · 8.91 KB
/
file_transfer_service.py
File metadata and controls
217 lines (185 loc) · 8.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
"""
This service is used to manage the transfers to and from Aries's Editorial Manager system.
"""
__author__ = "Rosetta Reatherford"
__license__ = "AGPL v3"
__maintainer__ = "The Public Library of Science (PLOS)"
import os
from typing import List
from plugins.editorial_manager_transfer_service import logger_messages
from plugins.editorial_manager_transfer_service.enums.report_state import ReportState
from plugins.editorial_manager_transfer_service.file_exporter import ExportFileCreation
from utils.logger import get_logger
logger = get_logger(__name__)
class FileTransferService:
"""
Manages the transfers to and from Aries's Editorial Manager system.
"""
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
"""
Constructor.
"""
if not hasattr(self, '_initialized'): # Prevent re-initialization on subsequent calls
self.exports: dict[str, ExportFileCreation] = dict()
self.files_to_delete: List[str] = list()
self._initialized = True
def get_export_file_creator(self, journal_code: str, article_id: int,
can_create: bool = False) -> ExportFileCreation | None:
"""
Gets the export file creator for the given article.
:param can_create: True if this fetch can create the export file creator, false otherwise.
:param journal_code: The journal code of the journal where the article lives.
:param article_id: The article id.
:return: The export file creator.
"""
dictionary_identifier: str = self.__get_dictionary_identifier(journal_code, article_id)
file_creator: ExportFileCreation | None = self.exports.get(dictionary_identifier)
if not file_creator:
if can_create:
file_creator = ExportFileCreation(journal_code, article_id)
self.exports[dictionary_identifier] = file_creator
else:
return None
return self.exports[dictionary_identifier]
@staticmethod
def __get_dictionary_identifier(journal_code: str, article_id: int) -> 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}".strip()
def get_export_zip_filepath(self, journal_code: str, article_id: int) -> str | None:
"""
Gets the export zip file path for the given article.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
:return: The export zip file path.
"""
file_export_creator = self.get_export_file_creator(journal_code, article_id, True)
return file_export_creator.get_zip_filepath() if file_export_creator else None
def get_export_go_filepath(self, journal_code: str, article_id: int) -> str | None:
"""
Gets the export go file path for the given article.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
:return: The export go file path.
"""
file_export_creator = self.get_export_file_creator(journal_code, article_id, True)
return file_export_creator.get_go_filepath() if file_export_creator else None
def log_export_error(self, journal_code: str,
article_id: int,
error_message: str = None,
error: Exception = None) -> None:
"""
Logs an error message in both the database and plaintext logs.
:param error: The exception, if there is one.
:param error_message: The error message to print out.
: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, error_message),
error, stage=ReportState.FAILED_INGEST)
def log_export_success_go_file(self, journal_code: str,
article_id: int) -> 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_go_file()
self.delete_export_files(journal_code, article_id)
def log_export_success_zip_file(self, journal_code: str,
article_id: int) -> 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_zip_file()
def delete_export_files(self, journal_code: str, article_id: int) -> 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
file_exporter = self.exports.pop(dictionary_identifier)
self.files_to_delete.append(file_exporter.get_zip_filepath())
self.files_to_delete.append(file_exporter.get_go_filepath())
del file_exporter
def __delete_files(self) -> None:
for file in self.files_to_delete:
if self.__delete_file(file):
self.files_to_delete.remove(file)
@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:
os.remove(filepath)
except OSError as e:
logger.exception(e)
logger.error(logger_messages.export_process_failed_delete_file(filepath))
return False
return True
def get_export_zip_filepath(journal_code: str, article_id: int) -> str | None:
"""
Gets the zip file path for a given article.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
:return: The zip file path.
"""
return FileTransferService().get_export_zip_filepath(journal_code, article_id)
def get_export_go_filepath(journal_code: str, article_id: int) -> str | None:
"""
Gets the export file path for a go file created for a given article.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
:return: The export go file path.
"""
return FileTransferService().get_export_go_filepath(journal_code, article_id)
def export_success_callback_go_file(journal_code: str, article_id: int) -> None:
"""
The callback in case of a successful export.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
"""
FileTransferService().log_export_success_go_file(journal_code, article_id)
def export_success_callback_zip_file(journal_code: str, article_id: int) -> None:
"""
The callback in case of a successful export.
:param journal_code: The journal code of the journal the article lives in.
:param article_id: The article id.
"""
FileTransferService().log_export_success_zip_file(journal_code, article_id)
def export_failure_callback(journal_code: str, article_id: int, error_message: str = None,
error: Exception = None) -> None:
"""
The callback in case of a failed export.
:param error: The exception, if there is one.
:param error_message: The error message to print.
: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, error_message, error)
FileTransferService().delete_export_files(journal_code, article_id)