Merge branch 'master' into pmitros/name-change
Conflicts: static/css/application.css static/css/marketing.css
This commit is contained in:
@@ -5,6 +5,7 @@ import operator
|
||||
import re
|
||||
|
||||
import numpy
|
||||
import numbers
|
||||
import scipy.constants
|
||||
|
||||
from pyparsing import Word, alphas, nums, oneOf, Literal
|
||||
@@ -121,7 +122,7 @@ def evaluator(variables, functions, string, cs=False):
|
||||
def number_parse_action(x): # [ '7' ] -> [ 7 ]
|
||||
return [super_float("".join(x))]
|
||||
def exp_parse_action(x): # [ 2 ^ 3 ^ 2 ] -> 512
|
||||
x = [e for e in x if type(e) in [float, numpy.float64, numpy.complex]] # Ignore ^
|
||||
x = [e for e in x if isinstance(e, numbers.Number)] # Ignore ^
|
||||
x.reverse()
|
||||
x=reduce(lambda a,b:b**a, x)
|
||||
return x
|
||||
@@ -130,7 +131,7 @@ def evaluator(variables, functions, string, cs=False):
|
||||
return x[0]
|
||||
if 0 in x:
|
||||
return float('nan')
|
||||
x = [1./e for e in x if type(e) == float] # Ignore ^
|
||||
x = [1./e for e in x if isinstance(e, numbers.Number)] # Ignore ||
|
||||
return 1./sum(x)
|
||||
def sum_parse_action(x): # [ 1 + 2 - 3 ] -> 0
|
||||
total = 0.0
|
||||
@@ -217,4 +218,7 @@ if __name__=='__main__':
|
||||
print evaluator({},{}, "-(7+5)")
|
||||
print evaluator({},{}, "-0.33")
|
||||
print evaluator({},{}, "-.33")
|
||||
print evaluator({},{}, "5+1*j")
|
||||
print evaluator({},{}, "j||1")
|
||||
print evaluator({},{}, "e^(j*pi)")
|
||||
print evaluator({},{}, "5+7 QWSEKO")
|
||||
|
||||
@@ -8,7 +8,8 @@ class textline(object):
|
||||
def render(element, value, state):
|
||||
eid=element.get('id')
|
||||
count = int(eid.split('_')[-2])-1 # HACK
|
||||
context = {'id':eid, 'value':value, 'state':state, 'count':count}
|
||||
size = element.get('size')
|
||||
context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size}
|
||||
html=render_to_string("textinput.html", context)
|
||||
return etree.XML(html)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import math
|
||||
import numbers
|
||||
import numpy
|
||||
import random
|
||||
import scipy
|
||||
@@ -37,7 +38,7 @@ class numericalresponse(object):
|
||||
def __init__(self, xml, context):
|
||||
self.xml = xml
|
||||
self.correct_answer = contextualize_text(xml.get('answer'), context)
|
||||
self.correct_answer = float(self.correct_answer)
|
||||
self.correct_answer = complex(self.correct_answer)
|
||||
self.tolerance_xml = xml.xpath('//*[@id=$id]//responseparam[@type="tolerance"]/@default',
|
||||
id=xml.get('id'))[0]
|
||||
self.tolerance = contextualize_text(self.tolerance_xml, context)
|
||||
@@ -49,7 +50,10 @@ class numericalresponse(object):
|
||||
student_answer = student_answers[self.answer_id]
|
||||
try:
|
||||
correct = compare_with_tolerance (evaluator(dict(),dict(),student_answer), self.correct_answer, self.tolerance)
|
||||
except:
|
||||
# We should catch this explicitly.
|
||||
# I think this is just pyparsing.ParseException, calc.UndefinedVariable:
|
||||
# But we'd need to confirm
|
||||
except:
|
||||
raise StudentInputError('Invalid input -- please use a number only')
|
||||
|
||||
if correct:
|
||||
@@ -141,7 +145,7 @@ class formularesponse(object):
|
||||
except:
|
||||
#traceback.print_exc()
|
||||
raise StudentInputError("Error in formula")
|
||||
if math.isnan(student_result) or math.isinf(student_result):
|
||||
if numpy.isnan(student_result) or numpy.isinf(student_result):
|
||||
return {self.answer_id:"incorrect"}
|
||||
if not compare_with_tolerance(student_result, instructor_result, self.tolerance):
|
||||
return {self.answer_id:"incorrect"}
|
||||
@@ -153,9 +157,9 @@ class formularesponse(object):
|
||||
keys and all non-numeric values stripped out. All values also
|
||||
converted to float. Used so we can safely use Python contexts.
|
||||
'''
|
||||
d=dict([(k, float(d[k])) for k in d if type(k)==str and \
|
||||
d=dict([(k, numpy.complex(d[k])) for k in d if type(k)==str and \
|
||||
k.isalnum() and \
|
||||
(type(d[k]) == float or type(d[k]) == int) ])
|
||||
isinstance(d[k], numbers.Number)])
|
||||
return d
|
||||
|
||||
def get_answers(self):
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import logging
|
||||
import urllib
|
||||
from lxml import etree
|
||||
|
||||
import courseware.content_parser as content_parser
|
||||
from models import StudentModule
|
||||
from django.conf import settings
|
||||
import courseware.modules
|
||||
import logging
|
||||
import random
|
||||
import urllib
|
||||
|
||||
from collections import namedtuple
|
||||
from django.conf import settings
|
||||
from lxml import etree
|
||||
from models import StudentModule
|
||||
from student.models import UserProfile
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
Score = namedtuple("Score", "earned possible graded section")
|
||||
|
||||
def get_grade(user, problem, cache):
|
||||
## HACK: assumes max score is fixed per problem
|
||||
id = problem.get('id')
|
||||
correct = 0
|
||||
|
||||
# If the ID is not in the cache, add the item
|
||||
if id not in cache:
|
||||
if id not in cache:
|
||||
module = StudentModule(module_type = 'problem', # TODO: Move into StudentModule.__init__?
|
||||
module_id = id,
|
||||
student = user,
|
||||
@@ -44,6 +47,17 @@ def get_grade(user, problem, cache):
|
||||
return (correct, total)
|
||||
|
||||
def grade_sheet(student):
|
||||
"""
|
||||
This pulls a summary of all problems in the course. It returns a dictionary with two datastructures:
|
||||
|
||||
- courseware_summary is a summary of all sections with problems in the course. It is organized as an array of chapters,
|
||||
each containing an array of sections, each containing an array of scores. This contains information for graded and ungraded
|
||||
problems, and is good for displaying a course summary with due dates, etc.
|
||||
|
||||
- grade_summary is a summary of how the final grade breaks down. It is an array of "sections". Each section can either be
|
||||
a conglomerate of scores (like labs or homeworks) which has subscores and a totalscore, or a section can be all from one assignment
|
||||
(such as a midterm or final) and only has a totalscore. Each section has a weight that shows how it contributes to the total grade.
|
||||
"""
|
||||
dom=content_parser.course_file(student)
|
||||
course = dom.xpath('//course/@name')[0]
|
||||
xmlChapters = dom.xpath('//course[@name=$course]/chapter', course=course)
|
||||
@@ -54,11 +68,14 @@ def grade_sheet(student):
|
||||
response_by_id[response.module_id] = response
|
||||
|
||||
|
||||
total_scores = {}
|
||||
|
||||
totaled_scores = {}
|
||||
chapters=[]
|
||||
for c in xmlChapters:
|
||||
sections = []
|
||||
chname=c.get('name')
|
||||
|
||||
|
||||
for s in dom.xpath('//course[@name=$course]/chapter[@name=$chname]/section',
|
||||
course=course, chname=chname):
|
||||
problems=dom.xpath('//course[@name=$course]/chapter[@name=$chname]/section[@name=$section]//problem',
|
||||
@@ -84,22 +101,26 @@ def grade_sheet(student):
|
||||
else:
|
||||
correct = total
|
||||
|
||||
scores.append((int(correct),total, graded ))
|
||||
scores.append( Score(int(correct),total, graded, s.get("name")) )
|
||||
|
||||
|
||||
section_total = Score(sum([score.earned for score in scores]),
|
||||
sum([score.possible for score in scores]),
|
||||
False,
|
||||
p.get("id"))
|
||||
|
||||
section_total = (sum([score[0] for score in scores]),
|
||||
sum([score[1] for score in scores]))
|
||||
graded_total = Score(sum([score.earned for score in scores if score.graded]),
|
||||
sum([score.possible for score in scores if score.graded]),
|
||||
True,
|
||||
p.get("id"))
|
||||
|
||||
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
|
||||
#Add the graded total to totaled_scores
|
||||
format = s.get('format') if s.get('format') else ""
|
||||
subtitle = s.get('subtitle') if s.get('subtitle') else format
|
||||
if format and graded_total[1] > 0:
|
||||
format_scores = total_scores[ format ] if format in total_scores else []
|
||||
format_scores.append( graded_total + (s.get("name"),) )
|
||||
total_scores[ format ] = format_scores
|
||||
format_scores = totaled_scores.get(format, [])
|
||||
format_scores.append( graded_total )
|
||||
totaled_scores[ format ] = format_scores
|
||||
|
||||
score={'section':s.get("name"),
|
||||
'scores':scores,
|
||||
@@ -114,7 +135,20 @@ def grade_sheet(student):
|
||||
chapters.append({'course':course,
|
||||
'chapter' : c.get("name"),
|
||||
'sections' : sections,})
|
||||
|
||||
|
||||
|
||||
grade_summary = grade_summary_6002x(totaled_scores)
|
||||
|
||||
return {'courseware_summary' : chapters,
|
||||
'grade_summary' : grade_summary}
|
||||
|
||||
|
||||
def grade_summary_6002x(totaled_scores):
|
||||
"""
|
||||
This function takes the a dictionary of (graded) section scores, and applies the course grading rules to create
|
||||
the grade_summary. For 6.002x this means homeworks and labs all have equal weight, with the lowest 2 of each
|
||||
being dropped. There is one midterm and one final.
|
||||
"""
|
||||
|
||||
def totalWithDrops(scores, drop_count):
|
||||
#Note that this key will sort the list descending
|
||||
@@ -131,12 +165,12 @@ def grade_sheet(student):
|
||||
return aggregate_score, dropped_indices
|
||||
|
||||
#Figure the homework scores
|
||||
homework_scores = total_scores['Homework'] if 'Homework' in total_scores else []
|
||||
homework_scores = totaled_scores['Homework'] if 'Homework' in totaled_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 = "Homework {0} - {1} - {2:.0%} ({3:g}/{4:g})".format( i + 1, homework_scores[i][2] , percentage, homework_scores[i][0], homework_scores[i][1] )
|
||||
percentage = homework_scores[1].earned / float(homework_scores[i].possible)
|
||||
summary = "Homework {0} - {1} - {2:.0%} ({3:g}/{4:g})".format( i + 1, homework_scores[i].section , percentage, homework_scores[i].earned, homework_scores[i].possible )
|
||||
else:
|
||||
percentage = 0
|
||||
summary = "Unreleased Homework {0} - 0% (?/?)".format(i + 1)
|
||||
@@ -153,13 +187,12 @@ def grade_sheet(student):
|
||||
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_scores = totaled_scores['Lab'] if 'Lab' in totaled_scores else []
|
||||
lab_percentages = []
|
||||
log.debug("lab_scores: {0}".format(lab_scores))
|
||||
for i in range(12):
|
||||
if i < len(lab_scores):
|
||||
percentage = lab_scores[i][0] / float(lab_scores[i][1])
|
||||
summary = "Lab {0} - {1} - {2:.0%} ({3:g}/{4:g})".format( i + 1, lab_scores[i][2] , percentage, lab_scores[i][0], lab_scores[i][1] )
|
||||
percentage = lab_scores[i].earned / float(lab_scores[i].possible)
|
||||
summary = "Lab {0} - {1} - {2:.0%} ({3:g}/{4:g})".format( i + 1, lab_scores[i].section , percentage, lab_scores[i].earned, lab_scores[i].possible )
|
||||
else:
|
||||
percentage = 0
|
||||
summary = "Unreleased Lab {0} - 0% (?/?)".format(i + 1)
|
||||
@@ -177,18 +210,18 @@ def grade_sheet(student):
|
||||
|
||||
|
||||
#TODO: Pull this data about the midterm and final from the databse. It should be exactly similar to above, but we aren't sure how exams will be done yet.
|
||||
midterm_score = ('?', '?')
|
||||
midterm_score = Score('?', '?', True, "?")
|
||||
midterm_percentage = 0
|
||||
|
||||
final_score = ('?', '?')
|
||||
final_score = Score('?', '?', True, "?")
|
||||
final_percentage = 0
|
||||
|
||||
if settings.GENERATE_PROFILE_SCORES:
|
||||
midterm_score = (random.randrange(50, 150), 150)
|
||||
midterm_percentage = midterm_score[0] / float(midterm_score[1])
|
||||
midterm_score = Score(random.randrange(50, 150), 150, True, "?")
|
||||
midterm_percentage = midterm_score.earned / float(midterm_score.possible)
|
||||
|
||||
final_score = (random.randrange(100, 300), 300)
|
||||
final_percentage = final_score[0] / float(final_score[1])
|
||||
final_score = Score(random.randrange(100, 300), 300, True, "?")
|
||||
final_percentage = final_score.earned / float(final_score.possible)
|
||||
|
||||
|
||||
grade_summary = [
|
||||
@@ -196,7 +229,8 @@ def grade_sheet(student):
|
||||
'category': 'Homework',
|
||||
'subscores' : homework_percentages,
|
||||
'dropped_indices' : homework_dropped_indices,
|
||||
'totalscore' : {'score' : homework_total, 'summary' : "Homework Average - {0:.0%}".format(homework_total)},
|
||||
'totalscore' : homework_total,
|
||||
'totalscore_summary' : "Homework Average - {0:.0%}".format(homework_total),
|
||||
'totallabel' : 'HW Avg',
|
||||
'weight' : 0.15,
|
||||
},
|
||||
@@ -204,25 +238,25 @@ def grade_sheet(student):
|
||||
'category': 'Labs',
|
||||
'subscores' : lab_percentages,
|
||||
'dropped_indices' : lab_dropped_indices,
|
||||
'totalscore' : {'score' : lab_total, 'summary' : "Lab Average - {0:.0%}".format(lab_total)},
|
||||
'totalscore' : lab_total,
|
||||
'totalscore_summary' : "Lab Average - {0:.0%}".format(lab_total),
|
||||
'totallabel' : 'Lab Avg',
|
||||
'weight' : 0.15,
|
||||
},
|
||||
{
|
||||
'category': 'Midterm',
|
||||
'totalscore' : {'score' : midterm_percentage, 'summary' : "Midterm - {0:.0%} ({1}/{2})".format(midterm_percentage, midterm_score[0], midterm_score[1])},
|
||||
'totalscore' : midterm_percentage,
|
||||
'totalscore_summary' : "Midterm - {0:.0%} ({1}/{2})".format(midterm_percentage, midterm_score.earned, midterm_score.possible),
|
||||
'totallabel' : 'Midterm',
|
||||
'weight' : 0.30,
|
||||
},
|
||||
{
|
||||
'category': 'Final',
|
||||
'totalscore' : {'score' : final_percentage, 'summary' : "Final - {0:.0%} ({1}/{2})".format(final_percentage, final_score[0], final_score[1])},
|
||||
'totalscore' : final_percentage,
|
||||
'totalscore_summary' : "Final - {0:.0%} ({1}/{2})".format(final_percentage, final_score.earned, final_score.possible),
|
||||
'totallabel' : 'Final',
|
||||
'weight' : 0.40,
|
||||
}
|
||||
]
|
||||
|
||||
return {'grade_summary' : grade_summary,
|
||||
'chapters':chapters}
|
||||
|
||||
|
||||
return grade_summary
|
||||
|
||||
@@ -20,7 +20,7 @@ class ModelsTest(unittest.TestCase):
|
||||
variables={'R1':2.0, 'R3':4.0}
|
||||
functions={'sin':numpy.sin, 'cos':numpy.cos}
|
||||
|
||||
self.assertEqual(calc.evaluator(variables, functions, "10000||sin(7+5)-6k"), 4000.0)
|
||||
self.assertTrue(abs(calc.evaluator(variables, functions, "10000||sin(7+5)+0.5356"))<0.01)
|
||||
self.assertEqual(calc.evaluator({'R1': 2.0, 'R3':4.0}, {}, "13"), 13)
|
||||
self.assertEqual(calc.evaluator(variables, functions, "13"), 13)
|
||||
self.assertEqual(calc.evaluator({'a': 2.2997471478310274, 'k': 9, 'm': 8, 'x': 0.66009498411213041}, {}, "5"), 5)
|
||||
@@ -30,6 +30,8 @@ class ModelsTest(unittest.TestCase):
|
||||
self.assertEqual(calc.evaluator(variables, functions, "R1*R3"), 8.0)
|
||||
self.assertTrue(abs(calc.evaluator(variables, functions, "sin(e)-0.41"))<0.01)
|
||||
self.assertTrue(abs(calc.evaluator(variables, functions, "k*T/q-0.025"))<0.001)
|
||||
self.assertTrue(abs(calc.evaluator(variables, functions, "e^(j*pi)")+1)<0.00001)
|
||||
self.assertTrue(abs(calc.evaluator(variables, functions, "j||1")-0.5-0.5j)<0.00001)
|
||||
exception_happened = False
|
||||
try:
|
||||
calc.evaluator({},{}, "5+7 QWSEKO")
|
||||
|
||||
Reference in New Issue
Block a user