Commit d261ea4a by swdanielli

add some tooltips and code documentation

parent b8a79af5
"""TO-DO: Write a description of what this XBlock is."""
import json, string, random, re import json, string, random, re
import pkg_resources import pkg_resources
...@@ -24,27 +22,58 @@ from webob.response import Response ...@@ -24,27 +22,58 @@ from webob.response import Response
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 which may be helpful to students solving a given problem.
The resources are provided and edited by students; they can also vote for useful resources and flag problematic ones.
""" """
# 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 default help resources", default=[], scope=Scope.content)
# Block-wide. List of JSON objects corresponding to recommendations as defined in XML. # A list of default recommenations, it is a JSON object across all users, all runs of a course, for this xblock.
# Usage: default_recommendations[index] = {
# "id": (Integer) id of a resource,
# "title": (String) title of a resource; a 1-3 sentence summary of a resource
# "upvotes" : (Integer) number of upvotes,
# "downvotes" : (Integer) number of downvotes,
# "url" : (String) the url of a resource,
# "description" : (String) the url of a resource's screenshot,
# "descriptionText" : (String) a paragraph of description/summary of a resource }
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. # A list of recommenations provided by students, it is a JSON object aggregated across many users of a single block.
s3_configuration = Dict(help="List of help resources", default={}, scope=Scope.user_state_summary) # Usage: the same as default_recommendations
# List of deleted recommendation ID.
deletedRecommendationIds = List(help="List of help resources", default=[], scope=Scope.user_state_summary) s3_configuration = Dict(help="Dictionary of Amazon S3 information", default={}, scope=Scope.user_state_summary)
# Ids of upvoted resources for this particular user # A dictionary of Amazon S3 information for file uploading, it is a JSON object aggregated across many users of a single block.
upvotedIds = List(help="List of items user gave upvote to", default=[], scope=Scope.user_state) # Usage: s3_configuration = {
# Ids of downvoted resources for this particular user # "aws_access_key": (String) access key of Amazon S3 account
downvotedIds = List(help="List of items user gave downvote to", default=[], scope=Scope.user_state) # "aws_secret_key": (String) secret key of Amazon S3 account
# Ids of flagged resource (problematic resource) for this particular user # "bucketName": (String) Bucket name of Amazon S3 account
flaggedIds = List(help="List of items user flagged to", default=[], scope=Scope.user_state) # "uploadedFileDir": (String) The path (relative to root directory) of the directory for storing uploaded files }
# Reasons of flagged resource (problematic resource) for this particular user
flaggedReasons = List(help="List of reasons of items user flagged to", default=[], scope=Scope.user_state)
deletedRecommendationIds = List(help="List of deleted resources' ID", default=[], scope=Scope.user_state_summary)
# A list of deleted recommendations' ids, it is a JSON object aggregated across many users of a single block.
# Usage: deletedRecommendationIds[index] = (Integer) id of a deleted resource
upvotedIds = List(help="List of resources' ids which user upvoted to", default=[], scope=Scope.user_state)
# A list of recommendations' ids which user upvoted to; it is a JSON object for one user, for one block, and for one run of a course.
# Usage: upvotedIds[index] = (Integer) id of a resource which was upvoted by the current user
downvotedIds = List(help="List of resources' ids which user downvoted to", default=[], scope=Scope.user_state)
# A list of recommendations' ids which user downvoted to; it is a JSON object for one user, for one block, and for one run of a course.
# Usage: downvotedIds[index] = (Integer) id of a resource which was downvoted by the current user
flaggedIds = List(help="List of problematic resources' ids which user flagged to", default=[], scope=Scope.user_state)
# A list of problematic recommendations' ids which user flagged to; it is a JSON object for one user, for one block, and for one run of a course.
# Usage: flaggedIds[index] = (Integer) id of a problematic resource which was flagged by the current user
# Reasons of flagged resource (problematic resource) for this particular user
flaggedReasons = List(help="List of reasons why the corresponding resources were flagged by user as problematic", default=[], scope=Scope.user_state)
# A list of reasons why the corresponding resources were flagged by user as problematic; it is a JSON object for one user, for one block, and for one run of a course.
# Usage: flaggedReasons[index] = (String) reason why the corresponding resource was flagged by the current user as problematic
template_lookup = None template_lookup = None
resource_content_fields = ['url', 'title', 'description', 'descriptionText'] resource_content_fields = ['url', 'title', 'description', 'descriptionText']
# the dictionary keys for storing the content of a recommendation
def resource_string(self, path): def resource_string(self, path):
"""Handy helper for getting static file resources from our Python package.""" """Handy helper for getting static file resources from our Python package."""
...@@ -76,6 +105,12 @@ class RecommenderXBlock(XBlock): ...@@ -76,6 +105,12 @@ class RecommenderXBlock(XBlock):
return idx return idx
return -1 return -1
def checkRedundancy(self, url1, url2):
"""
Check redundant resource by comparing the url.
"""
return url1.split('#')[0].split('%23')[0] == url2.split('#')[0].split('%23')[0]
@XBlock.json_handler @XBlock.json_handler
def delete_resource(self, data, suffix=''): def delete_resource(self, data, suffix=''):
""" """
...@@ -292,7 +327,7 @@ class RecommenderXBlock(XBlock): ...@@ -292,7 +327,7 @@ class RecommenderXBlock(XBlock):
# check url for redundancy # check url for redundancy
for recommendation in self.recommendations: for recommendation in self.recommendations:
if recommendation['url'] == data['url']: if self.checkRedundancy(recommendation['url'], data['url']):
result['error'] = 'redundant resource' result['error'] = 'redundant resource'
tracker.emit('add_resource', result) tracker.emit('add_resource', result)
result['Success'] = False result['Success'] = False
...@@ -337,9 +372,9 @@ class RecommenderXBlock(XBlock): ...@@ -337,9 +372,9 @@ class RecommenderXBlock(XBlock):
result['old_' + field] = self.recommendations[idx][field] result['old_' + field] = self.recommendations[idx][field]
result[field] = data[field] result[field] = data[field]
# check url for redundancy # check url for redundancy
if self.recommendations[idx]['url'] != data['url']: if not( self.checkRedundancy(self.recommendations[idx]['url'], data['url']) ):
for recommendation in self.recommendations: for recommendation in self.recommendations:
if recommendation['url'] == data['url']: if self.checkRedundancy(recommendation['url'], data['url']):
result['error'] = 'existing url' result['error'] = 'existing url'
for field in self.resource_content_fields: for field in self.resource_content_fields:
result['dup_' + field] = self.recommendations[self.recommendations.index(recommendation)][field] result['dup_' + field] = self.recommendations[self.recommendations.index(recommendation)][field]
...@@ -465,8 +500,6 @@ class RecommenderXBlock(XBlock): ...@@ -465,8 +500,6 @@ class RecommenderXBlock(XBlock):
frag.initialize_js('RecommenderXBlock') frag.initialize_js('RecommenderXBlock')
return frag return frag
# TO-DO: change this to create the scenarios you'd like to see in the
# workbench while developing your XBlock.
@staticmethod @staticmethod
def workbench_scenarios(): def workbench_scenarios():
"""A canned scenario for display in the workbench.""" """A canned scenario for display in the workbench."""
......
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
<input type="button" value="Flag resource" class="flag_reason_submit"> <input type="button" value="Flag resource" class="flag_reason_submit">
</div> </div>
<div class="editSourceBlock"> <div class="editSourceBlock">
<div class="editSourceBlockTitle">Edit the resource and make it more helpful for other students with this problem. Please do not give the answer directly.</div>
<div style="float: left">Title&nbsp;</div><div class = 'redTxt'>*</div> <div style="float: left">Title&nbsp;</div><div class = 'redTxt'>*</div>
<textarea <textarea
class="edit_title" class="edit_title"
...@@ -102,12 +103,12 @@ ...@@ -102,12 +103,12 @@
class="edit_descriptionText" class="edit_descriptionText"
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/> placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
<form method="post" id="editResourceForm"> <form method="post" id="editResourceForm">
Previewing screenshot: <input type="file" name="file"><br/> Preview screenshot: <input type="file" name="file" class='editResourceScreenshot'><br/>
<input type="button" value="Save change" class="edit_submit" disabled > <input type="button" value="Save change" class="edit_submit" disabled >
</form> </form>
</div> </div>
<div class="recommender_add"> <div class="recommender_add">
<div class="addSourceBlockTitle">Suggest a resource which can help other students with this problem.</div> <div class="addSourceBlockTitle">Suggest a resource which can help other students with this problem. Please do not give the answer directly.</div>
<div style="float: left">Title&nbsp;</div><div class = 'redTxt'>*</div> <div style="float: left">Title&nbsp;</div><div class = 'redTxt'>*</div>
<textarea <textarea
class="in_title" class="in_title"
...@@ -121,7 +122,7 @@ ...@@ -121,7 +122,7 @@
class="in_descriptionText" class="in_descriptionText"
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/> placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
<form method="post" id="addResourceForm"> <form method="post" id="addResourceForm">
Previewing screenshot: <input type="file" name="file"><br/> Preview screenshot: <input type="file" name="file" class='addResourceScreenshot'><br/>
<input type="button" value="Add resource" class="add_submit" disabled > <input type="button" value="Add resource" class="add_submit" disabled >
</form> </form>
</div> </div>
......
...@@ -4,7 +4,8 @@ var tooltipsCatsPerResource = [ ...@@ -4,7 +4,8 @@ var tooltipsCatsPerResource = [
'.recommender_vote_arrow_down', '.recommender_vote_arrow_down',
'.recommender_vote_score', '.recommender_vote_score',
'a', 'a',
'.flagResource' '.flagResource',
'.staffEdition'
]; ];
var tooltipsCats = [ var tooltipsCats = [
...@@ -12,28 +13,50 @@ var tooltipsCats = [ ...@@ -12,28 +13,50 @@ var tooltipsCats = [
'.previewingImg', '.previewingImg',
'.in_title', '.in_title',
'.in_url', '.in_url',
'.edit_title', '.in_descriptionText',
'.edit_url', '.addResourceScreenshot',
'.backToViewButton', '.backToViewButton',
'.flag_reason', '.flag_reason',
'.aws_access_key',
'.aws_secret_key',
'.bucketName',
'.uploadedFileDir',
'.delete_resource',
'.recommender_row_top.resource_list_expanded' '.recommender_row_top.resource_list_expanded'
]; ];
var tooltipsEditCats = [
'.edit_title',
'.edit_url',
'.edit_descriptionText',
'.editResourceScreenshot'
];
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',
'.recommender_vote_score': 'Votes', // '.recommender_vote_score': 'Votes', //
'a': 'Resource title', // TODO: I would suggest making the description be the tooltip. 'a': 'Resource title', // TODO: I would suggest making the description be the tooltip.
'.staffEdition': 'Add Amazon S3 information for file uploading or delete this resource; these functions are restricted to course staff',
'.previewingImg': 'Preview image (typically, a screenshot)', '.previewingImg': 'Preview image (typically, a screenshot)',
'.in_title': 'Give a short (1-3 sentence) summary of the resource; ideally, this should be concise, but give enough detail to let students know whether this resources is useful to them', '.in_title': 'Give a short (1-3 sentence) summary of the resource; ideally, this should be concise, but give enough detail to let students know whether this resources is useful to them',
'.in_url': 'Cut-and-paste the URL of the resource.', '.in_url': 'Cut-and-paste the URL of the resource.',
'.in_descriptionText': 'Give a paragraph of summary of the resource; the summary should be more detailed than you gave in Title',
'.addResourceScreenshot': 'Upload a preview screenshot (in GIF/PNG/JPG) of the resource; ideally, this should let students know whether this resources is useful to them',
'.edit_title': 'Give a short (1-3 sentence) summary of the resource; ideally, this should be concise, but give enough detail to let students know whether this resources is useful to them', '.edit_title': 'Give a short (1-3 sentence) summary of the resource; ideally, this should be concise, but give enough detail to let students know whether this resources is useful to them',
'.edit_url': 'Cut-and-paste the URL of the resource.', // TODO: Give instructions to go to element of learning sequence, or time in video '.edit_url': 'Cut-and-paste the URL of the resource.', // TODO: Give instructions to go to element of learning sequence, or time in video
'.edit_descriptionText': 'Give a paragraph of summary of the resource; the summary should be more detailed than you gave in Title',
'.editResourceScreenshot': 'Upload a preview screenshot (in GIF/PNG/JPG) of the resource; ideally, this should let students know whether this resources is useful to them',
'.backToViewButton': 'Go back to the main list', '.backToViewButton': 'Go back to the main list',
'.flag_reason': 'Give a meaningful reason for why this resource should be removed', '.flag_reason': 'Give a meaningful reason for why this resource should be removed',
'.flagResource': 'Flag this resource as problematic and give your reason', '.flagResource': 'Flag this resource as problematic and give your reason',
'.aws_access_key': 'Give the access key of your Amazon s3 account',
'.aws_secret_key': 'Give the secret key of your Amazon s3 account',
'.bucketName': 'Give the bucket name of your Amazon s3 account',
'.uploadedFileDir': 'Give the path (relative to root directory) of the directory for storing uploaded files',
'.delete_resource': 'Delete this resource',
'.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'
}; };
...@@ -49,7 +72,7 @@ var staff_edit_buttons_text = { ...@@ -49,7 +72,7 @@ var staff_edit_buttons_text = {
'delete_resource': "Delete resource", 'delete_resource': "Delete resource",
"endorse_resource": "Endorse resource", "endorse_resource": "Endorse resource",
"deendorse_resource": "De-endorse resource", "deendorse_resource": "De-endorse resource",
"submit_s3_info": "Submit information for Amazon Web Services S3" "submit_s3_info": "Add Amazon S3 information for file uploading"
} }
var staff_edit_textareas = [ var staff_edit_textareas = [
...@@ -60,10 +83,10 @@ var staff_edit_textareas = [ ...@@ -60,10 +83,10 @@ var staff_edit_textareas = [
] ]
var staff_edit_textareas_text = { var staff_edit_textareas_text = {
'aws_access_key': 'Amazon Web Services access key', 'aws_access_key': 'Amazon S3 access key',
'aws_secret_key': 'Amazon Web Services secret key', 'aws_secret_key': 'Amazon S3 secret key',
'bucketName': 'Bucket name of your Amazon Web Services', 'bucketName': 'Bucket name of your Amazon S3',
'uploadedFileDir': 'Directory for your upload files' 'uploadedFileDir': 'Directory for your uploaded files'
} }
var staff_edit_textareas_placeholder = { var staff_edit_textareas_placeholder = {
......
...@@ -449,6 +449,9 @@ function RecommenderXBlock(runtime, element) { ...@@ -449,6 +449,9 @@ function RecommenderXBlock(runtime, element) {
$('#editResourceForm').find("input[name='file']").change(function() { $('#editResourceForm').find("input[name='file']").change(function() {
if ($(this).val() != '') { enableEditSubmit(); } if ($(this).val() != '') { enableEditSubmit(); }
}); });
/* Add tooltips for editting page */
addTooltipPerCats(tooltipsEditCats);
/* Upload the screen shot, submit the edited resource, save the resource in the database, and update the current view of resource */ /* Upload the screen shot, submit the edited resource, save the resource in the database, and update the current view of resource */
$('.edit_submit').unbind(); $('.edit_submit').unbind();
...@@ -626,7 +629,8 @@ function RecommenderXBlock(runtime, element) { ...@@ -626,7 +629,8 @@ function RecommenderXBlock(runtime, element) {
maxWidth: '300' maxWidth: '300'
}); });
return; return;
} }
if ($(cats).hasClass('tooltipstered')) { return; }
$(cats).tooltipster({ $(cats).tooltipster({
content: $('<span>' + tooltipsCatsText[cats] + '</span>'), content: $('<span>' + tooltipsCatsText[cats] + '</span>'),
theme: '.my-custom-theme', theme: '.my-custom-theme',
...@@ -637,6 +641,26 @@ function RecommenderXBlock(runtime, element) { ...@@ -637,6 +641,26 @@ function RecommenderXBlock(runtime, element) {
}); });
} }
/* Add tooltips to each cat in cats */
function addTooltipPerCats(cats) {
cats.forEach(function(cat, ind) {
try {
$(cat).tooltipster('destroy');
}
catch (e) { }
});
cats.forEach(function(cat, ind) {
try {
$(cat).tooltipster({
content: $('<span>' + tooltipsCatsText[cat] + '</span>'),
theme: '.my-custom-theme',
maxWidth: '300'
});
}
catch (e) { }
});
}
/* Add tooltips to each component in each resource */ /* Add tooltips to each component in each resource */
function addTooltipPerResource(ele) { function addTooltipPerResource(ele) {
tooltipsCatsPerResource.forEach(function(cats, ind) { tooltipsCatsPerResource.forEach(function(cats, ind) {
...@@ -673,7 +697,7 @@ function RecommenderXBlock(runtime, element) { ...@@ -673,7 +697,7 @@ function RecommenderXBlock(runtime, element) {
if (result['is_user_staff']) { if (result['is_user_staff']) {
is_user_staff = true; is_user_staff = true;
addFunctionsForStaff(); addFunctionsForStaff();
$('.recommender_resource').each(function(index, ele) { addFunctionsForStaffPerResource(ele); }); $('.recommender_resource').each(function(index, ele) { addFunctionsForStaffPerResource(ele); addTooltipPerResource(ele); });
} }
} }
}); });
......
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