Commit 17765d8c by swdanielli

server side event, some code clean up and corner case handling

parent 087e0074
......@@ -3,6 +3,9 @@
import json, string, random, re
import pkg_resources
from eventtracking import tracker
from mako.template import Template
from mako.lookup import TemplateLookup
......@@ -102,9 +105,11 @@ class RecommenderXBlock(XBlock):
''' untested '''
resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations)
oldVotes = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resource in self.upvotes:
del self.upvotes[self.upvotes.index(resource)]
self.recommendations[idx]['upvotes'] -= 1
tracker.emit('recommender_upvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Upvote unclicked!"
return {"Success": True}
......@@ -113,6 +118,7 @@ class RecommenderXBlock(XBlock):
self.recommendations[idx]['downvotes'] -= 1
self.upvotes.append(resource)
self.recommendations[idx]['upvotes'] += 1
tracker.emit('recommender_upvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Upvote clicked!"
return {"Success": True}
......@@ -121,9 +127,11 @@ class RecommenderXBlock(XBlock):
''' untested '''
resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations)
oldVotes = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resource in self.downvotes:
del self.downvotes[self.downvotes.index(resource)]
self.recommendations[idx]['downvotes'] -= 1
tracker.emit('recommender_downvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Downvote unclicked!"
return {"Success": True}
......@@ -132,6 +140,7 @@ class RecommenderXBlock(XBlock):
self.recommendations[idx]['upvotes'] -= 1
self.downvotes.append(resource)
self.recommendations[idx]['downvotes'] += 1
tracker.emit('recommender_downvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Downvote clicked!"
return {"Success": True}
......@@ -146,8 +155,13 @@ class RecommenderXBlock(XBlock):
elif re.search('.jpg$', str(request.POST['file'].file)):
filetype = '.jpg'
else:
tracker.emit('upload_screenshot', {'uploadedFileName': 'FILETYPEERROR'})
print "Wrong file type"
return {"Success": False}
response = Response()
response.body = 'FILETYPEERROR'
response.headers['Content-Type'] = 'text/plain'
return response
myS3FS = S3FS('danielswli', aws_access_key=aws_access_key_env, aws_secret_key=aws_secret_key_env)
while True:
file_id = ''.join(random.choice(chars) for _ in range(size))
......@@ -163,30 +177,34 @@ class RecommenderXBlock(XBlock):
response = Response()
response.body = filename
response.headers['Content-Type'] = 'text/plain'
print 'before return'
tracker.emit('upload_screenshot', {'uploadedFileName': filename})
return response
@XBlock.json_handler
def add_resource(self, data, suffix=''):
''' untested '''
resource = data['resource']
# Construct new resource
valid_fields = ['url', 'title', 'description']
new_resource = {}
for field in valid_fields:
new_resource[field] = data['resource'][field]
# check url for redundancy
for recom in self.recommendations:
if recom['url'] == data['resource']['url']:
print "add redundant resource"
new_resource['error'] = 'redundant resource'
tracker.emit('add_resource', new_resource)
return {"Success": False}
# Construct new resource
valid_fields = ['url', 'title', 'description']
new_resource = {}
for field in valid_fields:
new_resource[field] = data['resource'][field]
new_resource['id'] = self.getResourceNewId()
tracker.emit('add_resource', new_resource)
new_resource['upvotes'] = 0
new_resource['downvotes'] = 0
new_resource['id'] = self.getResourceNewId()
new_resource['isProblematic'] = "notProblematic"
new_resource['problematicReason'] = ""
# self.resources.append(new_resource)
self.recommendations.append(new_resource)
return {"Success": True, "id": new_resource['id']}
......@@ -196,36 +214,54 @@ class RecommenderXBlock(XBlock):
idx = self.getEntryIndex(resource, self.recommendations)
if idx not in range(0, len(self.recommendations)):
print "Edit failed!"
tracker.emit('edit_resource', {'id': resource, 'error': 'bad id'})
return {"Success": False}
valid_fields = ['url', 'title', 'description']
resource_log = {}
resource_log['id'] = resource
for field in valid_fields:
resource_log['old_' + field] = self.recommendations[idx][field]
resource_log[field] = data[field]
# check url for redundancy
for recom in self.recommendations:
if self.recommendations[idx]['url'] == data['url']:
continue
if recom['url'] == data['url']:
resource_log['error'] = 'existing url'
print "provided url is existing"
tracker.emit('edit_resource', resource_log)
return {"Success": False}
for key in data:
if key == 'resource':
next
if data[key] == '':
next
continue
if data[key] == "":
continue
self.recommendations[idx][key] = data[key]
tracker.emit('edit_resource', resource_log)
return {"Success": True}
@XBlock.json_handler
def flag_resource(self, data, suffix=''):
flagLog = {}
flagLog['id'] = data['resource']
flagLog['isProblematic'] = data['isProblematic']
flagLog['reason'] = data['reason']
if data['isProblematic'] == True:
if data['resource'] in self.flagId:
flagLog['oldReason'] = self.flagReason[self.flagId.index(data['resource'])]
self.flagReason[self.flagId.index(data['resource'])] = data['reason']
else:
self.flagId.append(data['resource'])
self.flagReason.append(data['reason'])
else:
if data['resource'] in self.flagId:
flagLog['oldReason'] = self.flagReason[self.flagId.index(data['resource'])]
idx = self.flagId.index(data['resource'])
del self.flagId[idx]
del self.flagReason[idx]
tracker.emit('flag_resource', flagLog)
return {"Success": True}
# TO-DO: change this view to display your data your own way.
......
......@@ -9,6 +9,12 @@
</div>
<div class='recommender_row_inner'>
<div class="recommender_row">
<div class="noResourceIntro hidden">
This will be a list of resources your fellow students
thought might be helpful, but it is empty currently.
If you find useful resources, either on edx.org or
elsewhere, please add it.
</div>
% for elem in resources:
<%
if elem['id'] in downvotes:
......@@ -26,21 +32,20 @@
reason = ''
%>
<%include file="resourcebox.html" args="id=elem['id'],title=elem['title'],votes=elem['votes'],url=elem['url'],description=elem['description'],isProblematic=flagMode,problematicReason=reason,voteMode=voteMode" />
% endfor
</div>
<div class='pagination'> </div>
<div class="recommender_description">
<div class="descriptionImg">
<div> This is a list of resources your fellow students
thought might be helpful. If you find another useful
resource, either on edx.org or elsewhere, please add
it. If you can improve the description or preview of a
resource, please do so as well. If you find a resource
helpful, upvote it. If it&rsquo;s not so helpful, downvote
it. If it has issues (illegal material, incorrect, etc.),
please flag it and let us know the reason. </div>
</div>
<div> This is a list of resources your fellow students
thought might be helpful. If you find another useful
resource, either on edx.org or elsewhere, please add
it. If you can improve the description or preview of a
resource, please do so as well. If you find a resource
helpful, upvote it. If it&rsquo;s not so helpful, downvote
it. If it has issues (illegal material, incorrect, etc.),
please flag it and let us know the reason. </div>
</div>
</div>
</div>
</div>
......@@ -48,28 +53,38 @@
<div class='recommender_modify_title_container'><div class='backToViewButton'>&lt; Related resources</div><div class='recommender_modify_title'></div></div>
<div class="flagSourceBlock">
<div class="flagSourceBlockTitle">Why would you like to flag this resource?</div>
<input type="text"
class="flag_reason"
style="height: 25px; position: relative; left: 10px; width:100%"
name="flag_rationale"
placeholder="Reason for why this resource should be removed"/><br/>
<input type="text"
class="flag_reason"
style="height: 25px; position: relative; left: 10px; width:100%"
name="flag_rationale"
placeholder="Reason for why this resource should be removed"/><br/>
<input type="button" value="Flag resource" class="flag_reason_submit" style="margin-top: 0.5em;">
</div>
<div class="editSourceBlock">
<div class="editSourceBlockTitle">Edit the description, hypelink, and previewing screenshot for the selected resource.</div>
<div style="float: left">Description&nbsp;</div><div class = 'redTxt'>*</div>
<textarea
class="edit_title"
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
<div style="float: left">Location&nbsp;</div><div class = 'redTxt'>*</div>
<input type="text"
class="edit_url"
placeholder="http://en.wikipedia.org/wiki/Edx"/><br/>
<form method="post" id="editResourceForm">
Previewing screenshot: <input type="file" name="file"><br/>
<input type="button" value="Save change" class="edit_submit" style="margin-top: 0.5em" disabled >
</form>
</div>
<div class="recommender_add">
<div class="addSourceBlockTitle">Suggest a resource which can help other students with this problem.</div>
Description <input type="text"
class="in_title"
style="height: 25px; position: relative; left: 10px; width:100%"
placeholder="Provide a meaningful description so other students know whether this is useful to them"/><br/>
Location <input type="text"
class="in_url"
style="height: 25px; position: relative; left: 22px; width:100%"
placeholder="http://en.wikipedia.org/wiki/Edx"/><br/>
<div style="float: left">Description&nbsp;</div><div class = 'redTxt'>*</div><textarea
class="in_title"
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
<div style="float: left">Location&nbsp;</div><div class = 'redTxt'>*</div> <input type="text"
class="in_url"
placeholder="http://en.wikipedia.org/wiki/Edx"/><br/>
<form method="post" id="addResourceForm">
Previewing screenshot: <input type="file" name="file"><br/>
<div class='in_filename hidden'></div>
<input type="button" value="Add resource" class="add_submit" style="margin-top: 0.5em" disabled >
</form>
</div>
......
......@@ -14,13 +14,11 @@ function RecommenderXBlock(runtime, element) {
/* resource list collapse or expansion */
$(".hide-show").click(function () {
if ($(this).hasClass('resource_list_expanded')) {
//$(this).find('.hide-show-icon').removeClass('upArrowIcon').addClass('downArrowIcon');
$(".recommender_row_inner", element).slideUp('fast');
$('.resource_add_button').css('visibility', 'hidden');
$(this).css('cursor', 's-resize');
}
else {
//$(this).find('.hide-show-icon').removeClass('downArrowIcon').addClass('upArrowIcon');
$(".recommender_row_inner", element).slideDown('fast');
$('.resource_add_button').css('visibility', 'visible');
$(this).css('cursor', 'n-resize');
......@@ -112,7 +110,6 @@ function RecommenderXBlock(runtime, element) {
$('.in_title').val('');
$('.in_url').val('');
$('#addResourceForm').find("input[name='file']").val('');
$('.in_filename').text('');
$('.add_submit').attr('disabled', true);
}
......@@ -129,91 +126,99 @@ function RecommenderXBlock(runtime, element) {
$('.in_title').bind('input propertychange', function() { enableAddSubmit(); });
$('.in_url').bind('input propertychange', function() { enableAddSubmit(); });
/* upload once student select a file */
$('#addResourceForm').find("input[name='file']").change(function (){
if ($(this).val() == '') { return false; }
var formDiv = $(this).parent();
var data = new FormData($(formDiv)[0]);
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: data,
contentType: false,
cache: false,
processData: false,
async: false,
/* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATE ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */
complete: function(data) {
/* save filename in hidden div */
$('.in_filename').text(data.responseText);
enableAddSubmit();
},
});
});
/* submit the resource, save to database, update the current view */
/* upload screenshot, submit the resource, save to database, update the current view */
$('.add_submit').click(function() {
/* data: parameter passed to database */
var data = {};
data['resource'] = {};
data['resource']['url'] = $('.in_url').val();
data['resource']['title'] = $('.in_title').val();
data['resource']['description'] = baseUrl + $(this).parent().find('.in_filename').text();
$.ajax({
type: "POST",
url: addResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* decide the rigth place for the added resource (pos), based on sorting the votes */
var pos = -1;
$('.recommender_vote_score').each(function(idx, ele){
if (parseInt($(ele).text()) < 0) {
pos = idx;
return false;
}
});
/* html for the new resource */
var content = '<div class="recommender_resource">' +
'<div class="recommender_vote_box">' +
'<div class="recommender_vote_arrow_up nonevoting" role="button" aria-label="upvote" tabindex="0">' +
'<b>↑</b></div>' +
'<div class="recommender_vote_score nonevoting"><b>0</b></div>' +
'<div class="recommender_vote_arrow_down nonevoting" role="button" aria-label="downvote" tabindex="0">' +
'<b>↓</b></div>' +
'</div>' +
'<div class="recommender_blurb"><div class="recommender_title">' +
'<a href="' + data['resource']['url'] + '" target="_blank">' + data['resource']['title'] + '</a>' + '</div>' +
'<div class="recommender_descriptionSlot">' + data['resource']['description'] +
'</div><div class="recommender_entryId">' + result['id'] +
'</div><div class="recommender_problematicReason"></div>' +
'</div><div class="recommender_edit">' +
'<span class="ui-icon ui-icon-pencil resource_edit_button"></span>' +
'<span class="ui-icon ui-icon-flag flagResource" title="Flag irrelevant resource">' +
'</span></div></div>';
/* show the added resource at right place (pos), based on sorting the votes, and lead student to that page */
if (pos == -1) {
$('.recommender_resource:last').after(content);
currentPage = Math.ceil($('.recommender_resource').length/entriesPerPage);
}
else {
$('.recommender_resource:eq(' + pos.toString() + ')').before(content);
currentPage = Math.ceil((pos + 1)/entriesPerPage);
data['resource']['description'] = '';
var formDiv = $('#addResourceForm');
var file = new FormData($(formDiv)[0]);
if ($(formDiv).find("input[name='file']").val() == '') { addResource(data); }
else {
/* upload once student select a file */
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: file,
contentType: false,
cache: false,
processData: false,
async: false,
/* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATES ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */
complete: function(result) {
if (result.responseText == 'FILETYPEERROR') {
alert('Please upload an image');
$(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['resource']['description'] = baseUrl + result.responseText;
addResource(data);
}
addResourceReset();
unbindEvent();
bindEvent();
paginationRow();
pagination();
backToView();
},
});
}
});
function addResource(data) {
$.ajax({
type: "POST",
url: addResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* decide the rigth place for the added resource (pos), based on sorting the votes */
var pos = -1;
$('.recommender_vote_score').each(function(idx, ele){
if (parseInt($(ele).text()) < 0) {
pos = idx;
return false;
}
else { alert('add redundant resource'); }
});
/* html for the new resource */
var content = '<div class="recommender_resource">' +
'<div class="recommender_vote_box">' +
'<div class="recommender_vote_arrow_up nonevoting" role="button" aria-label="upvote" tabindex="0">' +
'<b>↑</b></div>' +
'<div class="recommender_vote_score nonevoting"><b>0</b></div>' +
'<div class="recommender_vote_arrow_down nonevoting" role="button" aria-label="downvote" tabindex="0">' +
'<b>↓</b></div>' +
'</div>' +
'<div class="recommender_blurb"><div class="recommender_title">' +
'<a href="' + data['resource']['url'] + '" target="_blank">' + data['resource']['title'] + '</a>' + '</div>' +
'<div class="recommender_descriptionSlot">' + data['resource']['description'] +
'</div><div class="recommender_entryId">' + result['id'] +
'</div><div class="recommender_problematicReason"></div>' +
'</div><div class="recommender_edit">' +
'<span class="ui-icon ui-icon-pencil resource_edit_button"></span>' +
'<span class="ui-icon ui-icon-flag flagResource" title="Flag irrelevant resource">' +
'</span></div></div>';
/* show the added resource at right place (pos), based on sorting the votes, and lead student to that page */
if (pos == -1) {
$('.recommender_resource:last').after(content);
currentPage = Math.ceil($('.recommender_resource').length/entriesPerPage);
}
});
});
else {
$('.recommender_resource:eq(' + pos.toString() + ')').before(content);
currentPage = Math.ceil((pos + 1)/entriesPerPage);
}
addResourceReset();
unbindEvent();
bindEvent();
paginationRow();
pagination();
backToView();
}
else { alert('add redundant resource'); }
}
});
}
/* unbind event for each entry of resources */
function unbindEvent() {
......@@ -311,23 +316,12 @@ function RecommenderXBlock(runtime, element) {
$('.recommender_modify').show();
$('.recommender_modify_title').text('Edit existing resource');
$('.editSourceBlock').empty();
var uploadForm = '<form id="editResourceForm" method="post">'
+ 'Previewing screenshot: <input type="file" name="file"><br/>'
+ '<div class="edit_filename hidden"></div>'
+ '<input type="button" value="Save change" class="edit_submit" style="margin-top: 0.5em" disabled></form>';
$('.editSourceBlock').append(
'<div class="editSourceBlockTitle">Edit the description, hypelink, and previewing screenshot for the selected resource</div>' +
'Description: ' + '<input type="text" class="edit_title" style="height: 25px; position: relative; left: 10px;"><br/>' +
'HyperLink: <input type="text" class="edit_url" style="height: 25px; position: relative; left: 22px;"><br/>' + uploadForm);
/* initialize the text area */
$('.edit_title').val($(this).parent().parent().find('.recommender_title').find('a').text());
$('.edit_url').val($(this).parent().parent().find('.recommender_title').find('a').attr('href'));
addTooltip();
var divEdit = this;
$('#editResourceForm').find("input[name='file']").val('');
$('.edit_submit').attr('disabled', true);
var divEdit = this;
/* check whether enough information (title/url) is provided for editing a resource, if yes, enable summission button */
function enableEditSubmit() {
......@@ -339,57 +333,68 @@ function RecommenderXBlock(runtime, element) {
}
/* check whether the input text area is changed, if yes, check whether student can submit the resource */
$('.edit_title').unbind();
$('.edit_url').unbind();
$('.edit_title').bind('input propertychange', function() { enableEditSubmit(); });
$('.edit_url').bind('input propertychange', function() { enableEditSubmit(); });
/* upload once student select a file */
$('#editResourceForm').find("input[name='file']").change(function (){
if ($(this).val() == '') { return false; }
var formDiv = $(this).parent();
var data = new FormData($(formDiv)[0]);
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: data,
contentType: false,
cache: false,
processData: false,
async: false,
/* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATE ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */
complete: function(data) {
/* save filename in hidden div */
$('.edit_filename').text(data.responseText);
enableEditSubmit();
},
});
});
/* submit the edited resource, save to database, update the current view */
/* upload the screen shot, submit the edited resource, save to database, update the current view */
$('.edit_submit').unbind();
$('.edit_submit').click(function() {
/* data: parameter passed to database */
var data = {};
data['resource'] = parseInt($(divEdit).parent().parent().find('.recommender_entryId').text());
data['url'] = $('.edit_url').val();
data['title'] = $('.edit_title').val();
data['description'] = ''
if (data['url'] == '' || data['title'] == '') { return; }
if ($(this).parent().find('.edit_filename').text() != '') { data['description'] = baseUrl + $(this).parent().find('.edit_filename').text(); }
$.ajax({
type: "POST",
url: editResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* show the edited resource */
$(divEdit).parent().parent().find('.recommender_title').find('a').text(data['title']);
$(divEdit).parent().parent().find('.recommender_title').find('a').attr('href', data['url']);
if ("description" in data) { $(divEdit).parent().parent().find('.recommender_descriptionSlot').text(data['description']); }
$('.editSourceBlock').empty();
backToView();
var formDiv = $('#editResourceForm');
var file = new FormData($(formDiv)[0]);
if ($(formDiv).find("input[name='file']").val() == '') { editResource(data); }
else {
/* upload once student select a file */
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: file,
contentType: false,
cache: false,
processData: false,
async: false,
/* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATES ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */
complete: function(result) {
if (result.responseText == 'FILETYPEERROR') {
alert('Please upload an image');
$(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['description'] = baseUrl + result.responseText;
editResource(data);
}
},
});
}
function editResource (data) {
$.ajax({
type: "POST",
url: editResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* show the edited resource */
$(divEdit).parent().parent().find('.recommender_title').find('a').text(data['title']);
$(divEdit).parent().parent().find('.recommender_title').find('a').attr('href', data['url']);
if (data["description"] != "") { $(divEdit).parent().parent().find('.recommender_descriptionSlot').text(data['description']); }
backToView();
}
else { alert('add redundant resource'); }
}
else { alert('add redundant resource'); }
}
});
});
}
});
});
......@@ -493,6 +498,10 @@ function RecommenderXBlock(runtime, element) {
pagination();
addResourceReset();
bindEvent();
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').show();
}
}
initial();
}
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