Commit 282220da by Michael Roytman

refactor and add multiple filter functionality

parent d62e2498
......@@ -28,17 +28,22 @@ from util.json_request import JsonResponse
__all__ = ['assets_handler']
# pylint: disable=unused-argument
REQUEST_DEFAULTS = {
'page': 0,
'page_size': 50,
'sort': 'date_added',
'direction': '',
'asset_type': ''
}
@login_required
@ensure_csrf_cookie
def assets_handler(request, course_key_string=None, asset_key_string=None):
"""
'''
The restful handler for assets.
It allows retrieval of all the assets (as an HTML page), as well as uploading new assets,
deleting assets, and changing the "locked" state of an asset.
deleting assets, and changing the 'locked' state of an asset.
GET
html: return an html page which will show all course assets. Note that only the asset container
......@@ -46,38 +51,47 @@ def assets_handler(request, course_key_string=None, asset_key_string=None):
json: returns a page of assets. The following parameters are supported:
page: the desired page of results (defaults to 0)
page_size: the number of items per page (defaults to 50)
sort: the asset field to sort by (defaults to "date_added")
direction: the sort direction (defaults to "descending")
sort: the asset field to sort by (defaults to 'date_added')
direction: the sort direction (defaults to 'descending')
POST
json: create (or update?) an asset. The only updating that can be done is changing the lock state.
PUT
json: update the locked state of an asset
DELETE
json: delete an asset
"""
'''
course_key = CourseKey.from_string(course_key_string)
if not has_course_author_access(request.user, course_key):
raise PermissionDenied()
response_format = request.GET.get('format') or request.POST.get('format') or 'html'
if response_format == 'json' or 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'):
response_format = _get_response_format(request)
if _request_response_format_is_json(request, response_format):
if request.method == 'GET':
return _assets_json(request, course_key)
else:
asset_key = AssetKey.from_string(asset_key_string) if asset_key_string else None
return _update_asset(request, course_key, asset_key)
asset_key = AssetKey.from_string(asset_key_string) if asset_key_string else None
return _update_asset(request, course_key, asset_key)
elif request.method == 'GET': # assume html
return _asset_index(request, course_key)
else:
return HttpResponseNotFound()
return _asset_index(course_key)
return HttpResponseNotFound()
def _get_response_format(request):
return request.GET.get('format') or request.POST.get('format') or 'html'
def _request_response_format_is_json(request, response_format):
return response_format == 'json' or 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json')
def _asset_index(request, course_key):
"""
def _asset_index(course_key):
'''
Display an editable asset library.
Supports start (0-based index into the list of assets) and max query parameters.
"""
'''
course_module = modulestore().get_course(course_key)
return render_to_response('asset_index.html', {
......@@ -90,102 +104,202 @@ def _asset_index(request, course_key):
def _assets_json(request, course_key):
"""
'''
Display an editable asset library.
Supports start (0-based index into the list of assets) and max query parameters.
"""
requested_page = int(request.GET.get('page', 0))
requested_page_size = int(request.GET.get('page_size', 50))
requested_sort = request.GET.get('sort', 'date_added')
requested_filter = request.GET.get('asset_type', '')
requested_file_types = settings.FILES_AND_UPLOAD_TYPE_FILTERS.get(
requested_filter, None)
filter_params = None
if requested_filter:
if requested_filter == 'OTHER':
all_filters = settings.FILES_AND_UPLOAD_TYPE_FILTERS
where = []
for all_filter in all_filters:
extension_filters = all_filters[all_filter]
where.extend(
["JSON.stringify(this.contentType).toUpperCase() != JSON.stringify('{}').toUpperCase()".format(
extension_filter) for extension_filter in extension_filters])
filter_params = {
"$where": ' && '.join(where),
}
else:
where = ["JSON.stringify(this.contentType).toUpperCase() == JSON.stringify('{}').toUpperCase()".format(
req_filter) for req_filter in requested_file_types]
filter_params = {
"$where": ' || '.join(where),
}
'''
request_options = _parse_request_to_dictionary(request)
sort_direction = DESCENDING
if request.GET.get('direction', '').lower() == 'asc':
sort_direction = ASCENDING
filter_parameters = None
# Convert the field name to the Mongo name
if requested_sort == 'date_added':
requested_sort = 'uploadDate'
elif requested_sort == 'display_name':
requested_sort = 'displayname'
sort = [(requested_sort, sort_direction)]
if request_options['requested_asset_type']:
filters_are_invalid_error = _get_error_if_invalid_parameters(request_options['requested_asset_type'])
if filters_are_invalid_error is not None:
return filters_are_invalid_error
filter_parameters = _get_filter_parameters_for_mongo(request_options['requested_asset_type'])
current_page = max(requested_page, 0)
start = current_page * requested_page_size
options = {
sort_type_and_direction = _get_sort_type_and_direction(request_options)
requested_page_size = request_options['requested_page_size']
current_page = _get_current_page(request_options['requested_page'])
first_asset_to_display_index = _get_first_asset_index(current_page, requested_page_size)
query_options = {
'current_page': current_page,
'page_size': requested_page_size,
'sort': sort,
'filter_params': filter_params
'sort': sort_type_and_direction,
'filter_params': filter_parameters
}
assets, total_count = _get_assets_for_page(request, course_key, options)
end = start + len(assets)
# If the query is beyond the final page, then re-query the final page so
# that at least one asset is returned
if requested_page > 0 and start >= total_count:
options['current_page'] = current_page = int(math.floor((total_count - 1) / requested_page_size))
start = current_page * requested_page_size
assets, total_count = _get_assets_for_page(request, course_key, options)
end = start + len(assets)
asset_json = []
for asset in assets:
asset_location = asset['asset_key']
# note, due to the schema change we may not have a 'thumbnail_location'
# in the result set
thumbnail_location = asset.get('thumbnail_location', None)
if thumbnail_location:
thumbnail_location = course_key.make_asset_key(
'thumbnail', thumbnail_location[4])
asset_locked = asset.get('locked', False)
asset_json.append(_get_asset_json(
asset['displayname'],
asset['contentType'],
asset['uploadDate'],
asset_location,
thumbnail_location,
asset_locked
))
return JsonResponse({
'start': start,
'end': end,
assets, total_count = _get_assets_for_page(course_key, query_options)
if request_options['requested_page'] > 0 and first_asset_to_display_index >= total_count:
_update_options_to_requery_final_page(query_options, total_count)
current_page = query_options['current_page']
first_asset_to_display_index = _get_first_asset_index(current_page, requested_page_size)
assets, total_count = _get_assets_for_page(course_key, query_options)
last_asset_to_display_index = first_asset_to_display_index + len(assets)
assets_in_json_format = _get_assets_in_json_format(assets, course_key)
response_payload = {
'start': first_asset_to_display_index,
'end': last_asset_to_display_index,
'page': current_page,
'pageSize': requested_page_size,
'totalCount': total_count,
'assets': asset_json,
'sort': requested_sort,
})
'assets': assets_in_json_format,
'sort': request_options['requested_sort'],
}
return JsonResponse(response_payload)
def _parse_request_to_dictionary(request):
return {
'requested_page': int(_get_requested_attribute(request, 'page')),
'requested_page_size': int(_get_requested_attribute(request, 'page_size')),
'requested_sort': _get_requested_attribute(request, 'sort'),
'requested_sort_direction': _get_requested_attribute(request, 'direction'),
'requested_asset_type': _get_requested_attribute(request, 'asset_type')
}
def _get_requested_attribute(request, attribute):
return request.GET.get(attribute, REQUEST_DEFAULTS.get(attribute))
def _get_error_if_invalid_parameters(requested_filter):
requested_file_types = _get_requested_file_types_from_requested_filter(requested_filter)
invalid_filters = []
# OTHER is not described in the settings file as a filter
all_valid_file_types = set(_get_files_and_upload_type_filters().keys())
all_valid_file_types.add('OTHER')
for requested_file_type in requested_file_types:
if requested_file_type not in all_valid_file_types:
invalid_filters.append(requested_file_type)
if invalid_filters:
error_message = {
'error_code': 'invalid_asset_type_filter',
'developer_message': 'The asset_type parameter to the request is invalid. '
'The {} filters are not described in the settings.FILES_AND_UPLOAD_TYPE_FILTERS '
'dictionary.'.format(invalid_filters)
}
return JsonResponse({'error': error_message}, status=400)
def _get_filter_parameters_for_mongo(requested_filter):
requested_file_types = _get_requested_file_types_from_requested_filter(requested_filter)
mongo_where_operator_parameters = _get_mongo_where_operator_parameters_for_filters(requested_file_types)
return mongo_where_operator_parameters
def _get_mongo_where_operator_parameters_for_filters(requested_file_types):
javascript_filters = []
for requested_file_type in requested_file_types:
if requested_file_type == 'OTHER':
javascript_filters_for_file_types = _get_javascript_expressions_for_other_()
javascript_filters.append(javascript_filters_for_file_types)
else:
javascript_filters_for_file_types = _get_javascript_expressions_for_filter(requested_file_type)
javascript_filters.append(javascript_filters_for_file_types)
javascript_filters = _join_javascript_expressions_for_filters_with_separator(javascript_filters, '||')
return _format_javascript_filters_for_mongo_where(javascript_filters)
def _format_javascript_filters_for_mongo_where(javascript_filters):
return {
'$where': javascript_filters,
}
def _get_javascript_expressions_for_other_():
file_extensions_for_requested_file_types = _get_files_and_upload_type_filters().values()
file_extensions_for_requested_file_types_flattened = [extension for extensions in
file_extensions_for_requested_file_types for extension in
extensions]
javascript_expression_to_filter_extensions = _get_javascript_expressions_to_filter_extensions_with_operator(
file_extensions_for_requested_file_types_flattened, '!=')
joined_javascript_expressions_to_filter_extensions = _join_javascript_expressions_for_filters_with_separator(
javascript_expression_to_filter_extensions, ' && ')
return joined_javascript_expressions_to_filter_extensions
def _get_javascript_expressions_for_filter(requested_file_type):
file_extensions_for_requested_file_type = _get_extensions_for_file_type(requested_file_type)
javascript_expressions_to_filter_extensions = _get_javascript_expressions_to_filter_extensions_with_operator(
file_extensions_for_requested_file_type, '==')
joined_javascript_expressions_to_filter_extensions = _join_javascript_expressions_for_filters_with_separator(
javascript_expressions_to_filter_extensions, ' || ')
return joined_javascript_expressions_to_filter_extensions
def _get_files_and_upload_type_filters():
return settings.FILES_AND_UPLOAD_TYPE_FILTERS
def _get_requested_file_types_from_requested_filter(requested_filter):
return requested_filter.split(',')
def _get_extensions_for_file_type(requested_file_type):
return _get_files_and_upload_type_filters().get(requested_file_type)
def _get_javascript_expressions_to_filter_extensions_with_operator(file_extensions, operator):
return ["JSON.stringify(this.contentType).toUpperCase() " + operator + " JSON.stringify('{}').toUpperCase()".format(
file_extension) for file_extension in file_extensions]
def _join_javascript_expressions_for_filters_with_separator(javascript_expressions_for_filtering, separator):
return separator.join(javascript_expressions_for_filtering)
def _get_sort_type_and_direction(request_options):
sort_type = _get_mongo_sort_from_requested_sort(request_options['requested_sort'])
sort_direction = _get_sort_direction_from_requested_sort(request_options['requested_sort_direction'])
return [(sort_type, sort_direction)]
def _get_mongo_sort_from_requested_sort(requested_sort):
if requested_sort == 'date_added':
sort = 'uploadDate'
elif requested_sort == 'display_name':
sort = 'displayname'
else:
sort = requested_sort
return sort
def _get_sort_direction_from_requested_sort(requested_sort_direction):
if requested_sort_direction.lower() == 'asc':
return ASCENDING
return DESCENDING
def _get_assets_for_page(request, course_key, options):
"""
Returns the list of assets for the specified page and page size.
"""
def _get_current_page(requested_page):
return max(requested_page, 0)
def _get_first_asset_index(current_page, page_size):
return current_page * page_size
def _get_assets_for_page(course_key, options):
current_page = options['current_page']
page_size = options['page_size']
sort = options['sort']
......@@ -197,50 +311,54 @@ def _get_assets_for_page(request, course_key, options):
)
def get_file_size(upload_file):
"""
Helper method for getting file size of an upload file.
Can be used for mocking test file sizes.
"""
return upload_file.size
def _update_options_to_requery_final_page(query_options, total_asset_count):
query_options['current_page'] = int(math.floor((total_asset_count - 1) / query_options['page_size']))
def update_course_run_asset(course_key, upload_file):
filename = upload_file.name
mime_type = upload_file.content_type
size = get_file_size(upload_file)
max_size_in_mb = settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
max_file_size_in_bytes = max_size_in_mb * 1000 ** 2
if size > max_file_size_in_bytes:
msg = 'File {filename} exceeds the maximum size of {max_size_in_mb} MB.'.format(
filename=filename,
max_size_in_mb=max_size_in_mb
def _get_assets_in_json_format(assets, course_key):
assets_in_json_format = []
for asset in assets:
thumbnail_asset_key = _get_thumbnail_asset_key(asset, course_key)
asset_is_locked = asset.get('locked', False)
asset_in_json = _get_asset_json(
asset['displayname'],
asset['contentType'],
asset['uploadDate'],
asset['asset_key'],
thumbnail_asset_key,
asset_is_locked
)
raise AssetSizeTooLargeException(msg)
content_loc = StaticContent.compute_location(course_key, filename)
assets_in_json_format.append(asset_in_json)
chunked = upload_file.multiple_chunks()
sc_partial = partial(StaticContent, content_loc, filename, mime_type)
if chunked:
content = sc_partial(upload_file.chunks())
tempfile_path = upload_file.temporary_file_path()
else:
content = sc_partial(upload_file.read())
tempfile_path = None
return assets_in_json_format
def update_course_run_asset(course_key, upload_file):
course_exists_response = _get_error_if_course_does_not_exist(course_key)
if course_exists_response is not None:
return course_exists_response
file_metadata = _get_file_metadata_as_dictionary(upload_file)
is_file_too_large = _check_file_size_is_too_large(file_metadata)
if is_file_too_large:
error_message = _get_file_too_large_error_message(file_metadata['filename'])
raise AssetSizeTooLargeException(error_message)
# Verify a thumbnail can be created
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(content, tempfile_path=tempfile_path)
content, temporary_file_path = _get_file_content_and_path(file_metadata, course_key)
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(content,
tempfile_path=temporary_file_path)
# delete cached thumbnail even if one couldn't be created this time (else the old thumbnail will continue to show)
del_cached_content(thumbnail_location)
# now store thumbnail location only if we could create it
if thumbnail_content is not None:
if _check_thumbnail_uploaded(thumbnail_content):
content.thumbnail_location = thumbnail_location
# then commit the content
contentstore().save(content)
del_cached_content(content.location)
......@@ -251,18 +369,10 @@ def update_course_run_asset(course_key, upload_file):
@ensure_csrf_cookie
@login_required
def _upload_asset(request, course_key):
"""
This method allows for POST uploading of files into the course asset
library, which will be supported by GridFS in MongoDB.
"""
# Does the course actually exist?!? Get anything from it to prove its
# existence
try:
modulestore().get_course(course_key)
except ItemNotFoundError:
# no return it as a Bad Request response
logging.error("Could not find course: %s", course_key)
return HttpResponseBadRequest()
course_exists_error = _get_error_if_course_does_not_exist(course_key)
if course_exists_error is not None:
return course_exists_error
# compute a 'filename' which is similar to the location formatting, we're
# using the 'filename' nomenclature since we're using a FileSystem paradigm
......@@ -272,8 +382,8 @@ def _upload_asset(request, course_key):
try:
content = update_course_run_asset(course_key, upload_file)
except AssetSizeTooLargeException as ex:
return JsonResponse({'error': ex.message}, status=413)
except AssetSizeTooLargeException as exception:
return JsonResponse({'error': exception.message}, status=413)
# readback the saved content - we need the database timestamp
readback = contentstore().find(content.location)
......@@ -291,16 +401,93 @@ def _upload_asset(request, course_key):
})
@require_http_methods(("DELETE", "POST", "PUT"))
def _get_error_if_course_does_not_exist(course_key):
try:
modulestore().get_course(course_key)
except ItemNotFoundError:
logging.error('Could not find course: %s', course_key)
return HttpResponseBadRequest()
def _get_file_metadata_as_dictionary(upload_file):
# compute a 'filename' which is similar to the location formatting; we're
# using the 'filename' nomenclature since we're using a FileSystem paradigm
# here; we're just imposing the Location string formatting expectations to
# keep things a bit more consistent
return {
'upload_file': upload_file,
'filename': upload_file.name,
'mime_type': upload_file.content_type,
'upload_file_size': get_file_size(upload_file)
}
def get_file_size(upload_file):
# can be used for mocking test file sizes.
return upload_file.size
def _check_file_size_is_too_large(file_metadata):
upload_file_size = file_metadata['upload_file_size']
maximum_file_size_in_megabytes = settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
maximum_file_size_in_bytes = maximum_file_size_in_megabytes * 1000 ** 2
return upload_file_size > maximum_file_size_in_bytes
def _get_file_too_large_error_message(filename):
return _(
'File {filename} exceeds maximum size of '
'{maximum_size_in_megabytes} MB.'
).format(
filename=filename,
maximum_size_in_megabytes=settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB,
)
def _get_file_content_and_path(file_metadata, course_key):
content_location = StaticContent.compute_location(course_key, file_metadata['filename'])
upload_file = file_metadata['upload_file']
file_can_be_chunked = upload_file.multiple_chunks()
static_content_partial = partial(StaticContent, content_location, file_metadata['filename'],
file_metadata['mime_type'])
if file_can_be_chunked:
content = static_content_partial(upload_file.chunks())
temporary_file_path = upload_file.temporary_file_path()
else:
content = static_content_partial(upload_file.read())
temporary_file_path = None
return content, temporary_file_path
def _check_thumbnail_uploaded(thumbnail_content):
return thumbnail_content is not None
def _get_thumbnail_asset_key(asset, course_key):
# note, due to the schema change we may not have a 'thumbnail_location' in the result set
thumbnail_location = asset.get('thumbnail_location', None)
thumbnail_asset_key = None
if thumbnail_location:
thumbnail_path = thumbnail_location[4]
thumbnail_asset_key = course_key.make_asset_key('thumbnail', thumbnail_path)
return thumbnail_asset_key
@require_http_methods(('DELETE', 'POST', 'PUT'))
@login_required
@ensure_csrf_cookie
def _update_asset(request, course_key, asset_key):
"""
'''
restful CRUD operations for a course asset.
Currently only DELETE, POST, and PUT methods are implemented.
asset_path_encoding: the odd /c4x/org/course/category/name repr of the asset (used by Backbone as the id)
"""
'''
if request.method == 'DELETE':
try:
delete_asset(course_key, asset_key)
......@@ -311,56 +498,60 @@ def _update_asset(request, course_key, asset_key):
elif request.method in ('PUT', 'POST'):
if 'file' in request.FILES:
return _upload_asset(request, course_key)
else:
# Update existing asset
try:
modified_asset = json.loads(request.body)
except ValueError:
return HttpResponseBadRequest()
contentstore().set_attr(asset_key, 'locked', modified_asset['locked'])
# Delete the asset from the cache so we check the lock status the next time it is requested.
del_cached_content(asset_key)
return JsonResponse(modified_asset, status=201)
# update existing asset
try:
modified_asset = json.loads(request.body)
except ValueError:
return HttpResponseBadRequest()
contentstore().set_attr(asset_key, 'locked', modified_asset['locked'])
# delete the asset from the cache so we check the lock status the next time it is requested.
del_cached_content(asset_key)
return JsonResponse(modified_asset, status=201)
def _save_content_to_trash(content):
contentstore('trashcan').save(content)
def delete_asset(course_key, asset_key):
"""
Deletes asset represented by given 'asset_key' in the course represented by given course_key.
"""
# Make sure the item to delete actually exists.
content = _check_existence_and_get_asset_content(asset_key)
_save_content_to_trash(content)
_delete_thumbnail(content.thumbnail_location, course_key, asset_key)
contentstore().delete(content.get_id())
del_cached_content(content.location)
def _check_existence_and_get_asset_content(asset_key):
try:
content = contentstore().find(asset_key)
return content
except NotFoundError:
raise AssetNotFoundException
# ok, save the content into the trashcan
contentstore('trashcan').save(content)
# see if there is a thumbnail as well, if so move that as well
if content.thumbnail_location is not None:
def _delete_thumbnail(thumbnail_location, course_key, asset_key):
if thumbnail_location is not None:
# We are ignoring the value of the thumbnail_location-- we only care whether
# or not a thumbnail has been stored, and we can now easily create the correct path.
thumbnail_location = course_key.make_asset_key('thumbnail', asset_key.name)
try:
thumbnail_content = contentstore().find(thumbnail_location)
contentstore('trashcan').save(thumbnail_content)
# hard delete thumbnail from origin
_save_content_to_trash(thumbnail_content)
contentstore().delete(thumbnail_content.get_id())
# remove from any caching
del_cached_content(thumbnail_location)
except Exception: # pylint: disable=broad-except
logging.warning('Could not delete thumbnail: %s', thumbnail_location)
# delete the original
contentstore().delete(content.get_id())
# remove from cache
del_cached_content(content.location)
def _get_asset_json(display_name, content_type, date, location, thumbnail_location, locked):
"""
'''
Helper method for formatting the asset information to send to client.
"""
'''
asset_url = StaticContent.serialize_asset_key_with_slash(location)
external_url = settings.LMS_BASE + asset_url
return {
......@@ -372,6 +563,6 @@ def _get_asset_json(display_name, content_type, date, location, thumbnail_locati
'portable_url': StaticContent.get_static_path_from_location(location),
'thumbnail': StaticContent.serialize_asset_key_with_slash(thumbnail_location) if thumbnail_location else None,
'locked': locked,
# Needed for Backbone delete/update.
# needed for Backbone delete/update.
'id': unicode(location)
}
......@@ -174,6 +174,15 @@ class PaginationTestCase(AssetsTestCase):
self.assert_correct_filter_response(self.url, 'asset_type', 'OTHER')
self.assert_correct_filter_response(
self.url, 'asset_type', 'Documents')
self.assert_correct_filter_response(
self.url, 'asset_type', 'Documents,Images')
self.assert_correct_filter_response(
self.url, 'asset_type', 'Documents,OTHER')
#Verify invalid request parameters
self.assert_invalid_parameters_error(self.url, 'asset_type', 'edX')
self.assert_invalid_parameters_error(self.url, 'asset_type', 'edX, OTHER')
self.assert_invalid_parameters_error(self.url, 'asset_type', 'edX, Images')
# Verify querying outside the range of valid pages
self.assert_correct_asset_response(
......@@ -247,24 +256,46 @@ class PaginationTestCase(AssetsTestCase):
"""
Get from the url w/ a filter option and ensure items honor that filter
"""
requested_file_types = settings.FILES_AND_UPLOAD_TYPE_FILTERS.get(
filter_value, None)
filter_value_split = filter_value.split(',')
requested_file_extensions = []
all_file_extensions = []
for requested_filter in filter_value_split:
if requested_filter == 'OTHER':
for file_type in settings.FILES_AND_UPLOAD_TYPE_FILTERS:
all_file_extensions.extend(file_type)
else:
file_extensions = settings.FILES_AND_UPLOAD_TYPE_FILTERS.get(
requested_filter, None)
if file_extensions is not None:
requested_file_extensions.extend(file_extensions)
resp = self.client.get(
url + '?' + filter_type + '=' + filter_value, HTTP_ACCEPT='application/json')
json_response = json.loads(resp.content)
assets_response = json_response['assets']
if filter_value is not '':
content_types = [asset['content_type'].lower()
for asset in assets_response]
if filter_value is 'OTHER':
all_file_type_extensions = []
for file_type in settings.FILES_AND_UPLOAD_TYPE_FILTERS:
all_file_type_extensions.extend(file_type)
if 'OTHER' in filter_value_split:
for content_type in content_types:
self.assertNotIn(content_type, all_file_type_extensions)
# content_type is either not any defined type (i.e. OTHER) or is a defined type (if multiple
# parameters including OTHER are used)
self.assertTrue(content_type in requested_file_extensions or content_type not in all_file_extensions)
else:
for content_type in content_types:
self.assertIn(content_type, requested_file_types)
self.assertIn(content_type, requested_file_extensions)
def assert_invalid_parameters_error(self, url, filter_type, filter_value):
"""
Get from the url w/ invalid filter option(s) and ensure error is received
"""
resp = self.client.get(
url + '?' + filter_type + '=' + filter_value, HTTP_ACCEPT='application/json')
self.assertEquals(resp.status_code, 400)
@ddt
......
......@@ -1257,7 +1257,8 @@ ADVANCED_PROBLEM_TYPES = [
# Files and Uploads type filter values
FILES_AND_UPLOAD_TYPE_FILTERS = {
"Images": ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/tiff', 'image/tif', 'image/x-icon'],
"Images": ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/tiff', 'image/tif', 'image/x-icon',
'image/svg+xml', 'image/bmp', 'image/x-ms-bmp', ],
"Documents": [
'application/pdf',
'text/plain',
......@@ -1271,7 +1272,25 @@ FILES_AND_UPLOAD_TYPE_FILTERS = {
'application/msword',
'application/vnd.ms-excel',
'application/vnd.ms-powerpoint',
'application/csv',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'text/x-tex',
'application/x-pdf',
'application/vnd.ms-excel.sheet.macroenabled.12',
'file/pdf',
'image/pdf',
'text/csv',
'text/pdf',
'text/x-sh',
'\application/pdf\""',
],
"Audio": ['audio/mpeg', 'audio/mp3', 'audio/x-wav', 'audio/ogg', 'audio/wav', 'audio/aac', 'audio/x-m4a',
'audio/mp4', 'audio/x-ms-wma', ],
"Code": ['application/json', 'text/html', 'text/javascript', 'application/javascript', 'text/css', 'text/x-python',
'application/x-java-jnlp-file', 'application/xml', 'application/postscript', 'application/x-javascript',
'application/java-vm', 'text/x-c++src', 'text/xml', 'text/x-scss', 'application/x-python-code',
'application/java-archive', 'text/x-python-script', 'application/x-ruby', 'application/mathematica',
'text/coffeescript', 'text/x-matlab', 'application/sql', 'text/php', ]
}
# Default to no Search Engine
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment