Coverage for src / mesh / ojs / import_submission.py: 0%
122 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
1import datetime
2import logging
3from typing import TYPE_CHECKING
5from django.db import transaction
7from mesh.models import EditorialDecision, Submission, SubmissionAuthor, SubmissionVersion, User
8from mesh.models.crud import create_submission
9from mesh.models.orm.submission_models import SubmissionState
10from mesh.ojs import (
11 NSMAP,
12 OJS_STATUS_MAPPING,
13)
14from mesh.ojs.import_file import (
15 import_ojs_file,
16 version_additional_file_factory,
17 version_main_file_factory,
18)
19from mesh.ojs.import_review import import_ojs_reviews
20from mesh.ojs.import_round import (
21 import_ojs_round_decision,
22 import_ojs_round_files,
23)
24from mesh.ojs.import_user import get_user_or_create_fake_one
25from mesh.ojs.lxml_utils import (
26 find_and_make_test_email_or_raise,
27 find_or_raise,
28 find_xpath_or_raise,
29 findall_xpath,
30 findtext_or_raise,
31 get_and_make_test_email_or_raise,
32 get_or_raise,
33 str_to_datetime,
34)
35from mesh.views.utils import assign_editor
37if TYPE_CHECKING:
38 from lxml import etree
40 from mesh.models import JournalSection
42logger = logging.getLogger(__name__)
45def import_ojs_article(
46 extended_article: "etree._Element", journal_section: "JournalSection", path: str
47):
48 ojs_publication = find_or_raise(extended_article, "publication")
49 owner_author_id = ojs_publication.get("primary_contact_id")
50 if not owner_author_id:
51 raise ValueError("Publication does not have any owner")
52 ojs_authors = ojs_publication.findall("authors/author", NSMAP)
54 # AUTHORS
55 # keys are ojs author ID
56 authors: dict[str, SubmissionAuthor] = {}
57 for ojs_author in ojs_authors:
58 # TODO : should we handle user_group_ref here ?
59 ojs_author_id = get_or_raise(ojs_author, "id")
61 authors[ojs_author_id] = SubmissionAuthor(
62 email=find_and_make_test_email_or_raise(ojs_author, "email"),
63 first_name=findtext_or_raise(ojs_author, "givenname"),
64 last_name=findtext_or_raise(ojs_author, "familyname"),
65 )
67 if owner_author_id not in authors:
68 raise ValueError("Publication's primary_contact_id not found in authors")
69 author_owner = authors[owner_author_id]
71 try:
72 created_by_user = get_user_or_create_fake_one(email=author_owner.email)
73 except User.DoesNotExist:
74 raise ValueError(
75 f"OJS ID : {findtext_or_raise(extended_article, 'id')}| User associated with author does not exists"
76 )
78 # SUBMISSION
79 # submitted_event_log = find_or_raise(
80 # extended_article, "event_logs/event_log[@message='submission.event.submissionSubmitted']"
81 # )
82 submission_date_submitted = str_to_datetime(get_or_raise(extended_article, "date_submitted"))
84 # first_event_log = find_xpath_or_raise(extended_article, "ojs:event_logs/ojs:event_log[last()]")
85 # submission_date_created = str_to_datetime(get_or_raise(first_event_log, "date_logged"))
87 try:
88 with transaction.atomic():
89 submission = Submission.objects.get(
90 ojs_id=findtext_or_raise(extended_article, "id"), journal_section=journal_section
91 )
92 submission.delete()
93 except Submission.DoesNotExist:
94 pass
95 submission = create_submission(user=created_by_user, date=submission_date_submitted)
97 submission.ojs_id = int(findtext_or_raise(extended_article, "id"))
98 submission.journal_section = journal_section
99 submission.created_by = created_by_user
100 submission.name = findtext_or_raise(ojs_publication, "title")
101 try:
102 submission.abstract = findtext_or_raise(ojs_publication, "abstract")
103 except ValueError:
104 submission.abstract = ""
105 submission.author_agreement = True
106 for author in authors.values():
107 author.submission = submission
108 author.save()
110 # EDITORS
111 ojs_submission_stage = find_or_raise(extended_article, "stage[@path='submission']")
112 for participant in ojs_submission_stage.findall("participant", NSMAP):
113 if get_or_raise(participant, "user_group_ref") not in (
114 "Managing editor",
115 "Section editor",
116 "Associate editor",
117 ):
118 continue
119 # if get_or_raise(participant, "recommend_only") == 1:
120 # continue
121 # if get_or_raise(participant, "can_change_metadata") == 0:
122 # continue
123 email = get_and_make_test_email_or_raise(participant, "user_email")
124 user = get_user_or_create_fake_one(email)
125 if not user:
126 logger.error(f"Cannot assign {email} as editor : user does not exists in database")
127 continue
129 # Find editor assigned event log
130 try:
131 event_log = find_xpath_or_raise(
132 extended_article,
133 f"ojs:event_logs/ojs:event_log[@message='submission.event.participantAdded'][@participant_email='{email}']",
134 )
135 editor_assigned_date = str_to_datetime(get_or_raise(event_log, "date_logged"))
136 manager_email = get_and_make_test_email_or_raise(event_log, "user_email")
137 manager = User.objects.filter(email=manager_email).first()
138 except BaseException:
139 logger.debug(f"Couldn't find editor assigned date for {email}")
140 editor_assigned_date = submission_date_submitted + datetime.timedelta(0, 1)
141 manager = None
143 if not submission.editors.filter(user=user).exists():
144 assign_editor(
145 submission,
146 user,
147 manager=manager,
148 date=editor_assigned_date,
149 )
151 # First version
152 # Submission main file
153 submission_main_file_search = extended_article.findall(
154 "submission_file[@stage='submission'][@genre='Manuscript']", NSMAP
155 )
156 if len(submission_main_file_search) > 1:
157 raise ValueError("Found multiple main file candidates")
158 if len(submission_main_file_search) == 0:
159 raise ValueError("Couldn't find main file")
160 submission_main_file = submission_main_file_search[0]
161 current_version = submission.versions.first()
162 if current_version is None:
163 raise ValueError("Submission does not have a current version")
165 import_ojs_file(
166 submission_main_file,
167 version_main_file_factory(current_version),
168 created_by_user,
169 path,
170 )
172 # Submission additional files
173 submission_files = findall_xpath(
174 extended_article, "ojs:submission_file[@stage='submission'][not(@genre='Manuscript')]"
175 )
176 for submission_file_element in submission_files:
177 import_ojs_file(
178 submission_file_element,
179 version_additional_file_factory(current_version),
180 created_by_user,
181 path,
182 )
183 submission.submit(user=created_by_user, date_submitted=submission_date_submitted)
184 # Rounds
185 rounds_list = list(
186 extended_article.findall("stage[@path='externalReview']/review_round", NSMAP)
187 )
188 rounds_list.sort(key=lambda review_round: get_or_raise(review_round, "round"))
189 # OJS allows authors to send answers to reviews without creating a new round
190 # This doesn't works with MESH, so we have to create a fictional submission at the end instead
191 # Every round is thus shifted by +1
192 if len(rounds_list) > 0:
193 # Pseudo Do While
194 round = rounds_list.pop(0)
195 current_version = submission.versions.first()
196 if current_version is None:
197 raise ValueError("Submission does not have a current version")
198 import_ojs_reviews(round, current_version, path)
199 # import_ojs_round_decision(round, submission, current_version)
200 decision = EditorialDecision(value=SubmissionState.REVISION_REQUESTED.value)
201 decision.version = current_version
202 decision.save()
203 # TODO : extract real date
204 submission.apply_editorial_decision(decision, None, date=submission_date_submitted)
206 current_version = SubmissionVersion(submission=submission)
207 current_version.save()
208 submission.refresh_from_db()
209 # Those files are the files that the reviewer have access to
210 import_ojs_round_files(round, current_version, path, created_by_user)
211 previous_round = round
212 for round in rounds_list:
213 # TODO : extract real date
214 submission.submit(user=created_by_user, date_submitted=submission_date_submitted)
215 current_version.refresh_from_db()
216 # TODO : extract real date
217 submission.override_saved_date(date_last_modified=submission_date_submitted)
218 import_ojs_reviews(round, current_version, path)
219 import_ojs_round_decision(previous_round, submission, current_version)
221 current_version = SubmissionVersion(submission=submission)
222 current_version.save()
223 submission.refresh_from_db()
224 # Those files are the files that the author sent as an answer to the previous round
225 # Since we do not have those in MESH, those are now the files for the next round
226 import_ojs_round_files(
227 round, current_version, path, created_by_user, stage="review_revision"
228 )
229 previous_round = round
231 submission.submit(user=created_by_user, date_submitted=submission_date_submitted)
232 import_ojs_round_decision(previous_round, submission, current_version)
233 # STATE
234 ojs_status = get_or_raise(extended_article, "hr_status")
236 # TODO : Better submission state handling
237 if ojs_status not in OJS_STATUS_MAPPING:
238 raise ValueError(f"Publication status {ojs_status} not recognized")
239 submission.state = OJS_STATUS_MAPPING[ojs_status].value
241 submission.save()
242 return submission