Commit f318e563 by Justin Riley

latest changes from production

Refactored for latest edX changes
parent 3fe35770
import json
import logging
import random
import requests
from lxml import etree
......@@ -12,7 +11,8 @@ from django.contrib.auth.models import User
from xmodule.x_module import XModule
from xmodule.seq_module import SequenceDescriptor
from xblock.core import Integer, Scope, String
from xblock.fields import Scope, String, Boolean
from xblock.fragment import Fragment
log = logging.getLogger('mitx.' + __name__)
......@@ -43,14 +43,15 @@ class ProctorPanel(object):
self.user = User.objects.get(pk=user_id)
def is_released(self):
url = '{2}/cmd/status/{0}/{1}'.format(self.user_id, self.procset_name, self.ProctorPanelServer)
#url = '{2}/cmd/status/{0}/{1}'.format(self.user_id, self.procset_name, self.ProctorPanelServer)
url = '{1}/cmd/status/{0}'.format(self.user_id, self.ProctorPanelServer)
log.info('ProctorPanel url={0}'.format(url))
#ret = self.ses.post(url, data={'userid' : self.user_id, 'urlname': self.procset_name}, verify=False)
auth = (self.ProctorPanelInterface.get('username'), self.ProctorPanelInterface.get('password'))
ret = self.ses.get(url, verify=False, auth=auth)
ret = self.ses.get(url, verify=False, auth=auth, params={'problem': self.procset_name})
try:
retdat = json.loads(ret.content)
except Exception as err:
except Exception:
log.error('bad return from proctor panel: ret.content={0}'.format(ret.content))
retdat = {}
......@@ -60,7 +61,15 @@ class ProctorPanel(object):
class ProctorFields(object):
#display_name = String(
# display_name="Display Name",
# help="This name appears in the grades progress page",
# scope=Scope.settings,
# default="Proctored Module"
#)
procset_name = String(help="Name of this proctored set", scope=Scope.settings)
staff_release = Boolean(help="True if staff forced release independent of proctor panel",
default=False, scope=Scope.user_state)
class ProctorModule(ProctorFields, XModule):
......@@ -82,17 +91,21 @@ class ProctorModule(ProctorFields, XModule):
"""
js = {'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee'),
resource_string(__name__, 'js/src/conditional/display.coffee'),
resource_string(__name__, 'js/src/collapsible.coffee'),
]}
js = {
'coffee': [
resource_string(__name__, 'js/src/javascript_loader.coffee'),
resource_string(__name__, 'js/src/conditional/display.coffee')
],
'js': [
resource_string(__name__, 'js/src/collapsible.js')
],
}
js_module_name = "Conditional"
css = {'scss': [resource_string(__name__, 'css/capa/display.scss')]}
def __init__(self, *args, **kwargs):
XModule.__init__(self, *args, **kwargs)
super(ProctorModule, self).__init__(*args, **kwargs)
# check proctor panel to see if this should be released
user_id = self.system.seed
self.pp = ProctorPanel(user_id, self.procset_name)
......@@ -103,7 +116,16 @@ class ProctorModule(ProctorFields, XModule):
log.info('Proctor module child={0}'.format(self.child))
log.info('Proctor module child display_name={0}'.format(self.child.display_name))
self.display_name = self.child.display_name
# TODO: This attr is read-only now - need to figure out if/why this is
# needed and find a fix if necessary (disabling doesnt appear to break
# anything)
#self.display_name = self.child.display_name
def is_released(self):
if self.staff_release:
return True
return self.pp.is_released()
def get_child_descriptors(self):
......@@ -114,16 +136,19 @@ class ProctorModule(ProctorFields, XModule):
def not_released_html(self):
return self.system.render_template('proctor_release.html', {
return Fragment(self.system.render_template('proctor_release.html', {
'element_id': self.location.html_id(),
'id': self.id,
'name': self.display_name or self.procset_name,
'pp': self.pp,
})
'location': self.location,
'ajax_url': self.system.ajax_url,
'is_staff': self.system.user_is_staff,
}))
def get_html(self):
if not self.pp.is_released(): # check for release each time we do get_html()
def student_view(self, context):
if not self.is_released(): # check for release each time we do get_html()
log.info('is_released False')
return self.not_released_html()
# return "<div>%s not yet released</div>" % self.display_name
......@@ -131,21 +156,38 @@ class ProctorModule(ProctorFields, XModule):
log.info('is_released True')
# for sequential module, just return HTML (no ajax container)
if self.child.category in ['sequential', 'videosequence', 'problemset']:
return self.child.get_html()
if self.child.category in ['sequential', 'videosequence', 'problemset', 'randomize']:
html = self.child.render('student_view', context)
if self.staff_release:
dishtml = self.system.render_template('proctor_disable.html', {
'element_id': self.location.html_id(),
'is_staff': self.system.user_is_staff,
'ajax_url': self.system.ajax_url,
})
html.content = dishtml + html.content
return html
# return ajax container, so that we can dynamically check for is_released changing
return self.system.render_template('conditional_ajax.html', {
return Fragment(self.system.render_template('conditional_ajax.html', {
'element_id': self.location.html_id(),
'id': self.id,
'ajax_url': self.system.ajax_url,
'depends': '',
})
}))
def handle_ajax(self, _dispatch, _data):
if not self.pp.is_released(): # check for release each time we do get_html()
if self.system.user_is_staff and _dispatch=='release':
self.staff_release = True
# return '<html><head><META HTTP-EQUIV="refresh" CONTENT="15"></head><body>Release successful</body></html>'
return json.dumps({'html': 'staff_release successful'})
if self.system.user_is_staff and _dispatch=='disable':
self.staff_release = False
return json.dumps({'html': 'staff_disable successful'})
# return '<html><head><META HTTP-EQUIV="refresh" CONTENT="15"></head><body>Disable successful</body></html>'
if not self.is_released(): # check for release each time we do get_html()
log.info('is_released False')
# html = "<div>%s not yet released</div>" % self.display_name
html = self.not_released_html()
......@@ -171,6 +213,5 @@ class ProctorDescriptor(ProctorFields, SequenceDescriptor):
xml_object = etree.Element('proctor')
for child in self.get_children():
xml_object.append(
etree.fromstring(child.export_to_xml(resource_fs)))
self.runtime.add_block_as_child_node(child, xml_object)
return xml_object
import json
import logging
import random
......@@ -53,16 +54,20 @@ class RandomizeModule(RandomizeFields, XModule):
if self.choice is None:
# choose one based on the system seed, or randomly if that's not available
if num_choices > 0:
if self.system.seed is not None:
if self.system.seed is not None and 'use_randrange' not in (self.descriptor.xml_attributes or []):
self.choice = self.system.seed % num_choices
log.debug('using seed for %s choice=%s' % (str(self.location), self.choice))
else:
self.choice = random.randrange(0, num_choices)
log.debug('using randrange for %s' % str(self.location))
else:
log.debug('error in randomize: num_choices = %s' % num_choices)
if self.choice is not None:
self.child_descriptor = self.descriptor.get_children()[self.choice]
# Now get_children() should return a list with one element
log.debug("children of randomize module (should be only 1): %s",
self.get_children())
log.debug("choice=%s in %s, children of randomize module (should be only 1): %s",
self.choice, str(self.location), self.get_children())
self.child = self.get_children()[0]
else:
self.child_descriptor = None
......@@ -83,8 +88,32 @@ class RandomizeModule(RandomizeFields, XModule):
# raise error instead? In fact, could complain on descriptor load...
return Fragment(content=u"<div>Nothing to randomize between</div>")
if self.system.user_is_staff:
dishtml = self.system.render_template('randomize_control.html', {
'element_id': self.location.html_id(),
'is_staff': self.system.user_is_staff,
'ajax_url': self.system.ajax_url,
'choice': self.choice,
'num_choices': len(self.descriptor.get_children()),
})
# html = '<html><p>Welcome, staff. Randomize loc=%s ; Choice=%s</p><br/><hr/></br/>' % (str(self.location), self.choice)
child_html = self.child.render('student_view', context)
return Fragment(u"<html>" + dishtml + child_html.content + u"</html>")
return self.child.render('student_view', context)
def handle_ajax(self, dispatch, data):
if dispatch=='next':
self.choice = self.choice + 1
elif dispatch=='jump':
log.debug('jump, data=%s' % data)
self.choice = int(data['choice'])
num_choices = len(self.descriptor.get_children())
if self.choice >= num_choices:
self.choice = 0
result = {'ret': "choice=%s" % self.choice}
return json.dumps(result)
def get_icon_class(self):
return self.child.get_icon_class() if self.child else 'other'
......
#!/usr/bin/python
#
# Largely for 3.091-exam.
#
# Compute number of times a given problem has been attempted by a student, including StudentModuleHistory.
# Do this by walking through the course tree. For every assessment problem, look up all matching
# StudehtModuleHistory items. Count number of attempts passed, and number failed. Remove staff data.
#
# Output table with: problem url_id, problem name, number students assigned, number attempts failed, number attempts succeeded
#
from courseware.module_tree_reset import *
from courseware.access import get_access_group_name
from django.core.management.base import BaseCommand
import json
import csv
#-----------------------------------------------------------------------------
from django.conf import settings
from xmodule.modulestore.django import modulestore
from django.dispatch import Signal
from request_cache.middleware import RequestCache
from django.core.cache import get_cache
if True:
CACHE = get_cache('mongo_metadata_inheritance')
for store_name in settings.MODULESTORE:
store = modulestore(store_name)
store.metadata_inheritance_cache_subsystem = CACHE
store.request_cache = RequestCache.get_request_cache()
modulestore_update_signal = Signal(providing_args=['modulestore', 'course_id', 'location'])
store.modulestore_update_signal = modulestore_update_signal
#-----------------------------------------------------------------------------
def ComputeStats():
pminfo = ProctorModuleInfo()
# get list of all problems
all_problems = []
stats = []
self = pminfo
if True:
for rpmod in self.rpmods:
assignment_set_name = rpmod.ra_ps.display_name
for ploc in rpmod.ra_rand.children:
problem = self.ms.get_item(ploc)
problem.assignment_set_name = assignment_set_name
all_problems.append(problem)
staffgroup = get_access_group_name(self.course, 'staff')
cnt = 0
class Stats(object):
def __init__(self):
self.nassigned = 0
self.nattempts = 0
self.npassed = 0
for problem in all_problems:
print problem.id
stat = Stats()
smset0 = StudentModule.objects.filter(module_state_key=problem.id, student__is_staff=False)
smset = smset0.exclude(student__groups__name=staffgroup)
def passed(state):
if 'correct_map' not in state:
return False
if not state['correct_map']: # correct_map = {}
return False
return all([x['correctness']=='correct' for x in state.get('correct_map').values()]) # must be all correct to pass
def update_stats(sm, stat, history=False):
if sm.grade is None:
return
state = json.loads(sm.state)
if not 'attempts' in state:
return
if not state.get('done', False):
return "notdone"
if not history:
stat.nattempts += state['attempts']
if passed(state):
stat.npassed += 1
return "passed"
return "attempted"
for sm in smset:
stat.nassigned += 1
ret = update_stats(sm, stat)
if ret in ['passed', 'attempted']:
continue
smhset = StudentModuleHistory.objects.filter(student_module=sm)
states = [ json.loads(smh.state) for smh in smhset ]
okset = [ passed(x) for x in states ]
attempts = [ x.get('attempts', 0) for x in states]
stat.nattempts += max(attempts)
if any(okset):
stat.npassed += 1
#print " assigned=%d, attempts=%d, passed=%d" % (nassigned, nattempts, npassed)
stats.append(dict(problem_id=problem.id,
pset=problem.assignment_set_name,
problem_name=problem.display_name,
due=str(problem.due),
max_attempts=problem.max_attempts,
assigned=stat.nassigned,
attempts=stat.nattempts,
passed=stat.npassed,
))
cnt += 1
#if cnt>5:
# break
if True:
dddir = settings.MITX_FEATURES.get('DOWNLOAD_DATA_DIR','')
fndir = dddir / (self.course.id.replace('/','__'))
dt = datetime.datetime.now().strftime('%Y-%m-%d-%H%M')
fn = fndir / "problem-stats-%s-%s.csv" % (self.course.id.split('/')[1], dt)
print "Saving data to %s" % fn
# fn = "stats.csv"
fieldnames = ['problem_id', 'pset', 'problem_name', 'due', 'max_attempts', 'assigned', 'attempts', 'passed']
fp = open(fn,'w')
writer = csv.DictWriter(fp, fieldnames, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL)
writer.writeheader()
for row in stats:
try:
writer.writerow(row)
except Exception as err:
print "Oops, failed to write %s, error=%s" % (row, err)
fp.close()
#-----------------------------------------------------------------------------
class Command(BaseCommand):
help = """Generate CSV file with problem attempts statistics;
CSV file columns include problem id, assigned, max_attempts, attempts, passed
for every problem in the course. Arguments: None. Works only on 3.091-exam"""
def handle(self, *args, **options):
ComputeStats()
#!/usr/bin/python
#
# 19-Sep-13 ichuang@mit.edu
import csv
from courseware.module_tree_reset import *
from django.core.management.base import BaseCommand
#-----------------------------------------------------------------------------
from django.conf import settings
from xmodule.modulestore.django import modulestore
from django.dispatch import Signal
from request_cache.middleware import RequestCache
from django.core.cache import get_cache
if True:
CACHE = get_cache('mongo_metadata_inheritance')
for store_name in settings.MODULESTORE:
store = modulestore(store_name)
store.metadata_inheritance_cache_subsystem = CACHE
store.request_cache = RequestCache.get_request_cache()
modulestore_update_signal = Signal(providing_args=['modulestore', 'course_id', 'location'])
store.modulestore_update_signal = modulestore_update_signal
#-----------------------------------------------------------------------------
class Command(BaseCommand):
help = """Reset exam attempts for 3.091 exam, Fall 2013.
Records students and problems which were reset.
Give filename as argument. Output is CSV file."""
def handle(self, *args, **options):
# fn = 'reset_3091exam.csv'
fn = args[0]
pminfo = ProctorModuleInfo()
students = User.objects.filter(courseenrollment__course_id=pminfo.course.id).order_by('username')
# students = User.objects.filter(username='ichuang')
data = []
# write out to csv file
fieldnames = ['id', 'name', 'username', 'assignment', 'problem', 'date', 'earned', 'possible']
fp = open(fn,'w')
csvf = csv.DictWriter(fp, fieldnames, dialect="excel", quotechar='"', quoting=csv.QUOTE_ALL)
csvf.writeheader()
cnt = 0
for student in students:
dat = pminfo.get_assignments_attempted_and_failed(student, do_reset=True)
data += dat
for row in dat:
csvf.writerow(row)
fp.flush()
cnt += 1
#if cnt>3:
# break
fp.close()
# print data
import datetime
import json
import pytz
from courseware.views import *
from instructor.offline_gradecalc import student_grades
from collections import OrderedDict
log = logging.getLogger("mitx.module_tree_reset")
#-----------------------------------------------------------------------------
class TreeNode(object):
def __init__(self, module, level, student):
self.module = module
self.level = level
self.student = student
self.smstate = None
if self.module.category in ['randomize', 'problem', 'problemset']:
try:
self.smstate = StudentModule.objects.get(module_state_key=str(self.module.location), student=student)
except StudentModule.DoesNotExist:
pass
def __str__(self):
s = "-"*self.level + ("> %s" % str(self.module.location))
s += ' (%s)' % self.module.display_name
if self.smstate is not None:
s += ' [%s]' % self.smstate.state
return s
__repr__ = __str__
class TreeNodeSet(list):
def __init__(self, module, ms, student):
list.__init__(self)
self.parent_module = module
self.student = student
self.get_tree(module, ms)
self.get_modules_to_reset()
def get_tree(self, module, ms, level=1):
self.append(TreeNode(module, level, self.student))
for child in getattr(module, 'children', []):
m = ms.get_item(child)
if m is not None:
self.get_tree(m, ms, level+1)
def get_modules_to_reset(self):
self.rset = []
self.pset = []
for tn in self:
if tn.module.category=='randomize':
self.rset.append(tn)
elif tn.module.category in ['problem', 'problemset']:
self.pset.append(tn)
def reset_randomization(self):
'''
Go through all <problem> and <randomize> modules in tree and reset their StudentModule state to empty.
'''
msg = "Resetting all <problem> and <randomize> in tree of parent module %s\n" % self.parent_module
for module in self.rset + self.pset:
msg += " Resetting %s, old state=%s\n" % (module, (module.smstate.state if module.smstate is not None else {}))
if module.smstate is not None:
module.smstate.state = '{}'
module.smstate.grade = None
module.smstate.save()
return msg
#-----------------------------------------------------------------------------
class DummyRequest(object):
META = {}
def __init__(self):
return
def get_host(self):
return 'edx.mit.edu'
def is_secure(self):
return False
#-----------------------------------------------------------------------------
class ProctorModuleInfo(object):
def __init__(self, course_loc=''):
if not course_loc:
course_loc = 'i4x://MITx/3.091r-exam/course/2013_Fall_residential_exam'
self.ms = modulestore()
self.course = self.ms.get_item(course_loc)
self.get_released_proctor_modules()
def get_released_proctor_modules(self):
chapters = []
for loc in self.course.children:
chapters.append(self.ms.get_item(loc))
#print "chapters:"
#print [c.id for c in chapters]
pmods = []
for c in chapters:
seq = self.ms.get_item(c.children[0])
if seq.category=='proctor':
pmods.append(seq)
#print "proctor modules:"
#print [x.id for x in pmods]
now = datetime.datetime.now(pytz.utc)
rpmods = [p for p in pmods if p.lms.start < now]
for rpmod in rpmods:
rpmod.ra_ps = self.ms.get_item(rpmod.children[0]) # the problemset
rpmod.ra_rand = self.ms.get_item(rpmod.ra_ps.children[0]) # the randomize
# rpmod.ra_prob = self.ms.get_item(rpmod.ra_rand.children[0]) # the problem
#print "released pmods"
#print [x.id for x in rpmods]
self.chapters = chapters
self.pmods = pmods
self.rpmods = rpmods
return rpmods
def get_grades(self, student=None, request=None):
if student is None:
student = self.student
if request is None:
request = DummyRequest()
request.user = student
request.session = {}
try:
gradeset = student_grades(student, request, self.course, keep_raw_scores=False, use_offline=False)
except Exception as err:
#log.exception("Failed to get grades for %s" % student)
print("Failed to get grades for %s" % student)
gradeset = []
self.gradeset = gradeset
return gradeset
def get_student_status(self, student):
'''
For a given student, and for all released proctored modules, get StudentModule state for each, and
see which randomized problem was selected for a student (if any).
'''
if isinstance(student, str):
student = User.objects.get(username=student)
self.student = student
smstates = OrderedDict()
# temporary - for debugging; flushes db cache
if False:
from django.db import transaction
try:
transaction.commit()
except Exception as err:
print "db cache flushed"
class StateInfo(object):
def __init__(self):
self.state = '{}'
return
for rpmod in self.rpmods: # assume <proctor><problemset><randomized/></problemset></prcotor> structure
try:
sm = StudentModule.objects.get(module_state_key=str(rpmod.ra_rand.location), student=student) # randomize state
except StudentModule.DoesNotExist:
sm = StateInfo()
sm.rpmod = rpmod
try:
ps_sm = StudentModule.objects.get(module_state_key=str(rpmod.ra_ps.location), student=student) # problemset state
except StudentModule.DoesNotExist:
ps_sm = StateInfo()
sm.ps_sm = ps_sm
sm.score = None
# get title (display_name) of problem assigned, if student had started a problem
# base this on the "choice" from the randmize module state
try:
sm.choice = int(json.loads(sm.state)['choice'])
except Exception as err:
sm.choice = None
if sm.choice is not None:
try:
sm.problem = self.ms.get_item(rpmod.ra_rand.children[sm.choice])
sm.problem_name = sm.problem.display_name
except Exception as err:
log.exception("Failed to get rand child choice=%s for %s student=%s" % (sm.choice, rpmod.ra_rand, student))
sm.problem = None
sm.problem_name = None
else:
sm.problem = None
sm.problem_name = None
smstates[rpmod.url_name] = sm # the url_name should be like 'LS1' and be the same key used in the grade scores
self.smstates = smstates
# get grades, match gradeset assignments with StudentModule states, and put grades there
self.get_grades()
for score in self.gradeset['totaled_scores']['Assessment']:
if score.section in smstates:
smstates[score.section].score = score
s = 'State for student %s:\n' % student
status = {} # this can be turned into a JSON string for the proctor panel
status['student'] = dict(username=str(student), name=student.profile.name, id=student.id)
status['assignments'] = []
for (name, sm) in smstates.iteritems():
# attempted = (sm.score is not None) # this doesn't work, since score will always appear?
attempted = 'position' in sm.ps_sm.state # if student has visited the problemset then position will have been set
if not attempted and sm.score is not None and sm.score.earned:
attempted = True
stat = dict(name=name, assignment=sm.rpmod.ra_ps.display_name, pm_sm=sm.ps_sm.state, choice=sm.choice,
problem=sm.problem_name,
attempted=attempted,
earned=(sm.score.earned if sm.score is not None else None),
possible=(sm.score.possible if sm.score is not None else None),
)
status['assignments'].append(stat)
s += "[%s] %s -> %s (%s) %s [%s]\n" % (name, stat['assignment'], stat['pm_sm'], sm.choice, sm.problem_name, sm.score)
self.status = status
self.status_str = s
return status
def get_student_grades(self, student):
'''
Return student grades for assessments as a dict suitable for CSV file output,
with id, name, username, prob1, grade1, prob2, grade2, ...
where grade1 = points earned on assignment LS1, or '' if not attempted
and prob1 = problem which was assigned or '' if not attempted
'''
status = self.get_student_status(student)
ret = OrderedDict()
ret['id'] = student.id
ret['name'] = student.profile.name
# ret['username'] = student.username
ret['email'] = '%s@mit.edu' % student.username
for stat in status['assignments']:
if stat['attempted']:
ret["problem_%s" % stat['name']] = stat['problem']
ret["grade_%s" % stat['name']] = stat['earned']
else:
ret["problem_%s" % stat['name']] = ''
ret["grade_%s" % stat['name']] = ''
return ret
def get_assignments_attempted_and_failed(self, student, do_reset=False):
status = self.get_student_status(student)
assignments = []
for stat in status['assignments']:
if stat['attempted']:
if not stat['earned'] == stat['possible']:
s = "Student %s Assignment %s attempted '%s' but failed (%s/%s)" % (student, stat['name'], stat['problem'], stat['earned'], stat['possible'])
assignments.append(OrderedDict(id=student.id,
name=student.profile.name,
username=student.username,
assignment=stat['name'],
problem=stat['problem'],
date=str(datetime.datetime.now()),
earned=stat['earned'],
possible=stat['possible'],
)
)
if do_reset:
aaf = assignments[-1]
try:
log.debug('resetting %s for student %s' % (aaf['assignment'], aaf['username']))
pmod = self.ms.get_item('i4x://MITx/3.091r-exam/proctor/%s' % aaf['assignment'])
tnset = TreeNodeSet(pmod, self.ms, student)
msg = tnset.reset_randomization()
log.debug(str(msg))
except Exception as err:
log.exception("Failed to do reset of %s for %s" % (aaf['assignment'], student))
return assignments
#-----------------------------------------------------------------------------
def getip(request):
'''
Extract IP address of requester from header, even if behind proxy
'''
ip = request.META.get('HTTP_X_REAL_IP', '') # nginx reverse proxy
if not ip:
ip = request.META.get('REMOTE_ADDR', 'None')
return ip
#-----------------------------------------------------------------------------
ALLOWED_IPS = [ '173.48.139.155', '10.152.159.162', '54.235.195.90' ]
#ALLOWED_IPS = [ ]
ALLOWED_STAFF = 'staff_MITx/3.091r-exam/2013_Fall_residential_exam'
def index(request):
ip = getip(request)
if not ip in ALLOWED_IPS:
if request.user and request.user.is_staff and False:
log.debug('request allowed because user=%s is staff' % request.user)
elif request.user is not None and request.user:
groups = [g.name for g in request.user.groups.all()]
if ALLOWED_STAFF in groups:
log.debug('request allowed because user=%s is in group %s' % (request.user, ALLOWED_STAFF))
else:
log.debug('request denied, user=%s, groups %s' % (request.user, groups))
# return HttpResponse('permission denied', status=403)
else:
log.debug('request denied, user=%s, groups %s' % (request.user, groups))
# return HttpResponse('permission denied', status=403)
else:
log.debug('request allowed, in ALLOWED_IPS')
username = request.GET.get('username')
try:
student = User.objects.get(username=username)
except User.DoesNotExist:
return HttpResponse(json.dumps({'msg':'User does not exist', 'error': True}))
cmd = request.GET.get('cmd')
if cmd=='reset':
location = request.GET.get('location') # eg proctor/Assessment_2
location = location.replace(' ','_')
ms = modulestore()
pmod = ms.get_item('i4x://MITx/3.091r-exam/proctor/%s' % location)
tnset = TreeNodeSet(pmod, ms, student)
s = ''
for r in tnset.rset + tnset.pset:
s += str(r) + '\n'
msg = tnset.reset_randomization()
# return render_to_response("module_tree_reset.html", {'status': s, 'msg': msg})
#cmd = 'grades'
cmd = 'status'
if cmd=='status':
try:
pminfo = ProctorModuleInfo()
status = pminfo.get_student_status(student)
except Exception as err:
log.exception("Failed to get status for %s" % student)
return HttpResponse(json.dumps({'msg':'Error getting grades for %s' % student, 'error': True, 'errstr': str(err)}))
return HttpResponse(json.dumps(status))
if cmd=='grades':
# from instructor.offline_gradecalc import student_grades
ms = modulestore()
course = ms.get_item('i4x://MITx/3.091r-exam/course/2013_Fall_residential_exam')
try:
gradeset = student_grades(student, request, course, keep_raw_scores=False, use_offline=False)
except Exception as err:
log.exception("Failed to get grades for %s" % student)
return HttpResponse(json.dumps({'msg':'Error getting grades for %s' % student, 'error': True}))
grades = gradeset['totaled_scores']
grades['student_id'] = student.id
return HttpResponse(json.dumps(grades))
return render_to_response("module_tree_reset.html", {'status': 'unknown command', 'msg': ''})
<p>Status:</p>
<pre>
${status}
</pre>
<p>Msg:</p>
<pre>
${msg}
</pre>
% if is_staff:
<div id="proctor_${element_id}_disable">
<p>
<input id="proctor_disable" type="button" value="This is a proctored module, released on staff override. Disable access"/></p>
</div>
<hr/>
% endif
<script type="text/javascript">
do_disable = function(){
$.get( "${ajax_url}/disable", function( data ) {
$( "#proctor_disable" ).html( data );
alert( "disable successful" );
location.reload();
});
}
$('#proctor_disable').click(do_disable);
</script>
......@@ -7,12 +7,36 @@
<div id="proctor_stat_${element_id}"></div>
% if is_staff:
<p><input id="proctor_release" type="button" value="Staff override: Release Module for myself"/></p>
% endif
</div>
% if is_staff:
<script type="text/javascript">
do_release = function(){
$.get( "${ajax_url}/release", function( data ) {
$( "#proctor_release" ).html( data );
procrel.set_skiperr();
alert( "release successful" );
location.reload();
});
}
$('#proctor_release').click(do_release);
</script>
% endif
<script type="text/javascript">
procrel = (function(){
var el = $('#proctor_${element_id}');
var skiperr = false;
var set_skiperr = function(){ skiperr = true; }
var mkurl = function(cmd) {
ps = encodeURIComponent("${pp.procset_name}");
return "${pp.ProctorPanelServer}/cmd/" + cmd + "/${pp.user_id}/" + ps;
......@@ -38,7 +62,9 @@ procrel = (function(){
success: gfun,
dataType: "json",
error: function(xhr, status, error) {
alert('Error: cannot connect to server' + status + "error:" + error);
if (!skiperr){
alert('Error: cannot connect to server' + status + "error:" + error);
}
}
});
}
......@@ -73,7 +99,10 @@ procrel = (function(){
el.click(make_request);
return { "check": check_access, "make": make_request, "mkurl": mkurl, "do_pp_get": do_pp_get };
return { "check": check_access, "make": make_request,
"mkurl": mkurl, "do_pp_get": do_pp_get,
"set_skiperr": set_skiperr
};
}() );
</script>
\ No newline at end of file
</script>
% if is_staff:
<div id="randomize_${element_id}_control">
<p>
<input id="randomize_next" type="button" value="This is randomized module ${choice} of ${num_choices}: Go to next"/>
or jump to:
<select id="randomize_jump">
% for kchoice in range(num_choices):
<% selected = "selected" if kchoice==choice else "" %>
<option value="${kchoice}" ${selected}>${kchoice}</option>
% endfor
</select>
</p>
</div>
<hr/>
<script type="text/javascript">
do_next = function(){
$.get( "${ajax_url}/next", function( data ) {
$( "#randomize_next" ).html( data );
alert( "next successful" );
location.reload();
});
}
$('#randomize_next').click(do_next);
rand_jump = function(){
var choice = $('#randomize_jump option:selected').text();
$.post( "${ajax_url}/jump", { 'choice': choice},
function( data ) {
$( "#randomize_next" ).html( data );
alert( "jump to " + choice + " successful" );
location.reload();
});
}
$('#randomize_jump').change(rand_jump);
</script>
% endif
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