Merged with profile changes again. Last time was backwards
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
from lxml import etree
|
||||
from mako.template import Template
|
||||
@@ -11,14 +12,14 @@ try: # This lets us do __name__ == ='__main__'
|
||||
except:
|
||||
settings = None
|
||||
|
||||
|
||||
|
||||
''' This file will eventually form an abstraction layer between the
|
||||
course XML file and the rest of the system.
|
||||
|
||||
TODO: Shift everything from xml.dom.minidom to XPath (or XQuery)
|
||||
'''
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
def fasthash(string):
|
||||
m = hashlib.new("md4")
|
||||
m.update(string)
|
||||
@@ -83,11 +84,40 @@ def id_tag(course):
|
||||
if elem.get('id'):
|
||||
pass
|
||||
elif elem.get(default_ids[elem.tag]):
|
||||
new_id = elem.get(default_ids[elem.tag]) # Convert to alphanumeric
|
||||
new_id = "".join([a for a in new_id if a.isalnum()])
|
||||
new_id = elem.get(default_ids[elem.tag])
|
||||
new_id = "".join([a for a in new_id if a.isalnum()]) # Convert to alphanumeric
|
||||
# Without this, a conflict may occur between an hmtl or youtube id
|
||||
new_id = default_ids[elem.tag] + new_id
|
||||
elem.set('id', new_id)
|
||||
else:
|
||||
elem.set('id', fasthash(etree.tostring(elem)))
|
||||
elem.set('id', fasthash(etree.tostring(elem)))
|
||||
|
||||
def propogate_downward_tag(element, attribute_name, parent_attribute = None):
|
||||
''' This call is to pass down an attribute to all children. If an element
|
||||
has this attribute, it will be "inherited" by all of its children. If a
|
||||
child (A) already has that attribute, A will keep the same attribute and
|
||||
all of A's children will inherit A's attribute. This is a recursive call.'''
|
||||
|
||||
if (parent_attribute == None): #This is the entry call. Select all due elements
|
||||
all_attributed_elements = element.xpath("//*[@" + attribute_name +"]")
|
||||
for attributed_element in all_attributed_elements:
|
||||
attribute_value = attributed_element.get(attribute_name)
|
||||
for child_element in attributed_element:
|
||||
propogate_downward_tag(child_element, attribute_name, attribute_value)
|
||||
else:
|
||||
'''The hack below is because we would get _ContentOnlyELements from the
|
||||
iterator that can't have due dates set. We can't find API for it. If we
|
||||
ever have an element which subclasses BaseElement, we will not tag it'''
|
||||
if not element.get(attribute_name) and type(element) == etree._Element:
|
||||
element.set(attribute_name, parent_attribute)
|
||||
|
||||
for child_element in element:
|
||||
propogate_downward_tag(child_element, attribute_name, parent_attribute)
|
||||
else:
|
||||
#This element would have already been found by Xpath, so we return
|
||||
#for now and trust that this element will get its turn to propogate
|
||||
#to its children later.
|
||||
return
|
||||
|
||||
template_lookup = TemplateLookup(directories = [settings.DATA_DIR],
|
||||
module_directory = settings.MAKO_MODULE_DIR)
|
||||
@@ -101,6 +131,8 @@ def course_file(user):
|
||||
|
||||
tree = etree.XML(data_template.render(**options))
|
||||
id_tag(tree)
|
||||
propogate_downward_tag(tree, "due")
|
||||
propogate_downward_tag(tree, "graded")
|
||||
return tree
|
||||
|
||||
def module_xml(coursefile, module, id_tag, module_id):
|
||||
|
||||
@@ -24,7 +24,7 @@ class StudentModule(models.Model):
|
||||
module_id = models.CharField(max_length=255, db_index=True) # Filename for homeworks, etc.
|
||||
student = models.ForeignKey(User, db_index=True)
|
||||
class Meta:
|
||||
unique_together = (('student', 'module_id', 'module_type'),)
|
||||
unique_together = (('student', 'module_id'),)
|
||||
|
||||
## Internal state of the object
|
||||
state = models.TextField(null=True, blank=True)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import StringIO
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import sys
|
||||
@@ -32,6 +33,8 @@ import courseware.modules.seq_module
|
||||
import courseware.modules.vertical_module
|
||||
import courseware.modules.video_module
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
## TODO: Add registration mechanism
|
||||
modx_modules={'problem':courseware.modules.capa_module.LoncapaModule,
|
||||
'video':courseware.modules.video_module.VideoModule,
|
||||
@@ -59,11 +62,10 @@ def make_track_function(request):
|
||||
def modx_dispatch(request, module=None, dispatch=None, id=None):
|
||||
''' Generic view for extensions. '''
|
||||
# Grab the student information for the module from the database
|
||||
s = StudentModule.objects.filter(module_type=module,
|
||||
student=request.user,
|
||||
s = StudentModule.objects.filter(student=request.user,
|
||||
module_id=id)
|
||||
if len(s) == 0:
|
||||
print "ls404", module, request.user, id
|
||||
log.debug("Couldnt find module for user and id " + str(module) + " " + str(request.user) + " "+ str(id))
|
||||
raise Http404
|
||||
|
||||
s=s[0]
|
||||
|
||||
@@ -46,13 +46,13 @@ class LoncapaModule(XModule):
|
||||
|
||||
def get_html(self):
|
||||
return render_to_string('problem_ajax.html',
|
||||
{'id':self.filename,
|
||||
{'id':self.item_id,
|
||||
'ajax_url':self.ajax_url,
|
||||
})
|
||||
|
||||
def get_init_js(self):
|
||||
return render_to_string('problem.js',
|
||||
{'id':self.filename,
|
||||
{'id':self.item_id,
|
||||
'ajax_url':self.ajax_url,
|
||||
})
|
||||
|
||||
@@ -100,7 +100,7 @@ class LoncapaModule(XModule):
|
||||
|
||||
html=render_to_string('problem.html',
|
||||
{'problem' : content,
|
||||
'id' : self.filename,
|
||||
'id' : self.item_id,
|
||||
'check_button' : check_button,
|
||||
'reset_button' : reset_button,
|
||||
'save_button' : save_button,
|
||||
|
||||
@@ -43,6 +43,12 @@ def profile(request):
|
||||
chapters = dom.xpath('//course[@name=$course]/chapter', course=course)
|
||||
|
||||
responses=StudentModule.objects.filter(student=request.user)
|
||||
response_by_id = {}
|
||||
for response in responses:
|
||||
response_by_id[response.module_id] = response
|
||||
|
||||
|
||||
total_scores = {}
|
||||
|
||||
for c in chapters:
|
||||
chname=c.get('name')
|
||||
@@ -53,31 +59,124 @@ def profile(request):
|
||||
scores=[]
|
||||
if len(problems)>0:
|
||||
for p in problems:
|
||||
id = p.get('filename')
|
||||
id = p.get('id')
|
||||
correct = 0
|
||||
for response in responses:
|
||||
if response.module_id == id:
|
||||
if response.grade!=None:
|
||||
correct=response.grade
|
||||
else:
|
||||
correct=0
|
||||
if id in response_by_id:
|
||||
response = response_by_id[id]
|
||||
if response.grade!=None:
|
||||
correct=response.grade
|
||||
|
||||
total=courseware.modules.capa_module.LoncapaModule(etree.tostring(p), "id").max_score() # TODO: Add state. Not useful now, but maybe someday problems will have randomized max scores?
|
||||
scores.append((int(correct),total))
|
||||
scores.append((int(correct),total, ( True if s.get('graded') == "True" else False ) ))
|
||||
|
||||
|
||||
section_total = (sum([score[0] for score in scores]),
|
||||
sum([score[1] for score in scores]))
|
||||
|
||||
graded_total = (sum([score[0] for score in scores if score[2]]),
|
||||
sum([score[1] for score in scores if score[2]]))
|
||||
|
||||
#Add the graded total to total_scores
|
||||
format = s.get('format') if s.get('format') else ""
|
||||
if format and graded_total[1] > 0:
|
||||
format_scores = total_scores[ format ] if format in total_scores else []
|
||||
format_scores.append( graded_total )
|
||||
total_scores[ format ] = format_scores
|
||||
|
||||
score={'course':course,
|
||||
'section':s.get("name"),
|
||||
'chapter':c.get("name"),
|
||||
'scores':scores,
|
||||
'section_total' : section_total,
|
||||
'format' : format,
|
||||
}
|
||||
hw.append(score)
|
||||
|
||||
|
||||
|
||||
|
||||
def totalWithDrops(scores, drop_count):
|
||||
sorted_scores = sorted( enumerate(scores), key=lambda x: -x[1]['percentage'] ) #Note that this key will sort the list descending
|
||||
dropped_indices = [score[0] for score in sorted_scores[-drop_count:]] # A list of the indices of the dropped scores
|
||||
aggregate_score = 0
|
||||
for index, score in enumerate(scores):
|
||||
if index not in dropped_indices:
|
||||
aggregate_score += score['percentage']
|
||||
|
||||
aggregate_score /= len(scores) - drop_count
|
||||
|
||||
return aggregate_score, dropped_indices
|
||||
|
||||
#Figure the homework scores
|
||||
homework_scores = total_scores['Homework'] if 'Homework' in total_scores else []
|
||||
homework_percentages = []
|
||||
for i in range(12):
|
||||
if i < len(homework_scores):
|
||||
percentage = homework_scores[i][0] / float(homework_scores[i][1])
|
||||
summary = "{:.0%} ({}/{})".format( percentage, homework_scores[i][0], homework_scores[i][1] )
|
||||
else:
|
||||
percentage = 0
|
||||
summary = "0% (?/?)"
|
||||
summary = "Homework {} - {}".format(i + 1, summary)
|
||||
|
||||
homework_percentages.append( {'percentage': percentage, 'summary': summary} )
|
||||
homework_total, homework_dropped_indices = totalWithDrops(homework_percentages, 2)
|
||||
|
||||
#Figure the lab scores
|
||||
lab_scores = total_scores['Lab'] if 'Lab' in total_scores else []
|
||||
lab_percentages = []
|
||||
for i in range(12):
|
||||
if i < len(lab_scores):
|
||||
percentage = lab_scores[i][0] / float(lab_scores[i][1])
|
||||
summary = "{:.0%} ({}/{})".format( percentage, lab_scores[i][0], lab_scores[i][1] )
|
||||
else:
|
||||
percentage = 0
|
||||
summary = "0% (?/?)"
|
||||
summary = "Lab {} - {}".format(i + 1, summary)
|
||||
lab_percentages.append( {'percentage': percentage, 'summary': summary} )
|
||||
lab_total, lab_dropped_indices = totalWithDrops(lab_percentages, 2)
|
||||
|
||||
midterm_score = (120, 150)
|
||||
midterm_percentage = midterm_score[0] / float(midterm_score[1])
|
||||
|
||||
final_score = (200, 300)
|
||||
final_percentage = final_score[0] / float(final_score[1])
|
||||
|
||||
grade_summary = [
|
||||
{
|
||||
'category': 'Homework',
|
||||
'subscores' : homework_percentages,
|
||||
'dropped_indices' : homework_dropped_indices,
|
||||
'totalscore' : {'score' : homework_total, 'summary' : "Homework Average - {:.0%}".format(homework_total)},
|
||||
'weight' : 0.15,
|
||||
},
|
||||
{
|
||||
'category': 'Labs',
|
||||
'subscores' : lab_percentages,
|
||||
'dropped_indices' : lab_dropped_indices,
|
||||
'totalscore' : {'score' : lab_total, 'summary' : "Lab Average - {:.0%}".format(lab_total)},
|
||||
'weight' : 0.15,
|
||||
},
|
||||
{
|
||||
'category': 'Midterm',
|
||||
'totalscore' : {'score' : midterm_percentage, 'summary' : "Midterm - {:.0%} ({}/{})".format(midterm_percentage, midterm_score[0], midterm_score[1])},
|
||||
'weight' : 0.30,
|
||||
},
|
||||
{
|
||||
'category': 'Final',
|
||||
'totalscore' : {'score' : final_percentage, 'summary' : "Final - {:.0%} ({}/{})".format(final_percentage, final_score[0], final_score[1])},
|
||||
'weight' : 0.40,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
user_info=UserProfile.objects.get(user=request.user)
|
||||
|
||||
context={'name':user_info.name,
|
||||
'username':request.user.username,
|
||||
'location':user_info.location,
|
||||
'language':user_info.language,
|
||||
'email':request.user.email,
|
||||
'homeworks':hw,
|
||||
'homeworks':hw,
|
||||
'grade_summary' : grade_summary,
|
||||
'csrf':csrf(request)['csrf_token']
|
||||
}
|
||||
return render_to_response('profile.html', context)
|
||||
|
||||
Reference in New Issue
Block a user