-
Notifications
You must be signed in to change notification settings - Fork 347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merge Numbas Integration feature into 8.0.x #443
Changes from 53 commits
3ae95f8
fa2de36
f035c44
4165b10
55fcbc5
061f49f
d53610a
dade800
aa8926e
3f19ffa
54c27ce
20d5265
5d80830
0cc4915
27253bd
ff5ff62
89a6615
ee992f4
a7c4006
b4d3f9d
e650ef1
3f5aa2b
26d75f5
d9f640f
8047f4c
3539d95
0812e20
b498924
d71ea14
910eecd
2a04a06
8c9a68b
c5055b8
1e00d41
28f3279
7d31f7c
273b62e
3d44ef2
1240b3f
1ae0347
4255347
fce7e75
69053ee
4139690
fc8134a
c854fc1
6108b52
3d86d04
c5240d8
5db5f35
08a0090
835d2b0
52450be
e740d82
eacbac1
7f3b752
9bdbe0e
6373eee
4359cf2
a49fc8c
bb29f84
5b8f5d3
9288fae
e3fab0d
58d8281
32b1d9f
460681a
210c3f4
d4808b0
6aea5c1
439669a
2fcb477
e01ed19
61f2f49
ebbacb9
4dae884
0bf29eb
6b08013
57acea2
03e9214
be21763
789fbad
1eadf31
4f3979b
6c74d7d
de3ec39
7d47eda
cc2f674
72b4c69
15ca645
68a7248
a249662
1b2a43c
38a6922
232dcaa
b70aeac
b868799
4e38e6c
908c803
752f26e
7e9adaa
efa6692
547dbb9
d84856b
b047e4c
166a0ad
cea12e5
edcd36a
527b810
70f095c
875b831
6753d80
3a4eb51
7dfd547
33ee3ce
3e0a1ba
e3d36c2
218afb9
1e3acd4
8139f41
758a51d
eb8859b
842f233
cbec03d
7023650
edbacb3
7de1a8f
e7a6eed
02af2cf
8059213
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Entities | ||
class ScormEntity < Grape::Entity | ||
macite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
expose :file_content, documentation: { type: 'string', desc: 'File content' } | ||
expose :content_type, documentation: { type: 'string', desc: 'Content type' } | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module Entities | ||
class TestAttemptEntity < Grape::Entity | ||
expose :id | ||
expose :task_id | ||
expose :attempted_time | ||
expose :terminated | ||
expose :success_status | ||
expose :score_scaled | ||
expose :completion_status | ||
expose :cmi_datamodel | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
require 'grape' | ||
require 'zip' | ||
require 'mime/types' | ||
class ScormApi < Grape::API | ||
# Include the AuthenticationHelpers for authentication functionality | ||
helpers AuthenticationHelpers | ||
helpers AuthorisationHelpers | ||
|
||
before do | ||
authenticated? | ||
end | ||
|
||
helpers do | ||
# Method to stream a file from a zip archive at the specified path | ||
# @param zip_path [String] the path to the zip archive | ||
# @param file_path [String] the path of the file within the zip archive | ||
def stream_file_from_zip(zip_path, file_path) | ||
file_stream = nil | ||
|
||
logger.debug "Streaming zip file at #{zip_path}" | ||
# Get an input stream for the requested file within the ZIP archive | ||
Zip::File.open(zip_path) do |zip_file| | ||
zip_file.each do |entry| | ||
next unless entry.name == file_path | ||
logger.debug "Found file #{file_path} from SCORM container" | ||
file_stream = entry.get_input_stream | ||
break | ||
end | ||
end | ||
|
||
# If the file was not found in the ZIP archive, return a 404 response | ||
unless file_stream | ||
error!({ error: 'File not found' }, 404) | ||
end | ||
|
||
# Set the content type based on the file extension | ||
content_type = MIME::Types.type_for(file_path).first.content_type | ||
logger.debug "Content type: #{content_type}" | ||
|
||
# Set the content type header | ||
header 'Content-Type', content_type | ||
|
||
# Set cache control header to prevent caching | ||
header 'Cache-Control', 'no-cache, no-store, must-revalidate' | ||
|
||
# Set the body to the contents of the file_stream and return the response | ||
body file_stream.read | ||
end | ||
end | ||
|
||
desc 'Serve SCORM content' | ||
params do | ||
requires :task_def_id, type: Integer, desc: 'Task Definition ID to get SCORM test data for' | ||
end | ||
get '/scorm/:task_def_id/:username/:auth_token/*file_path' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should any of the api calls here be limited to scorm tokens? |
||
task_def = TaskDefinition.find(params[:task_def_id]) | ||
|
||
unless authorise? current_user, task_def.unit, :get_unit | ||
error!({ error: 'You cannot access SCORM tests of unit' }, 403) | ||
end | ||
|
||
env['api.format'] = :txt | ||
if task_def.has_scorm_data? | ||
zip_path = task_def.task_scorm_data | ||
content_type 'application/octet-stream' | ||
stream_file_from_zip(zip_path, params[:file_path]) | ||
else | ||
error!({ error: 'SCORM data does not exist.' }, 404) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
require 'grape' | ||
|
||
class ScormExtensionCommentsApi < Grape::API | ||
helpers AuthenticationHelpers | ||
helpers AuthorisationHelpers | ||
|
||
desc 'Request a scorm extension for a task' | ||
params do | ||
requires :comment, type: String, desc: 'The details of the request' | ||
end | ||
post '/projects/:project_id/task_def_id/:task_definition_id/request_scorm_extension' do | ||
project = Project.find(params[:project_id]) | ||
task_definition = project.unit.task_definitions.find(params[:task_definition_id]) | ||
task = project.task_for_task_definition(task_definition) | ||
|
||
# check permissions using specific permission has with addition of request extension if allowed in unit | ||
unless authorise? current_user, task, :request_scorm_extension | ||
error!({ error: 'Not authorised to request a scorm extension for this task' }, 403) | ||
end | ||
|
||
if task_definition.scorm_attempt_limit == 0 | ||
error!({ message: 'This task allows unlimited attempts to complete the test' }, 400) | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. error! throws an exception - so this is unreachable... we should look and remove these unnecessary return statements |
||
end | ||
|
||
result = task.apply_for_scorm_extension(current_user, params[:comment]) | ||
present result.serialize(current_user), Grape::Presenters::Presenter | ||
end | ||
|
||
desc 'Assess a scorm extension for a task' | ||
params do | ||
requires :granted, type: Boolean, desc: 'Assess a scorm extension' | ||
end | ||
put '/projects/:project_id/task_def_id/:task_definition_id/assess_scorm_extension/:task_comment_id' do | ||
project = Project.find(params[:project_id]) | ||
task_definition = project.unit.task_definitions.find(params[:task_definition_id]) | ||
task = project.task_for_task_definition(task_definition) | ||
|
||
unless authorise? current_user, task, :assess_scorm_extension | ||
error!({ error: 'Not authorised to assess a scorm extension for this task' }, 403) | ||
end | ||
|
||
task_comment = task.all_comments.find(params[:task_comment_id]).becomes(ScormExtensionComment) | ||
|
||
unless task_comment.assess_scorm_extension(current_user, params[:granted]) | ||
if task_comment.errors.count >= 1 | ||
error!({ error: task_comment.errors.full_messages.first }, 403) | ||
else | ||
error!({ error: 'Error saving scorm extension' }, 403) | ||
end | ||
end | ||
present task_comment.serialize(current_user), Grape::Presenters::Presenter | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What can a scorm token access? It looks like there is not scope applied to these at the moment... where are they used in the auth process?