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

1import datetime 

2import logging 

3from typing import TYPE_CHECKING 

4 

5from django.db import transaction 

6 

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 

36 

37if TYPE_CHECKING: 

38 from lxml import etree 

39 

40 from mesh.models import JournalSection 

41 

42logger = logging.getLogger(__name__) 

43 

44 

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) 

53 

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") 

60 

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 ) 

66 

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] 

70 

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 ) 

77 

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")) 

83 

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")) 

86 

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) 

96 

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() 

109 

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 

128 

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 

142 

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 ) 

150 

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") 

164 

165 import_ojs_file( 

166 submission_main_file, 

167 version_main_file_factory(current_version), 

168 created_by_user, 

169 path, 

170 ) 

171 

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) 

205 

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) 

220 

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 

230 

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") 

235 

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 

240 

241 submission.save() 

242 return submission