@@ -53,6 +53,16 @@ def self.logger
5353 error! ( { error : "This task requires a group submission. Ensure you are in a group for the unit's #{ task_definition . group_set . name } " } , 403 )
5454 end
5555
56+ # A finished task stops accepting new student uploads. Without this the
57+ # upload lands, submission_date and file_uploaded_at are rewritten and the
58+ # assessed pdf is deleted and regenerated, while only the status transition
59+ # is skipped. Staff are still allowed through on purpose, because a tutor
60+ # sometimes has to upload on a student's behalf when a file is corrupt or
61+ # went to the wrong task.
62+ if task . task_submission_closed? && !authorise? ( current_user , project , :assess )
63+ error! ( { error : 'This task is closed for new submissions.' } , 403 )
64+ end
65+
5666 # Check that prerequisite tasks are in the required minimum submitted state
5767 prerequisites = task_definition . task_prerequisites
5868 prerequisites . each do |prerequisite |
@@ -156,16 +166,50 @@ def self.logger
156166 end
157167
158168 task = project . task_for_task_definition ( task_definition )
169+ error! ( { error : 'A submission for this task was not found.' } , 404 ) unless task
159170
160- if task && PortfolioEvidence . recreate_task_pdf ( task )
171+ begin
172+ task . regenerate_submission! ( current_user )
161173 result = 'done'
162- else
163- result = 'false'
174+ rescue ArgumentError => e
175+ error! ( { error : e . message } , 409 )
176+ rescue StandardError
177+ error! ( { error : 'The submitted files are not available to regenerate.' } , 422 )
164178 end
165179
166180 present :result , result , with : Grape ::Presenters ::Presenter
167181 end # put
168182
183+ desc 'Retry a failed or timed-out submission conversion'
184+ post '/projects/:id/task_def_id/:task_definition_id/submission/retry' do
185+ project = Project . find ( params [ :id ] )
186+ task_definition = project . unit . task_definitions . find ( params [ :task_definition_id ] )
187+
188+ unless authorise? current_user , project , :reprocess_submission
189+ error! ( { error : "Not authorised to retry task '#{ task_definition . name } '" } , 401 )
190+ end
191+
192+ task = project . task_for_task_definition ( task_definition )
193+ error! ( { error : 'A submission for this task was not found.' } , 404 ) unless task
194+
195+ begin
196+ task . retry_submission_processing! ( current_user )
197+ rescue ArgumentError => e
198+ error! ( { error : e . message } , 409 )
199+ rescue StandardError
200+ error! ( { error : 'The submitted files are not available to retry.' } , 422 )
201+ end
202+
203+ # For a group task the new state was written through other instances.
204+ task . reload
205+ present task . submission_processing_snapshot . merge (
206+ submission_date : task . submission_date ,
207+ processing_error_code : task . submission_processing_error_code ,
208+ processing_attempts : task . submission_processing_attempts ,
209+ task_status : task . task_status . status_key
210+ ) , with : Grape ::Presenters ::Presenter
211+ end
212+
169213 desc 'Get the timestamps of the last 10 submissions of a task'
170214 get '/projects/:id/task_def_id/:task_definition_id/submissions/timestamps' do
171215 project = Project . find ( params [ :id ] )
@@ -187,41 +231,89 @@ def self.logger
187231
188232 desc 'Get all retained submission histories for a task'
189233 get '/projects/:id/task_def_id/:task_definition_id/submission_histories' do
190- project = Project . find ( params [ :id ] )
191- task_definition = project . unit . task_definitions . find ( params [ :task_definition_id ] )
234+ project = Project . find_by ( id : params [ :id ] )
235+ error! ( { error : 'Submission history is not available' } , 404 ) unless project
192236
193- unless authorise? current_user , project , :get_submission
194- error! ( { error : "Not authorised to get submission history for task ' #{ task_definition . name } '" } , 401 )
237+ unless authorise? ( current_user , project , :get_submission )
238+ error! ( { error : 'Submission history is not available' } , 404 )
195239 end
196240
197- task = project . task_for_task_definition ( task_definition )
198- unless task
199- error! ( { error : 'A submission for this task definition has never been created' } , 404 )
241+ task_definition = project . unit . task_definitions . find_by ( id : params [ :task_definition_id ] )
242+ error! ( { error : 'Submission history is not available' } , 404 ) unless task_definition
243+
244+ task = project . tasks . find_by ( task_definition : task_definition )
245+ error! ( { error : 'Submission history is not available' } , 404 ) unless task
246+
247+ student_request = project . student == current_user
248+
249+ if student_request && !authorise? ( current_user , task , :get_submission )
250+ error! ( { error : 'Submission history is not available' } , 404 )
200251 end
201252
202- present task . submission_histories . order ( submission_timestamp : :desc ) ,
203- with : Entities ::SubmissionHistoryEntity
253+ histories = task . submission_histories . sort_by { |history | [ -history . submission_timestamp . to_i , -history . id ] }
254+
255+ if student_request
256+ archive_pending = SubmissionHistory . pending? ( task )
257+ latest_submission_at = task . submission_processing_started_at || task . submission_date
258+ student_histories = histories . each_with_index . map do |history , index |
259+ {
260+ id : history . id ,
261+ version_order : index + 1 ,
262+ current : index . zero? && !archive_pending && latest_submission_at . present? &&
263+ history . submission_timestamp . to_i >= latest_submission_at . to_i ,
264+ submission_timestamp : history . submission_timestamp ,
265+ status : history . has_submission_files? ? 'available' : 'unavailable'
266+ }
267+ end
268+
269+ status 202 if archive_pending
270+ present student_histories
271+ else
272+ present histories , with : Entities ::SubmissionHistoryEntity
273+ end
204274 end
205275
206276 desc 'Download a retained submission history archive'
207277 get '/projects/:id/task_def_id/:task_definition_id/submission_histories/:history_id/files' do
208- project = Project . find ( params [ :id ] )
209- task_definition = project . unit . task_definitions . find ( params [ :task_definition_id ] )
278+ project = Project . find_by ( id : params [ :id ] )
279+ error! ( { error : 'Submission history is not available' } , 404 ) unless project
210280
211- unless authorise? current_user , project . unit , :provide_feedback
212- error! ( { error : "Not authorised to get submission history for task '#{ task_definition . name } '" } , 401 )
281+ staff_access = authorise? ( current_user , project . unit , :provide_feedback )
282+ student_access =
283+ project . student == current_user && authorise? ( current_user , project , :get_submission )
284+
285+ unless staff_access || student_access
286+ error! ( { error : 'Submission history is not available' } , 404 )
213287 end
214288
215- task = project . task_for_task_definition ( task_definition )
216- history = task &.submission_histories &.find_by ( id : params [ :history_id ] )
217- error! ( { error : 'Submission history was not found' } , 404 ) unless history
218- error! ( { error : 'Submission history files are not available' } , 404 ) unless history . has_submission_files?
289+ task_definition = project . unit . task_definitions . find_by ( id : params [ :task_definition_id ] )
290+ error! ( { error : 'Submission history is not available' } , 404 ) unless task_definition
291+
292+ task = project . tasks . find_by ( task_definition : task_definition )
293+ error! ( { error : 'Submission history is not available' } , 404 ) unless task
294+
295+ student_access &&= authorise? ( current_user , task , :get_submission )
296+
297+ unless staff_access || student_access
298+ error! ( { error : 'Submission history is not available' } , 404 )
299+ end
300+
301+ history = task . submission_histories . find_by ( id : params [ :history_id ] )
302+ error! ( { error : 'Submission history is not available' } , 404 ) unless history
303+
304+ unless history . has_submission_files?
305+ error! ( { error : 'Submission history files are not available' } , 404 )
306+ end
219307
220308 filename = "#{ project . student . username } -#{ task_definition . abbreviation } -#{ history . submission_timestamp } .zip"
221309
222310 content_type 'application/octet-stream'
223311 header [ 'Content-Disposition' ] = "attachment; filename=#{ filename } "
224- submission_zip_data = history . submission_zip_data
312+ begin
313+ submission_zip_data = history . submission_zip_data
314+ rescue Zip ::Error , Errno ::ENOENT , Errno ::EACCES
315+ error! ( { error : 'Submission history files are not available' } , 404 )
316+ end
225317 header [ 'Content-Length' ] = submission_zip_data . bytesize . to_s
226318 env [ 'api.format' ] = :binary
227319 body submission_zip_data
@@ -339,7 +431,11 @@ def self.logger
339431 content_type 'application/octet-stream'
340432 header [ 'Content-Disposition' ] = "attachment; filename=#{ filename } "
341433
342- submission_zip_data = history . submission_zip_data
434+ begin
435+ submission_zip_data = history . submission_zip_data
436+ rescue Zip ::Error , Errno ::ENOENT , Errno ::EACCES
437+ error! ( { error : 'Submission history files are not available' } , 404 )
438+ end
343439 header [ 'Content-Length' ] = submission_zip_data . bytesize . to_s
344440 env [ 'api.format' ] = :binary
345441 body submission_zip_data
0 commit comments