@@ -47,16 +47,16 @@ def test_show_no_submissions(self, xblock):
47
47
def test_show_submissions (self , xblock ):
48
48
# Create some submissions (but fewer than the max that can be shown)
49
49
self ._create_submissions_and_scores (xblock , [
50
- (prepare_submission_for_serialization ((" test answer 1 part 1" , " test answer 1 part 2" )), 1 ),
51
- (prepare_submission_for_serialization ((" test answer 2 part 1" , " test answer 2 part 2" )), 2 )
50
+ (prepare_submission_for_serialization ((' test answer 1 part 1' , ' test answer 1 part 2' )), 1 ),
51
+ (prepare_submission_for_serialization ((' test answer 2 part 1' , ' test answer 2 part 2' )), 2 )
52
52
])
53
53
self ._assert_scores (xblock , [
54
- {" score" : 2 , " submission" : create_submission_dict (
55
- {" answer" : prepare_submission_for_serialization ((u" test answer 2 part 1" , u" test answer 2 part 2" ))},
54
+ {' score' : 2 , 'files' : [], ' submission' : create_submission_dict (
55
+ {' answer' : prepare_submission_for_serialization ((u' test answer 2 part 1' , u' test answer 2 part 2' ))},
56
56
xblock .prompts
57
57
)},
58
- {" score" : 1 , " submission" : create_submission_dict (
59
- {" answer" : prepare_submission_for_serialization ((u" test answer 1 part 1" , u" test answer 1 part 2" ))},
58
+ {' score' : 1 , 'files' : [], ' submission' : create_submission_dict (
59
+ {' answer' : prepare_submission_for_serialization ((u' test answer 1 part 1' , u' test answer 1 part 2' ))},
60
60
xblock .prompts
61
61
)}
62
62
])
@@ -68,21 +68,21 @@ def test_show_submissions(self, xblock):
68
68
69
69
# Create more submissions than the max
70
70
self ._create_submissions_and_scores (xblock , [
71
- (prepare_submission_for_serialization ((" test answer 3 part 1" , " test answer 3 part 2" )), 0 ),
72
- (prepare_submission_for_serialization ((" test answer 4 part 1" , " test answer 4 part 2" )), 10 ),
73
- (prepare_submission_for_serialization ((" test answer 5 part 1" , " test answer 5 part 2" )), 3 ),
71
+ (prepare_submission_for_serialization ((' test answer 3 part 1' , ' test answer 3 part 2' )), 0 ),
72
+ (prepare_submission_for_serialization ((' test answer 4 part 1' , ' test answer 4 part 2' )), 10 ),
73
+ (prepare_submission_for_serialization ((' test answer 5 part 1' , ' test answer 5 part 2' )), 3 ),
74
74
])
75
75
self ._assert_scores (xblock , [
76
- {" score" : 10 , " submission" : create_submission_dict (
77
- {" answer" : prepare_submission_for_serialization ((u" test answer 4 part 1" , u" test answer 4 part 2" ))},
76
+ {' score' : 10 , 'files' : [], ' submission' : create_submission_dict (
77
+ {' answer' : prepare_submission_for_serialization ((u' test answer 4 part 1' , u' test answer 4 part 2' ))},
78
78
xblock .prompts
79
79
)},
80
- {" score" : 3 , " submission" : create_submission_dict (
81
- {" answer" : prepare_submission_for_serialization ((u" test answer 5 part 1" , u" test answer 5 part 2" ))},
80
+ {' score' : 3 , 'files' : [], ' submission' : create_submission_dict (
81
+ {' answer' : prepare_submission_for_serialization ((u' test answer 5 part 1' , u' test answer 5 part 2' ))},
82
82
xblock .prompts
83
83
)},
84
- {" score" : 2 , " submission" : create_submission_dict (
85
- {" answer" : prepare_submission_for_serialization ((u" test answer 2 part 1" , u" test answer 2 part 2" ))},
84
+ {' score' : 2 , 'files' : [], ' submission' : create_submission_dict (
85
+ {' answer' : prepare_submission_for_serialization ((u' test answer 2 part 1' , u' test answer 2 part 2' ))},
86
86
xblock .prompts
87
87
)}
88
88
])
@@ -92,12 +92,12 @@ def test_show_submissions(self, xblock):
92
92
def test_show_submissions_that_have_greater_than_0_score (self , xblock ):
93
93
# Create some submissions (but fewer than the max that can be shown)
94
94
self ._create_submissions_and_scores (xblock , [
95
- (prepare_submission_for_serialization ((" test answer 0 part 1" , " test answer 0 part 2" )), 0 ),
96
- (prepare_submission_for_serialization ((" test answer 1 part 1" , " test answer 1 part 2" )), 1 )
95
+ (prepare_submission_for_serialization ((' test answer 0 part 1' , ' test answer 0 part 2' )), 0 ),
96
+ (prepare_submission_for_serialization ((' test answer 1 part 1' , ' test answer 1 part 2' )), 1 )
97
97
])
98
98
self ._assert_scores (xblock , [
99
- {" score" : 1 , " submission" : create_submission_dict (
100
- {" answer" : prepare_submission_for_serialization ((u" test answer 1 part 1" , u" test answer 1 part 2" ))},
99
+ {' score' : 1 , 'files' : [], ' submission' : create_submission_dict (
100
+ {' answer' : prepare_submission_for_serialization ((u' test answer 1 part 1' , u' test answer 1 part 2' ))},
101
101
xblock .prompts
102
102
)},
103
103
])
@@ -109,16 +109,16 @@ def test_show_submissions_that_have_greater_than_0_score(self, xblock):
109
109
110
110
# Create more submissions than the max
111
111
self ._create_submissions_and_scores (xblock , [
112
- (prepare_submission_for_serialization ((" test answer 2 part 1" , " test answer 2 part 2" )), 10 ),
113
- (prepare_submission_for_serialization ((" test answer 3 part 1" , " test answer 3 part 2" )), 0 )
112
+ (prepare_submission_for_serialization ((' test answer 2 part 1' , ' test answer 2 part 2' )), 10 ),
113
+ (prepare_submission_for_serialization ((' test answer 3 part 1' , ' test answer 3 part 2' )), 0 )
114
114
])
115
115
self ._assert_scores (xblock , [
116
- {" score" : 10 , " submission" : create_submission_dict (
117
- {" answer" : prepare_submission_for_serialization ((u" test answer 2 part 1" , u" test answer 2 part 2" ))},
116
+ {' score' : 10 , 'files' : [], ' submission' : create_submission_dict (
117
+ {' answer' : prepare_submission_for_serialization ((u' test answer 2 part 1' , u' test answer 2 part 2' ))},
118
118
xblock .prompts
119
119
)},
120
- {" score" : 1 , " submission" : create_submission_dict (
121
- {" answer" : prepare_submission_for_serialization ((u" test answer 1 part 1" , u" test answer 1 part 2" ))},
120
+ {' score' : 1 , 'files' : [], ' submission' : create_submission_dict (
121
+ {' answer' : prepare_submission_for_serialization ((u' test answer 1 part 1' , u' test answer 1 part 2' ))},
122
122
xblock .prompts
123
123
)}
124
124
])
@@ -127,60 +127,105 @@ def test_show_submissions_that_have_greater_than_0_score(self, xblock):
127
127
@scenario ('data/leaderboard_show.xml' )
128
128
def test_no_text_key_submission (self , xblock ):
129
129
self .maxDiff = None
130
- # Instead of using the default submission as a dict with " text" ,
130
+ # Instead of using the default submission as a dict with ' text' ,
131
131
# make the submission a string.
132
- self ._create_submissions_and_scores (xblock , [(" test answer" , 1 )], submission_key = None )
132
+ self ._create_submissions_and_scores (xblock , [(' test answer' , 1 )], submission_key = None )
133
133
134
134
# It should still work
135
135
self ._assert_scores (xblock , [
136
- {" score" : 1 }
136
+ {' score' : 1 , 'files' : [] }
137
137
])
138
138
139
139
@mock_s3
140
140
@override_settings (
141
141
AWS_ACCESS_KEY_ID = 'foobar' ,
142
142
AWS_SECRET_ACCESS_KEY = 'bizbaz' ,
143
- FILE_UPLOAD_STORAGE_BUCKET_NAME = " mybucket"
143
+ FILE_UPLOAD_STORAGE_BUCKET_NAME = ' mybucket'
144
144
)
145
145
@scenario ('data/leaderboard_show.xml' )
146
146
def test_non_text_submission (self , xblock ):
147
147
# Create a mock bucket
148
148
conn = boto .connect_s3 ()
149
149
bucket = conn .create_bucket ('mybucket' )
150
- # Create a non-text submission (the submission dict doesn't contain " text" )
151
- self ._create_submissions_and_scores (xblock , [(" s3key" , 1 )], submission_key = " file_key" )
150
+ # Create a non-text submission (the submission dict doesn't contain ' text' )
151
+ self ._create_submissions_and_scores (xblock , [(' s3key' , 1 )], submission_key = ' file_key' )
152
152
153
153
# Expect that we default to an empty string for content
154
154
self ._assert_scores (xblock , [
155
- {"submission" : "" , "score" : 1 , "file" : "" }
155
+ {'score' : 1 , 'files' : [], 'submission' : '' }
156
156
])
157
157
158
158
@mock_s3
159
159
@override_settings (
160
160
AWS_ACCESS_KEY_ID = 'foobar' ,
161
161
AWS_SECRET_ACCESS_KEY = 'bizbaz' ,
162
- FILE_UPLOAD_STORAGE_BUCKET_NAME = "mybucket"
162
+ FILE_UPLOAD_STORAGE_BUCKET_NAME = 'mybucket'
163
+ )
164
+ @scenario ('data/leaderboard_show_allowfiles.xml' )
165
+ def test_image_and_text_submission_multiple_files (self , xblock ):
166
+ """
167
+ Tests that leaderboard works as expected when multiple files are uploaded
168
+ """
169
+ file_keys = ['foo' , 'bar' ]
170
+ file_descriptions = ['{}-description' .format (file_key ) for file_key in file_keys ]
171
+
172
+ conn = boto .connect_s3 ()
173
+ bucket = conn .create_bucket ('mybucket' )
174
+ for file_key in file_keys :
175
+ key = Key (bucket , 'submissions_attachments/{}' .format (file_key ))
176
+ key .set_contents_from_string ("How d'ya do?" )
177
+ files_url_and_description = [
178
+ (api .get_download_url (file_key ), file_descriptions [idx ])
179
+ for idx , file_key in enumerate (file_keys )
180
+ ]
181
+
182
+ # Create a image and text submission
183
+ submission = prepare_submission_for_serialization (('test answer 1 part 1' , 'test answer 1 part 2' ))
184
+ submission [u'file_keys' ] = file_keys
185
+ submission [u'files_descriptions' ] = file_descriptions
186
+
187
+ self ._create_submissions_and_scores (xblock , [
188
+ (submission , 1 )
189
+ ])
190
+
191
+ self .maxDiff = None
192
+ # Expect that we retrieve both the text and the download URL for the file
193
+ self ._assert_scores (xblock , [
194
+ {'score' : 1 , 'files' : files_url_and_description , 'submission' : create_submission_dict (
195
+ {'answer' : submission },
196
+ xblock .prompts
197
+ )}
198
+ ])
199
+
200
+ @mock_s3
201
+ @override_settings (
202
+ AWS_ACCESS_KEY_ID = 'foobar' ,
203
+ AWS_SECRET_ACCESS_KEY = 'bizbaz' ,
204
+ FILE_UPLOAD_STORAGE_BUCKET_NAME = 'mybucket'
163
205
)
164
206
@scenario ('data/leaderboard_show_allowfiles.xml' )
165
207
def test_image_and_text_submission (self , xblock ):
208
+ """
209
+ Tests that text and image submission works as expected
210
+ """
166
211
# Create a file and get the download URL
167
212
conn = boto .connect_s3 ()
168
213
bucket = conn .create_bucket ('mybucket' )
169
- key = Key (bucket )
170
- key .key = "submissions_attachments/foo"
214
+ key = Key (bucket , 'submissions_attachments/foo' )
171
215
key .set_contents_from_string ("How d'ya do?" )
172
- downloadUrl = api .get_download_url ("foo" )
216
+
217
+ file_download_url = [(api .get_download_url ('foo' ), '' )]
173
218
# Create a image and text submission
174
- submission = prepare_submission_for_serialization ((" test answer 1 part 1" , " test answer 1 part 2" ))
175
- submission [u" file_key" ] = " foo"
219
+ submission = prepare_submission_for_serialization ((' test answer 1 part 1' , ' test answer 1 part 2' ))
220
+ submission [u' file_key' ] = ' foo'
176
221
self ._create_submissions_and_scores (xblock , [
177
222
(submission , 1 )
178
223
])
179
224
self .maxDiff = None
180
225
# Expect that we retrieve both the text and the download URL for the file
181
226
self ._assert_scores (xblock , [
182
- {"file" : downloadUrl , "score" : 1 , " submission" : create_submission_dict (
183
- {" answer" : submission },
227
+ {'score' : 1 , 'files' : file_download_url , ' submission' : create_submission_dict (
228
+ {' answer' : submission },
184
229
xblock .prompts
185
230
)}
186
231
])
@@ -209,7 +254,7 @@ def _create_submissions_and_scores(
209
254
# to anything without affecting the test.
210
255
student_item = xblock .get_student_item_dict ()
211
256
# adding rand number to the student_id to make it unique.
212
- student_item ['student_id' ] = " student {num} {num2}" .format (num = num , num2 = randint (2 , 1000 ))
257
+ student_item ['student_id' ] = ' student {num} {num2}' .format (num = num , num2 = randint (2 , 1000 ))
213
258
if submission_key is not None :
214
259
answer = {submission_key : submission }
215
260
else :
@@ -278,9 +323,9 @@ def _assert_path_and_context(self, xblock, expected_path, expected_context, work
278
323
279
324
# Strip query string parameters from the file URLs, since these are time-dependent
280
325
# (expiration and signature)
281
- if " topscores" in expected_context :
282
- context [" topscores" ] = self ._clean_score_filenames (context [" topscores" ])
283
- expected_context [" topscores" ] = self ._clean_score_filenames (context [ " topscores" ])
326
+ if ' topscores' in expected_context :
327
+ context [' topscores' ] = self ._clean_score_filenames (context [' topscores' ])
328
+ expected_context [' topscores' ] = self ._clean_score_filenames (expected_context [ ' topscores' ])
284
329
285
330
self .assertEqual (path , expected_path )
286
331
self .assertEqual (context , expected_context )
@@ -293,17 +338,22 @@ def _assert_leaderboard_visible(self, xblock, is_visible):
293
338
"""
294
339
Check that the leaderboard is displayed in the student view.
295
340
"""
296
- fragment = self .runtime .render (xblock , " student_view" )
341
+ fragment = self .runtime .render (xblock , ' student_view' )
297
342
has_leaderboard = 'step--leaderboard' in fragment .body_html ()
298
343
self .assertEqual (has_leaderboard , is_visible )
299
344
300
345
def _clean_score_filenames (self , scores ):
301
346
"""
302
347
Remove querystring parameters from the file name of the score.
303
348
"""
304
- for score in scores :
305
- if score .get ("file" ):
306
- url = urlparse (score ["file" ])
307
- score ["file" ] = url .scheme + "://" + url .netloc + url .path
349
+ def _clean_query_string (_file_url ):
350
+ url = urlparse (_file_url )
351
+ return url .scheme + '://' + url .netloc + url .path
308
352
353
+ for score in scores :
354
+ if score .get ('files' ):
355
+ score ['files' ] = [
356
+ (_clean_query_string (file_info [0 ]), file_info [1 ]) for file_info in score ['files' ]
357
+ ]
309
358
return scores
359
+
0 commit comments