Repository URL to install this package:
|
Version:
0.2.0 ▾
|
edx-gea
/
gea.py
|
|---|
"""GEA (Grade External Activity). An XBlock used to grade external activities.
All kind of student activities can't be achieved inside edx-platform. Therefore a teacher
will only describe the activity in the lms and add a GEA XBlock to grade it.
"""
import csv
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext_lazy, ugettext as _
from webob.response import Response
from xblock.core import XBlock
from xblock.fields import Scope, String, Integer, Float
from xblock.fragment import Fragment
from xblockutils.studio_editable import StudioEditableXBlockMixin
from xblockutils.resources import ResourceLoader
from .forms import UploadAssessmentFileForm, get_default_delimiter
from .gea_assessment import GeaAssessment, Score
loader = ResourceLoader(__name__)
class GradeExternalActivityXBlock(XBlock, StudioEditableXBlockMixin):
"""XBlock to grade external activities."""
has_score = True #: This flags the XBlock as a problem.
icon_class = 'problem'
display_name = String(
display_name=ugettext_lazy(u"External Activity Grader"),
default=u"External Activity Grader",
scope=Scope.settings,
help=u"This name appears in the horizontal navigation at the top of "
"the page."
)
points = Integer(
display_name=ugettext_lazy(u"Maximum grade"),
help=(ugettext_lazy(u"Maximum grade of the external activity.")),
default=10,
scope=Scope.settings
)
weight = None #: needed by courseware.grades.get_score. (We don't use it.)
editable_fields = ('points',)
usernames = {} #: A dict used to avoid multiple calls to db {'username' : models.User , ...}.
max_assessment_file_lines = 1000 #: The limit of csv lines in the uploded assessment file.
csv_delimiter = None #: The csv delimiter used in the uploaded assessment file.
def student_view(self, context=None):
"""Display the student assessment (score and comment).
Note:
The LMS Runtime only calls the XBlock.student_view. In order to get two different
views, depending on the runtime user being a staff member or a student, we check inside the student_view
if the user is a staff and call the staff_view accordingly.
"""
if self.is_course_staff():
return self.staff_view()
gea_assessment = GeaAssessment(User.objects.get(id=self.xmodule_runtime.user_id), self)
frag = Fragment(loader.render_template("templates/edx_gea/student.html",
{'score' : gea_assessment.score,
'comment' : gea_assessment.comment}))
return frag
def staff_view(self):
"""Display the form for uploading the assessement file."""
spinner_url = self.runtime.local_resource_url(self, 'public/static/images/spinner.gif')
frag = Fragment(loader.render_template("templates/edx_gea/staff.html",
{'upload_assessment_file_form' : UploadAssessmentFileForm(auto_id=True, initial={'csv_delimiter' : get_default_delimiter()}),
'spinner_url' : spinner_url,
'max_assessment_file_lines' : self.max_assessment_file_lines}))
frag.add_css(loader.load_unicode("static/css/gea.css"))
frag.add_javascript(loader.load_unicode("static/js/src/gea.js"))
frag.initialize_js('GeaXBlock')
return frag
@XBlock.handler
def upload_assessments(self, request, context=None):
"""Called when a staff has submitted an assessment file.
Perform the file validation. If the file is valid grade students, otherwise
return the list of errors and offer the staff to upload a new file.
"""
if not self.is_course_staff():
raise PermissionDenied
uploaded_file = request.POST['file'].file
self.csv_delimiter = request.POST['csv_delimiter']
form = UploadAssessmentFileForm(request.POST, files={'assessment_file' : uploaded_file},
auto_id=True, gea_xblock=self)
if form.is_valid():
self.handle_assessment_file(uploaded_file)
return Response(u"<p class='gea_success'>{}</p>".format(_("Thank you, the students assessment has been done.")))
else:
return Response(loader.render_template("templates/edx_gea/form_errors.html",
{'upload_assessment_file_form' : form}))
def handle_assessment_file(self, file):
"""Read the file and set score and comment through the GeaAssessment class.
Note:
We expect a csv file following this structure:
+----------+----+------------+
| student1 | 10 | Good work. |
+----------+----+------------+
| student2 | 20 | Bad work. |
+----------+----+------------+
Args:
file: The assessment file.
"""
assessment_file = csv.DictReader(file, ['username', 'score', 'comment'],
delimiter=str(self.csv_delimiter))
for row in assessment_file:
gea_assessment = GeaAssessment(self.usernames[row['username']], self)
gea_assessment.comment = row['comment']
gea_assessment.score = Score(row['score'], self.max_score())
def is_course_staff(self):
return getattr(self.xmodule_runtime, 'user_is_staff', False)
def max_score(self):
"""Return the max score of the external activity.
Also called by courseware.grades.get_score function.
"""
return self.points