Commit cc6716a4 by swdanielli

Merge branch 'staff_manipulation'

Conflicts:
	recommender/static/js/src/cats.js
parents e0a6aa9b c25566ab
...@@ -16,25 +16,22 @@ from mako.template import Template ...@@ -16,25 +16,22 @@ from mako.template import Template
from mako.lookup import TemplateLookup from mako.lookup import TemplateLookup
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, Integer, String, BlockScope, List from xblock.fields import Scope, Integer, String, BlockScope, List, Dict
from xblock.fragment import Fragment from xblock.fragment import Fragment
from fs.s3fs import S3FS from fs.s3fs import S3FS
from webob.response import Response from webob.response import Response
aws_access_key_env='AKIAIRDHSV6YZJZ4RFGA'
aws_secret_key_env='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'
bucketName='danielswli'
uploadedFileDir = 'uploads/'
class RecommenderXBlock(XBlock): class RecommenderXBlock(XBlock):
""" """
This XBlock will show a set of recommended resources provided by students. This XBlock will show a set of recommended resources provided by students.
""" """
# Scope-wide. List of JSON objects corresponding to recommendations combine XML and user. # Scope-wide. List of JSON objects corresponding to recommendations combine XML and user.
default_recommendations = List(help="List of help resources", default=[], scope=Scope.content) default_recommendations = List(help="List of help resources", default=[], scope=Scope.content)
# Scope-wide. List of JSON objects corresponding to recommendations as defined in XML. # Block-wide. List of JSON objects corresponding to recommendations as defined in XML.
recommendations = List(help="List of help resources", default=[], scope=Scope.user_state_summary) recommendations = List(help="List of help resources", default=[], scope=Scope.user_state_summary)
# Block-wide. Dict of JSON objects corresponding to configurations of amazon web service for file uploading.
s3_configuration = Dict(help="List of help resources", default={}, scope=Scope.user_state_summary)
# List of deleted recommendation ID. # List of deleted recommendation ID.
deletedRecommendationIds = List(help="List of help resources", default=[], scope=Scope.user_state_summary) deletedRecommendationIds = List(help="List of help resources", default=[], scope=Scope.user_state_summary)
# Ids of upvoted resources for this particular user # Ids of upvoted resources for this particular user
...@@ -217,10 +214,17 @@ class RecommenderXBlock(XBlock): ...@@ -217,10 +214,17 @@ class RecommenderXBlock(XBlock):
response: HTTP response response: HTTP response
response.body (response.responseText): name of the uploaded file response.body (response.responseText): name of the uploaded file
Env variables: Env variables:
aws_access_key_env: s3 access key aws_access_key: s3 access key
aws_secret_key_env: s3 secret key aws_secret_key: s3 secret key
bucket: name of the s3 bucket bucket: name of the s3 bucket
""" """
if self.s3_configuration == {}:
tracker.emit('upload_screenshot', {'uploadedFileName': 'IMPROPER_S3_SETUP'})
response = Response()
response.body = 'IMPROPER_S3_SETUP'
response.headers['Content-Type'] = 'text/plain'
return response
chars=string.ascii_uppercase + string.digits chars=string.ascii_uppercase + string.digits
fileNameLength=11 fileNameLength=11
...@@ -235,21 +239,34 @@ class RecommenderXBlock(XBlock): ...@@ -235,21 +239,34 @@ class RecommenderXBlock(XBlock):
response.body = 'FILETYPEERROR' response.body = 'FILETYPEERROR'
response.headers['Content-Type'] = 'text/plain' response.headers['Content-Type'] = 'text/plain'
return response return response
S3FS_handler = S3FS(bucketName, aws_access_key=aws_access_key_env, aws_secret_key=aws_secret_key_env)
while True:
fileId = ''.join(random.choice(chars) for _ in range(fileNameLength))
fileName = uploadedFileDir + fileId + fileType
if not S3FS_handler.exists(fileName):
break
content = request.POST['file'].file.read()
fhwrite = S3FS_handler.open(fileName, 'wb')
fhwrite.write(content)
fhwrite.close()
S3FS_handler.makepublic(fileName)
try:
fileId = ''
fileName = ''
S3FS_handler = S3FS(self.s3_configuration['bucketName'], aws_access_key=self.s3_configuration['aws_access_key'], aws_secret_key=self.s3_configuration['aws_secret_key'])
while True:
fileId = ''.join(random.choice(chars) for _ in range(fileNameLength))
fileName = str(self.s3_configuration['uploadedFileDir']) + fileId + fileType
if not S3FS_handler.exists(fileName):
break
dirUrl = S3FS_handler.getpathurl("/")
content = request.POST['file'].file.read()
fhwrite = S3FS_handler.open(fileName, 'wb')
fhwrite.write(content)
fhwrite.close()
S3FS_handler.makepublic(fileName)
except:
tracker.emit('upload_screenshot', {'uploadedFileName': 'IMPROPER_S3_SETUP'})
response = Response()
response.body = 'IMPROPER_S3_SETUP'
response.headers['Content-Type'] = 'text/plain'
return response
response = Response() response = Response()
response.body = fileName if self.s3_configuration['uploadedFileDir'] == "/":
response.body = str(dirUrl + fileId + fileType)
else:
response.body = str(dirUrl + self.s3_configuration['uploadedFileDir'] + fileId + fileType)
response.headers['Content-Type'] = 'text/plain' response.headers['Content-Type'] = 'text/plain'
tracker.emit('upload_screenshot', {'uploadedFileName': fileName}) tracker.emit('upload_screenshot', {'uploadedFileName': fileName})
return response return response
...@@ -391,7 +408,30 @@ class RecommenderXBlock(XBlock): ...@@ -391,7 +408,30 @@ class RecommenderXBlock(XBlock):
""" """
return {'is_user_staff': self.xmodule_runtime.user_is_staff} return {'is_user_staff': self.xmodule_runtime.user_is_staff}
# TO-DO: change this view to display your data your own way. @XBlock.json_handler
def set_s3_info(self, data, suffix=''):
"""
Set required information of amazon web service for file uploading.
Args:
data: dict in JSON format
data['aws_access_key']: Amazon Web Services access key
data['aws_secret_key']: Amazon Web Services secret key
data['bucketName']: Bucket name of your Amazon Web Services
data['uploadedFileDir']: Directory for your upload files
Returns:
result['Success']: the boolean indicator for whether the setting is complete
"""
print
self.s3_configuration['aws_access_key'] = data['aws_access_key']
self.s3_configuration['aws_secret_key'] = data['aws_secret_key']
self.s3_configuration['bucketName'] = data['bucketName']
if data['uploadedFileDir'][len(data['uploadedFileDir'])-1] == '/':
self.s3_configuration['uploadedFileDir'] = data['uploadedFileDir']
else:
self.s3_configuration['uploadedFileDir'] = data['uploadedFileDir'] + '/'
return {'Success': True}
def student_view(self, context=None): def student_view(self, context=None):
""" """
The primary view of the RecommenderXBlock, shown to students The primary view of the RecommenderXBlock, shown to students
......
...@@ -164,7 +164,7 @@ ...@@ -164,7 +164,7 @@
.resource_add_button { text-align: right; font-weight: bold; } .resource_add_button { text-align: right; font-weight: bold; }
.resource_edit_button { float: left; } .resource_edit_button { float: left; }
.addSourceBlockTitle, .editSourceBlockTitle, .flagSourceBlockTitle { .addSourceBlockTitle, .editSourceBlockTitle, .flagSourceBlockTitle, .staffEditionBlockTitle {
margin-bottom: 1em; margin-bottom: 1em;
} }
...@@ -194,12 +194,22 @@ ...@@ -194,12 +194,22 @@
margin-left: 0.5em; margin-left: 0.5em;
width:100%; width:100%;
} }
.in_url, .edit_url, .flag_reason { .in_url, .edit_url, .flag_reason, .aws_access_key, .aws_secret_key, .bucketName, .uploadedFileUrl,.uploadedFileDir {
height: 25px; height: 25px;
margin-left: 0.5em; margin-left: 0.5em;
width:100%; width:100%;
} }
.submit_s3_info { margin-top: 0.5em; margin-bottom: 0.5em; }
.division_line {
margin-top: 1em;
margin-bottom: 1em;
border-bottom-width: thin;
border-bottom-style: inset;
border-bottom-color: gray;
}
.resource_hovered { background-color:#F2F7FA } .resource_hovered { background-color:#F2F7FA }
.recommender_modify_title { float: right; padding-right: 2em; overflow: auto; } .recommender_modify_title { float: right; padding-right: 2em; overflow: auto; }
......
...@@ -77,11 +77,7 @@ ...@@ -77,11 +77,7 @@
<div class='recommender_modify'> <div class='recommender_modify'>
<div class='recommender_modify_title_container'><div class='backToViewButton'>&lt; Related resources</div><div class='recommender_modify_title'></div></div> <div class='recommender_modify_title_container'><div class='backToViewButton'>&lt; Related resources</div><div class='recommender_modify_title'></div></div>
<div class="staffEditionBlock"> <div class="staffEditionBlock">
<div class="staffEditionBlockTitle">Delete, endorse or de-endorse a resource.</div> <div class="staffEditionBlockTitle">Provide your s3 information for file uploading; delete, endorse or de-endorse a resource.</div>
<input type="button" value="Delete resource" class="delete_resource">
<input type="button" value="Endorse resource" class="endorse_resource hidden">
<input type="button" value="De-endorse resource" class="deendorse_resource hidden">
<!-- TODO: endorsement and de-endorsement -->
</div> </div>
<div class="flagSourceBlock"> <div class="flagSourceBlock">
<div class="flagSourceBlockTitle">Why would you like to flag this resource? The staff will review all flagged resources, and remove inappropriate ones (spam, incorrect, abusive, etc.). Giving a clear reason will help us do this efficiently. </div> <div class="flagSourceBlockTitle">Why would you like to flag this resource? The staff will review all flagged resources, and remove inappropriate ones (spam, incorrect, abusive, etc.). Giving a clear reason will help us do this efficiently. </div>
......
var tooltipsCats = [ var tooltipsCats = [
'.resource_add_button', '.resource_add_button',
'.resource_edit_button', '.resource_edit_button',
'.recommender_vote_arrow_up', '.recommender_vote_arrow_up',
'.recommender_vote_arrow_down', '.recommender_vote_arrow_down',
...@@ -19,7 +19,7 @@ var tooltipsCats = [ ...@@ -19,7 +19,7 @@ var tooltipsCats = [
]; ];
var tooltipsCatsText = { var tooltipsCatsText = {
'.resource_add_button': 'Recommend a new resource which may be helpful to other students solving this problem', '.resource_add_button': 'Recommend a new resource which may be helpful to other students solving this problem',
'.resource_edit_button': 'Edit this resource', '.resource_edit_button': 'Edit this resource',
'.recommender_vote_arrow_up': 'Upvote if the resource is helpful', '.recommender_vote_arrow_up': 'Upvote if the resource is helpful',
'.recommender_vote_arrow_down': 'Downvote if the resource is not helpful', '.recommender_vote_arrow_down': 'Downvote if the resource is not helpful',
...@@ -35,5 +35,40 @@ var tooltipsCatsText = { ...@@ -35,5 +35,40 @@ var tooltipsCatsText = {
'.flagResource': 'Flag this resource as problematic', '.flagResource': 'Flag this resource as problematic',
'.flagResource.problematic': 'Unflag this problematic resource or edit the reason for it', '.flagResource.problematic': 'Unflag this problematic resource or edit the reason for it',
'.recommender_row_top': 'Show a list of student-recommented related resources', '.recommender_row_top': 'Show a list of student-recommented related resources',
'.recommender_row_top.resource_list_expanded': 'Hide the recommendations list' '.recommender_row_top.resource_list_expanded': 'Hide the recommendations list'
}; };
\ No newline at end of file
var staff_edit_buttons = [
"submit_s3_info",
'delete_resource'/*,
"endorse_resource",
"deendorse_resource"*/
]
var staff_edit_buttons_text = {
'delete_resource': "Delete resource",
"endorse_resource": "Endorse resource",
"deendorse_resource": "De-endorse resource",
"submit_s3_info": "Submit information for Amazon Web Services S3"
}
var staff_edit_textareas = [
'aws_access_key',
'aws_secret_key',
'bucketName',
'uploadedFileDir'
]
var staff_edit_textareas_text = {
'aws_access_key': 'Amazon Web Services access key',
'aws_secret_key': 'Amazon Web Services secret key',
'bucketName': 'Bucket name of your Amazon Web Services',
'uploadedFileDir': 'Directory for your upload files'
}
var staff_edit_textareas_placeholder = {
'aws_access_key': '',
'aws_secret_key': '',
'bucketName': 'danielswli',
'uploadedFileDir': 'uploads/'
}
...@@ -14,9 +14,9 @@ function RecommenderXBlock(runtime, element) { ...@@ -14,9 +14,9 @@ function RecommenderXBlock(runtime, element) {
var uploadScreenshotUrl = runtime.handlerUrl(element, 'upload_screenshot'); var uploadScreenshotUrl = runtime.handlerUrl(element, 'upload_screenshot');
var isUserStaffUrl = runtime.handlerUrl(element, 'is_user_staff'); var isUserStaffUrl = runtime.handlerUrl(element, 'is_user_staff');
var deleteResourceUrl = runtime.handlerUrl(element, 'delete_resource'); var deleteResourceUrl = runtime.handlerUrl(element, 'delete_resource');
var setS3InfoUrl = runtime.handlerUrl(element, 'set_s3_info');
/* Parameters for resource display */ /* Parameters for resource display */
var baseUrl = 'http://s3-us-west-2.amazonaws.com/danielswli/';
var currentPage = 1; var currentPage = 1;
var entriesPerPage = 5; var entriesPerPage = 5;
var pageSpan = 2; var pageSpan = 2;
...@@ -158,9 +158,8 @@ function RecommenderXBlock(runtime, element) { ...@@ -158,9 +158,8 @@ function RecommenderXBlock(runtime, element) {
/* Initialize resource addition mode */ /* Initialize resource addition mode */
function addResourceReset() { function addResourceReset() {
$('.in_title').val(''); $('.recommender_add').find('input[type="text"]').val('');
$('.in_url').val(''); $('.recommender_add').find('textarea').val('')
$('.in_descriptionText').val('');
$('#addResourceForm').find("input[name='file']").val(''); $('#addResourceForm').find("input[name='file']").val('');
$('.add_submit').attr('disabled', true); $('.add_submit').attr('disabled', true);
} }
...@@ -215,9 +214,13 @@ function RecommenderXBlock(runtime, element) { ...@@ -215,9 +214,13 @@ function RecommenderXBlock(runtime, element) {
alert('Please upload an image'); alert('Please upload an image');
$(formDiv).find("input[name='file']").val(''); $(formDiv).find("input[name='file']").val('');
} }
else if (result.responseText == 'IMPROPER_S3_SETUP'){
alert('The configuration of Amazon Web Services is not properly set');
$(formDiv).find("input[name='file']").val('');
}
else { else {
/* Submit the new resource */ /* Submit the new resource */
data['description'] = baseUrl + result.responseText; data['description'] = result.responseText;
addResource(data); addResource(data);
} }
}, },
...@@ -484,9 +487,13 @@ function RecommenderXBlock(runtime, element) { ...@@ -484,9 +487,13 @@ function RecommenderXBlock(runtime, element) {
alert('Please upload an image'); alert('Please upload an image');
$(formDiv).find("input[name='file']").val(''); $(formDiv).find("input[name='file']").val('');
} }
else if (result.responseText == 'IMPROPER_S3_SETUP'){
alert('The configuration of Amazon Web Services is not properly set');
$(formDiv).find("input[name='file']").val('');
}
else { else {
/* Submit the edited resource */ /* Submit the edited resource */
data['description'] = baseUrl + result.responseText; data['description'] = result.responseText;
editResource(data); editResource(data);
} }
}, },
...@@ -633,6 +640,52 @@ function RecommenderXBlock(runtime, element) { ...@@ -633,6 +640,52 @@ function RecommenderXBlock(runtime, element) {
if (result['is_user_staff']) { if (result['is_user_staff']) {
/* Add the button for entering staff-edit mode */ /* Add the button for entering staff-edit mode */
$('.recommender_edit').append('<span class="ui-icon ui-icon-gear staffEdition"></span>'); $('.recommender_edit').append('<span class="ui-icon ui-icon-gear staffEdition"></span>');
/* Add buttons in the staff-edit mode */
staff_edit_textareas.forEach(function(ele, ind) {
$('.staffEditionBlock').append('<div>' + staff_edit_textareas_text[ele] + '</div>')
.append('<input type="text" class="' + ele + '" placeholder="' + staff_edit_textareas_placeholder[ele] + '"/><br/>');
});
staff_edit_buttons.forEach(function(ele, ind) {
$('.staffEditionBlock').append('<input type="button" value="' + staff_edit_buttons_text[ele] + '" class="' + ele + '">');
if (ind == 0) {
$('.' + ele).attr('disabled', true);
$('.staffEditionBlock').append('<div class="division_line"></div>');
}
});
/* Check whether enough information is provided for S3, if yes, enable summission button */
function enableS3Submit(divPtr) {
var emptyFlag = false;
staff_edit_textareas.forEach(function(ele, ind) {
if ($('.' + ele).val() == '') {
$('.submit_s3_info').attr('disabled', true);
emptyFlag = true;
return;
}
});
if (!emptyFlag) { $('.submit_s3_info').attr('disabled', false); }
}
/* If the input (text) area is changed, check whether staff provides enough information for S3 */
staff_edit_textareas.forEach(function(ele, ind) {
$('.' + ele).bind('input propertychange', function() { enableS3Submit(); });
});
/* Submit the information for S3; this action is independent of selected resource */
$('.submit_s3_info').click(function() {
var data = {};
staff_edit_textareas.forEach(function(ele, ind) {
data[ele] = $('.' + ele).val();
});
$.ajax({
type: "POST",
url: setS3InfoUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success']) { backToView(); }
else { alert('Submission of S3 information is failed'); }
}
});
});
/* Enter staff-edit mode */ /* Enter staff-edit mode */
$('.staffEdition').click(function() { $('.staffEdition').click(function() {
...@@ -640,6 +693,7 @@ function RecommenderXBlock(runtime, element) { ...@@ -640,6 +693,7 @@ function RecommenderXBlock(runtime, element) {
$('.recommender_content').hide(); $('.recommender_content').hide();
$('.recommender_modify').show(); $('.recommender_modify').show();
$('.recommender_modify_title').text('Staff manipulation'); $('.recommender_modify_title').text('Staff manipulation');
$('.staffEditionBlock').find('input[type="text"]').val('');
var data = {}; var data = {};
data['id'] = parseInt($(this).parent().parent().find('.recommender_entryId').text()); data['id'] = parseInt($(this).parent().parent().find('.recommender_entryId').text());
......
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