Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion common/djangoapps/heartbeat/views.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import json
from datetime import datetime
from pytz import UTC
from django.http import HttpResponse
from xmodule.modulestore.django import modulestore
from dogapi import dog_stats_api


@dog_stats_api.timed('edxapp.heartbeat')
def heartbeat(request):
"""
Simple view that a loadbalancer can check to verify that the app is up
"""
output = {
'date': datetime.now().isoformat(),
'date': datetime.now(UTC).isoformat(),
'courses': [course.location.url() for course in modulestore().get_courses()],
}
return HttpResponse(json.dumps(output, indent=4))
58 changes: 30 additions & 28 deletions common/djangoapps/student/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,30 +69,33 @@ class Meta:
location = models.CharField(blank=True, max_length=255, db_index=True)

# Optional demographic data we started capturing from Fall 2012
this_year = datetime.now().year
this_year = datetime.now(UTC).year
VALID_YEARS = range(this_year, this_year - 120, -1)
year_of_birth = models.IntegerField(blank=True, null=True, db_index=True)
GENDER_CHOICES = (('m', 'Male'), ('f', 'Female'), ('o', 'Other'))
gender = models.CharField(blank=True, null=True, max_length=6, db_index=True,
choices=GENDER_CHOICES)
gender = models.CharField(
blank=True, null=True, max_length=6, db_index=True, choices=GENDER_CHOICES
)

# [03/21/2013] removed these, but leaving comment since there'll still be
# p_se and p_oth in the existing data in db.
# ('p_se', 'Doctorate in science or engineering'),
# ('p_oth', 'Doctorate in another field'),
LEVEL_OF_EDUCATION_CHOICES = (('p', 'Doctorate'),
('m', "Master's or professional degree"),
('b', "Bachelor's degree"),
('a', "Associate's degree"),
('hs', "Secondary/high school"),
('jhs', "Junior secondary/junior high/middle school"),
('el', "Elementary/primary school"),
('none', "None"),
('other', "Other"))
LEVEL_OF_EDUCATION_CHOICES = (
('p', 'Doctorate'),
('m', "Master's or professional degree"),
('b', "Bachelor's degree"),
('a', "Associate's degree"),
('hs', "Secondary/high school"),
('jhs', "Junior secondary/junior high/middle school"),
('el', "Elementary/primary school"),
('none', "None"),
('other', "Other")
)
level_of_education = models.CharField(
blank=True, null=True, max_length=6, db_index=True,
choices=LEVEL_OF_EDUCATION_CHOICES
)
blank=True, null=True, max_length=6, db_index=True,
choices=LEVEL_OF_EDUCATION_CHOICES
)
mailing_address = models.TextField(blank=True, null=True)
goals = models.TextField(blank=True, null=True)
allow_certificate = models.BooleanField(default=1)
Expand Down Expand Up @@ -307,18 +310,18 @@ def _can_encode_as_latin(fieldvalue):
ACCOMMODATION_REJECTED_CODE = 'NONE'

ACCOMMODATION_CODES = (
(ACCOMMODATION_REJECTED_CODE, 'No Accommodation Granted'),
('EQPMNT', 'Equipment'),
('ET12ET', 'Extra Time - 1/2 Exam Time'),
('ET30MN', 'Extra Time - 30 Minutes'),
('ETDBTM', 'Extra Time - Double Time'),
('SEPRMM', 'Separate Room'),
('SRREAD', 'Separate Room and Reader'),
('SRRERC', 'Separate Room and Reader/Recorder'),
('SRRECR', 'Separate Room and Recorder'),
('SRSEAN', 'Separate Room and Service Animal'),
('SRSGNR', 'Separate Room and Sign Language Interpreter'),
)
(ACCOMMODATION_REJECTED_CODE, 'No Accommodation Granted'),
('EQPMNT', 'Equipment'),
('ET12ET', 'Extra Time - 1/2 Exam Time'),
('ET30MN', 'Extra Time - 30 Minutes'),
('ETDBTM', 'Extra Time - Double Time'),
('SEPRMM', 'Separate Room'),
('SRREAD', 'Separate Room and Reader'),
('SRRERC', 'Separate Room and Reader/Recorder'),
('SRRECR', 'Separate Room and Recorder'),
('SRSEAN', 'Separate Room and Service Animal'),
('SRSGNR', 'Separate Room and Sign Language Interpreter'),
)

ACCOMMODATION_CODE_DICT = {code: name for (code, name) in ACCOMMODATION_CODES}

