Skip to content
Open
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
15 changes: 8 additions & 7 deletions openassessment/assessment/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import logging
import math

import six
from lazy import lazy

from django.core.cache import cache
Expand Down Expand Up @@ -105,7 +106,7 @@ def content_hash_from_dict(rubric_dict):
rubric_dict.pop("content_hash", None)

canonical_form = json.dumps(rubric_dict, sort_keys=True)
return sha1(canonical_form).hexdigest()
return sha1(canonical_form.encode('utf-8')).hexdigest()

@staticmethod
def structure_hash_from_dict(rubric_dict):
Expand Down Expand Up @@ -137,7 +138,7 @@ def structure_hash_from_dict(rubric_dict):
for criterion in rubric_dict.get('criteria', [])
]
canonical_form = json.dumps(structure, sort_keys=True)
return sha1(canonical_form).hexdigest()
return sha1(canonical_form.encode('utf-8')).hexdigest()


class Criterion(models.Model):
Expand Down Expand Up @@ -510,7 +511,7 @@ def get_median_score_dict(cls, scores_dict):

"""
median_scores = {}
for criterion, criterion_scores in scores_dict.iteritems():
for criterion, criterion_scores in six.iteritems(scores_dict):
criterion_score = Assessment.get_median_score(criterion_scores)
median_scores[criterion] = criterion_score
return median_scores
Expand Down Expand Up @@ -673,7 +674,7 @@ def create_from_option_names(cls, assessment, selected, feedback=None):

# Validate that we have selections for all criteria
# This will raise an exception if we're missing any selections/feedback required for criteria
cls._check_all_criteria_assessed(rubric_index, selected.keys(), feedback.keys())
cls._check_all_criteria_assessed(rubric_index, list(selected.keys()), list(feedback.keys()))

# Retrieve the criteria/option/feedback for criteria that have options.
# Since we're using the rubric's index, we'll get an `InvalidRubricSelection` error
Expand All @@ -684,13 +685,13 @@ def create_from_option_names(cls, assessment, selected, feedback=None):
'option': rubric_index.find_option(criterion_name, option_name),
'feedback': feedback.get(criterion_name, u"")[0:cls.MAX_FEEDBACK_SIZE],
}
for criterion_name, option_name in selected.iteritems()
for criterion_name, option_name in six.iteritems(selected)
]

# Some criteria may have feedback but no options, only feedback.
# For these, we set `option` to None, indicating that the assessment part
# is not associated with any option, only a criterion.
for criterion_name, feedback_text in feedback.iteritems():
for criterion_name, feedback_text in six.iteritems(feedback):
if criterion_name not in selected:
assessment_parts.append({
'criterion': rubric_index.find_criterion(criterion_name),
Expand Down Expand Up @@ -742,7 +743,7 @@ def create_from_option_points(cls, assessment, selected):
'criterion': rubric_index.find_criterion(criterion_name),
'option': rubric_index.find_option_for_points(criterion_name, option_points),
}
for criterion_name, option_points in selected.iteritems()
for criterion_name, option_points in six.iteritems(selected)
]

# Add in feedback-only criteria
Expand Down
6 changes: 4 additions & 2 deletions openassessment/assessment/models/training.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from hashlib import sha1
import json

import six

from django.core.cache import cache
from django.db import models

Expand Down Expand Up @@ -54,7 +56,7 @@ def create_example(cls, answer, options_selected, rubric):

# This will raise `InvalidRubricSelection` if the selected options
# do not match the rubric.
for criterion_name, option_name in options_selected.iteritems():
for criterion_name, option_name in six.iteritems(options_selected):
option = rubric.index.find_option(criterion_name, option_name)
example.options_selected.add(option)
return example
Expand Down Expand Up @@ -133,7 +135,7 @@ def calculate_hash(answer, options_selected, rubric):
'options_selected': options_selected,
'rubric': rubric.id
})
return sha1(contents).hexdigest()
return sha1(contents.encode('utf-8')).hexdigest()

@classmethod
def cache_key(cls, answer, options_selected, rubric):
Expand Down
3 changes: 2 additions & 1 deletion openassessment/assessment/test/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""
Constants used as test data.
"""
import six

STUDENT_ITEM = {
'student_id': u'𝓽𝓮𝓼𝓽 𝓼𝓽𝓾𝓭𝓮𝓷𝓽',
Expand Down Expand Up @@ -76,7 +77,7 @@
"expected_points": sum(
RUBRIC_OPTIONS[i]["points"] for i in value
)
} for key, value in OPTIONS_SELECTED_CHOICES.iteritems()
} for key, value in six.iteritems(OPTIONS_SELECTED_CHOICES)
}

