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