Expand Down Expand Up @@ -572,7 +575,6 @@ def clean_accommodation_code(self):
return code



def get_testcenter_registration(user, course_id, exam_series_code):
try:
tcu = TestCenterUser.objects.get(user=user)
Expand Down
8 changes: 4 additions & 4 deletions common/djangoapps/student/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ def get_date_for_press(publish_date):
# strip off extra months, and just use the first:
date = re.sub(multimonth_pattern, ", ", publish_date)
if re.search(day_pattern, date):
date = datetime.datetime.strptime(date, "%B %d, %Y")
date = datetime.datetime.strptime(date, "%B %d, %Y").replace(tzinfo=UTC)
else:
date = datetime.datetime.strptime(date, "%B, %Y")
date = datetime.datetime.strptime(date, "%B, %Y").replace(tzinfo=UTC)
return date


Expand Down Expand Up @@ -1100,7 +1100,7 @@ def confirm_email_change(request, key):
meta = up.get_meta()
if 'old_emails' not in meta:
meta['old_emails'] = []
meta['old_emails'].append([user.email, datetime.datetime.now().isoformat()])
meta['old_emails'].append([user.email, datetime.datetime.now(UTC).isoformat()])
up.set_meta(meta)
up.save()
# Send it to the old email...
Expand Down Expand Up @@ -1198,7 +1198,7 @@ def accept_name_change_by_id(id):
meta = up.get_meta()
if 'old_names' not in meta:
meta['old_names'] = []
meta['old_names'].append([up.name, pnc.rationale, datetime.datetime.now().isoformat()])
meta['old_names'].append([up.name, pnc.rationale, datetime.datetime.now(UTC).isoformat()])
up.set_meta(meta)

up.name = pnc.new_name
Expand Down
68 changes: 47 additions & 21 deletions common/lib/capa/capa/capa_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import capa.responsetypes as responsetypes
from capa.safe_exec import safe_exec

from pytz import UTC

# dict of tagname, Response Class -- this should come from auto-registering
response_tag_dict = dict([(x.response_tag, x) for x in responsetypes.__all__])

Expand All @@ -42,13 +44,22 @@
response_properties = ["codeparam", "responseparam", "answer", "openendedparam"]

# special problem tags which should be turned into innocuous HTML
html_transforms = {'problem': {'tag': 'div'},
'text': {'tag': 'span'},
'math': {'tag': 'span'},
}
html_transforms = {
'problem': {'tag': 'div'},
'text': {'tag': 'span'},
'math': {'tag': 'span'},
}

# These should be removed from HTML output, including all subelements
html_problem_semantics = ["codeparam", "responseparam", "answer", "script", "hintgroup", "openendedparam", "openendedrubric"]
html_problem_semantics = [
"codeparam",
"responseparam",
"answer",
"script",
"hintgroup",
"openendedparam",
"openendedrubric"
]

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -242,11 +253,15 @@ def get_recentmost_queuetime(self):
return None

# Get a list of timestamps of all queueing requests, then convert it to a DateTime object
queuetime_strs = [self.correct_map.get_queuetime_str(answer_id)
for answer_id in self.correct_map
if self.correct_map.is_queued(answer_id)]
queuetimes = [datetime.strptime(qt_str, xqueue_interface.dateformat)
for qt_str in queuetime_strs]
queuetime_strs = [
self.correct_map.get_queuetime_str(answer_id)
for answer_id in self.correct_map
if self.correct_map.is_queued(answer_id)
]
queuetimes = [
datetime.strptime(qt_str, xqueue_interface.dateformat).replace(tzinfo=UTC)
for qt_str in queuetime_strs
]

return max(queuetimes)

Expand Down Expand Up @@ -404,10 +419,16 @@ def _process_includes(self):
# open using ModuleSystem OSFS filestore
ifp = self.system.filestore.open(filename)
except Exception as err:
log.warning('Error %s in problem xml include: %s' % (
err, etree.tostring(inc, pretty_print=True)))
log.warning('Cannot find file %s in %s' % (
filename, self.system.filestore))
log.warning(
'Error %s in problem xml include: %s' % (
err, etree.tostring(inc, pretty_print=True)
)
)
log.warning(
'Cannot find file %s in %s' % (
filename, self.system.filestore
)
)
# if debugging, don't fail - just log error
# TODO (vshnayder): need real error handling, display to users
if not self.system.get('DEBUG'):
Expand All @@ -418,8 +439,11 @@ def _process_includes(self):
# read in and convert to XML
incxml = etree.XML(ifp.read())
except Exception as err:
log.warning('Error %s in problem xml include: %s' % (
err, etree.tostring(inc, pretty_print=True)))
log.warning(
'Error %s in problem xml include: %s' % (
err, etree.tostring(inc, pretty_print=True)
)
)
log.warning('Cannot parse XML in %s' % (filename))
# if debugging, don't fail - just log error
# TODO (vshnayder): same as above
Expand Down Expand Up @@ -579,8 +603,9 @@ def _extract_html(self, problemtree): # private
# let each Response render itself
if problemtree in self.responders:
overall_msg = self.correct_map.get_overall_message()
return self.responders[problemtree].render_html(self._extract_html,
response_msg=overall_msg)
return self.responders[problemtree].render_html(
self._extract_html, response_msg=overall_msg
)