EXAMPLES = [
Expand Down
7 changes: 5 additions & 2 deletions openassessment/fileupload/backends/swift.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
'''
Add in /edx/app/edxapp/edx-platform/lms/envs/aws.py:
ORA2_SWIFT_URL = AUTH_TOKENS["ORA2_SWIFT_URL"]
Expand All @@ -11,10 +12,12 @@
'swift stat -v' to get it.
'''

from __future__ import absolute_import, division, print_function, unicode_literals

import logging
import urlparse

import requests
from six.moves.urllib.parse import urlparse
import swiftclient

from django.conf import settings
Expand Down Expand Up @@ -96,5 +99,5 @@ def get_settings():

url = getattr(settings, 'ORA2_SWIFT_URL', None)
key = getattr(settings, 'ORA2_SWIFT_KEY', None)
url = urlparse.urlparse(url)
url = urlparse(url)
return key, url
4 changes: 3 additions & 1 deletion openassessment/workflow/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"""
import logging

import six

from django.db import DatabaseError

from openassessment.assessment.errors import PeerAssessmentError, PeerAssessmentInternalError
Expand Down Expand Up @@ -340,7 +342,7 @@ def _get_workflow_model(submission_uuid):
problem.

"""
if not isinstance(submission_uuid, basestring):
if not isinstance(submission_uuid, six.string_types):
raise AssessmentWorkflowRequestError("submission_uuid must be a string type")

try:
Expand Down
2 changes: 1 addition & 1 deletion openassessment/workflow/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel):
an after the fact recording of the last known state of that information so
we can search easily.
"""
STEPS = ASSESSMENT_API_DICT.keys()
STEPS = list(ASSESSMENT_API_DICT.keys())

STATUSES = [
"waiting", # User has done all necessary assessment but hasn't been
Expand Down
7 changes: 7 additions & 0 deletions openassessment/workflow/test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
@ddt.ddt
class TestAssessmentWorkflowApi(CacheResetTest):

def test_status_values_include_all_steps(self):
self.assertIsInstance(AssessmentWorkflow.STEPS, list)
self.assertEqual(
AssessmentWorkflow.STATUS_VALUES,
AssessmentWorkflow.STEPS + AssessmentWorkflow.STATUSES
)

@ddt.file_data('data/assessments.json')
def test_create_workflow(self, data):
first_step = data["steps"][0] if data["steps"] else "peer"
Expand Down
6 changes: 5 additions & 1 deletion openassessment/xblock/course_items_listing_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

"""

from __future__ import absolute_import

import json

import six

from webob import Response
from xblock.core import XBlock

Expand All @@ -26,5 +30,5 @@ def get_ora2_responses(self, request, suffix=''): # pylint: disable=unused-argu
"""
# Import is placed here to avoid model import at project startup.
from openassessment.data import OraAggregateData
responses = OraAggregateData.collect_ora2_responses(unicode(self.course_id))
responses = OraAggregateData.collect_ora2_responses(six.text_type(self.course_id))
return Response(json.dumps(responses), content_type='application/json')
7 changes: 5 additions & 2 deletions openassessment/xblock/data_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
Data Conversion utility methods for handling ORA2 XBlock data transformations and validation.

"""
from __future__ import absolute_import

import json

import six


def convert_training_examples_list_to_dict(examples_list):
"""
Expand Down Expand Up @@ -85,8 +89,7 @@ def update_assessments_format(assessments):
for assessment in assessments:
if 'examples' in assessment and assessment['examples']:
for example in assessment['examples']:
if (isinstance(example, dict) and
(isinstance(example['answer'], unicode) or isinstance(example['answer'], str))):
if isinstance(example, dict) and isinstance(example['answer'], six.string_types):
example['answer'] = {
'parts': [
{'text': example['answer']}
Expand Down
13 changes: 9 additions & 4 deletions openassessment/xblock/grade_mixin.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# -*- coding: utf-8 -*-
"""
Grade step in the OpenAssessment XBlock.
"""
from __future__ import absolute_import, division, print_function, unicode_literals

import copy

from lazy import lazy
from xblock.core import XBlock

from django.utils.translation import ugettext as _

from data_conversion import create_submission_dict
from openassessment.assessment.errors import PeerAssessmentError, SelfAssessmentError

from .data_conversion import create_submission_dict


class GradeMixin(object):
"""Grade Mixin introduces all handlers for displaying grades
Expand Down Expand Up @@ -410,6 +414,7 @@ def _peer_median_option(self, submission_uuid, criterion):