# let each custom renderer render itself:
if problemtree.tag in customrender.registry.registered_tags():
Expand Down Expand Up @@ -628,9 +653,10 @@ def _preprocess_problem(self, tree): # private

answer_id = 1
input_tags = inputtypes.registry.registered_tags()
inputfields = tree.xpath("|".join(['//' + response.tag + '[@id=$id]//' + x
for x in (input_tags + solution_tags)]),
id=response_id_str)
inputfields = tree.xpath(
"|".join(['//' + response.tag + '[@id=$id]//' + x for x in (input_tags + solution_tags)]),
id=response_id_str
)

# assign one answer_id for each input type or solution type
for entry in inputfields:
Expand Down
34 changes: 19 additions & 15 deletions common/lib/capa/capa/correctmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,27 @@ def __iter__(self):
return self.cmap.__iter__()

# See the documentation for 'set_dict' for the use of kwargs
def set(self,
answer_id=None,
correctness=None,
npoints=None,
msg='',
hint='',
hintmode=None,
queuestate=None, **kwargs):
def set(
self,
answer_id=None,
correctness=None,
npoints=None,
msg='',
hint='',
hintmode=None,
queuestate=None,
**kwargs
):

if answer_id is not None:
self.cmap[str(answer_id)] = {'correctness': correctness,
'npoints': npoints,
'msg': msg,
'hint': hint,
'hintmode': hintmode,
'queuestate': queuestate,
}
self.cmap[str(answer_id)] = {
'correctness': correctness,
'npoints': npoints,
'msg': msg,
'hint': hint,
'hintmode': hintmode,
'queuestate': queuestate,
}

def __repr__(self):
return repr(self.cmap)
Expand Down
27 changes: 16 additions & 11 deletions common/lib/capa/capa/responsetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from calc import evaluator, UndefinedVariable
from . import correctmap
from datetime import datetime
from pytz import UTC
from .util import *
from lxml import etree
from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME?
Expand Down Expand Up @@ -1365,9 +1366,11 @@ def get_score(self, student_answers):
# Note that submission can be a file
submission = student_answers[self.answer_id]
except Exception as err:
log.error('Error in CodeResponse %s: cannot get student answer for %s;'
' student_answers=%s' %
(err, self.answer_id, convert_files_to_filenames(student_answers)))
log.error(
'Error in CodeResponse %s: cannot get student answer for %s;'
' student_answers=%s' %
(err, self.answer_id, convert_files_to_filenames(student_answers))
)
raise Exception(err)

# We do not support xqueue within Studio.
Expand All @@ -1381,19 +1384,20 @@ def get_score(self, student_answers):
#------------------------------------------------------------

qinterface = self.system.xqueue['interface']
qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat)
qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat)

anonymous_student_id = self.system.anonymous_student_id

# Generate header
queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime +
anonymous_student_id +
self.answer_id)
queuekey = xqueue_interface.make_hashkey(
str(self.system.seed) + qtime + anonymous_student_id + self.answer_id
)
callback_url = self.system.xqueue['construct_callback']()
xheader = xqueue_interface.make_xheader(
lms_callback_url=callback_url,
lms_key=queuekey,
queue_name=self.queue_name)
queue_name=self.queue_name
)

# Generate body
if is_list_of_files(submission):
Expand All @@ -1406,9 +1410,10 @@ def get_score(self, student_answers):

# Metadata related to the student submission revealed to the external
# grader
student_info = {'anonymous_student_id': anonymous_student_id,
'submission_time': qtime,
}
student_info = {
'anonymous_student_id': anonymous_student_id,
'submission_time': qtime,
}
contents.update({'student_info': json.dumps(student_info)})

# Submit request. When successful, 'msg' is the prior length of the
Expand Down
Loading