median_scores = peer_api.get_assessment_median_scores(submission_uuid)
median_score = median_scores.get(criterion['name'], None)
median_score = -1 if median_score is None else median_score

def median_options():
"""
Expand All @@ -423,7 +428,7 @@ def median_options():
5. Options A=1, B=3 and C=5, a median score of 6 returns [C]
Note: 5 should not happen as a median should never be out of range.
"""
last_score = None
last_score = -1
median_options = []

# Sort the options first by name and then by points, so that if there
Expand All @@ -436,7 +441,7 @@ def median_options():
current_score = option['points']

# If we have reached a new score, then decide what to do next
if current_score is not last_score:
if current_score != last_score:

# If the last score we saw was already larger than the median
# score, then we must have collected enough so return all
Expand Down Expand Up @@ -472,7 +477,7 @@ def median_options():
return options[0]
return {
'label': u' / '.join([option['label'] for option in options]),
'points': median_score,
'points': median_score if median_score != -1 else None,
'explanation': None,
}

Expand Down
6 changes: 5 additions & 1 deletion openassessment/xblock/leaderboard_mixin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"""
Leaderboard step in the OpenAssessment XBlock.
"""
from __future__ import absolute_import

import six

from xblock.core import XBlock

from django.utils.translation import ugettext as _
Expand Down Expand Up @@ -94,7 +98,7 @@ def render_leaderboard_complete(self, student_item_dict):
if 'text' in score['content'] or 'parts' in score['content']:
submission = {'answer': score.pop('content')}
score['submission'] = create_submission_dict(submission, self.prompts)
elif isinstance(score['content'], basestring):
elif isinstance(score['content'], six.string_types):
pass
# Currently, we do not handle non-text submissions.
else:
Expand Down
6 changes: 5 additions & 1 deletion openassessment/xblock/lms_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
Fields and methods used by the LMS and Studio.
"""

from __future__ import absolute_import

import six

from xblock.fields import DateTime, Dict, Float, Scope, String


Expand All @@ -13,7 +17,7 @@ def from_json(self, access_dict):

def to_json(self, access_dict):
if access_dict is not None:
return {unicode(k): access_dict[k] for k in access_dict}
return {six.text_type(k): access_dict[k] for k in access_dict}


class LmsCompatibilityMixin(object):
Expand Down
7 changes: 5 additions & 2 deletions openassessment/xblock/openassessmentblock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""An XBlock where students can read a question and compose their response"""

from __future__ import absolute_import

import copy
import datetime as dt
import json
Expand All @@ -9,6 +11,7 @@
from lazy import lazy
import pkg_resources
import pytz
from six import text_type
from webob import Response
from xblock.core import XBlock
from xblock.fields import Boolean, Integer, List, Scope, String
Expand Down Expand Up @@ -362,7 +365,7 @@ def get_student_item_dict(self, anonymous_user_id=None):
if self.scope_ids.user_id is None:
student_id = None
else:
student_id = unicode(self.scope_ids.user_id)
student_id = text_type(self.scope_ids.user_id)

student_item_dict = dict(
student_id=student_id,
Expand Down Expand Up @@ -1104,7 +1107,7 @@ def _serialize_opaque_key(self, key):
if hasattr(key, 'to_deprecated_string'):
return key.to_deprecated_string()
else:
return unicode(key)
return text_type(key)

def get_username(self, anonymous_user_id):
"""
Expand Down
8 changes: 6 additions & 2 deletions openassessment/xblock/resolve_dates.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
"""
Resolve unspecified dates and date strings to datetimes.
"""
from __future__ import absolute_import

import datetime as dt

from dateutil.parser import parse as parse_date
import pytz
import six
from six.moves import range, zip


class InvalidDateFormat(Exception):
Expand Down Expand Up @@ -43,7 +47,7 @@ def _parse_date(value, _):
if isinstance(value, dt.datetime):
return value.replace(tzinfo=pytz.utc)

elif isinstance(value, basestring):
elif isinstance(value, six.string_types):
try:
return parse_date(value).replace(tzinfo=pytz.utc)
except ValueError:
Expand Down Expand Up @@ -226,7 +230,7 @@ def resolve_dates(start, end, date_ranges, _):
prev_end = step_end

# Combine the resolved dates back into a list of tuples
resolved_ranges = zip(resolved_starts, resolved_ends)
resolved_ranges = list(zip(resolved_starts, resolved_ends))

# Now that we have resolved both start and end dates, we can safely compare them
for resolved_start, resolved_end in resolved_ranges:
Expand Down
Loading