diff --git a/.travis.yml b/.travis.yml index 96033a3082..bf0f2060b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,43 +1,31 @@ addons: apt: packages: - - aspell - - aspell-en - - g++ - - gcc - - git - - gfortran - - libblas3gf - - libblas-dev - - liblapack3gf - - liblapack-dev - - libatlas-base-dev - - libfontconfig1 - - libmysqlclient-dev - - libxml2-dev - - libxslt1-dev - nodejs - - python2.7 - - python2.7-dev - - python-pip - - python-software-properties language: python sudo: false python: - "2.7" +env: + - TOXENV=django18 + - TOXENV=django111 +matrix: + include: + - python: 2.7 + env: TOXENV=quality + - python: 2.7 + env: TOXENV=js +before_install: + - "pip install -U pip" +cache: + directories: + - $HOME/.cache/pip install: - - "pip install coveralls" - - "make install" -before_script: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - "pip install tox" script: - - "make verify-generated-files" - - "make test" - - "python manage.py makemessages -l eo" + - tox branches: only: - master - - ora-staff-grading after_success: coveralls diff --git a/MANIFEST.in b/MANIFEST.in index f950a7db26..6b636074de 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,13 +1,13 @@ include LICENSE include AUTHORS include README.rst +include setup.cfg include openassessment/xblock/static/css/*.css include openassessment/xblock/static/css/lib/backgrid/*.css include openassessment/xblock/static/js/openassessment*.min.js include openassessment/xblock/static/js/lib/backgrid/*.js +include requirements/*.txt recursive-include openassessment/xblock/static/js/src *.js recursive-include openassessment/templates *.html *.underscore recursive-include openassessment/locale *.po recursive-include openassessment/locale *.mo -global-exclude */test* -global-exclude */test?/* diff --git a/Makefile b/Makefile index 3ac51697a8..48d2e32ac1 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,29 @@ -all: install test - -.PHONY: install test - -# not used by travis -install-system: - sudo apt-get update -qq - sudo xargs -a apt-packages.txt apt-get install -qq --fix-missing - -# not used by travis -install-node: - sudo apt-get install -qq nodejs - -install-wheels: - ./scripts/install-wheels.sh - - +################## +# Install commands +################## install-python: - ./scripts/install-python.sh + pip install -r requirements/django.txt + pip install -r requirements/base.txt --only-binary=lxml,libsass install-js: npm install -install-nltk-data: - ./scripts/download-nltk-data.sh +install-test: + pip install -r requirements/test.txt +install: install-python install-js install-test javascript sass +############################## +# Generate js/css output files +############################## STATIC_JS = openassessment/xblock/static/js STATIC_CSS = openassessment/xblock/static/css +update-npm-requirements: + npm update --silent + cp ./node_modules/backgrid/lib/backgrid*.js $(STATIC_JS)/lib/backgrid/ + cp ./node_modules/backgrid/lib/backgrid*.css $(STATIC_CSS)/lib/backgrid/ + javascript: update-npm-requirements node_modules/.bin/uglifyjs $(STATIC_JS)/src/oa_shared.js $(STATIC_JS)/src/*.js $(STATIC_JS)/src/lms/*.js $(STATIC_JS)/lib/backgrid/backgrid.min.js -c warnings=false > "$(STATIC_JS)/openassessment-lms.min.js" node_modules/.bin/uglifyjs $(STATIC_JS)/src/oa_shared.js $(STATIC_JS)/src/*.js $(STATIC_JS)/src/studio/*.js $(STATIC_JS)/lib/backgrid/backgrid.min.js -c warnings=false > "$(STATIC_JS)/openassessment-studio.min.js" @@ -38,27 +34,17 @@ sass: verify-generated-files: @git diff --quiet || (echo 'Modifications exist locally! Run `make javascript` or `make sass` to update bundled files.'; exit 1) -install-test: - pip install -q -r requirements/test.txt - -install-sys-requirements: install-system install-node - npm config set loglevel warn - -install-dev: - pip install -q -r requirements/dev.txt - -install: install-wheels install-python install-js install-nltk-data install-test install-dev javascript sass - +################ +#Tests and checks +################ quality: ./node_modules/.bin/jshint $(STATIC_JS)/src -c .jshintrc --verbose ./node_modules/jscs/bin/jscs $(STATIC_JS)/src --verbose ./scripts/run-pep8.sh ./scripts/run-pylint.sh -test: quality test-python test-js - test-python: - ./scripts/test-python.sh + coverage run manage.py test openassessment render-templates: ./scripts/render-templates.sh @@ -69,15 +55,13 @@ test-js: render-templates test-js-debug: render-templates ./scripts/js-debugger.sh -test-sandbox: test-acceptance test-a11y +test: quality test-python test-js +# acceptance and a11y tests require a functioning sandbox, and do not run on travis test-acceptance: ./scripts/test-acceptance.sh tests test-a11y: ./scripts/test-acceptance.sh accessibility -update-npm-requirements: - npm update --silent - cp ./node_modules/backgrid/lib/backgrid*.js $(STATIC_JS)/lib/backgrid/ - cp ./node_modules/backgrid/lib/backgrid*.css $(STATIC_CSS)/lib/backgrid/ +test-sandbox: test-acceptance test-a11y diff --git a/README.rst b/README.rst index b47622fc22..50538efcf2 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ Open Response Assessment |build-status| |coverage-status| Installation, Tests, and other Developer Tasks ============================================== -EdX engineers follow the `guides on our wiki `_. +EdX engineers follow the `guides on our wiki `_. License ======= diff --git a/apt-packages.txt b/apt-packages.txt deleted file mode 100644 index 6a879a824a..0000000000 --- a/apt-packages.txt +++ /dev/null @@ -1,20 +0,0 @@ -aspell -g++ -gcc -git -gfortran -libblas-dev -liblapack-dev -libatlas-base-dev -libfontconfig1 -libmysqlclient-dev -libxml2-dev -libxslt1-dev -nodejs -npm -python2.7 -python2.7-dev -python-mysqldb -python-pip -python-software-properties -rubygems diff --git a/conf/locale b/conf/locale deleted file mode 120000 index cc5900e633..0000000000 --- a/conf/locale +++ /dev/null @@ -1 +0,0 @@ -../openassessment/locale/ \ No newline at end of file diff --git a/logs/README.txt b/logs/README.txt deleted file mode 100644 index a899d429c2..0000000000 --- a/logs/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -Log files: - -apps_info.log = INFO level logging for all edx-ora2 apps and OpenAssessmentBlock -apps_debug.log = same as above, except DEBUG level -errors.log = all ERROR and CRITICAL logs, stack traces -events.log = Analytics events from the xblock-sdk workbench runtime's publish() -trace.log = The kitchen sink. Massive because of SQL debug logs from Django. diff --git a/manage.py b/manage.py index dfd6e97d6e..3b6da0ba63 100755 --- a/manage.py +++ b/manage.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -import sys import os +import sys if __name__ == "__main__": diff --git a/openassessment/assessment/admin.py b/openassessment/assessment/admin.py index 73c5d8547c..7f5480f514 100644 --- a/openassessment/assessment/admin.py +++ b/openassessment/assessment/admin.py @@ -4,13 +4,10 @@ import json from django.contrib import admin -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse_lazy from django.utils import html -from openassessment.assessment.models import ( - Assessment, AssessmentFeedback, PeerWorkflow, PeerWorkflowItem, Rubric, - AIGradingWorkflow, AITrainingWorkflow, AIClassifierSet, AIClassifier -) +from openassessment.assessment.models import Assessment, AssessmentFeedback, PeerWorkflow, PeerWorkflowItem, Rubric from openassessment.assessment.serializers import RubricSerializer @@ -92,7 +89,7 @@ def rubric_link(self, assessment_obj): """ Returns the rubric link for this assessment. """ - url = reverse( + url = reverse_lazy( 'admin:assessment_rubric_change', args=[assessment_obj.rubric.id] ) @@ -141,7 +138,7 @@ def assessments_by(self, assessment_feedback): """ links = [ u'{}'.format( - reverse('admin:assessment_assessment_change', args=[asmt.id]), + reverse_lazy('admin:assessment_assessment_change', args=[asmt.id]), html.escape(asmt.scorer_id) ) for asmt in assessment_feedback.assessments.all() @@ -150,44 +147,7 @@ def assessments_by(self, assessment_feedback): assessments_by.allow_tags = True -class AIGradingWorkflowAdmin(admin.ModelAdmin): - """ - Django admin model for AIGradingWorkflows. - """ - list_display = ('uuid', 'submission_uuid') - search_fields = ('uuid', 'submission_uuid', 'student_id', 'item_id', 'course_id') - readonly_fields = ('uuid', 'submission_uuid', 'student_id', 'item_id', 'course_id') - - -class AITrainingWorkflowAdmin(admin.ModelAdmin): - """ - Django admin model for AITrainingWorkflows. - """ - list_display = ('uuid',) - search_fields = ('uuid', 'course_id', 'item_id',) - readonly_fields = ('uuid', 'course_id', 'item_id',) - - -class AIClassifierInline(admin.TabularInline): - """ - Django admin model for AIClassifiers. - """ - model = AIClassifier - - -class AIClassifierSetAdmin(admin.ModelAdmin): - """ - Django admin model for AICLassifierSets. - """ - list_display = ('id',) - search_fields = ('id',) - inlines = [AIClassifierInline] - - admin.site.register(Rubric, RubricAdmin) admin.site.register(PeerWorkflow, PeerWorkflowAdmin) admin.site.register(Assessment, AssessmentAdmin) admin.site.register(AssessmentFeedback, AssessmentFeedbackAdmin) -admin.site.register(AIGradingWorkflow, AIGradingWorkflowAdmin) -admin.site.register(AITrainingWorkflow, AITrainingWorkflowAdmin) -admin.site.register(AIClassifierSet, AIClassifierSetAdmin) diff --git a/openassessment/assessment/api/ai.py b/openassessment/assessment/api/ai.py deleted file mode 100644 index 9a5a37742c..0000000000 --- a/openassessment/assessment/api/ai.py +++ /dev/null @@ -1,396 +0,0 @@ -""" -Public interface for AI training and grading, used by students/course authors. -""" -import logging -from django.db import DatabaseError -from submissions import api as sub_api -from openassessment.assessment.serializers import ( - deserialize_training_examples, rubric_from_dict, - InvalidTrainingExample, InvalidRubric, full_assessment_dict -) -from openassessment.assessment.errors import ( - AITrainingRequestError, AITrainingInternalError, AIGradingRequestError, - AIGradingInternalError, AIReschedulingRequestError, ANTICIPATED_CELERY_ERRORS -) -from openassessment.assessment.models import ( - Assessment, AITrainingWorkflow, AIGradingWorkflow, - InvalidRubricSelection, NoTrainingExamples, - AI_ASSESSMENT_TYPE, AIClassifierSet -) -from openassessment.assessment.worker import training as training_tasks -from openassessment.assessment.worker import grading as grading_tasks - - -logger = logging.getLogger(__name__) - - -def submitter_is_finished(submission_uuid, ai_requirements): - """ - Determine if the submitter has finished their requirements for Example - Based Assessment. Always returns True. - - Args: - submission_uuid (str): Not used. - ai_requirements (dict): Not used. - - Returns: - True - - """ - return True - - -def assessment_is_finished(submission_uuid, ai_requirements): - """ - Determine if the assessment of the given submission is completed. This - checks to see if the AI has completed the assessment. - - Args: - submission_uuid (str): The UUID of the submission being graded. - ai_requirements (dict): Not used. - - Returns: - True if the assessment has been completed for this submission. - - """ - return bool(get_latest_assessment(submission_uuid)) - - -def get_score(submission_uuid, ai_requirements): - """ - Generate a score based on a completed assessment for the given submission. - If no assessment has been completed for this submission, this will return - None. - - Args: - submission_uuid (str): The UUID for the submission to get a score for. - ai_requirements (dict): Not used. - - Returns: - A dictionary with the points earned, points possible, and - contributing_assessments information, along with a None staff_id. - - """ - assessment = get_latest_assessment(submission_uuid) - if not assessment: - return None - - return { - "points_earned": assessment["points_earned"], - "points_possible": assessment["points_possible"], - "contributing_assessments": [assessment["id"]], - "staff_id": None, - } - - -def on_init(submission_uuid, rubric=None, algorithm_id=None): - """ - Submit a response for AI assessment. - This will: - (a) create a workflow (database record) to track the grading task - (b) if classifiers exist for the rubric, schedule an asynchronous grading task. - - Args: - submission_uuid (str): The UUID of the submission to assess. - - Keyword Arguments: - rubric (dict): Serialized rubric model. - algorithm_id (unicode): Use only classifiers trained with the specified algorithm. - - Returns: - grading_workflow_uuid (str): The UUID of the grading workflow. - Usually the caller of `submit()` won't need this (since the workers - are parameterized by grading workflow UUID), but it's - useful for testing. - - Raises: - AIGradingRequestError - AIGradingInternalError - - Example Usage: - - >>> on_init('74a9d63e8a5fea369fd391d07befbd86ae4dc6e2', rubric, 'ease') - '10df7db776686822e501b05f452dc1e4b9141fe5' - - """ - if rubric is None: - raise AIGradingRequestError(u'No rubric provided') - - if algorithm_id is None: - raise AIGradingRequestError(u'No algorithm ID provided') - - try: - workflow = AIGradingWorkflow.start_workflow(submission_uuid, rubric, algorithm_id) - except (sub_api.SubmissionNotFoundError, sub_api.SubmissionRequestError) as ex: - msg = ( - u"An error occurred while retrieving the " - u"submission with UUID {uuid}: {ex}" - ).format(uuid=submission_uuid, ex=ex) - raise AIGradingRequestError(msg) - except InvalidRubric as ex: - msg = ( - u"An error occurred while parsing the serialized " - u"rubric {rubric}: {ex}" - ).format(rubric=rubric, ex=ex) - raise AIGradingRequestError(msg) - except (sub_api.SubmissionInternalError, DatabaseError) as ex: - msg = ( - u"An unexpected error occurred while submitting an " - u"essay for AI grading: {ex}" - ).format(ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - - # If we find classifiers for this rubric/algorithm - # then associate the classifiers with the workflow - # and schedule a grading task. - # Otherwise, the task will need to be scheduled later, - # once the classifiers have been trained. - if workflow.classifier_set is not None: - try: - grading_tasks.grade_essay.apply_async(args=[workflow.uuid]) - logger.info(( - u"Scheduled grading task for AI grading workflow with UUID {workflow_uuid} " - u"(submission UUID = {sub_uuid}, algorithm ID = {algorithm_id})" - ).format(workflow_uuid=workflow.uuid, sub_uuid=submission_uuid, algorithm_id=algorithm_id)) - return workflow.uuid - except (DatabaseError,) + ANTICIPATED_CELERY_ERRORS as ex: - msg = ( - u"An unexpected error occurred while scheduling the " - u"AI grading task for the submission with UUID {uuid}: {ex}" - ).format(uuid=submission_uuid, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - else: - logger.info(( - u"Cannot schedule a grading task for AI grading workflow with UUID {workflow_uuid} " - u"because no classifiers are available for the rubric associated with submission {sub_uuid} " - u"for the algorithm {algorithm_id}" - ).format(workflow_uuid=workflow.uuid, sub_uuid=submission_uuid, algorithm_id=algorithm_id)) - - -def get_latest_assessment(submission_uuid): - """ - Retrieve the latest AI assessment for a submission. - - Args: - submission_uuid (str): The UUID of the submission being assessed. - - Returns: - dict: The serialized assessment model - or None if no assessments are available - - Raises: - AIGradingInternalError - - Example usage: - - >>> get_latest_assessment('10df7db776686822e501b05f452dc1e4b9141fe5') - { - 'points_earned': 6, - 'points_possible': 12, - 'scored_at': datetime.datetime(2014, 1, 29, 17, 14, 52, 649284 tzinfo=), - 'scorer': u"ease", - 'feedback': u'' - } - - """ - try: - assessments = Assessment.objects.filter( - submission_uuid=submission_uuid, - score_type=AI_ASSESSMENT_TYPE, - )[:1] - except DatabaseError as ex: - msg = ( - u"An error occurred while retrieving AI graded assessments " - u"for the submission with UUID {uuid}: {ex}" - ).format(uuid=submission_uuid, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - - if len(assessments) > 0: - return full_assessment_dict(assessments[0]) - else: - return None - - -def get_assessment_scores_by_criteria(submission_uuid): - """Get the score for each rubric criterion - - Args: - submission_uuid (str): The submission uuid is used to get the - assessment used to score this submission. - - Returns: - (dict): A dictionary of rubric criterion names, with a score of - the example based assessments. - - Raises: - AIGradingInternalError: If any error occurs while retrieving - information from the scores, an error is raised. - """ - try: - assessments = list( - Assessment.objects.filter( - score_type=AI_ASSESSMENT_TYPE, submission_uuid=submission_uuid - ).order_by('-scored_at')[:1] - ) - scores = Assessment.scores_by_criterion(assessments) - return Assessment.get_median_score_dict(scores) - except DatabaseError: - error_message = u"Error getting example-based assessment scores for {}".format(submission_uuid) - logger.exception(error_message) - raise AIGradingInternalError(error_message) - - -def train_classifiers(rubric_dict, examples, course_id, item_id, algorithm_id): - """ - Schedule a task to train classifiers. - All training examples must match the rubric! - After training of classifiers completes successfully, all AIGradingWorkflows that are incomplete will be - automatically rescheduled to complete. - - Args: - rubric_dict (dict): The rubric used to assess the classifiers. - examples (list of dict): Serialized training examples. - algorithm_id (unicode): The ID of the algorithm used to train the classifiers. - - Returns: - training_workflow_uuid (str): The UUID of the training workflow. - Usually the caller will not need this (since the workers - are parametrized by training workflow UUID), but it's - useful for testing. - - Raises: - AITrainingRequestError - AITrainingInternalError - - Example usage: - - >>> train_classifiers(rubric, examples, 'ease') - '10df7db776686822e501b05f452dc1e4b9141fe5' - - """ - # Get or create the rubric and training examples - try: - examples = deserialize_training_examples(examples, rubric_dict) - except (InvalidRubric, InvalidTrainingExample, InvalidRubricSelection) as ex: - msg = u"Could not parse rubric and/or training examples: {ex}".format(ex=ex) - raise AITrainingRequestError(msg) - - # Create the workflow model - try: - workflow = AITrainingWorkflow.start_workflow(examples, course_id, item_id, algorithm_id) - except NoTrainingExamples as ex: - raise AITrainingRequestError(ex) - except: - msg = ( - u"An unexpected error occurred while creating " - u"the AI training workflow" - ) - logger.exception(msg) - raise AITrainingInternalError(msg) - - # Schedule the task, parametrized by the workflow UUID - try: - training_tasks.train_classifiers.apply_async(args=[workflow.uuid]) - except ANTICIPATED_CELERY_ERRORS as ex: - msg = ( - u"An unexpected error occurred while scheduling incomplete training workflows with" - u" course_id={cid} and item_id={iid}: {ex}" - ).format(cid=course_id, iid=item_id, ex=ex) - logger.exception(msg) - raise AITrainingInternalError(msg) - - # Return the workflow UUID - return workflow.uuid - - -def reschedule_unfinished_tasks(course_id=None, item_id=None, task_type=u"grade"): - """ - Check for unfinished tasks (both grading and training) and reschedule them. - Optionally restrict by course/item ID and task type. Default use case is to - only reschedule the unfinished grade tasks. Applied use case (with button in - staff mixin) is to call without argument, and to reschedule grades only. - - Keyword Arguments: - course_id (unicode): Restrict to unfinished tasks in a particular course. - item_id (unicode): Restrict to unfinished tasks for a particular item in a course. - NOTE: if you specify the item ID, you must also specify the course ID. - task_type (unicode): Either "grade" or "train". Restrict to unfinished tasks of this type. - if task_type is specified as None, both training and grading will be rescheduled, in that order. - - Raises: - AIGradingInternalError - AITrainingInternalError - AIReschedulingRequestError - """ - - if course_id is None or item_id is None: - msg = u"Rescheduling tasks was not possible because the course_id / item_id was not assigned." - logger.exception(msg) - raise AIReschedulingRequestError - - # Reschedules all of the training tasks - if task_type == u"train" or task_type is None: - try: - training_tasks.reschedule_training_tasks.apply_async(args=[course_id, item_id]) - except ANTICIPATED_CELERY_ERRORS as ex: - msg = ( - u"Rescheduling training tasks for course {cid} and item {iid} failed with exception: {ex}" - ).format(cid=course_id, iid=item_id, ex=ex) - logger.exception(msg) - raise AITrainingInternalError(ex) - - # Reschedules all of the grading tasks - if task_type == u"grade" or task_type is None: - try: - grading_tasks.reschedule_grading_tasks.apply_async(args=[course_id, item_id]) - except ANTICIPATED_CELERY_ERRORS as ex: - msg = ( - u"Rescheduling grading tasks for course {cid} and item {iid} failed with exception: {ex}" - ).format(cid=course_id, iid=item_id, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(ex) - - -def get_classifier_set_info(rubric_dict, algorithm_id, course_id, item_id): - """ - Get information about the classifier available for a particular problem. - This is the classifier that would be selected to grade essays for the problem. - - Args: - rubric_dict (dict): The serialized rubric model. - algorithm_id (unicode): The algorithm to use for classification. - course_id (unicode): The course identifier for the current problem. - item_id (unicode): The item identifier for the current problem. - - Returns: - dict with keys 'created_at', 'algorithm_id', 'course_id', and 'item_id' - Note that course ID and item ID might be different than the current problem - if a classifier from a different problem with a similar rubric - is the best available match. - - """ - try: - rubric = rubric_from_dict(rubric_dict) - classifier_set = AIClassifierSet.most_recent_classifier_set( - rubric, algorithm_id, course_id, item_id - ) - if classifier_set is not None: - return { - 'created_at': classifier_set.created_at, - 'algorithm_id': classifier_set.algorithm_id, - 'course_id': classifier_set.course_id, - 'item_id': classifier_set.item_id - } - else: - return None - except InvalidRubric: - msg = u"Could not retrieve classifier set info: the rubric definition was not valid." - logger.exception(msg) - raise AIGradingRequestError(msg) - except DatabaseError as ex: - msg = u"An unexpected error occurred while retrieving classifier set info: {ex}".format(ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) diff --git a/openassessment/assessment/api/ai_worker.py b/openassessment/assessment/api/ai_worker.py deleted file mode 100644 index ca08b110a2..0000000000 --- a/openassessment/assessment/api/ai_worker.py +++ /dev/null @@ -1,347 +0,0 @@ -""" -Public interface for AI training and grading, used by workers. -""" -import logging -from httplib import HTTPException -from django.db import DatabaseError -from dogapi import dog_stats_api -from openassessment.assessment.models import ( - essay_text_from_submission, - AITrainingWorkflow, AIGradingWorkflow, - ClassifierUploadError, ClassifierSerializeError, - IncompleteClassifierSet, NoTrainingExamples, - InvalidRubricSelection -) -from openassessment.assessment.errors import ( - AITrainingRequestError, AITrainingInternalError, - AIGradingRequestError, AIGradingInternalError -) - - -logger = logging.getLogger(__name__) - - -@dog_stats_api.timed('openassessment.assessment.ai.get_grading_task_params') -def get_grading_task_params(grading_workflow_uuid): - """ - Retrieve the classifier set and algorithm ID - associated with a particular grading workflow. - - Args: - grading_workflow_uuid (str): The UUID of the grading workflow. - - Returns: - dict with keys: - * essay_text (unicode): The text of the essay submission. - * classifier_set (dict): Maps criterion names to serialized classifiers. - * valid_scores (dict): Maps criterion names to a list of valid scores for that criterion. - * algorithm_id (unicode): ID of the algorithm used to perform training. - - Raises: - AIGradingRequestError - AIGradingInternalError - - """ - try: - workflow = AIGradingWorkflow.objects.get(uuid=grading_workflow_uuid) - except AIGradingWorkflow.DoesNotExist: - msg = ( - u"Could not retrieve the AI grading workflow with uuid {}" - ).format(grading_workflow_uuid) - raise AIGradingRequestError(msg) - except DatabaseError as ex: - msg = ( - u"An unexpected error occurred while retrieving the " - u"AI grading workflow with uuid {uuid}: {ex}" - ).format(uuid=grading_workflow_uuid, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - - classifier_set = workflow.classifier_set - # Though tasks shouldn't be scheduled until classifer set(s) exist, off of the happy path this is a likely - # occurrence. Our response is to log this lack of compliance to dependency as an exception, and then thrown - # an error with the purpose of killing the celery task running this code. - if classifier_set is None: - msg = ( - u"AI grading workflow with UUID {} has no classifier set, but was scheduled for grading" - ).format(grading_workflow_uuid) - logger.exception(msg) - raise AIGradingInternalError(msg) - - try: - return { - 'essay_text': workflow.essay_text, - 'classifier_set': workflow.classifier_set.classifier_data_by_criterion, - 'algorithm_id': workflow.algorithm_id, - 'valid_scores': workflow.classifier_set.valid_scores_by_criterion, - } - except ( - DatabaseError, ClassifierSerializeError, IncompleteClassifierSet, - ValueError, IOError, HTTPException - ) as ex: - msg = ( - u"An unexpected error occurred while retrieving " - u"classifiers for the grading workflow with UUID {uuid}: {ex}" - ).format(uuid=grading_workflow_uuid, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - - -@dog_stats_api.timed('openassessment.assessment.ai.create_assessment') -def create_assessment(grading_workflow_uuid, criterion_scores): - """ - Create an AI assessment (complete the AI grading task). - - Args: - grading_workflow_uuid (str): The UUID of the grading workflow. - criterion_scores (dict): Dictionary mapping criteria names to integer scores. - - Returns: - None - - Raises: - AIGradingRequestError - AIGradingInternalError - - """ - try: - workflow = AIGradingWorkflow.objects.get(uuid=grading_workflow_uuid) - except AIGradingWorkflow.DoesNotExist: - msg = ( - u"Could not retrieve the AI grading workflow with uuid {}" - ).format(grading_workflow_uuid) - raise AIGradingRequestError(msg) - except DatabaseError as ex: - msg = ( - u"An unexpected error occurred while retrieving the " - u"AI grading workflow with uuid {uuid}: {ex}" - ).format(uuid=grading_workflow_uuid, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - - # Optimization: if the workflow has already been marked complete - # (perhaps the task was picked up by multiple workers), - # then we don't need to do anything. - # Otherwise, create the assessment mark the workflow complete. - try: - if not workflow.is_complete: - workflow.complete(criterion_scores) - logger.info(( - u"Created assessment for AI grading workflow with UUID {workflow_uuid} " - u"(algorithm ID {algorithm_id})" - ).format(workflow_uuid=workflow.uuid, algorithm_id=workflow.algorithm_id)) - else: - msg = u"Grading workflow with UUID {} is already marked complete".format(workflow.uuid) - logger.info(msg) - except DatabaseError as ex: - msg = ( - u"An unexpected error occurred while creating the assessment " - u"for AI grading workflow with uuid {uuid}: {ex}" - ).format(uuid=grading_workflow_uuid, ex=ex) - logger.exception(msg) - raise AIGradingInternalError(msg) - - # Fire a signal to update the workflow API - # This will allow students to receive a score if they're - # waiting on an AI assessment. - # The signal receiver is responsible for catching and logging - # all exceptions that may occur when updating the workflow. - from openassessment.assessment.signals import assessment_complete_signal - assessment_complete_signal.send(sender=None, submission_uuid=workflow.submission_uuid) - - -@dog_stats_api.timed('openassessment.assessment.ai.get_training_task_params') -def get_training_task_params(training_workflow_uuid): - """ - Retrieve the training examples and algorithm ID - associated with a training task. - - Args: - training_workflow_uuid (str): The UUID of the training workflow. - - Returns: - dict with keys: - * training_examples (list of dict): The examples used to train the classifiers. - * course_id (unicode): The course ID that the training task is associated with. - * item_id (unicode): Identifies the item that the AI will be training to grade. - * algorithm_id (unicode): The ID of the algorithm to use for training. - - Raises: - AITrainingRequestError - AITrainingInternalError - - Example usage: - >>> params = get_training_task_params('abcd1234') - >>> params['algorithm_id'] - u'ease' - >>> params['training_examples'] - [ - { - "text": u"Example answer number one", - "scores": { - "vocabulary": 1, - "grammar": 2 - } - }, - { - "text": u"Example answer number two", - "scores": { - "vocabulary": 3, - "grammar": 1 - } - } - ] - - """ - try: - workflow = AITrainingWorkflow.objects.get(uuid=training_workflow_uuid) - returned_examples = [] - - for example in workflow.training_examples.all(): - scores = { - option.criterion.name: option.points - for option in example.options_selected.all() - } - - returned_examples.append({ - 'text': essay_text_from_submission({'answer': example.answer}), - 'scores': scores - }) - - return { - 'training_examples': returned_examples, - 'algorithm_id': workflow.algorithm_id, - 'course_id': workflow.course_id, - 'item_id': workflow.item_id - } - except AITrainingWorkflow.DoesNotExist: - msg = ( - u"Could not retrieve AI training workflow with UUID {}" - ).format(training_workflow_uuid) - raise AITrainingRequestError(msg) - except DatabaseError: - msg = ( - u"An unexpected error occurred while retrieving " - u"training examples for the AI training workflow with UUID {}" - ).format(training_workflow_uuid) - logger.exception(msg) - raise AITrainingInternalError(msg) - - -@dog_stats_api.timed('openassessment.assessment.ai.create_classifiers') -def create_classifiers(training_workflow_uuid, classifier_set): - """ - Upload trained classifiers and mark the workflow complete. - - If grading tasks were submitted before any classifiers were trained, - this call will automatically reschedule those tasks. - - Args: - training_workflow_uuid (str): The UUID of the training workflow. - classifier_set (dict): Mapping of criteria names to serialized classifiers. - - Returns: - None - - Raises: - AITrainingRequestError - AITrainingInternalError - - """ - try: - workflow = AITrainingWorkflow.objects.get(uuid=training_workflow_uuid) - - # If the task is executed multiple times, the classifier set may already - # have been created. If so, log it, then return immediately. - if workflow.is_complete: - msg = u"AI training workflow with UUID {} already has trained classifiers.".format(workflow.uuid) - logger.info(msg) - else: - workflow.complete(classifier_set) - logger.info(( - u"Created trained classifiers for the AI training workflow with UUID {workflow_uuid} " - u"(using algorithm ID {algorithm_id})" - ).format(workflow_uuid=workflow.uuid, algorithm_id=workflow.algorithm_id)) - except AITrainingWorkflow.DoesNotExist: - msg = ( - u"Could not retrieve AI training workflow with UUID {}" - ).format(training_workflow_uuid) - raise AITrainingRequestError(msg) - except NoTrainingExamples as ex: - logger.exception(ex) - raise AITrainingInternalError(ex) - except (IncompleteClassifierSet, InvalidRubricSelection) as ex: - msg = ( - u"An error occurred while creating the classifier set " - u"for the training workflow with UUID {uuid}: {ex}" - ).format(uuid=training_workflow_uuid, ex=ex) - raise AITrainingRequestError(msg) - except (ClassifierSerializeError, ClassifierUploadError, DatabaseError) as ex: - msg = ( - u"An unexpected error occurred while creating the classifier " - u"set for training workflow UUID {uuid}: {ex}" - ).format(uuid=training_workflow_uuid, ex=ex) - logger.exception(msg) - raise AITrainingInternalError(msg) - - -def is_training_workflow_complete(workflow_uuid): - """ - Check whether the training workflow is complete. - - Args: - workflow_uuid (str): The UUID of the training workflow - - Returns: - bool - - Raises: - AITrainingRequestError - AITrainingInternalError - - """ - try: - return AITrainingWorkflow.is_workflow_complete(workflow_uuid) - except AITrainingWorkflow.DoesNotExist: - msg = ( - u"Could not retrieve training workflow " - u"with uuid {uuid} to check whether it's complete." - ).format(uuid=workflow_uuid) - raise AITrainingRequestError(msg) - except DatabaseError: - msg = ( - u"An unexpected error occurred while checking " - u"the training workflow with uuid {uuid} for completeness" - ).format(uuid=workflow_uuid) - raise AITrainingInternalError(msg) - - -def is_grading_workflow_complete(workflow_uuid): - """ - Check whether the grading workflow is complete. - - Args: - workflow_uuid (str): The UUID of the grading workflow - - Returns: - bool - - Raises: - AIGradingRequestError - AIGradingInternalError - - """ - try: - return AIGradingWorkflow.is_workflow_complete(workflow_uuid) - except AIGradingWorkflow.DoesNotExist: - msg = ( - u"Could not retrieve grading workflow " - u"with uuid {uuid} to check whether it's complete." - ).format(uuid=workflow_uuid) - raise AIGradingRequestError(msg) - except DatabaseError: - msg = ( - u"An unexpected error occurred while checking " - u"the grading workflow with uuid {uuid} for completeness" - ).format(uuid=workflow_uuid) - raise AIGradingInternalError(msg) diff --git a/openassessment/assessment/api/peer.py b/openassessment/assessment/api/peer.py index 5c5c335655..d0915264cc 100644 --- a/openassessment/assessment/api/peer.py +++ b/openassessment/assessment/api/peer.py @@ -5,22 +5,17 @@ """ import logging -from django.utils import timezone + from django.db import DatabaseError, IntegrityError, transaction -from dogapi import dog_stats_api +from django.utils import timezone -from openassessment.assessment.models import ( - Assessment, AssessmentFeedback, AssessmentPart, - InvalidRubricSelection, PeerWorkflow, PeerWorkflowItem, -) -from openassessment.assessment.serializers import ( - AssessmentFeedbackSerializer, RubricSerializer, - full_assessment_dict, rubric_from_dict, serialize_assessments, - InvalidRubric -) -from openassessment.assessment.errors import ( - PeerAssessmentRequestError, PeerAssessmentWorkflowError, PeerAssessmentInternalError -) +from dogapi import dog_stats_api +from openassessment.assessment.errors import (PeerAssessmentInternalError, PeerAssessmentRequestError, + PeerAssessmentWorkflowError) +from openassessment.assessment.models import (Assessment, AssessmentFeedback, AssessmentPart, InvalidRubricSelection, + PeerWorkflow, PeerWorkflowItem) +from openassessment.assessment.serializers import (AssessmentFeedbackSerializer, InvalidRubric, RubricSerializer, + full_assessment_dict, rubric_from_dict, serialize_assessments) from submissions import api as sub_api logger = logging.getLogger("openassessment.assessment.api.peer") diff --git a/openassessment/assessment/api/self.py b/openassessment/assessment/api/self.py index 53b9e49b38..c940f0955f 100644 --- a/openassessment/assessment/api/self.py +++ b/openassessment/assessment/api/self.py @@ -2,20 +2,15 @@ Public interface for self-assessment. """ import logging -from django.db import DatabaseError, transaction -from dogapi import dog_stats_api -from submissions.api import get_submission_and_student, SubmissionNotFoundError -from openassessment.assessment.serializers import ( - InvalidRubric, full_assessment_dict, rubric_from_dict, serialize_assessments -) -from openassessment.assessment.models import ( - Assessment, AssessmentPart, InvalidRubricSelection -) -from openassessment.assessment.errors import ( - SelfAssessmentRequestError, SelfAssessmentInternalError -) +from django.db import DatabaseError, transaction +from dogapi import dog_stats_api +from openassessment.assessment.errors import SelfAssessmentInternalError, SelfAssessmentRequestError +from openassessment.assessment.models import Assessment, AssessmentPart, InvalidRubricSelection +from openassessment.assessment.serializers import (InvalidRubric, full_assessment_dict, rubric_from_dict, + serialize_assessments) +from submissions.api import SubmissionNotFoundError, get_submission_and_student # Assessments are tagged as "self-evaluation" SELF_TYPE = "SE" diff --git a/openassessment/assessment/api/staff.py b/openassessment/assessment/api/staff.py index a4ed6428fe..f327d94fb5 100644 --- a/openassessment/assessment/api/staff.py +++ b/openassessment/assessment/api/staff.py @@ -2,25 +2,15 @@ Public interface for staff grading, used by students/course staff. """ import logging + from django.db import DatabaseError, transaction from django.utils.timezone import now -from dogapi import dog_stats_api +from openassessment.assessment.errors import StaffAssessmentInternalError, StaffAssessmentRequestError +from openassessment.assessment.models import Assessment, AssessmentPart, InvalidRubricSelection, StaffWorkflow +from openassessment.assessment.serializers import InvalidRubric, full_assessment_dict, rubric_from_dict from submissions import api as submissions_api -from openassessment.assessment.models import ( - Assessment, AssessmentFeedback, AssessmentPart, - InvalidRubricSelection, StaffWorkflow, -) -from openassessment.assessment.serializers import ( - AssessmentFeedbackSerializer, RubricSerializer, - full_assessment_dict, rubric_from_dict, serialize_assessments, - InvalidRubric -) -from openassessment.assessment.errors import ( - StaffAssessmentRequestError, StaffAssessmentInternalError -) - logger = logging.getLogger("openassessment.assessment.api.staff") STAFF_TYPE = "ST" diff --git a/openassessment/assessment/api/student_training.py b/openassessment/assessment/api/student_training.py index 6413fe2380..f7029ac61d 100644 --- a/openassessment/assessment/api/student_training.py +++ b/openassessment/assessment/api/student_training.py @@ -7,19 +7,15 @@ """ import logging -from django.utils.translation import ugettext as _ + from django.db import DatabaseError -from submissions import api as sub_api -from openassessment.assessment.models import StudentTrainingWorkflow, InvalidRubricSelection -from openassessment.assessment.serializers import ( - deserialize_training_examples, serialize_training_example, - validate_training_example_format, - InvalidTrainingExample, InvalidRubric -) -from openassessment.assessment.errors import ( - StudentTrainingRequestError, StudentTrainingInternalError -) +from django.utils.translation import ugettext as _ +from openassessment.assessment.errors import StudentTrainingInternalError, StudentTrainingRequestError +from openassessment.assessment.models import InvalidRubricSelection, StudentTrainingWorkflow +from openassessment.assessment.serializers import (InvalidRubric, InvalidTrainingExample, deserialize_training_examples, + serialize_training_example, validate_training_example_format) +from submissions import api as sub_api logger = logging.getLogger(__name__) diff --git a/openassessment/assessment/data_conversion.py b/openassessment/assessment/data_conversion.py index d8a119adc6..70582d0b27 100644 --- a/openassessment/assessment/data_conversion.py +++ b/openassessment/assessment/data_conversion.py @@ -2,7 +2,6 @@ Data Conversion utility methods for handling assessment data transformations. """ -import json def update_training_example_answer_format(answer): @@ -14,7 +13,7 @@ def update_training_example_answer_format(answer): Returns: dict """ - if isinstance(answer, unicode) or isinstance(answer, str): + if isinstance(answer, (str, unicode)): return { 'parts': [ {'text': answer} diff --git a/openassessment/assessment/errors/__init__.py b/openassessment/assessment/errors/__init__.py index 1c9651f6fa..dfb3825f0e 100644 --- a/openassessment/assessment/errors/__init__.py +++ b/openassessment/assessment/errors/__init__.py @@ -8,4 +8,3 @@ from .self import * from .staff import * from .student_training import * -from .ai import * diff --git a/openassessment/assessment/errors/ai.py b/openassessment/assessment/errors/ai.py deleted file mode 100644 index 1b64a69f70..0000000000 --- a/openassessment/assessment/errors/ai.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Errors related to AI assessment. -""" - -from celery.exceptions import InvalidTaskError, NotConfigured, NotRegistered, QueueNotFound -from socket import error as socket_error - -ANTICIPATED_CELERY_ERRORS = (InvalidTaskError, NotConfigured, NotRegistered, QueueNotFound, socket_error) - -class AIError(Exception): - """ - A general error occurred while using the AI assessment API. - """ - pass - - -class AITrainingRequestError(AIError): - """ - There was a problem with the request sent to the AI assessment API. - """ - pass - - -class AITrainingInternalError(AIError): - """ - An unexpected error occurred while using the AI assessment API. - """ - pass - - -class AIGradingRequestError(AIError): - """ - There was a problem with the request sent to the AI assessment API. - """ - pass - - -class AIGradingInternalError(AIError): - """ - An unexpected error occurred while using the AI assessment API. - """ - pass - - -class AIReschedulingRequestError(AIError): - """ - There was a problem with the request sent to the AI assessment API. - """ - pass - - -class AIReschedulingInternalError(AIError): - """ - An unexpected error occurred while using the AI assessment API. - """ - pass diff --git a/openassessment/assessment/migrations/0001_initial.py b/openassessment/assessment/migrations/0001_initial.py index 34a1ae2b5a..e1209ae5d0 100644 --- a/openassessment/assessment/migrations/0001_initial.py +++ b/openassessment/assessment/migrations/0001_initial.py @@ -2,10 +2,8 @@ # pylint: skip-file from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models import django.utils.timezone -import django_extensions.db.fields -import openassessment.assessment.models.ai class Migration(migrations.Migration): @@ -14,54 +12,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name='AIClassifier', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('classifier_data', models.FileField(upload_to=openassessment.assessment.models.ai.upload_to_path)), - ], - ), - migrations.CreateModel( - name='AIClassifierSet', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('created_at', models.DateTimeField(default=django.utils.timezone.now, db_index=True)), - ('algorithm_id', models.CharField(max_length=128, db_index=True)), - ('course_id', models.CharField(max_length=40, db_index=True)), - ('item_id', models.CharField(max_length=128, db_index=True)), - ], - options={ - 'ordering': ['-created_at', '-id'], - }, - ), - migrations.CreateModel( - name='AIGradingWorkflow', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('uuid', django_extensions.db.fields.UUIDField(db_index=True, unique=True, version=1, editable=False, blank=True)), - ('course_id', models.CharField(max_length=40, db_index=True)), - ('item_id', models.CharField(max_length=128, db_index=True)), - ('scheduled_at', models.DateTimeField(default=django.utils.timezone.now, db_index=True)), - ('completed_at', models.DateTimeField(null=True, db_index=True)), - ('algorithm_id', models.CharField(max_length=128, db_index=True)), - ('submission_uuid', models.CharField(max_length=128, db_index=True)), - ('essay_text', models.TextField(blank=True)), - ('student_id', models.CharField(max_length=40, db_index=True)), - ], - ), - migrations.CreateModel( - name='AITrainingWorkflow', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('uuid', django_extensions.db.fields.UUIDField(db_index=True, unique=True, version=1, editable=False, blank=True)), - ('course_id', models.CharField(max_length=40, db_index=True)), - ('item_id', models.CharField(max_length=128, db_index=True)), - ('scheduled_at', models.DateTimeField(default=django.utils.timezone.now, db_index=True)), - ('completed_at', models.DateTimeField(null=True, db_index=True)), - ('algorithm_id', models.CharField(max_length=128, db_index=True)), - ('classifier_set', models.ForeignKey(related_name='+', default=None, to='assessment.AIClassifierSet', null=True)), - ], - ), migrations.CreateModel( name='Assessment', fields=[ @@ -235,41 +185,6 @@ class Migration(migrations.Migration): name='rubric', field=models.ForeignKey(to='assessment.Rubric'), ), - migrations.AddField( - model_name='aitrainingworkflow', - name='training_examples', - field=models.ManyToManyField(related_name='+', to='assessment.TrainingExample'), - ), - migrations.AddField( - model_name='aigradingworkflow', - name='assessment', - field=models.ForeignKey(related_name='+', default=None, to='assessment.Assessment', null=True), - ), - migrations.AddField( - model_name='aigradingworkflow', - name='classifier_set', - field=models.ForeignKey(related_name='+', default=None, to='assessment.AIClassifierSet', null=True), - ), - migrations.AddField( - model_name='aigradingworkflow', - name='rubric', - field=models.ForeignKey(related_name='+', to='assessment.Rubric'), - ), - migrations.AddField( - model_name='aiclassifierset', - name='rubric', - field=models.ForeignKey(related_name='+', to='assessment.Rubric'), - ), - migrations.AddField( - model_name='aiclassifier', - name='classifier_set', - field=models.ForeignKey(related_name='classifiers', to='assessment.AIClassifierSet'), - ), - migrations.AddField( - model_name='aiclassifier', - name='criterion', - field=models.ForeignKey(related_name='+', to='assessment.Criterion'), - ), migrations.AlterUniqueTogether( name='studenttrainingworkflowitem', unique_together=set([('workflow', 'order_num')]), diff --git a/openassessment/assessment/migrations/0003_expand_course_id.py b/openassessment/assessment/migrations/0003_expand_course_id.py index 56e5804818..0db2f5accc 100644 --- a/openassessment/assessment/migrations/0003_expand_course_id.py +++ b/openassessment/assessment/migrations/0003_expand_course_id.py @@ -12,21 +12,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterField( - model_name='aiclassifierset', - name='course_id', - field=models.CharField(max_length=255, db_index=True), - ), - migrations.AlterField( - model_name='aigradingworkflow', - name='course_id', - field=models.CharField(max_length=255, db_index=True), - ), - migrations.AlterField( - model_name='aitrainingworkflow', - name='course_id', - field=models.CharField(max_length=255, db_index=True), - ), migrations.AlterField( model_name='peerworkflow', name='course_id', diff --git a/openassessment/assessment/models/__init__.py b/openassessment/assessment/models/__init__.py index 4098b07155..f6e7d01975 100644 --- a/openassessment/assessment/models/__init__.py +++ b/openassessment/assessment/models/__init__.py @@ -7,5 +7,4 @@ from .peer import * from .training import * from .student_training import * -from .ai import * from .staff import * diff --git a/openassessment/assessment/models/ai.py b/openassessment/assessment/models/ai.py deleted file mode 100644 index 0b02ea6d45..0000000000 --- a/openassessment/assessment/models/ai.py +++ /dev/null @@ -1,874 +0,0 @@ -""" -Database models for AI assessment. -""" -from uuid import uuid4 -import json -import logging -from django.conf import settings -from django.core import signals -from django.core.files.base import ContentFile -from django.core.cache import cache, _create_cache -from django.db import models, transaction, DatabaseError -from django.utils.timezone import now -from django_extensions.db.fields import UUIDField -from dogapi import dog_stats_api -from submissions import api as sub_api -from .base import Rubric, Criterion, Assessment, AssessmentPart -from .training import TrainingExample - - -AI_ASSESSMENT_TYPE = "AI" - -logger = logging.getLogger(__name__) - - -def create_cache(backend, **kwargs): - """ - Create cache backend. Using this custom function to avoid deprecation warnings. - """ - cache = _create_cache(backend, **kwargs) - # Some caches -- python-memcached in particular -- need to do a cleanup at the - # end of a request cycle. If not implemented in a particular backend - # cache.close is a no-op - signals.request_finished.connect(cache.close) - return cache - - -# Use an in-memory cache to hold classifier data, but allow settings to override this. -# The classifier data will generally be larger than memcached's default max size -CLASSIFIERS_CACHE_IN_MEM = getattr( - settings, 'ORA2_CLASSIFIERS_CACHE_IN_MEM', - create_cache( - 'django.core.cache.backends.locmem.LocMemCache', - LOCATION='openassessment.ai.classifiers_dict' - ) -) - -CLASSIFIERS_CACHE_IN_FILE = getattr( - settings, 'ORA2_CLASSIFIERS_CACHE_IN_FILE', - create_cache( - 'django.core.cache.backends.filebased.FileBasedCache', - LOCATION='/tmp/ora2_classifier_cache' - ) -) - - -def essay_text_from_submission(submission): - """ - Retrieve the submission text. - - Submissions are arbitrary JSON-blobs, which *should* - contain a single key, "answer", containing the essay - submission text. - If not, though, assume we've been given the essay text - directly (convenient for testing). - """ - if isinstance(submission, dict): - if 'answer' in submission: - # Format used for answer in examples. - if isinstance(submission['answer'], unicode): - return submission['answer'] - # Initially there was one prompt and submission had the structure - # {'answer': {'text': 'The text.'}} - elif 'text' in submission['answer']: - essay_text = submission['answer']['text'] - # When multiple prompts were introduced the structure of submission become: - # {'answer': {'parts': [{'text': 'The text part 1.'}, {'text': 'The text part 2.'}]}} - # We concatenate these parts and let AI grader evaluate the total text. - else: - essay_text = u'\n'.join([part['text'] for part in submission['answer']['parts']]) - else: - essay_text = unicode(submission) - return essay_text - - -class IncompleteClassifierSet(Exception): - """ - The classifier set is missing a classifier for a criterion in the rubric. - """ - def __init__(self, missing_criteria): - """ - Construct an error message that explains which criteria were missing. - - Args: - missing_criteria (list): The list of criteria names that were missing. - - """ - msg = ( - u"Missing classifiers for the following " - u"criteria: {missing}" - ).format(missing=missing_criteria) - super(IncompleteClassifierSet, self).__init__(msg) - - -class ClassifierUploadError(Exception): - """ - An error occurred while uploading classifier data. - """ - pass - - -class ClassifierSerializeError(Exception): - """ - An error occurred while serializing classifier data. - """ - pass - - -class NoTrainingExamples(Exception): - """ - No training examples were provided to the workflow. - """ - def __init__(self, workflow_uuid=None): - msg = u"No training examples were provided" - if workflow_uuid is not None: - msg = u"{msg} to the training workflow with UUID {uuid}".format( - msg=msg, uuid=workflow_uuid - ) - super(NoTrainingExamples, self).__init__(msg) - - -class AIClassifierSet(models.Model): - """ - A set of trained classifiers (immutable). - """ - - class Meta: - app_label = "assessment" - ordering = ['-created_at', '-id'] - - # The rubric associated with this set of classifiers - # We should have one classifier for each of the criteria in the rubric. - rubric = models.ForeignKey(Rubric, related_name="+") - - # Timestamp for when the classifier set was created. - # This allows us to find the most recently trained set of classifiers. - created_at = models.DateTimeField(default=now, db_index=True) - - # The ID of the algorithm that was used to train classifiers in this set. - algorithm_id = models.CharField(max_length=128, db_index=True) - - # Course Entity and Item Discriminator - # Though these items are duplicated in the database tables for the AITrainingWorkflow, - # this is okay because it will drastically speed up the operation of assigning classifiers - # to AIGradingWorkflows - course_id = models.CharField(max_length=255, db_index=True) - item_id = models.CharField(max_length=128, db_index=True) - - @classmethod - @transaction.atomic - def create_classifier_set(cls, classifiers_dict, rubric, algorithm_id, course_id, item_id): - """ - Create a set of classifiers. - - Args: - classifiers_dict (dict): Mapping of criterion names to - JSON-serializable classifiers. - rubric (Rubric): The rubric model. - algorithm_id (unicode): The ID of the algorithm used to train the classifiers. - course_id (unicode): The ID of the course that the classifier is going to be grading - item_id (unicode): The item within the course that the classifier is trained to grade. - - Returns: - AIClassifierSet - - Raises: - ClassifierSerializeError - ClassifierUploadError - InvalidRubricSelection - DatabaseError - - """ - # Create the classifier set - classifier_set = cls.objects.create( - rubric=rubric, algorithm_id=algorithm_id, item_id=item_id, course_id=course_id - ) - - # Retrieve the criteria for this rubric, - # then organize them by criterion name - try: - rubric_index = rubric.index - except DatabaseError as ex: - msg = ( - u"An unexpected error occurred while retrieving rubric criteria with the" - u"rubric hash {rh} and algorithm_id {aid}: {ex}" - ).format(rh=rubric.content_hash, aid=algorithm_id, ex=ex) - logger.exception(msg) - raise - - # Check that we have classifiers for all criteria in the rubric - # Ignore criteria that have no options: since these have only written feedback, - # we can't assign them a score. - all_criteria = set(classifiers_dict.keys()) - all_criteria |= set( - criterion.name for criterion in - rubric_index.find_criteria_without_options() - ) - missing_criteria = rubric_index.find_missing_criteria(all_criteria) - if missing_criteria: - raise IncompleteClassifierSet(missing_criteria) - - # Create classifiers for each criterion - for criterion_name, classifier_data in classifiers_dict.iteritems(): - classifier = AIClassifier.objects.create( - classifier_set=classifier_set, - criterion=rubric_index.find_criterion(criterion_name) - ) - - # Serialize the classifier data and upload - try: - contents = ContentFile(json.dumps(classifier_data)) - except (TypeError, ValueError, UnicodeDecodeError) as ex: - msg = ( - u"Could not serialize classifier data as JSON: {ex}" - ).format(ex=ex) - raise ClassifierSerializeError(msg) - - filename = uuid4().hex - try: - classifier.classifier_data.save(filename, contents) - except Exception as ex: - full_filename = upload_to_path(classifier, filename) - msg = ( - u"Could not upload classifier data to {filename}: {ex}" - ).format(filename=full_filename, ex=ex) - raise ClassifierUploadError(msg) - - return classifier_set - - @classmethod - def most_recent_classifier_set(cls, rubric, algorithm_id, course_id, item_id): - """ - Finds the most relevant classifier set based on the following line of succession: - - 1 -- Classifier sets with the same COURSE, ITEM, RUBRIC *content* hash, and ALGORITHM - - Newest first. If none exist... - 2 -- Classifier sets with the same COURSE, ITEM, and RUBRIC *structure* hash, and ALGORITHM. - - Newest first. If none exist... - 3 -- The newest classifier set with the same RUBRIC and ALGORITHM - - Newest first. If none exist... - 4 -- Do no assignment and return False - - Case #1 is ideal: we get a classifier set trained for the rubric as currently defined. - - Case #2 handles when a course author makes a cosmetic change to a rubric after training. - We don't want to stop grading students because an author fixed a typo! - - Case #3 handles problems that are duplicated, such as the default problem prompt. - If we've already trained classifiers for the identical rubric somewhere else, - then the author can use them to test out the feature immediately. - - Case #4: Someone will need to schedule training; however, we will still accept - student submissions and grade them once training completes. - - Args: - rubric (Rubric): The rubric associated with the classifier set. - algorithm_id (unicode): The algorithm used to create the classifier set. - course_id (unicode): The course identifier for the current problem. - item_id (unicode): The item identifier for the current problem. - - Returns: - ClassifierSet or None - - Raises: - DatabaseError - - """ - # List of the parameters we will search for, in order of decreasing priority - search_parameters = [ - # Case #1: same course / item / rubric (exact) / algorithm - { - 'rubric__content_hash': rubric.content_hash, - 'algorithm_id': algorithm_id, - 'course_id': course_id, - 'item_id': item_id - }, - - # Case #2: same course / item / rubric (structure only) / algorithm - { - 'rubric__structure_hash': rubric.structure_hash, # pylint: disable=E1101 - 'algorithm_id': algorithm_id, - 'course_id': course_id, - 'item_id': item_id - }, - - # Case #3: same rubric (exact) / algorithm - { - 'rubric__content_hash': rubric.content_hash, - 'algorithm_id': algorithm_id - } - ] - - # Perform each query, starting with the highest priority - for params in search_parameters: - - # Retrieve the most recent classifier set that matches our query - # (rely on implicit ordering in the model definition) - classifier_set_candidates = cls.objects.filter(**params)[:1] - - # If we find a classifier set, - # then associate the most recent classifiers with it and return true - if len(classifier_set_candidates) > 0: - return classifier_set_candidates[0] - - # If we get to this point, no classifiers exist with this rubric and algorithm. - return None - - @property - def classifier_data_by_criterion(self): - """ - Return info for all classifiers in this classifier set in a dictionary - that maps criteria names to classifier data. - - Returns: - dict: keys are criteria names, values are JSON-serializable classifier data - - Raises: - ValueError - IOError - httplib.HTTPException - - """ - # First check the in-memory cache - # We use an in-memory cache because the classifier data will most often - # be several megabytes, which exceeds the default memcached size limit. - # If we find it, we can avoid calls to the database, S3, and json. - cache_key = self._cache_key("classifier_data_by_criterion") - classifiers_dict = CLASSIFIERS_CACHE_IN_MEM.get(cache_key) - - # If we can't find the classifier in-memory, check the filesystem cache - # We can't always rely on the in-memory cache because worker processes - # terminate when max retries are exceeded. - if classifiers_dict is None: - msg = ( - u"Could not find classifiers dict in the in-memory " - u"cache for key {key}. Falling back to the file-based cache." - ).format(key=cache_key) - logger.info(msg) - classifiers_dict = CLASSIFIERS_CACHE_IN_FILE.get(cache_key) - else: - msg = ( - u"Found classifiers dict in the in-memory cache " - u"(cache key was {key})" - ).format(key=cache_key) - logger.info(msg) - - # If we can't find the classifiers dict in the cache, - # we need to look up the classifiers in the database, - # then download the classifier data. - if classifiers_dict is None: - classifiers_dict = { - classifier.criterion.name: classifier.download_classifier_data() - for classifier in self.classifiers.select_related().all() # pylint: disable=E1101 - } - CLASSIFIERS_CACHE_IN_MEM.set(cache_key, classifiers_dict) - CLASSIFIERS_CACHE_IN_FILE.set(cache_key, classifiers_dict) - msg = ( - u"Could not find classifiers dict in either the in-memory " - u"or file-based cache. Downloaded the data from S3 and cached " - u"it using key {key}" - ).format(key=cache_key) - logger.info(msg) - - return classifiers_dict - - @property - def valid_scores_by_criterion(self): - """ - Return the valid scores for each classifier in this classifier set. - - Returns: - dict: maps rubric criterion names to lists of valid scores. - - """ - cache_key = self._cache_key("valid_scores_by_criterion") - valid_scores_by_criterion = cache.get(cache_key) - if valid_scores_by_criterion is None: - valid_scores_by_criterion = { - classifier.criterion.name: classifier.valid_scores - for classifier in self.classifiers.select_related().all() # pylint: disable=E1101 - } - cache.set(cache_key, valid_scores_by_criterion) - return valid_scores_by_criterion - - def _cache_key(self, data_name): - """ - Return a cache key for this classifier set. - - Args: - data_name (unicode): Name for the data associated with this key. - - Returns: - unicode - - """ - return u"openassessment.assessment.ai.classifier_set.{pk}.{data_name}".format( - pk=self.pk, data_name=data_name - ) - - -# Directory in which classifiers will be stored -# For instance, if we're using the default file system storage backend -# for local development, this will be a subdirectory. -# If using an S3 storage backend, this will be a subdirectory in -# an AWS S3 bucket. -AI_CLASSIFIER_STORAGE = "ora2_ai_classifiers" - - -def upload_to_path(instance, filename): # pylint:disable=W0613 - """ - Calculate the file path where classifiers should be uploaded. - Optionally prepends the path with a prefix (determined by Django settings). - This allows us to put classifiers from different environments - (stage / prod) in different directories within the same S3 bucket. - - Args: - instance (AIClassifier): Not used. - filename (unicode): The filename provided when saving the file. - - Returns: - unicode - - """ - prefix = getattr(settings, 'ORA2_FILE_PREFIX', None) - if prefix is not None: - return u"{prefix}/{root}/{filename}".format( - prefix=prefix, - root=AI_CLASSIFIER_STORAGE, - filename=filename - ) - else: - return u"{root}/{filename}".format( - root=AI_CLASSIFIER_STORAGE, - filename=filename - ) - - -class AIClassifier(models.Model): - """ - A trained classifier (immutable). - """ - - class Meta: - app_label = "assessment" - - # The set of classifiers this classifier belongs to - classifier_set = models.ForeignKey(AIClassifierSet, related_name="classifiers") - - # The criterion (in the rubric) that this classifier evaluates. - criterion = models.ForeignKey(Criterion, related_name="+") - - # The serialized classifier - # Because this may be large, we store it using a Django `FileField`, - # which allows us to plug in different storage backends (such as S3) - classifier_data = models.FileField(upload_to=upload_to_path) - - def download_classifier_data(self): - """ - Download and deserialize the classifier data. - - Returns: - JSON-serializable - - Raises: - ValueError - IOError - httplib.HTTPException - - """ - return json.loads(self.classifier_data.read()) # pylint:disable=E1101 - - @property - def valid_scores(self): - """ - Return a list of valid scores for the rubric criterion associated - with this classifier. - - Returns: - list of integer scores, in ascending order. - - """ - return sorted([option.points for option in self.criterion.options.all()]) - - -class AIWorkflow(models.Model): - """ - Abstract base class for AI workflow database models. - """ - class Meta: - app_label = "assessment" - abstract = True - - # Unique identifier used to track this workflow - uuid = UUIDField(version=1, db_index=True, unique=True) - - # Course Entity and Item Discriminator - # Though these items are duplicated in the database tables for the submissions app, - # and every workflow has a reference to a submission entry, this is okay because - # submissions are immutable. - course_id = models.CharField(max_length=255, db_index=True) - item_id = models.CharField(max_length=128, db_index=True) - - # Timestamps - # The task is *scheduled* as soon as a client asks the API to - # train classifiers. - # The task is *completed* when a worker has successfully created a - # classifier set based on the training examples. - scheduled_at = models.DateTimeField(default=now, db_index=True) - completed_at = models.DateTimeField(null=True, db_index=True) - - # The ID of the algorithm used to train the classifiers - # This is a parameter passed to and interpreted by the workers. - # Django settings allow the users to map algorithm ID strings - # to the Python code they should use to perform the training. - algorithm_id = models.CharField(max_length=128, db_index=True) - - # The set of trained classifiers. - # In the training task, this field will be set when the task completes successfully. - # In the grading task, this may be set to null if no classifiers are available - # when the student submits an essay for grading. - classifier_set = models.ForeignKey( - AIClassifierSet, related_name='+', - null=True, default=None - ) - - @property - def is_complete(self): - """ - Check whether the workflow is complete. - - Returns: - bool - - """ - return self.completed_at is not None - - def mark_complete_and_save(self): - """ - Mark the workflow as complete. - - Returns: - None - - """ - self.completed_at = now() - self.save() - self._log_complete_workflow() - - @classmethod - def get_incomplete_workflows(cls, course_id, item_id): - """ - Gets all incomplete grading workflows for a given course and item. - - Args: - course_id (unicode): Uniquely identifies the course - item_id (unicode): The discriminator for the item we are looking for - - Yields: - All incomplete workflows for this item, as a delayed "stream" - - Raises: - DatabaseError - cls.DoesNotExist - """ - - # Finds all of the uuid's for workflows contained within the query - grade_workflow_uuids = [ - wflow['uuid'] for wflow in cls.objects.filter( - course_id=course_id, item_id=item_id, completed_at__isnull=True - ).values('uuid') - ] - - # Continues to generate output until all workflows in the queryset have been output - for workflow_uuid in grade_workflow_uuids: - - # Returns the grading workflow associated with the uuid stored in the initial query - workflow = cls.objects.get(uuid=workflow_uuid) - yield workflow - - @classmethod - def is_workflow_complete(cls, workflow_uuid): - """ - Check whether the workflow with a given UUID has been marked complete. - - Args: - workflow_uuid (str): The UUID of the workflow to check. - - Returns: - bool - - Raises: - DatabaseError - cls.DoesNotExist - - """ - workflow = cls.objects.get(uuid=workflow_uuid) - return workflow.is_complete - - def _log_start_workflow(self): - """ - A logging operation called at the beginning of an AI Workflows life. - Increments the number of tasks of that kind. - """ - - # Identifies whether the type of task for reporting - class_name = self.__class__.__name__ - data_path = 'openassessment.assessment.ai_task.' + class_name - - # Sets identity tags which allow sorting by course and item - tags = [ - u"course_id:{course_id}".format(course_id=self.course_id), - u"item_id:{item_id}".format(item_id=self.item_id), - ] - - logger.info(u"{class_name} with uuid {uuid} was started.".format(class_name=class_name, uuid=self.uuid)) - - dog_stats_api.increment(data_path + '.scheduled_count', tags=tags) - - def _log_complete_workflow(self): - """ - A logging operation called at the end of an AI Workflow's Life - Reports the total time the task took. - """ - - # Identifies whether the type of task for reporting - class_name = self.__class__.__name__ - data_path = 'openassessment.assessment.ai_task.' + class_name - - tags = [ - u"course_id:{course_id}".format(course_id=self.course_id), - u"item_id:{item_id}".format(item_id=self.item_id), - ] - - # Calculates the time taken to complete the task and reports it to datadog - time_delta = self.completed_at - self.scheduled_at - dog_stats_api.histogram( - data_path + '.turnaround_time', - time_delta.total_seconds(), - tags=tags - ) - - dog_stats_api.increment(data_path + '.completed_count', tags=tags) - - logger.info( - ( - u"{class_name} with uuid {uuid} completed its workflow successfully " - u"in {seconds} seconds." - ).format(class_name=class_name, uuid=self.uuid, seconds=time_delta.total_seconds()) - ) - - -class AITrainingWorkflow(AIWorkflow): - """ - Used to track AI training tasks. - - Training tasks take as input an algorithm ID and a set of training examples - (which are associated with a rubric). - On successful completion, training tasks output a set of trained classifiers. - - """ - class Meta: - app_label = "assessment" - - # The training examples (essays + scores) used to train the classifiers. - # This is a many-to-many field because - # (a) we need multiple training examples to train a classifier, and - # (b) we may want to re-use training examples - # (for example, if a training task is executed by Celery workers multiple times) - training_examples = models.ManyToManyField(TrainingExample, related_name="+") - - @classmethod - @transaction.atomic - def start_workflow(cls, examples, course_id, item_id, algorithm_id): - """ - Start a workflow to track a training task. - - Args: - examples (list of TrainingExample): The training examples used to create the classifiers. - course_id (unicode): The ID for the course that the training workflow is associated with. - item_id (unicode): The ID for the item that the training workflow is training to assess. - algorithm_id (unicode): The ID of the algorithm to use for training. - - Returns: - AITrainingWorkflow - - Raises: - NoTrainingExamples - - """ - if len(examples) == 0: - raise NoTrainingExamples() - - workflow = AITrainingWorkflow.objects.create(algorithm_id=algorithm_id, item_id=item_id, course_id=course_id) - workflow.training_examples.add(*examples) - workflow.save() - workflow._log_start_workflow() - return workflow - - @property - def rubric(self): - """ - Return the rubric associated with this classifier set. - - Returns: - Rubric or None (if no training examples are available) - - Raises: - NoTrainingExamples - - """ - # We assume that all the training examples we have been provided are using - # the same rubric (this is enforced by the API call that deserializes - # the training examples). - first_example = list(self.training_examples.all()[:1]) # pylint: disable=E1101 - if first_example: - return first_example[0].rubric - else: - raise NoTrainingExamples(workflow_uuid=self.uuid) - - def complete(self, classifier_set): - """ - Add a classifier set to the workflow and mark it complete. - - Args: - classifier_set (dict): Mapping of criteria names to serialized classifiers. - - Returns: - None - - Raises: - NoTrainingExamples - IncompleteClassifierSet - ClassifierSerializeError - ClassifierUploadError - InvalidRubricSelection - DatabaseError - """ - self.classifier_set = AIClassifierSet.create_classifier_set( - classifier_set, self.rubric, self.algorithm_id, self.course_id, self.item_id - ) - self.mark_complete_and_save() - - -class AIGradingWorkflow(AIWorkflow): - """ - Used to track AI grading tasks. - - Grading tasks take as input an essay submission - and a set of classifiers; the tasks select options - for each criterion in the rubric. - - """ - class Meta: - app_label = "assessment" - - # The UUID of the submission being graded - submission_uuid = models.CharField(max_length=128, db_index=True) - - # The text of the essay submission to grade - # We duplicate this here to avoid having to repeatedly look up - # the submission. Since submissions are immutable, this is safe. - essay_text = models.TextField(blank=True) - - # The rubric used to evaluate the submission. - # We store this so we can look for classifiers for the same rubric - # if none are available when the workflow is created. - rubric = models.ForeignKey(Rubric, related_name="+") - - # The assessment produced by the AI grading algorithm - # Until the task completes successfully, this will be set to null - assessment = models.ForeignKey( - Assessment, related_name="+", null=True, default=None - ) - - # Identifier information associated with the student's submission - # Useful for finding workflows for a particular course/item/student - # Since submissions are immutable, and since the workflow is - # associated with one submission, it's safe to duplicate - # this information here from the submissions models. - student_id = models.CharField(max_length=40, db_index=True) - - def assign_most_recent_classifier_set(self): - """ - Find the most recent classifier set and assign it to this workflow. - - Returns: - (bool) indicates whether or not classifiers were able to be assigned to the AIGradingWorkflow - - Raises: - DatabaseError - """ - classifier_set = AIClassifierSet.most_recent_classifier_set( - self.rubric, self.algorithm_id, self.course_id, self.item_id - ) - if classifier_set is not None: - self.classifier_set = classifier_set - self.save() - return classifier_set is not None - - @classmethod - @transaction.atomic - def start_workflow(cls, submission_uuid, rubric_dict, algorithm_id): - """ - Start a grading workflow. - - Args: - submission_uuid (str): The UUID of the submission to grade. - rubric_dict (dict): The serialized rubric model. - algorithm_id (unicode): The ID of the algorithm to use for grading. - - Returns: - AIGradingWorkflow - - Raises: - SubmissionNotFoundError - SubmissionRequestError - SubmissionInternalError - InvalidRubric - DatabaseError - - """ - # Retrieve info about the submission - submission = sub_api.get_submission_and_student(submission_uuid) - - # Get or create the rubric - from openassessment.assessment.serializers import rubric_from_dict - rubric = rubric_from_dict(rubric_dict) - - # Create the workflow - workflow = cls.objects.create( - submission_uuid=submission_uuid, - essay_text=essay_text_from_submission(submission), - algorithm_id=algorithm_id, - student_id=submission['student_item']['student_id'], - item_id=submission['student_item']['item_id'], - course_id=submission['student_item']['course_id'], - rubric=rubric - ) - - # Retrieve and assign classifier set candidates - workflow.assign_most_recent_classifier_set() - - workflow._log_start_workflow() - - return workflow - - @transaction.atomic - def complete(self, criterion_scores): - """ - Create an assessment with scores from the AI classifiers - and mark the workflow complete. - - Args: - criterion_scores (dict): Dictionary mapping criteria names to integer scores. - - Raises: - InvalidRubricSelection - DatabaseError - - """ - self.assessment = Assessment.create( - self.rubric, self.algorithm_id, self.submission_uuid, AI_ASSESSMENT_TYPE - ) - AssessmentPart.create_from_option_points(self.assessment, criterion_scores) - self.mark_complete_and_save() diff --git a/openassessment/assessment/models/base.py b/openassessment/assessment/models/base.py index e4395b3c98..43243ec66c 100644 --- a/openassessment/assessment/models/base.py +++ b/openassessment/assessment/models/base.py @@ -12,18 +12,19 @@ ./manage.py schemamigration openassessment.assessment --auto """ -import math from collections import defaultdict from copy import deepcopy from hashlib import sha1 import json +import logging +import math + +from lazy import lazy from django.core.cache import cache from django.db import models from django.utils.timezone import now -from lazy import lazy -import logging logger = logging.getLogger("openassessment.assessment.models") diff --git a/openassessment/assessment/models/peer.py b/openassessment/assessment/models/peer.py index 840f471894..f0ce6ac9fb 100644 --- a/openassessment/assessment/models/peer.py +++ b/openassessment/assessment/models/peer.py @@ -7,16 +7,16 @@ ./manage.py schemamigration openassessment.assessment --auto """ -import random from datetime import timedelta +import logging +import random -from django.db import models, DatabaseError +from django.db import DatabaseError, models from django.utils.timezone import now +from openassessment.assessment.errors import PeerAssessmentInternalError, PeerAssessmentWorkflowError from openassessment.assessment.models.base import Assessment -from openassessment.assessment.errors import PeerAssessmentWorkflowError, PeerAssessmentInternalError -import logging logger = logging.getLogger("openassessment.assessment.models") diff --git a/openassessment/assessment/models/staff.py b/openassessment/assessment/models/staff.py index abc2a545b6..d5e5a8a692 100644 --- a/openassessment/assessment/models/staff.py +++ b/openassessment/assessment/models/staff.py @@ -3,10 +3,9 @@ """ from datetime import timedelta -from django.db import models, DatabaseError +from django.db import DatabaseError, models from django.utils.timezone import now -from openassessment.assessment.models.base import Assessment from openassessment.assessment.errors import StaffAssessmentInternalError diff --git a/openassessment/assessment/models/student_training.py b/openassessment/assessment/models/student_training.py index e450813acc..e9ce91682a 100644 --- a/openassessment/assessment/models/student_training.py +++ b/openassessment/assessment/models/student_training.py @@ -1,9 +1,11 @@ """ Django models specific to the student training assessment type. """ -from django.db import models, transaction, IntegrityError +from django.db import IntegrityError, models, transaction from django.utils import timezone + from submissions import api as sub_api + from .training import TrainingExample diff --git a/openassessment/assessment/models/training.py b/openassessment/assessment/models/training.py index 62e3cc20ad..17e13497c4 100644 --- a/openassessment/assessment/models/training.py +++ b/openassessment/assessment/models/training.py @@ -1,11 +1,13 @@ """ Django models for training (both student and AI). """ -import json from hashlib import sha1 +import json + from django.core.cache import cache from django.db import models -from .base import Rubric, CriterionOption + +from .base import CriterionOption, Rubric class TrainingExample(models.Model): diff --git a/openassessment/assessment/serializers/base.py b/openassessment/assessment/serializers/base.py index 4aa7e08a4e..1a67bbb0a4 100644 --- a/openassessment/assessment/serializers/base.py +++ b/openassessment/assessment/serializers/base.py @@ -5,13 +5,12 @@ from copy import deepcopy import logging -from django.core.cache import cache from rest_framework import serializers -from rest_framework.fields import IntegerField, DateTimeField -from openassessment.assessment.models import ( - Assessment, AssessmentPart, Criterion, CriterionOption, Rubric, -) +from rest_framework.fields import DateTimeField, IntegerField + +from django.core.cache import cache +from openassessment.assessment.models import Assessment, AssessmentPart, Criterion, CriterionOption, Rubric logger = logging.getLogger(__name__) @@ -211,8 +210,8 @@ def full_assessment_dict(assessment, rubric_dict=None): # `CriterionOption` again, we simply index into the places we expect them to # be from the big, saved `Rubric` serialization. parts = [] - for part in assessment.parts.all().select_related("criterion", "option"): - criterion_dict = rubric_dict["criteria"][part.criterion.order_num] + for part in assessment.parts.order_by('criterion__order_num').all().select_related("criterion", "option"): + criterion_dict = dict(rubric_dict["criteria"][part.criterion.order_num]) options_dict = None if part.option is not None: options_dict = criterion_dict["options"][part.option.order_num] diff --git a/openassessment/assessment/serializers/peer.py b/openassessment/assessment/serializers/peer.py index cf9b954ab1..47057f35c6 100644 --- a/openassessment/assessment/serializers/peer.py +++ b/openassessment/assessment/serializers/peer.py @@ -2,11 +2,11 @@ Serializers specific to peer assessment. """ from rest_framework import serializers + +from openassessment.assessment.models import (AssessmentFeedback, AssessmentFeedbackOption, PeerWorkflow, + PeerWorkflowItem) + from .base import AssessmentSerializer -from openassessment.assessment.models import ( - AssessmentFeedback, AssessmentFeedbackOption, - PeerWorkflow, PeerWorkflowItem -) class AssessmentFeedbackOptionSerializer(serializers.ModelSerializer): diff --git a/openassessment/assessment/serializers/training.py b/openassessment/assessment/serializers/training.py index a6b712ae0c..af727a9409 100644 --- a/openassessment/assessment/serializers/training.py +++ b/openassessment/assessment/serializers/training.py @@ -2,10 +2,12 @@ Serializers for the training assessment type. """ from django.core.cache import cache -from django.db import transaction, IntegrityError -from openassessment.assessment.models import TrainingExample +from django.db import IntegrityError, transaction + from openassessment.assessment.data_conversion import update_training_example_answer_format -from .base import rubric_from_dict, RubricSerializer +from openassessment.assessment.models import TrainingExample + +from .base import RubricSerializer, rubric_from_dict class InvalidTrainingExample(Exception): diff --git a/openassessment/assessment/tasks.py b/openassessment/assessment/tasks.py deleted file mode 100644 index 385d3301a9..0000000000 --- a/openassessment/assessment/tasks.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Celery looks for tasks in this module, -so import the tasks we want the workers to implement. -""" -# pylint:disable=W0611 -from .worker.training import train_classifiers, reschedule_training_tasks -from .worker.grading import grade_essay, reschedule_grading_tasks diff --git a/openassessment/assessment/test/test_ai.py b/openassessment/assessment/test/test_ai.py deleted file mode 100644 index 7a534ac7fc..0000000000 --- a/openassessment/assessment/test/test_ai.py +++ /dev/null @@ -1,702 +0,0 @@ -# coding=utf-8 -""" -Tests for AI assessment. -""" -import copy -import mock -from nose.tools import raises -from celery.exceptions import NotConfigured -from django.db import DatabaseError -from django.test.utils import override_settings -from openassessment.test_utils import CacheResetTest -from submissions import api as sub_api -from openassessment.assessment.api import ai as ai_api -from openassessment.assessment.models import ( - AITrainingWorkflow, AIGradingWorkflow, AIClassifierSet, Assessment -) -from openassessment.assessment.worker.algorithm import AIAlgorithm -from openassessment.assessment.serializers import rubric_from_dict -from openassessment.assessment.errors import ( - AITrainingRequestError, AITrainingInternalError, AIGradingRequestError, - AIReschedulingInternalError, AIGradingInternalError, AIError -) -from openassessment.assessment.test.constants import RUBRIC, EXAMPLES, STUDENT_ITEM, ANSWER - - -class StubAIAlgorithm(AIAlgorithm): - """ - Stub implementation of a supervised ML algorithm. - """ - # The format of the serialized classifier is controlled - # by the AI algorithm implementation, so we can return - # anything here as long as it's JSON-serializable - FAKE_CLASSIFIER = { - 'name': u'ƒαкє ¢ℓαѕѕιƒιєя', - 'binary_content': "TWFuIGlzIGRpc3Rpbmd1aX" - } - - def train_classifier(self, examples): - """ - Stub implementation that returns fake classifier data. - """ - # Include the input essays in the classifier - # so we can test that the correct inputs were used - classifier = copy.copy(self.FAKE_CLASSIFIER) - classifier['examples'] = examples - classifier['score_override'] = 0 - return classifier - - def score(self, text, classifier, cache): - """ - Stub implementation that returns whatever scores were - provided in the serialized classifier data. - - Expect `classifier` to be a dict with a single key, - "score_override" containing the score to return. - """ - return classifier['score_override'] - - -ALGORITHM_ID = "test-stub" -COURSE_ID = STUDENT_ITEM.get('course_id') -ITEM_ID = STUDENT_ITEM.get('item_id') - -AI_ALGORITHMS = { - ALGORITHM_ID: '{module}.StubAIAlgorithm'.format(module=__name__), -} - - -def train_classifiers(rubric_dict, classifier_score_overrides): - """ - Simple utility function to train classifiers. - - Args: - rubric_dict (dict): The rubric to train the classifiers on. - classifier_score_overrides (dict): A dictionary of classifier overrides - to set the scores for the given submission. - - """ - rubric = rubric_from_dict(rubric_dict) - AIClassifierSet.create_classifier_set( - classifier_score_overrides, rubric, ALGORITHM_ID, COURSE_ID, ITEM_ID - ) - - -class AITrainingTest(CacheResetTest): - """ - Tests for AI training tasks. - """ - - EXPECTED_INPUT_SCORES = { - u'vøȼȺƀᵾłȺɍɏ': [1, 0], - u'ﻭɼค๓๓คɼ': [0, 2] - } - - # Use a stub AI algorithm - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_train_classifiers(self): - # Schedule a training task - # Because Celery is configured in "always eager" mode, - # expect the task to be executed synchronously. - workflow_uuid = ai_api.train_classifiers(RUBRIC, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - # Retrieve the classifier set from the database - workflow = AITrainingWorkflow.objects.get(uuid=workflow_uuid) - classifier_set = workflow.classifier_set - self.assertIsNot(classifier_set, None) - - # Retrieve a dictionary mapping criteria names to deserialized classifiers - classifiers = classifier_set.classifier_data_by_criterion - - # Check that we have classifiers for all criteria in the rubric - criteria = set(criterion['name'] for criterion in RUBRIC['criteria']) - self.assertEqual(set(classifiers.keys()), criteria) - - # Check that the classifier data matches the data from our stub AI algorithm - # Since the stub data includes the training examples, we also verify - # that the classifier was trained using the correct examples. - for criterion in RUBRIC['criteria']: - classifier = classifiers[criterion['name']] - self.assertEqual(classifier['name'], StubAIAlgorithm.FAKE_CLASSIFIER['name']) - self.assertEqual(classifier['binary_content'], StubAIAlgorithm.FAKE_CLASSIFIER['binary_content']) - - # Verify that the correct essays and scores were used to create the classifier - # Our stub AI algorithm provides these for us, but they would not ordinarily - # be included in the trained classifier. - self.assertEqual(len(classifier['examples']), len(EXAMPLES)) - expected_scores = self.EXPECTED_INPUT_SCORES[criterion['name']] - for data in zip(EXAMPLES, classifier['examples'], expected_scores): - sent_example, received_example, expected_score = data - received_example = AIAlgorithm.ExampleEssay(*received_example) - self.assertEqual(received_example.text, sent_example['answer']) - self.assertEqual(received_example.score, expected_score) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_train_classifiers_feedback_only_criterion(self): - # Modify the rubric to include a feedback-only criterion - # (a criterion with no options, just written feedback) - rubric = copy.deepcopy(RUBRIC) - rubric['criteria'].append({ - 'name': 'feedback only', - 'prompt': 'feedback', - 'options': [] - }) - - # Schedule a training task - # (we use training examples that do NOT include the feedback-only criterion) - workflow_uuid = ai_api.train_classifiers(rubric, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - # Verify that no classifier was created for the feedback-only criterion - # Since there's no points associated with that criterion, - # there's no way for the AI algorithm to score it anyway. - workflow = AITrainingWorkflow.objects.get(uuid=workflow_uuid) - classifier_data = workflow.classifier_set.classifier_data_by_criterion - self.assertNotIn('feedback only', classifier_data) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_train_classifiers_all_feedback_only_criteria(self): - # Modify the rubric to include only feedback-only criteria - # (a criterion with no options, just written feedback) - rubric = copy.deepcopy(RUBRIC) - for criterion in rubric['criteria']: - criterion['options'] = [] - - # Modify the training examples to provide no scores - examples = copy.deepcopy(EXAMPLES) - for example in examples: - example['options_selected'] = {} - - # Schedule a training task - # Our training examples have no options - workflow_uuid = ai_api.train_classifiers(rubric, examples, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - # Verify that no classifier was created for the feedback-only criteria - workflow = AITrainingWorkflow.objects.get(uuid=workflow_uuid) - classifier_data = workflow.classifier_set.classifier_data_by_criterion - self.assertEqual(classifier_data, {}) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_train_classifiers_invalid_examples(self): - # Mutate an example so it does not match the rubric - mutated_examples = copy.deepcopy(EXAMPLES) - mutated_examples[0]['options_selected'] = {'invalid': 'invalid'} - - # Expect a request error - with self.assertRaises(AITrainingRequestError): - ai_api.train_classifiers(RUBRIC, mutated_examples, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - def test_train_classifiers_no_examples(self): - # Empty list of training examples - with self.assertRaises(AITrainingRequestError): - ai_api.train_classifiers(RUBRIC, [], COURSE_ID, ITEM_ID, ALGORITHM_ID) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - @mock.patch.object(AITrainingWorkflow.objects, 'create') - def test_start_workflow_database_error(self, mock_create): - # Simulate a database error when creating the training workflow - mock_create.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AITrainingInternalError): - ai_api.train_classifiers(RUBRIC, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_train_classifiers_celery_error(self): - with mock.patch('openassessment.assessment.api.ai.training_tasks.train_classifiers.apply_async') as mock_train: - mock_train.side_effect = NotConfigured - with self.assertRaises(AITrainingInternalError): - ai_api.train_classifiers(RUBRIC, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - -class AIGradingTest(CacheResetTest): - """ - Tests for AI grading tasks. - """ - - CLASSIFIER_SCORE_OVERRIDES = { - u"vøȼȺƀᵾłȺɍɏ": {'score_override': 1}, - u"ﻭɼค๓๓คɼ": {'score_override': 2} - } - - def setUp(self): - """ - Create a submission and a fake classifier set. - """ - # Create a submission - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - self.submission_uuid = submission['uuid'] - - train_classifiers(RUBRIC, self.CLASSIFIER_SCORE_OVERRIDES) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_grade_essay(self): - # Schedule a grading task - # Because Celery is configured in "always eager" mode, this will - # be executed synchronously. - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - # Verify that we got the scores we provided to the stub AI algorithm - assessment = ai_api.get_latest_assessment(self.submission_uuid) - for part in assessment['parts']: - criterion_name = part['option']['criterion']['name'] - expected_score = self.CLASSIFIER_SCORE_OVERRIDES[criterion_name]['score_override'] - self.assertEqual(part['option']['points'], expected_score) - - score = ai_api.get_score(self.submission_uuid, {}) - self.assertEquals(score["points_possible"], 4) - self.assertEquals(score["points_earned"], 3) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_grade_essay_feedback_only_criterion(self): - # Modify the rubric to include a feedback-only criterion - # (a criterion with no options, just written feedback) - rubric = copy.deepcopy(RUBRIC) - rubric['criteria'].append({ - 'name': 'feedback only', - 'prompt': 'feedback', - 'options': [] - }) - - # Train classifiers for the rubric - train_classifiers(rubric, self.CLASSIFIER_SCORE_OVERRIDES) - - # Schedule a grading task and retrieve the assessment - ai_api.on_init(self.submission_uuid, rubric=rubric, algorithm_id=ALGORITHM_ID) - assessment = ai_api.get_latest_assessment(self.submission_uuid) - - # Verify that the criteria with options were given scores - # (from the score override used by our fake classifiers) - self.assertEqual(assessment['parts'][0]['criterion']['name'], u"vøȼȺƀᵾłȺɍɏ") - self.assertEqual(assessment['parts'][0]['option']['points'], 1) - self.assertEqual(assessment['parts'][1]['criterion']['name'], u"ﻭɼค๓๓คɼ") - self.assertEqual(assessment['parts'][1]['option']['points'], 2) - - # Verify that the criteria with no options (only feedback) - # has no score and empty feedback - self.assertEqual(assessment['parts'][2]['criterion']['name'], u"feedback only") - self.assertIs(assessment['parts'][2]['option'], None) - self.assertEqual(assessment['parts'][2]['feedback'], u"") - - # Check the scores by criterion dict - score_dict = ai_api.get_assessment_scores_by_criteria(self.submission_uuid) - self.assertEqual(score_dict[u"vøȼȺƀᵾłȺɍɏ"], 1) - self.assertEqual(score_dict[u"ﻭɼค๓๓คɼ"], 2) - self.assertEqual(score_dict['feedback only'], 0) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_grade_essay_all_feedback_only_criteria(self): - # Modify the rubric to include only feedback-only criteria - rubric = copy.deepcopy(RUBRIC) - for criterion in rubric['criteria']: - criterion['options'] = [] - - # Train classifiers for the rubric - train_classifiers(rubric, {}) - - # Schedule a grading task and retrieve the assessment - ai_api.on_init(self.submission_uuid, rubric=rubric, algorithm_id=ALGORITHM_ID) - assessment = ai_api.get_latest_assessment(self.submission_uuid) - - # Verify that all assessment parts have feedback set to an empty string - for part in assessment['parts']: - self.assertEqual(part['feedback'], u"") - - # Check the scores by criterion dict - # Since none of the criteria had options, the scores should all default to 0 - score_dict = ai_api.get_assessment_scores_by_criteria(self.submission_uuid) - self.assertItemsEqual(score_dict, { - u"vøȼȺƀᵾłȺɍɏ": 0, - u"ﻭɼค๓๓คɼ": 0, - }) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_get_assessment_scores_by_criteria(self): - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - # Verify that we got the scores we provided to the stub AI algorithm - assessment = ai_api.get_latest_assessment(self.submission_uuid) - assessment_score_dict = ai_api.get_assessment_scores_by_criteria(self.submission_uuid) - for part in assessment['parts']: - criterion_name = part['option']['criterion']['name'] - expected_score = self.CLASSIFIER_SCORE_OVERRIDES[criterion_name]['score_override'] - self.assertEqual(assessment_score_dict[criterion_name], expected_score) - - @raises(ai_api.AIGradingInternalError) - @mock.patch.object(Assessment.objects, 'filter') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_error_getting_assessment_scores(self, mock_filter): - mock_filter.side_effect = DatabaseError("Oh no!") - ai_api.get_assessment_scores_by_criteria(self.submission_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_submission_not_found(self): - with self.assertRaises(AIGradingRequestError): - ai_api.on_init("no_such_submission", rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_invalid_rubric(self): - invalid_rubric = {'not_valid': True} - with self.assertRaises(AIGradingRequestError): - ai_api.on_init(self.submission_uuid, rubric=invalid_rubric, algorithm_id=ALGORITHM_ID) - - @mock.patch.object(AIGradingWorkflow.objects, 'create') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_database_error_create(self, mock_call): - mock_call.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AIGradingInternalError): - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - @mock.patch.object(Assessment.objects, 'filter') - def test_get_latest_assessment_database_error(self, mock_call): - mock_call.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AIGradingInternalError): - ai_api.get_latest_assessment(self.submission_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_celery_error(self): - with mock.patch('openassessment.assessment.api.ai.grading_tasks.grade_essay.apply_async') as mock_grade: - mock_grade.side_effect = NotConfigured - with self.assertRaises(AIGradingInternalError): - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - @mock.patch.object(AIClassifierSet.objects, 'filter') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_database_error_filter(self, mock_filter): - mock_filter.side_effect = DatabaseError("rumble... ruMBLE, RUMBLE! BOOM!") - with self.assertRaises(AIGradingInternalError): - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - @mock.patch.object(AIClassifierSet.objects, 'filter') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_no_classifiers(self, mock_call): - mock_call.return_value = [] - with mock.patch('openassessment.assessment.api.ai.logger.info') as mock_log: - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - argument = mock_log.call_args[0][0] - self.assertTrue(u"no classifiers are available" in argument) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_submit_submission_db_error(self): - with mock.patch('openassessment.assessment.api.ai.AIGradingWorkflow.start_workflow') as mock_start: - mock_start.side_effect = sub_api.SubmissionInternalError - with self.assertRaises(AIGradingInternalError): - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - -class AIUntrainedGradingTest(CacheResetTest): - """ - Tests that do not run the setup to train classifiers. - - """ - def setUp(self): - """ - Create a submission. - """ - # Create a submission - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - self.submission_uuid = submission['uuid'] - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_no_score(self): - # Test that no score has been created, and get_score returns None. - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - score = ai_api.get_score(self.submission_uuid, {}) - self.assertIsNone(score) - - -class AIReschedulingTest(CacheResetTest): - """ - Tests AI rescheduling. - - Tests in both orders, and tests all error conditions that can arise as a result of calling rescheduling - """ - - def setUp(self): - """ - Sets up each test so that it will have unfinished tasks of both types - """ - # 1) Schedule Grading, have the scheduling succeeed but the grading fail because no classifiers exist - for _ in range(0, 10): - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - self.submission_uuid = submission['uuid'] - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - # 2) Schedule Training, have it INTENTIONALLY fail. Now we are a point where both parts need to be rescheduled - patched_method = 'openassessment.assessment.api.ai.training_tasks.train_classifiers.apply_async' - with mock.patch(patched_method) as mock_train_classifiers: - mock_train_classifiers.side_effect = AITrainingInternalError('Training Classifiers Failed for some Reason.') - with self.assertRaises(AITrainingInternalError): - ai_api.train_classifiers(RUBRIC, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - self._assert_complete(training_done=False, grading_done=False) - - def _assert_complete(self, training_done=None, grading_done=None): - """ - Asserts that the Training and Grading are of a given completion status - Serves as an assertion for a number of unit tests. - - Args: - training_done (bool): whether the user expects there to be unfinished training workflows - grading_done (bool): whether the user expects there to be unfinished grading workflows - """ - incomplete_training_workflows = AITrainingWorkflow.get_incomplete_workflows(course_id=COURSE_ID, item_id=ITEM_ID) - incomplete_grading_workflows = AIGradingWorkflow.get_incomplete_workflows(course_id=COURSE_ID, item_id=ITEM_ID) - if training_done is not None: - self.assertEqual(self._is_empty_generator(incomplete_training_workflows), training_done) - if grading_done is not None: - self.assertEqual(self._is_empty_generator(incomplete_grading_workflows), grading_done) - - def _is_empty_generator(self, gen): - """ - Tests whether a given generator has any more output. - Consumes a unit of output in test. - - Args: - gen (generator): A generator to test if empty - - Returns: - (bool): whether or not the generator contained output before testing - """ - try: - next(gen) - return False - except StopIteration: - return True - - def _call_reschedule_safe(self, task_type=u"grade"): - """ - A method which will reject an exception thrown by the unfinished task API. - - This method is necessary because when we set our celery workers to propogate all errors upward - (as we now do in our unit testing suite), that also means that when a task fails X times (say - a grading task fails because classifiers are not defined) that exception will be retruned from - the call of the grade_essay (even though asynchronous), and peroclate up. This method is used - to agknowledge the fact that we expect there to be an error, and allow us to call reschedule - unfinished tasks without catching that error directly. - - Args: - task_type (unicode): describes what tasks we should reschedule - """ - try: - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=task_type) - except Exception: # pylint: disable=W0703 - # This exception is being raised because of a timeout. - pass - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_reschedule_grading_success(self): - # Rescheduling grading only, expect no successes - self._call_reschedule_safe(task_type=u"grade") - - # Neither training nor grading should be complete. - self._assert_complete(grading_done=False, training_done=False) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_reschedule_training_success(self): - # Reschedule training, expect all successes - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=u"train") - - # Both training and grading should be complete. - self._assert_complete(grading_done=True, training_done=True) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_reschedule_training_and_grading_success(self): - # Reschedule everything, expect all successes - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=None) - - # Both training and grading should be complete. - self._assert_complete(grading_done=True, training_done=True) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_reschedule_non_valid_args(self): - with self.assertRaises(AIError): - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, task_type=u"train") - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_reschedule_all_large(self): - """ - Specifically tests the querying mechanisms (python generator functions), and ensures that our methodology - holds up for querysets with 125+ entries - """ - # Creates 125 more grades (for a total of 135) - for _ in range(0, 125): - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - self.submission_uuid = submission['uuid'] - ai_api.on_init(self.submission_uuid, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - # Both training and grading should not be complete. - self._assert_complete(grading_done=False, training_done=False) - - # Reschedule both - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=None) - - # Check that both training and grading are now complete - self._assert_complete(grading_done=True, training_done=True) - - def test_reschedule_grade_celery_error(self): - patched_method = 'openassessment.assessment.api.ai.grading_tasks.reschedule_grading_tasks.apply_async' - with mock.patch(patched_method) as mock_grade: - mock_grade.side_effect = NotConfigured - with self.assertRaises(AIGradingInternalError): - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID) - - def test_reschedule_train_celery_error(self): - patched_method = 'openassessment.assessment.api.ai.training_tasks.reschedule_training_tasks.apply_async' - with mock.patch(patched_method) as mock_train: - mock_train.side_effect = NotConfigured - with self.assertRaises(AITrainingInternalError): - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=None) - - @mock.patch.object(AIGradingWorkflow, 'get_incomplete_workflows') - def test_get_incomplete_workflows_error_grading(self, mock_incomplete): - mock_incomplete.side_effect = DatabaseError - with self.assertRaises(AIReschedulingInternalError): - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID) - - def test_get_incomplete_workflows_error_training(self): - patched_method = 'openassessment.assessment.models.ai.AIWorkflow.get_incomplete_workflows' - with mock.patch(patched_method) as mock_incomplete: - mock_incomplete.side_effect = DatabaseError - with self.assertRaises(Exception): - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=u"train") - - def test_reschedule_train_internal_celery_error(self): - patched_method = 'openassessment.assessment.worker.training.train_classifiers.apply_async' - with mock.patch(patched_method) as mock_train: - mock_train.side_effect = NotConfigured("NotConfigured") - with mock.patch('openassessment.assessment.worker.training.logger.exception') as mock_logger: - with self.assertRaises(Exception): - ai_api.reschedule_unfinished_tasks(course_id=COURSE_ID, item_id=ITEM_ID, task_type=u"train") - last_call = mock_logger.call_args[0][0] - self.assertTrue(u"NotConfigured" in last_call) - - -class AIAutomaticGradingTest(CacheResetTest): - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_automatic_grade(self): - # Create some submissions which will not succeed. No classifiers yet exist. - for _ in range(0, 10): - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - ai_api.on_init(submission['uuid'], rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - # Check that there are unresolved grading workflows - self._assert_complete(training_done=True, grading_done=False) - - # Create and train a classifier set. This should set off automatic grading. - ai_api.train_classifiers(RUBRIC, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - # Check to make sure that all work is done. - self._assert_complete(training_done=True, grading_done=True) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_automatic_grade_error(self): - # Create some submissions which will not succeed. No classifiers yet exist. - for _ in range(0, 10): - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - ai_api.on_init(submission['uuid'], rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - - # Check that there are unresolved grading workflows - self._assert_complete(training_done=True, grading_done=False) - - patched_method = 'openassessment.assessment.worker.training.reschedule_grading_tasks.apply_async' - with mock.patch(patched_method) as mocked_reschedule_grading: - mocked_reschedule_grading.side_effect = AIGradingInternalError("Kablewey.") - with self.assertRaises(AIGradingInternalError): - ai_api.train_classifiers(RUBRIC, EXAMPLES, COURSE_ID, ITEM_ID, ALGORITHM_ID) - - def _assert_complete(self, training_done=None, grading_done=None): - """ - Asserts that the Training and Grading are of a given completion status - Serves as an assertion for a number of unit tests. - - Args: - training_done (bool): whether the user expects there to be unfinished training workflows - grading_done (bool): whether the user expects there to be unfinished grading workflows - """ - incomplete_training_workflows = AITrainingWorkflow.get_incomplete_workflows(course_id=COURSE_ID, item_id=ITEM_ID) - incomplete_grading_workflows = AIGradingWorkflow.get_incomplete_workflows(course_id=COURSE_ID, item_id=ITEM_ID) - if training_done is not None: - self.assertEqual(self._is_empty_generator(incomplete_training_workflows), training_done) - if grading_done is not None: - self.assertEqual(self._is_empty_generator(incomplete_grading_workflows), grading_done) - - def _is_empty_generator(self, gen): - """ - Tests whether a given generator has any more output. - Consumes a unit of output in test. - - Args: - gen (generator): A generator to test if empty - - Returns: - (bool): whether or not the generator contained output before testing - """ - try: - next(gen) - return False - except StopIteration: - return True - - -class AIClassifierInfoTest(CacheResetTest): - """ - Tests for retrieving info about classifier sets. - """ - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_no_classifier_set(self): - classifier_info = ai_api.get_classifier_set_info( - RUBRIC, ALGORITHM_ID, 'test_course', 'test_item' - ) - self.assertIs(classifier_info, None) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_classifier_set_info(self): - workflow_uuid = ai_api.train_classifiers( - RUBRIC, EXAMPLES, 'test_course', 'test_item', ALGORITHM_ID - ) - classifier_info = ai_api.get_classifier_set_info( - RUBRIC, ALGORITHM_ID, 'test_course', 'test_item' - ) - - # Retrieve the classifier set so we can get its actual creation date - workflow = AITrainingWorkflow.objects.get(uuid=workflow_uuid) - classifier_set = workflow.classifier_set - expected_info = { - 'created_at': classifier_set.created_at, - 'algorithm_id': ALGORITHM_ID, - 'course_id': 'test_course', - 'item_id': 'test_item' - } - self.assertEqual(classifier_info, expected_info) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_multiple_classifier_sets(self): - # Train multiple classifiers - ai_api.train_classifiers( - RUBRIC, EXAMPLES, 'test_course', 'test_item', ALGORITHM_ID - ) - second_uuid = ai_api.train_classifiers( - RUBRIC, EXAMPLES, 'test_course', 'test_item', ALGORITHM_ID - ) - - # Expect that we get the info for the second classifier - classifier_info = ai_api.get_classifier_set_info( - RUBRIC, ALGORITHM_ID, 'test_course', 'test_item' - ) - workflow = AITrainingWorkflow.objects.get(uuid=second_uuid) - classifier_set = workflow.classifier_set - self.assertEqual(classifier_info['created_at'], classifier_set.created_at) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - @raises(AIGradingInternalError) - @mock.patch.object(AIClassifierSet, 'most_recent_classifier_set') - def test_database_error(self, mock_call): - mock_call.side_effect = DatabaseError('OH NO!') - ai_api.get_classifier_set_info( - RUBRIC, ALGORITHM_ID, 'test_course', 'test_item' - ) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - @raises(AIGradingRequestError) - def test_invalid_rubric_error(self): - invalid_rubric = {} - ai_api.get_classifier_set_info(invalid_rubric, ALGORITHM_ID, 'test_course', 'test_item') diff --git a/openassessment/assessment/test/test_ai_algorithm.py b/openassessment/assessment/test/test_ai_algorithm.py deleted file mode 100644 index c33cd374f0..0000000000 --- a/openassessment/assessment/test/test_ai_algorithm.py +++ /dev/null @@ -1,165 +0,0 @@ -# coding=utf-8 -""" -Tests for AI algorithm implementations. -""" -import unittest -import json -import mock -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.worker.algorithm import ( - AIAlgorithm, FakeAIAlgorithm, EaseAIAlgorithm, - TrainingError, InvalidClassifier -) - - -EXAMPLES = [ - AIAlgorithm.ExampleEssay(u"Mine's a tale that can't be told, my ƒяєє∂σм I hold dear.", 2), - AIAlgorithm.ExampleEssay(u"How years ago in days of old, when 𝒎𝒂𝒈𝒊𝒄 filled th air.", 1), - AIAlgorithm.ExampleEssay(u"Ṫ'ẅäṡ in the darkest depths of Ṁöṛḋöṛ, I met a girl so fair.", 1), - AIAlgorithm.ExampleEssay(u"But goレレuᄊ, and the evil one crept up and slipped away with her", 0), - AIAlgorithm.ExampleEssay(u"", 4), - AIAlgorithm.ExampleEssay(u".!?", 4), - AIAlgorithm.ExampleEssay(u"no punctuation", 4), - AIAlgorithm.ExampleEssay(u"one", 4), -] - -INPUT_ESSAYS = [ - u"Good times, 𝑩𝒂𝒅 𝑻𝒊𝒎𝒆𝒔, you know I had my share", - u"When my woman left home for a 𝒃𝒓𝒐𝒘𝒏 𝒆𝒚𝒆𝒅 𝒎𝒂𝒏", - u"Well, I still don't seem to 𝒄𝒂𝒓𝒆", - u"", - u".!?", - u"no punctuation", - u"one", -] - - -class AIAlgorithmTest(CacheResetTest): - """ - Base class for testing AI algorithm implementations. - """ - ALGORITHM_CLASS = None - - def setUp(self): - self.algorithm = self.ALGORITHM_CLASS() # pylint:disable=E1102 - - def _scores(self, classifier, input_essays): - """ - Use the classifier to score multiple input essays. - - Args: - input_essays (list of unicode): The essays to score. - - Returns: - list of int: The scores - - """ - cache = {} - return [ - self.algorithm.score(input_essay, classifier, cache) - for input_essay in input_essays - ] - - -class FakeAIAlgorithmTest(AIAlgorithmTest): - """ - Test for the fake AI algorithm implementation. - """ - ALGORITHM_CLASS = FakeAIAlgorithm - - def test_train_and_score(self): - classifier = self.algorithm.train_classifier(EXAMPLES) - expected_scores = [2, 0, 0, 0, 4, 2, 4] - scores = self._scores(classifier, INPUT_ESSAYS) - self.assertEqual(scores, expected_scores) - - def test_score_classifier_missing_key(self): - with self.assertRaises(InvalidClassifier): - self.algorithm.score(u"Test input", {}, {}) - - def test_score_classifier_no_scores(self): - with self.assertRaises(InvalidClassifier): - self.algorithm.score(u"Test input", {'scores': []}, {}) - - -# Try to import EASE -- if we can't, then skip the tests that require it -try: - import ease # pylint: disable=F0401,W0611 - EASE_INSTALLED = True -except ImportError: - EASE_INSTALLED = False - - -@unittest.skipUnless(EASE_INSTALLED, "EASE library required") -class EaseAIAlgorithmTest(AIAlgorithmTest): - """ - Test for the EASE AI library wrapper. - """ - ALGORITHM_CLASS = EaseAIAlgorithm - - def test_train_and_score(self): - classifier = self.algorithm.train_classifier(EXAMPLES) - scores = self._scores(classifier, INPUT_ESSAYS) - - # Check that we got scores in the correct range - valid_scores = set(example.score for example in EXAMPLES) - for score in scores: - self.assertIn(score, valid_scores) - - # Check that the scores are consistent when we re-run the algorithm - repeat_scores = self._scores(classifier, INPUT_ESSAYS) - self.assertEqual(scores, repeat_scores) - - def test_all_examples_have_same_score(self): - examples = [ - AIAlgorithm.ExampleEssay(u"Test ëṡṡäÿ", 1), - AIAlgorithm.ExampleEssay(u"Another test ëṡṡäÿ", 1), - ] - # No assertion -- just verifying that this does not raise an exception - classifier = self.algorithm.train_classifier(examples) - self._scores(classifier, INPUT_ESSAYS) - - def test_most_examples_have_same_score(self): - # All training examples have the same score except for one - examples = [ - AIAlgorithm.ExampleEssay(u"Test ëṡṡäÿ", 1), - AIAlgorithm.ExampleEssay(u"Another test ëṡṡäÿ", 1), - AIAlgorithm.ExampleEssay(u"Different score", 0), - ] - classifier = self.algorithm.train_classifier(examples) - scores = self._scores(classifier, INPUT_ESSAYS) - - # Check that we got scores back. - # This is not a very rigorous assertion -- we're mainly - # checking that we got this far without an exception. - self.assertEqual(len(scores), len(INPUT_ESSAYS)) - - def test_no_examples(self): - with self.assertRaises(TrainingError): - self.algorithm.train_classifier([]) - - def test_json_serializable(self): - classifier = self.algorithm.train_classifier(EXAMPLES) - serialized = json.dumps(classifier) - deserialized = json.loads(serialized) - - # This should not raise an exception - scores = self._scores(deserialized, INPUT_ESSAYS) - self.assertEqual(len(scores), len(INPUT_ESSAYS)) - - @mock.patch('openassessment.assessment.worker.algorithm.pickle') - def test_pickle_serialize_error(self, mock_pickle): - mock_pickle.dumps.side_effect = Exception("Test error!") - with self.assertRaises(TrainingError): - self.algorithm.train_classifier(EXAMPLES) - - def test_pickle_deserialize_error(self): - classifier = self.algorithm.train_classifier(EXAMPLES) - with mock.patch('openassessment.assessment.worker.algorithm.pickle.loads') as mock_call: - mock_call.side_effect = Exception("Test error!") - with self.assertRaises(InvalidClassifier): - self.algorithm.score(u"Test ëṡṡäÿ", classifier, {}) - - def test_serialized_classifier_not_a_dict(self): - with self.assertRaises(InvalidClassifier): - self.algorithm.score(u"Test ëṡṡäÿ", "not a dict", {}) diff --git a/openassessment/assessment/test/test_ai_models.py b/openassessment/assessment/test/test_ai_models.py deleted file mode 100644 index 6e5ee67c05..0000000000 --- a/openassessment/assessment/test/test_ai_models.py +++ /dev/null @@ -1,206 +0,0 @@ -# coding=utf-8 -""" -Test AI Django models. -""" -import copy -import ddt -from django.test import TestCase - -from django.test.utils import override_settings -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.models import ( - AIClassifierSet, AIClassifier, AIGradingWorkflow, AI_CLASSIFIER_STORAGE, - CLASSIFIERS_CACHE_IN_MEM, essay_text_from_submission -) -from openassessment.assessment.serializers import rubric_from_dict -from .constants import RUBRIC - - -CLASSIFIERS_DICT = { - u"vøȼȺƀᵾłȺɍɏ": "test data", - u"ﻭɼค๓๓คɼ": "more test data" -} -COURSE_ID = u"†3߆ çøU®ß3" -ITEM_ID = u"fake_item_id" - - -@ddt.ddt -class DataConversionTest(TestCase): - - @ddt.data( - (u'Answer', u'Answer'), - ({'answer': {'text': u'Answer'}}, u'Answer'), - ({'answer': {'parts': [{'text': u'Answer 1'}, {'text': u'Answer 2'}]}}, u'Answer 1\nAnswer 2') - ) - @ddt.unpack - def test_essay_text_from_submission(self, input, output): - self.assertEqual(essay_text_from_submission(input), output) - - -class AIClassifierTest(CacheResetTest): - """ - Tests for the AIClassifier model. - """ - - def test_upload_to_path_default(self): - # No path prefix provided in the settings - classifier = self._create_classifier() - components = classifier.classifier_data.name.split(u'/') - self.assertEqual(len(components), 2) - self.assertEqual(components[0], AI_CLASSIFIER_STORAGE) - self.assertGreater(len(components[1]), 0) - - @override_settings(ORA2_FILE_PREFIX=u"ƒιℓє_ρяєƒιχ") - def test_upload_to_path_with_prefix(self): - classifier = self._create_classifier() - components = classifier.classifier_data.name.split(u'/') - self.assertEqual(len(components), 3) - self.assertEqual(components[0], u"ƒιℓє_ρяєƒιχ") - self.assertEqual(components[1], AI_CLASSIFIER_STORAGE) - self.assertGreater(len(components[2]), 0) - - def _create_classifier(self): - """ - Create and return an AIClassifier. - """ - rubric = rubric_from_dict(RUBRIC) - classifier_set = AIClassifierSet.create_classifier_set( - CLASSIFIERS_DICT, rubric, "test_algorithm", COURSE_ID, ITEM_ID - ) - return AIClassifier.objects.filter(classifier_set=classifier_set)[0] - - -class AIClassifierSetTest(CacheResetTest): - """ - Tests for the AIClassifierSet model. - """ - def setUp(self): - super(AIClassifierSetTest, self).setUp() - rubric = rubric_from_dict(RUBRIC) - self.classifier_set = AIClassifierSet.create_classifier_set( - CLASSIFIERS_DICT, rubric, "test_algorithm", COURSE_ID, ITEM_ID - ) - - def test_cache_downloads(self): - # Retrieve the classifier dict twice, which should hit the caching code. - # We can check that we're using the cache by asserting that - # the number of database queries decreases. - with self.assertNumQueries(1): - first = self.classifier_set.classifier_data_by_criterion - - with self.assertNumQueries(0): - second = self.classifier_set.classifier_data_by_criterion - - # Verify that we got the same value both times - self.assertEqual(first, second) - - def test_file_cache_downloads(self): - # Retrieve the classifiers dict, which should be cached - # both in memory and on the file system - first = self.classifier_set.classifier_data_by_criterion - - # Clear the in-memory cache - # This simulates what happens when a worker process dies - # after exceeding the maximum number of retries. - CLASSIFIERS_CACHE_IN_MEM.clear() - - # We should still be able to retrieve the classifiers dict - # from the on-disk cache, even if memory has been cleared - with self.assertNumQueries(0): - second = self.classifier_set.classifier_data_by_criterion - - # Verify that we got the correct classifiers dict back - self.assertEqual(first, second) - - -class AIGradingWorkflowTest(CacheResetTest): - """ - Tests for the AIGradingWorkflow model. - """ - CLASSIFIERS_DICT = { - u"vøȼȺƀᵾłȺɍɏ": "test data", - u"ﻭɼค๓๓คɼ": "more test data" - } - COURSE_ID = u"test" - ITEM_ID = u"test" - ALGORITHM_ID = "test" - - def setUp(self): - """ - Create a new grading workflow. - """ - self.rubric = rubric_from_dict(RUBRIC) - self.workflow = AIGradingWorkflow.objects.create( - submission_uuid='test', essay_text='test', - rubric=self.rubric, algorithm_id=self.ALGORITHM_ID, - item_id=self.ITEM_ID, course_id=self.COURSE_ID - ) - - # Create a rubric with a similar structure, but different prompt - similar_rubric_dict = copy.deepcopy(RUBRIC) - similar_rubric_dict['prompts'] = [{"description": 'Different prompt!'}] - self.similar_rubric = rubric_from_dict(similar_rubric_dict) - - def test_assign_most_recent_classifier_set(self): - # No classifier sets are available - found = self.workflow.assign_most_recent_classifier_set() - self.assertFalse(found) - self.assertIs(self.workflow.classifier_set, None) - - # Same rubric (exact), but different course id - classifier_set = AIClassifierSet.create_classifier_set( - self.CLASSIFIERS_DICT, self.rubric, self.ALGORITHM_ID, - "different course!", self.ITEM_ID - ) - found = self.workflow.assign_most_recent_classifier_set() - self.assertTrue(found) - self.assertEqual(classifier_set.pk, self.workflow.classifier_set.pk) - - # Same rubric (exact) but different item id - classifier_set = AIClassifierSet.create_classifier_set( - self.CLASSIFIERS_DICT, self.rubric, self.ALGORITHM_ID, - self.COURSE_ID, "different item!" - ) - found = self.workflow.assign_most_recent_classifier_set() - self.assertTrue(found) - self.assertEqual(classifier_set.pk, self.workflow.classifier_set.pk) - - # Same rubric (exact), but different algorithm id - # Shouldn't change, since the algorithm ID doesn't match - AIClassifierSet.create_classifier_set( - self.CLASSIFIERS_DICT, self.rubric, "different algorithm!", - self.COURSE_ID, self.ITEM_ID - ) - found = self.workflow.assign_most_recent_classifier_set() - self.assertTrue(found) - self.assertEqual(classifier_set.pk, self.workflow.classifier_set.pk) - - # Same rubric *structure*, but in a different item - # Shouldn't change, since the rubric isn't an exact match. - AIClassifierSet.create_classifier_set( - self.CLASSIFIERS_DICT, self.similar_rubric, self.ALGORITHM_ID, - self.COURSE_ID, "different item!" - ) - found = self.workflow.assign_most_recent_classifier_set() - self.assertTrue(found) - self.assertEqual(classifier_set.pk, self.workflow.classifier_set.pk) - - # Same rubric *structure* AND in the same course/item - # This should replace our current classifier set - classifier_set = AIClassifierSet.create_classifier_set( - self.CLASSIFIERS_DICT, self.similar_rubric, self.ALGORITHM_ID, - self.COURSE_ID, self.ITEM_ID - ) - found = self.workflow.assign_most_recent_classifier_set() - self.assertTrue(found) - self.assertEqual(classifier_set.pk, self.workflow.classifier_set.pk) - - # Same rubric and same course/item - # This is the ideal, so we should always prefer it - classifier_set = AIClassifierSet.create_classifier_set( - self.CLASSIFIERS_DICT, self.rubric, self.ALGORITHM_ID, - self.COURSE_ID, self.ITEM_ID - ) - found = self.workflow.assign_most_recent_classifier_set() - self.assertTrue(found) - self.assertEqual(classifier_set.pk, self.workflow.classifier_set.pk) diff --git a/openassessment/assessment/test/test_ai_worker.py b/openassessment/assessment/test/test_ai_worker.py deleted file mode 100644 index 09dd3402aa..0000000000 --- a/openassessment/assessment/test/test_ai_worker.py +++ /dev/null @@ -1,335 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tests for AI worker API calls. -""" -import copy -import datetime -from uuid import uuid4 -import mock -from django.db import DatabaseError -from django.core.files.base import ContentFile -from submissions import api as sub_api -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.api import ai_worker as ai_worker_api -from openassessment.assessment.models import ( - AITrainingWorkflow, AIGradingWorkflow, - AIClassifier, AIClassifierSet, Assessment -) -from openassessment.assessment.serializers import ( - rubric_from_dict, deserialize_training_examples -) -from openassessment.assessment.errors import ( - AITrainingRequestError, AITrainingInternalError, - AIGradingRequestError, AIGradingInternalError -) -from openassessment.assessment.test.constants import ( - EXAMPLES, RUBRIC, STUDENT_ITEM, ANSWER -) - -ALGORITHM_ID = "test-algorithm" - -# Classifier data -# Since this is controlled by the AI algorithm implementation, -# we could put anything here as long as it's JSON-serializable. -CLASSIFIERS = { - u"vøȼȺƀᵾłȺɍɏ": { - 'name': u'𝒕𝒆𝒔𝒕 𝒄𝒍𝒂𝒔𝒔𝒊𝒇𝒊𝒆𝒓', - 'data': u'Öḧ ḷëẗ ẗḧë ṡüṅ ḅëäẗ ḋöẅṅ üṗöṅ ṁÿ ḟäċë, ṡẗäṛṡ ẗö ḟïḷḷ ṁÿ ḋṛëäṁ"' - }, - u"ﻭɼค๓๓คɼ": { - 'name': u'𝒕𝒆𝒔𝒕 𝒄𝒍𝒂𝒔𝒔𝒊𝒇𝒊𝒆𝒓', - 'data': u"І ам а тѓаvэlэѓ оf ъотЂ тімэ аиↁ ѕрасэ, то ъэ шЂэѓэ І Ђаvэ ъээи" - } -} - - -class AIWorkerTrainingTest(CacheResetTest): - """ - Tests for the AI API calls a worker would make when - completing a training task. - """ - - COURSE_ID = u"sämplë ċöürsë" - ITEM_ID = u"12231" - ALGORITHM_ID = "test-algorithm" - - # Classifier data - # Since this is controlled by the AI algorithm implementation, - # we could put anything here as long as it's JSON-serializable. - CLASSIFIERS = { - u"vøȼȺƀᵾłȺɍɏ": { - 'name': u'𝒕𝒆𝒔𝒕 𝒄𝒍𝒂𝒔𝒔𝒊𝒇𝒊𝒆𝒓', - 'data': u'Öḧ ḷëẗ ẗḧë ṡüṅ ḅëäẗ ḋöẅṅ üṗöṅ ṁÿ ḟäċë, ṡẗäṛṡ ẗö ḟïḷḷ ṁÿ ḋṛëäṁ"' - }, - u"ﻭɼค๓๓คɼ": { - 'name': u'𝒕𝒆𝒔𝒕 𝒄𝒍𝒂𝒔𝒔𝒊𝒇𝒊𝒆𝒓', - 'data': u"І ам а тѓаvэlэѓ оf ъотЂ тімэ аиↁ ѕрасэ, то ъэ шЂэѓэ І Ђаvэ ъээи" - } - } - - def setUp(self): - """ - Create a training workflow in the database. - """ - examples = deserialize_training_examples(EXAMPLES, RUBRIC) - - workflow = AITrainingWorkflow.start_workflow(examples, self.COURSE_ID, self.ITEM_ID, self.ALGORITHM_ID) - - self.workflow_uuid = workflow.uuid - - def test_get_training_task_params(self): - params = ai_worker_api.get_training_task_params(self.workflow_uuid) - expected_examples = [ - { - 'text': EXAMPLES[0]['answer'], - 'scores': { - u"vøȼȺƀᵾłȺɍɏ": 1, - u"ﻭɼค๓๓คɼ": 0 - } - }, - { - 'text': EXAMPLES[1]['answer'], - 'scores': { - u"vøȼȺƀᵾłȺɍɏ": 0, - u"ﻭɼค๓๓คɼ": 2 - } - }, - ] - self.assertItemsEqual(params['training_examples'], expected_examples) - self.assertItemsEqual(params['algorithm_id'], ALGORITHM_ID) - - def test_get_training_task_params_no_workflow(self): - with self.assertRaises(AITrainingRequestError): - ai_worker_api.get_training_task_params("invalid_uuid") - - @mock.patch.object(AITrainingWorkflow.objects, 'get') - def test_get_training_task_params_database_error(self, mock_get): - mock_get.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AITrainingInternalError): - ai_worker_api.get_training_task_params(self.workflow_uuid) - - def test_create_classifiers(self): - ai_worker_api.create_classifiers(self.workflow_uuid, CLASSIFIERS) - - # Expect that the workflow was marked complete - workflow = AITrainingWorkflow.objects.get(uuid=self.workflow_uuid) - self.assertIsNot(workflow.completed_at, None) - - # Expect that the classifier set was created with the correct data - self.assertIsNot(workflow.classifier_set, None) - saved_classifiers = workflow.classifier_set.classifier_data_by_criterion - self.assertItemsEqual(CLASSIFIERS, saved_classifiers) - - def test_create_classifiers_no_workflow(self): - with self.assertRaises(AITrainingRequestError): - ai_worker_api.create_classifiers("invalid_uuid", CLASSIFIERS) - - @mock.patch.object(AITrainingWorkflow.objects, 'get') - def test_create_classifiers_database_error(self, mock_get): - mock_get.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AITrainingInternalError): - ai_worker_api.create_classifiers(self.workflow_uuid, CLASSIFIERS) - - def test_create_classifiers_serialize_error(self): - # Mutate the classifier data so it is NOT JSON-serializable - classifiers = copy.deepcopy(CLASSIFIERS) - classifiers[u"vøȼȺƀᵾłȺɍɏ"] = datetime.datetime.now() - - # Expect an error when we try to create the classifiers - with self.assertRaises(AITrainingInternalError): - ai_worker_api.create_classifiers(self.workflow_uuid, classifiers) - - def test_create_classifiers_missing_criteria(self): - # Remove a criterion from the classifiers dict - classifiers = copy.deepcopy(CLASSIFIERS) - del classifiers[u"vøȼȺƀᵾłȺɍɏ"] - - # Expect an error when we try to create the classifiers - with self.assertRaises(AITrainingRequestError): - ai_worker_api.create_classifiers(self.workflow_uuid, classifiers) - - def test_create_classifiers_unrecognized_criterion(self): - # Add an extra criterion to the classifiers dict - classifiers = copy.deepcopy(CLASSIFIERS) - classifiers[u"extra_criterion"] = copy.deepcopy(classifiers[u"vøȼȺƀᵾłȺɍɏ"]) - - # Expect an error when we try to create the classifiers - with self.assertRaises(AITrainingRequestError): - ai_worker_api.create_classifiers(self.workflow_uuid, classifiers) - - @mock.patch.object(AIClassifier, 'classifier_data') - def test_create_classifiers_upload_error(self, mock_data): - # Simulate an error occurring when uploading the trained classifier - mock_data.save.side_effect = IOError("OH NO!!!") - with self.assertRaises(AITrainingInternalError): - ai_worker_api.create_classifiers(self.workflow_uuid, CLASSIFIERS) - - def test_create_classifiers_twice(self): - # Simulate repeated task execution for the same workflow - # Since these are executed sequentially, the second call should - # have no effect. - ai_worker_api.create_classifiers(self.workflow_uuid, CLASSIFIERS) - ai_worker_api.create_classifiers(self.workflow_uuid, CLASSIFIERS) - - # Expect that the workflow was marked complete - workflow = AITrainingWorkflow.objects.get(uuid=self.workflow_uuid) - self.assertIsNot(workflow.completed_at, None) - - # Expect that the classifier set was created with the correct data - self.assertIsNot(workflow.classifier_set, None) - saved_classifiers = workflow.classifier_set.classifier_data_by_criterion - self.assertItemsEqual(CLASSIFIERS, saved_classifiers) - - def test_create_classifiers_no_training_examples(self): - # Create a workflow with no training examples - workflow = AITrainingWorkflow.objects.create(algorithm_id=ALGORITHM_ID) - - # Expect an error when we try to create classifiers - with self.assertRaises(AITrainingInternalError): - ai_worker_api.create_classifiers(workflow.uuid, CLASSIFIERS) - - def test_is_workflow_complete(self): - self.assertFalse(ai_worker_api.is_training_workflow_complete(self.workflow_uuid)) - workflow = AITrainingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.mark_complete_and_save() - self.assertTrue(ai_worker_api.is_training_workflow_complete(self.workflow_uuid)) - - def test_is_workflow_complete_no_such_workflow(self): - with self.assertRaises(AITrainingRequestError): - ai_worker_api.is_training_workflow_complete('no such workflow') - - @mock.patch.object(AITrainingWorkflow.objects, 'get') - def test_is_workflow_complete_database_error(self, mock_call): - mock_call.side_effect = DatabaseError("Oh no!") - with self.assertRaises(AITrainingInternalError): - ai_worker_api.is_training_workflow_complete(self.workflow_uuid) - - -class AIWorkerGradingTest(CacheResetTest): - """ - Tests for the AI API calls a worker would make when - completing a grading task. - """ - - SCORES = { - u"vøȼȺƀᵾłȺɍɏ": 1, - u"ﻭɼค๓๓คɼ": 0 - } - - def setUp(self): - """ - Create a grading workflow in the database. - """ - # Create a submission - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - self.submission_uuid = submission['uuid'] - - # Create a workflow for the submission - workflow = AIGradingWorkflow.start_workflow(self.submission_uuid, RUBRIC, ALGORITHM_ID) - self.workflow_uuid = workflow.uuid - - # Associate the workflow with classifiers - rubric = rubric_from_dict(RUBRIC) - classifier_set = AIClassifierSet.create_classifier_set( - CLASSIFIERS, rubric, ALGORITHM_ID, STUDENT_ITEM.get('course_id'), STUDENT_ITEM.get('item_id') - ) - workflow.classifier_set = classifier_set - workflow.save() - - def test_get_grading_task_params(self): - params = ai_worker_api.get_grading_task_params(self.workflow_uuid) - expected_params = { - 'essay_text': ANSWER, - 'classifier_set': CLASSIFIERS, - 'algorithm_id': ALGORITHM_ID, - 'valid_scores': { - u"vøȼȺƀᵾłȺɍɏ": [0, 1, 2], - u"ﻭɼค๓๓คɼ": [0, 1, 2] - } - } - self.assertItemsEqual(params, expected_params) - - def test_get_grading_task_params_num_queries(self): - with self.assertNumQueries(6): - ai_worker_api.get_grading_task_params(self.workflow_uuid) - - # The second time through we should be caching the queries - # to determine the valid scores for a classifier - with self.assertNumQueries(2): - ai_worker_api.get_grading_task_params(self.workflow_uuid) - - def test_get_grading_task_params_no_workflow(self): - with self.assertRaises(AIGradingRequestError): - ai_worker_api.get_grading_task_params("invalid_uuid") - - def test_get_grading_task_params_no_classifiers(self): - # Remove the classifiers from the workflow - workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.classifier_set = None - workflow.save() - - # Should get an error when retrieving task params - with self.assertRaises(AIGradingInternalError): - ai_worker_api.get_grading_task_params(self.workflow_uuid) - - @mock.patch.object(AIGradingWorkflow.objects, 'get') - def test_get_grading_task_params_database_error(self, mock_call): - mock_call.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AIGradingInternalError): - ai_worker_api.get_grading_task_params(self.submission_uuid) - - def test_invalid_classifier_data(self): - # Modify the classifier data so it is not valid JSON - invalid_json = "{" - for classifier in AIClassifier.objects.all(): - classifier.classifier_data.save(uuid4().hex, ContentFile(invalid_json)) - - # Should get an error when retrieving task params - with self.assertRaises(AIGradingInternalError): - ai_worker_api.get_grading_task_params(self.workflow_uuid) - - def test_create_assessment(self): - ai_worker_api.create_assessment(self.workflow_uuid, self.SCORES) - assessment = Assessment.objects.get(submission_uuid=self.submission_uuid) - self.assertEqual(assessment.points_earned, 1) - - def test_create_assessment_no_workflow(self): - with self.assertRaises(AIGradingRequestError): - ai_worker_api.create_assessment("invalid_uuid", self.SCORES) - - def test_create_assessment_workflow_already_complete(self): - # Try to create assessments for the same workflow multiple times - ai_worker_api.create_assessment(self.workflow_uuid, self.SCORES) - ai_worker_api.create_assessment(self.workflow_uuid, self.SCORES) - - # Expect that only one assessment is created for the submission - num_assessments = Assessment.objects.filter(submission_uuid=self.submission_uuid).count() - self.assertEqual(num_assessments, 1) - - @mock.patch.object(AIGradingWorkflow.objects, 'get') - def test_create_assessment_database_error_retrieving_workflow(self, mock_call): - mock_call.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AIGradingInternalError): - ai_worker_api.create_assessment(self.workflow_uuid, self.SCORES) - - @mock.patch.object(Assessment.objects, 'create') - def test_create_assessment_database_error_complete_workflow(self, mock_call): - mock_call.side_effect = DatabaseError("KABOOM!") - with self.assertRaises(AIGradingInternalError): - ai_worker_api.create_assessment(self.workflow_uuid, self.SCORES) - - def test_is_workflow_complete(self): - self.assertFalse(ai_worker_api.is_grading_workflow_complete(self.workflow_uuid)) - workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.mark_complete_and_save() - self.assertTrue(ai_worker_api.is_grading_workflow_complete(self.workflow_uuid)) - - def test_is_workflow_complete_no_such_workflow(self): - with self.assertRaises(AIGradingRequestError): - ai_worker_api.is_grading_workflow_complete('no such workflow') - - @mock.patch.object(AIGradingWorkflow.objects, 'get') - def test_is_workflow_complete_database_error(self, mock_call): - mock_call.side_effect = DatabaseError("Oh no!") - with self.assertRaises(AIGradingInternalError): - ai_worker_api.is_grading_workflow_complete(self.workflow_uuid) diff --git a/openassessment/assessment/test/test_assessment_models.py b/openassessment/assessment/test/test_assessment_models.py index 412fc25e76..5e65d1c856 100644 --- a/openassessment/assessment/test/test_assessment_models.py +++ b/openassessment/assessment/test/test_assessment_models.py @@ -2,14 +2,19 @@ """ Tests for the assessment Django models. """ -import copy, ddt -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.serializers import rubric_from_dict -from openassessment.assessment.models import Assessment, AssessmentPart, InvalidRubricSelection -from .constants import RUBRIC +import copy + +import ddt + from openassessment.assessment.api.self import create_assessment -from submissions.api import create_submission from openassessment.assessment.errors import SelfAssessmentRequestError +from openassessment.assessment.models import Assessment, AssessmentPart, InvalidRubricSelection +from openassessment.assessment.serializers import rubric_from_dict +from openassessment.test_utils import CacheResetTest +from submissions.api import create_submission + +from .constants import RUBRIC + @ddt.ddt class AssessmentTest(CacheResetTest): @@ -212,4 +217,4 @@ def _create_data_structures_with_criterion_properties( if has_feedback: criterion_feedback['Quality'] = "This was an assignment of average quality." - return rubric, options_selected, criterion_feedback \ No newline at end of file + return rubric, options_selected, criterion_feedback diff --git a/openassessment/assessment/test/test_peer.py b/openassessment/assessment/test/test_peer.py index 41fea02434..7a0bc85774 100644 --- a/openassessment/assessment/test/test_peer.py +++ b/openassessment/assessment/test/test_peer.py @@ -1,20 +1,19 @@ # coding=utf-8 -import datetime -import pytz import copy +import datetime -from django.db import DatabaseError, IntegrityError -from django.utils import timezone from ddt import ddt, file_data from mock import patch from nose.tools import raises +import pytz + +from django.db import DatabaseError, IntegrityError +from django.utils import timezone -from openassessment.test_utils import CacheResetTest from openassessment.assessment.api import peer as peer_api -from openassessment.assessment.models import ( - Assessment, AssessmentPart, AssessmentFeedback, AssessmentFeedbackOption, - PeerWorkflow, PeerWorkflowItem -) +from openassessment.assessment.models import (Assessment, AssessmentFeedback, AssessmentFeedbackOption, AssessmentPart, + PeerWorkflow, PeerWorkflowItem) +from openassessment.test_utils import CacheResetTest from openassessment.workflow import api as workflow_api from submissions import api as sub_api @@ -410,7 +409,7 @@ def test_peer_leases_same_submission(self): def test_peer_workflow_integrity_error(self): tim_sub, __ = self._create_student_and_submission("Tim", "Tim's answer") - with patch.object(PeerWorkflow.objects, "get_or_create") as mock_peer: + with patch("openassessment.assessment.models.peer.PeerWorkflow.objects.get_or_create") as mock_peer: mock_peer.side_effect = IntegrityError("Oh no!") # This should not raise an exception peer_api.on_start(tim_sub["uuid"]) @@ -1113,7 +1112,7 @@ def test_close_active_assessment(self): self.assertEqual(xander_answer["uuid"], submission["uuid"]) self.assertIsNotNone(item.assessment) - @patch.object(PeerWorkflowItem.objects, "filter") + @patch("openassessment.assessment.models.peer.PeerWorkflowItem.objects.filter") @raises(peer_api.PeerAssessmentInternalError) def test_get_submitted_assessments_error(self, mock_filter): self._create_student_and_submission("Tim", "Tim's answer") @@ -1123,7 +1122,7 @@ def test_get_submitted_assessments_error(self, mock_filter): submitted_assessments = peer_api.get_submitted_assessments(bob_sub["uuid"]) self.assertEqual(1, len(submitted_assessments)) - @patch.object(PeerWorkflow.objects, 'raw') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.raw') @raises(peer_api.PeerAssessmentInternalError) def test_failure_to_get_review_submission(self, mock_filter): tim_answer, _ = self._create_student_and_submission("Tim", "Tim's answer", MONDAY) @@ -1131,21 +1130,21 @@ def test_failure_to_get_review_submission(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") tim_workflow.get_submission_for_review(3) - @patch.object(AssessmentFeedback.objects, 'get') + @patch('openassessment.assessment.models.AssessmentFeedback.objects.get') @raises(peer_api.PeerAssessmentInternalError) def test_get_assessment_feedback_error(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") tim_answer, tim = self._create_student_and_submission("Tim", "Tim's answer", MONDAY) peer_api.get_assessment_feedback(tim_answer['uuid']) - @patch.object(PeerWorkflowItem, 'get_scored_assessments') + @patch('openassessment.assessment.models.peer.PeerWorkflowItem.get_scored_assessments') @raises(peer_api.PeerAssessmentInternalError) def test_set_assessment_feedback_error(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") tim_answer, _ = self._create_student_and_submission("Tim", "Tim's answer", MONDAY) peer_api.set_assessment_feedback({'submission_uuid': tim_answer['uuid']}) - @patch.object(AssessmentFeedback, 'save') + @patch('openassessment.assessment.models.AssessmentFeedback.save') @raises(peer_api.PeerAssessmentInternalError) def test_set_assessment_feedback_error_on_save(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") @@ -1157,7 +1156,7 @@ def test_set_assessment_feedback_error_on_save(self, mock_filter): } ) - @patch.object(AssessmentFeedback, 'save') + @patch('openassessment.assessment.models.AssessmentFeedback.save') @raises(peer_api.PeerAssessmentRequestError) def test_set_assessment_feedback_error_on_huge_save(self, mock_filter): tim_answer, _ = self._create_student_and_submission("Tim", "Tim's answer", MONDAY) @@ -1168,20 +1167,20 @@ def test_set_assessment_feedback_error_on_huge_save(self, mock_filter): } ) - @patch.object(PeerWorkflow.objects, 'get') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get') @raises(peer_api.PeerAssessmentWorkflowError) def test_failure_to_get_latest_workflow(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") tim_answer, _ = self._create_student_and_submission("Tim", "Tim's answer", MONDAY) PeerWorkflow.get_by_submission_uuid(tim_answer['uuid']) - @patch.object(PeerWorkflow.objects, 'get_or_create') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get_or_create') @raises(peer_api.PeerAssessmentInternalError) def test_create_workflow_error(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") self._create_student_and_submission("Tim", "Tim's answer", MONDAY) - @patch.object(PeerWorkflow.objects, 'get_or_create') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get_or_create') @raises(peer_api.PeerAssessmentInternalError) def test_create_workflow_item_error(self, mock_filter): mock_filter.side_effect = DatabaseError("Oh no.") @@ -1240,25 +1239,25 @@ def test_no_open_assessment(self): @raises(peer_api.PeerAssessmentInternalError) def test_max_score_db_error(self): tim, _ = self._create_student_and_submission("Tim", "Tim's answer") - with patch.object(Assessment.objects, 'filter') as mock_filter: + with patch('openassessment.assessment.models.Assessment.objects.filter') as mock_filter: mock_filter.side_effect = DatabaseError("Bad things happened") peer_api.get_rubric_max_scores(tim["uuid"]) - @patch.object(PeerWorkflow.objects, 'get') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get') @raises(peer_api.PeerAssessmentInternalError) def test_median_score_db_error(self, mock_filter): mock_filter.side_effect = DatabaseError("Bad things happened") tim, _ = self._create_student_and_submission("Tim", "Tim's answer") peer_api.get_assessment_median_scores(tim["uuid"]) - @patch.object(Assessment.objects, 'filter') + @patch('openassessment.assessment.models.Assessment.objects.filter') @raises(peer_api.PeerAssessmentInternalError) def test_get_assessments_db_error(self, mock_filter): tim, _ = self._create_student_and_submission("Tim", "Tim's answer") mock_filter.side_effect = DatabaseError("Bad things happened") peer_api.get_assessments(tim["uuid"]) - @patch.object(PeerWorkflow.objects, 'get_or_create') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get_or_create') @raises(peer_api.PeerAssessmentInternalError) def test_error_on_assessment_creation(self, mock_filter): mock_filter.side_effect = DatabaseError("Bad things happened") @@ -1274,7 +1273,7 @@ def test_error_on_assessment_creation(self, mock_filter): MONDAY, ) - @patch.object(Assessment.objects, 'filter') + @patch('openassessment.assessment.models.Assessment.objects.filter') @raises(peer_api.PeerAssessmentInternalError) def test_error_on_get_assessment(self, mock_filter): self._create_student_and_submission("Tim", "Tim's answer") @@ -1529,7 +1528,7 @@ def test_create_assessment_database_error(self): submission, student = self._create_student_and_submission("Jim", "Jim's answer") peer_api.get_submission_to_assess(submission['uuid'], 1) - with patch.object(PeerWorkflow.objects, 'get') as mock_call: + with patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get') as mock_call: mock_call.side_effect = DatabaseError("Kaboom!") peer_api.create_assessment( submission['uuid'], diff --git a/openassessment/assessment/test/test_rubric.py b/openassessment/assessment/test/test_rubric.py index 9c379c9e9d..6f8c1a295d 100644 --- a/openassessment/assessment/test/test_rubric.py +++ b/openassessment/assessment/test/test_rubric.py @@ -4,11 +4,10 @@ """ import copy -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.models import ( - Rubric, Criterion, CriterionOption, InvalidRubricSelection -) + +from openassessment.assessment.models import Criterion, CriterionOption, InvalidRubricSelection, Rubric from openassessment.assessment.test.constants import RUBRIC +from openassessment.test_utils import CacheResetTest class RubricIndexTest(CacheResetTest): diff --git a/openassessment/assessment/test/test_self.py b/openassessment/assessment/test/test_self.py index 5c1367a579..355071eda7 100644 --- a/openassessment/assessment/test/test_self.py +++ b/openassessment/assessment/test/test_self.py @@ -5,14 +5,13 @@ import copy import datetime + +from mock import patch import pytz from django.db import DatabaseError -from mock import patch -from openassessment.assessment.api.self import ( - create_assessment, submitter_is_finished, get_assessment -) +from openassessment.assessment.api.self import create_assessment, get_assessment, submitter_is_finished from openassessment.assessment.errors import SelfAssessmentInternalError, SelfAssessmentRequestError from openassessment.test_utils import CacheResetTest from submissions.api import create_submission @@ -103,7 +102,7 @@ def test_create_assessment_no_submission(self): # Attempt to create a self-assessment for a submission that doesn't exist with self.assertRaises(SelfAssessmentRequestError): create_assessment( - 'invalid_submission_uuid', u'𝖙𝖊𝖘𝖙 𝖚𝖘𝖊𝖗', + 'deadbeef-1234-5678-9100-1234deadbeef', u'𝖙𝖊𝖘𝖙 𝖚𝖘𝖊𝖗', self.OPTIONS_SELECTED, self.CRITERION_FEEDBACK, self.OVERALL_FEEDBACK, self.RUBRIC, scored_at=datetime.datetime(2014, 4, 1).replace(tzinfo=pytz.utc) ) @@ -115,7 +114,7 @@ def test_create_assessment_wrong_user(self): # Attempt to create a self-assessment for the submission from a different user with self.assertRaises(SelfAssessmentRequestError): create_assessment( - 'invalid_submission_uuid', u'another user', + 'deadbeef-1234-5678-9100-1234deadbeef', u'another user', self.OPTIONS_SELECTED, self.CRITERION_FEEDBACK, self.OVERALL_FEEDBACK, self.RUBRIC, scored_at=datetime.datetime(2014, 4, 1).replace(tzinfo=pytz.utc) ) diff --git a/openassessment/assessment/test/test_serializers.py b/openassessment/assessment/test/test_serializers.py index f9ec1fcfd4..d1444717a9 100644 --- a/openassessment/assessment/test/test_serializers.py +++ b/openassessment/assessment/test/test_serializers.py @@ -3,18 +3,15 @@ Tests for assessment serializers. """ +import copy import json import os.path -import copy +from openassessment.assessment.models import Assessment, AssessmentFeedback, AssessmentPart +from openassessment.assessment.serializers import (AssessmentFeedbackSerializer, InvalidRubric, full_assessment_dict, + rubric_from_dict) from openassessment.test_utils import CacheResetTest -from openassessment.assessment.models import ( - Assessment, AssessmentPart, AssessmentFeedback -) -from openassessment.assessment.serializers import ( - rubric_from_dict, full_assessment_dict, - AssessmentFeedbackSerializer, InvalidRubric -) + from .constants import RUBRIC diff --git a/openassessment/assessment/test/test_staff.py b/openassessment/assessment/test/test_staff.py index a01ce32b8a..2335851184 100644 --- a/openassessment/assessment/test/test_staff.py +++ b/openassessment/assessment/test/test_staff.py @@ -3,30 +3,26 @@ Tests for staff assessments. """ import copy -import mock from datetime import timedelta +from ddt import data, ddt, unpack +import mock + from django.db import DatabaseError -from django.test.utils import override_settings from django.utils.timezone import now -from ddt import ddt, data, unpack -from .constants import OPTIONS_SELECTED_DICT, RUBRIC, RUBRIC_OPTIONS, RUBRIC_POSSIBLE_POINTS, STUDENT_ITEM -from openassessment.assessment.test.test_ai import ( - ALGORITHM_ID, - AI_ALGORITHMS, - AIGradingTest, - train_classifiers -) -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.api import staff as staff_api, ai as ai_api, peer as peer_api -from openassessment.assessment.api.self import create_assessment as self_assess +from openassessment.assessment.api import peer as peer_api +from openassessment.assessment.api import staff as staff_api from openassessment.assessment.api.peer import create_assessment as peer_assess -from openassessment.assessment.models import Assessment, StaffWorkflow -from openassessment.assessment.errors import StaffAssessmentRequestError, StaffAssessmentInternalError +from openassessment.assessment.api.self import create_assessment as self_assess +from openassessment.assessment.errors import StaffAssessmentInternalError, StaffAssessmentRequestError +from openassessment.assessment.models import StaffWorkflow +from openassessment.test_utils import CacheResetTest from openassessment.workflow import api as workflow_api from submissions import api as sub_api +from .constants import OPTIONS_SELECTED_DICT, RUBRIC, RUBRIC_OPTIONS, RUBRIC_POSSIBLE_POINTS, STUDENT_ITEM + @ddt class TestStaffAssessment(CacheResetTest): @@ -40,17 +36,6 @@ class TestStaffAssessment(CacheResetTest): # This is due to ddt not playing nicely with list comprehensions ASSESSMENT_SCORES_DDT = [key for key in OPTIONS_SELECTED_DICT] - @staticmethod - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def _ai_assess(sub): - """ - Helper to fulfill ai assessment requirements. - """ - # Note that CLASSIFIER_SCORE_OVERRIDES matches OPTIONS_SELECTED_DICT['most'] scores - train_classifiers(RUBRIC, AIGradingTest.CLASSIFIER_SCORE_OVERRIDES) - ai_api.on_init(sub, rubric=RUBRIC, algorithm_id=ALGORITHM_ID) - return ai_api.get_latest_assessment(sub) - @staticmethod def _peer_assess(scores): """ @@ -67,7 +52,6 @@ def _peer_assess(scores): 'staff', lambda sub, scorer_id, scores: staff_api.create_assessment(sub, scorer_id, scores, dict(), "", RUBRIC) ), - ('ai', lambda sub, scorer_id, scores: TestStaffAssessment._ai_assess(sub)) ] def _verify_done_state(self, uuid, requirements, expect_done=True): @@ -377,7 +361,7 @@ def test_invalid_rubric_options_exception(self, invalid_reason): ) self.assertEqual(str(context_manager.exception), u"Invalid options were selected in the rubric.") - @mock.patch.object(Assessment.objects, 'filter') + @mock.patch('openassessment.assessment.models.Assessment.objects.filter') def test_database_filter_error_handling(self, mock_filter): # Create a submission tim_sub, _ = self._create_student_and_submission("Tim", "Tim's answer") @@ -403,7 +387,7 @@ def test_database_filter_error_handling(self, mock_filter): u"Error getting staff assessment scores for {}".format(tim_sub["uuid"]) ) - @mock.patch.object(Assessment, 'create') + @mock.patch('openassessment.assessment.models.Assessment.create') def test_database_create_error_handling(self, mock_create): mock_create.side_effect = DatabaseError("KABOOM!") @@ -531,7 +515,5 @@ def _create_student_and_submission(student, answer, date=None, problem_steps=Non steps = problem_steps if 'peer' in steps: peer_api.on_start(submission["uuid"]) - if 'ai' in steps: - init_params['ai'] = {'rubric': RUBRIC, 'algorithm_id': ALGORITHM_ID} workflow_api.create_workflow(submission["uuid"], steps, init_params) return submission, new_student_item diff --git a/openassessment/assessment/test/test_student_training_api.py b/openassessment/assessment/test/test_student_training_api.py index 579bad1534..0571259d87 100644 --- a/openassessment/assessment/test/test_student_training_api.py +++ b/openassessment/assessment/test/test_student_training_api.py @@ -3,15 +3,18 @@ Tests for training assessment type. """ import copy -from django.db import DatabaseError + import ddt from mock import patch + +from django.db import DatabaseError + +from openassessment.assessment.api import student_training as training_api +from openassessment.assessment.errors import StudentTrainingInternalError, StudentTrainingRequestError from openassessment.test_utils import CacheResetTest -from .constants import STUDENT_ITEM, ANSWER, RUBRIC, EXAMPLES from submissions import api as sub_api -from openassessment.assessment.api import student_training as training_api -from openassessment.assessment.errors import StudentTrainingRequestError, StudentTrainingInternalError -from openassessment.assessment.models import StudentTrainingWorkflow + +from .constants import ANSWER, EXAMPLES, RUBRIC, STUDENT_ITEM @ddt.ddt @@ -210,13 +213,13 @@ def test_get_training_example_no_submission(self): with self.assertRaises(StudentTrainingRequestError): training_api.get_training_example("no_such_submission", RUBRIC, EXAMPLES) - @patch.object(StudentTrainingWorkflow.objects, 'get') + @patch('openassessment.assessment.models.student_training.StudentTrainingWorkflow.objects.get') def test_get_num_completed_database_error(self, mock_db): mock_db.side_effect = DatabaseError("Kaboom!") with self.assertRaises(StudentTrainingInternalError): training_api.get_num_completed(self.submission_uuid) - @patch.object(StudentTrainingWorkflow.objects, 'get') + @patch('openassessment.assessment.models.student_training.StudentTrainingWorkflow.objects.get') def test_get_training_example_database_error(self, mock_db): mock_db.side_effect = DatabaseError("Kaboom!") with self.assertRaises(StudentTrainingInternalError): @@ -224,7 +227,7 @@ def test_get_training_example_database_error(self, mock_db): def test_assess_training_example_database_error(self): training_api.get_training_example(self.submission_uuid, RUBRIC, EXAMPLES) - with patch.object(StudentTrainingWorkflow.objects, 'get') as mock_db: + with patch('openassessment.assessment.models.student_training.StudentTrainingWorkflow.objects.get') as mock_db: mock_db.side_effect = DatabaseError("Kaboom!") with self.assertRaises(StudentTrainingInternalError): training_api.assess_training_example(self.submission_uuid, EXAMPLES[0]['options_selected']) diff --git a/openassessment/assessment/test/test_student_training_models.py b/openassessment/assessment/test/test_student_training_models.py index f023d8ced5..ffa1112414 100644 --- a/openassessment/assessment/test/test_student_training_models.py +++ b/openassessment/assessment/test/test_student_training_models.py @@ -2,13 +2,14 @@ Tests for student training models. """ import mock + from django.db import IntegrityError -from submissions import api as sub_api + +from openassessment.assessment.models import StudentTrainingWorkflow, StudentTrainingWorkflowItem from openassessment.test_utils import CacheResetTest -from openassessment.assessment.models import ( - StudentTrainingWorkflow, StudentTrainingWorkflowItem -) -from .constants import STUDENT_ITEM, ANSWER, EXAMPLES +from submissions import api as sub_api + +from .constants import ANSWER, EXAMPLES, STUDENT_ITEM class StudentTrainingWorkflowTest(CacheResetTest): @@ -16,8 +17,8 @@ class StudentTrainingWorkflowTest(CacheResetTest): Tests for the student training workflow model. """ - @mock.patch.object(StudentTrainingWorkflow.objects, 'get') - @mock.patch.object(StudentTrainingWorkflow.objects, 'get_or_create') + @mock.patch('openassessment.assessment.models.student_training.StudentTrainingWorkflow.objects.get') + @mock.patch('openassessment.assessment.models.student_training.StudentTrainingWorkflow.objects.get_or_create') def test_create_workflow_integrity_error(self, mock_create, mock_get): # Simulate a race condition in which someone creates a workflow # after we check if it exists. This will violate the database uniqueness @@ -37,7 +38,7 @@ def test_create_workflow_integrity_error(self, mock_create, mock_get): workflow = StudentTrainingWorkflow.get_workflow(submission['uuid']) self.assertEqual(workflow, mock_workflow) - @mock.patch.object(StudentTrainingWorkflowItem.objects, 'create') + @mock.patch('openassessment.assessment.models.student_training.StudentTrainingWorkflowItem.objects.create') def test_create_workflow_item_integrity_error(self, mock_create): # Create a submission and workflow submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) diff --git a/openassessment/assessment/test/test_training.py b/openassessment/assessment/test/test_training.py index 1e3cefb8b8..0ad846160b 100644 --- a/openassessment/assessment/test/test_training.py +++ b/openassessment/assessment/test/test_training.py @@ -2,12 +2,16 @@ """ Tests for training models and serializers (common to student and AI training). """ +from collections import OrderedDict import copy + import mock + from django.db import IntegrityError -from openassessment.test_utils import CacheResetTest + from openassessment.assessment.models import TrainingExample from openassessment.assessment.serializers import deserialize_training_examples, serialize_training_example +from openassessment.test_utils import CacheResetTest class TrainingExampleSerializerTest(CacheResetTest): @@ -63,17 +67,17 @@ class TrainingExampleSerializerTest(CacheResetTest): u" 𝖜𝖍𝖊𝖓 𝖆 𝖒𝖆𝖓 𝖙𝖆𝖐𝖊𝖘 𝖙𝖍𝖎𝖘 𝖜𝖍𝖔𝖑𝖊 𝖚𝖓𝖎𝖛𝖊𝖗𝖘𝖊 𝖋𝖔𝖗 𝖆 𝖛𝖆𝖘𝖙 𝖕𝖗𝖆𝖈𝖙𝖎𝖈𝖆𝖑 𝖏𝖔𝖐𝖊, 𝖙𝖍𝖔𝖚𝖌𝖍 𝖙𝖍𝖊 𝖜𝖎𝖙 𝖙𝖍𝖊𝖗𝖊𝖔𝖋" u" 𝖍𝖊 𝖇𝖚𝖙 𝖉𝖎𝖒𝖑𝖞 𝖉𝖎𝖘𝖈𝖊𝖗𝖓𝖘, 𝖆𝖓𝖉 𝖒𝖔𝖗𝖊 𝖙𝖍𝖆𝖓 𝖘𝖚𝖘𝖕𝖊𝖈𝖙𝖘 𝖙𝖍𝖆𝖙 𝖙𝖍𝖊 𝖏𝖔𝖐𝖊 𝖎𝖘 𝖆𝖙 𝖓𝖔𝖇𝖔𝖉𝖞'𝖘 𝖊𝖝𝖕𝖊𝖓𝖘𝖊 𝖇𝖚𝖙 𝖍𝖎𝖘 𝖔𝖜𝖓." ), - 'options_selected': { + 'options_selected': OrderedDict({ u"vøȼȺƀᵾłȺɍɏ": u"𝓰𝓸𝓸𝓭", u"ﻭɼค๓๓คɼ": u"𝒑𝒐𝒐𝒓", - } + }) }, { 'answer': u"Tőṕ-héávӳ ẃáś thé śhíṕ áś á díńńéŕĺéśś śtúdéńt ẃíth áĺĺ Áŕíśtőtĺé íń híś héád.", - 'options_selected': { + 'options_selected': OrderedDict({ u"vøȼȺƀᵾłȺɍɏ": u"𝒑𝒐𝒐𝒓", u"ﻭɼค๓๓คɼ": u"єχ¢єℓℓєηт", - } + }) }, { 'answer': ( @@ -82,10 +86,10 @@ class TrainingExampleSerializerTest(CacheResetTest): u"azure..... Consider all this; and then turn to this green, gentle, and most docile earth; " u"consider them both, the sea and the land; and do you not find a strange analogy to something in yourself?" ), - 'options_selected': { + 'options_selected': OrderedDict({ u"vøȼȺƀᵾłȺɍɏ": u"𝒑𝒐𝒐𝒓", u"ﻭɼค๓๓คɼ": u"єχ¢єℓℓєηт", - } + }) }, ] @@ -156,8 +160,8 @@ def test_similar_training_examples_different_answer(self): for example in (first_examples + second_examples): self.assertIn(example, db_examples) - @mock.patch.object(TrainingExample.objects, 'get') - @mock.patch.object(TrainingExample, 'create_example') + @mock.patch('openassessment.assessment.models.TrainingExample.objects.get') + @mock.patch('openassessment.assessment.models.TrainingExample.create_example') def test_deserialize_integrity_error(self, mock_create, mock_get): # Simulate an integrity error when creating the training example # This can occur when using repeatable-read isolation mode. diff --git a/openassessment/assessment/test/test_worker.py b/openassessment/assessment/test/test_worker.py deleted file mode 100644 index 8f68145b39..0000000000 --- a/openassessment/assessment/test/test_worker.py +++ /dev/null @@ -1,417 +0,0 @@ -# coding=utf-8 -""" -Tests for AI worker tasks. -""" -from contextlib import contextmanager -import itertools -import mock -from django.test.utils import override_settings -from submissions import api as sub_api -from openassessment.test_utils import CacheResetTest -from openassessment.assessment.worker.training import train_classifiers, InvalidExample -from openassessment.assessment.worker.grading import grade_essay -from openassessment.assessment.api import ai_worker as ai_worker_api -from openassessment.assessment.models import AITrainingWorkflow, AIGradingWorkflow, AIClassifierSet -from openassessment.assessment.worker.algorithm import ( - AIAlgorithm, UnknownAlgorithm, AlgorithmLoadError, TrainingError, ScoreError -) -from openassessment.assessment.serializers import ( - deserialize_training_examples, rubric_from_dict -) -from openassessment.assessment.errors import ( - AITrainingRequestError, AIGradingInternalError, AIGradingRequestError -) -from openassessment.assessment.test.constants import ( - EXAMPLES, RUBRIC, STUDENT_ITEM, ANSWER -) - - -class StubAIAlgorithm(AIAlgorithm): - """ - Stub implementation of a supervised ML algorithm. - """ - def train_classifier(self, examples): - return {} - - def score(self, text, classifier, cache): - return 0 - - -class ErrorStubAIAlgorithm(AIAlgorithm): - """ - Stub implementation that raises an exception during training. - """ - def train_classifier(self, examples): - raise TrainingError("Test error!") - - def score(self, text, classifier, cache): - raise ScoreError("Test error!") - - -class InvalidScoreAlgorithm(AIAlgorithm): - """ - Stub implementation that returns a score that isn't in the rubric. - """ - SCORE_CYCLE = itertools.cycle([-100, 0.7, 1.2, 100]) - - def train_classifier(self, examples): - return {} - - def score(self, text, classifier, cache): - return self.SCORE_CYCLE.next() - - -ALGORITHM_ID = u"test-stub" -ERROR_STUB_ALGORITHM_ID = u"error-stub" -UNDEFINED_CLASS_ALGORITHM_ID = u"undefined_class" -UNDEFINED_MODULE_ALGORITHM_ID = u"undefined_module" -INVALID_SCORE_ALGORITHM_ID = u"invalid_score" -AI_ALGORITHMS = { - ALGORITHM_ID: '{module}.StubAIAlgorithm'.format(module=__name__), - ERROR_STUB_ALGORITHM_ID: '{module}.ErrorStubAIAlgorithm'.format(module=__name__), - UNDEFINED_CLASS_ALGORITHM_ID: '{module}.NotDefinedAIAlgorithm'.format(module=__name__), - UNDEFINED_MODULE_ALGORITHM_ID: 'openassessment.not.valid.NotDefinedAIAlgorithm', - INVALID_SCORE_ALGORITHM_ID: '{module}.InvalidScoreAlgorithm'.format(module=__name__), -} - - -class CeleryTaskTest(CacheResetTest): - """ - Test case for Celery tasks. - """ - @contextmanager - def assert_retry(self, task, final_exception): - """ - Context manager that asserts that the training task was retried. - - Args: - task (celery.app.task.Task): The Celery task object. - final_exception (Exception): The error thrown after retrying. - - Raises: - AssertionError - - """ - original_retry = task.retry - task.retry = mock.MagicMock() - task.retry.side_effect = lambda: original_retry(task) - try: - with self.assertRaises(final_exception): - yield - task.retry.assert_called_once() - finally: - task.retry = original_retry - - -class AITrainingTaskTest(CeleryTaskTest): - """ - Tests for the training task executed asynchronously by Celery workers. - """ - - COURSE_ID = u"10923" - ITEM_ID = u"12231" - ALGORITHM_ID = u"test-stub" - ERROR_STUB_ALGORITHM_ID = u"error-stub" - UNDEFINED_CLASS_ALGORITHM_ID = u"undefined_class" - UNDEFINED_MODULE_ALGORITHM_ID = u"undefined_module" - AI_ALGORITHMS = { - ALGORITHM_ID: '{module}.StubAIAlgorithm'.format(module=__name__), - ERROR_STUB_ALGORITHM_ID: '{module}.ErrorStubAIAlgorithm'.format(module=__name__), - UNDEFINED_CLASS_ALGORITHM_ID: '{module}.NotDefinedAIAlgorithm'.format(module=__name__), - UNDEFINED_MODULE_ALGORITHM_ID: 'openassessment.not.valid.NotDefinedAIAlgorithm' - } - - - def setUp(self): - """ - Create a training workflow in the database. - """ - examples = deserialize_training_examples(EXAMPLES, RUBRIC) - workflow = AITrainingWorkflow.start_workflow(examples, self.COURSE_ID, self.ITEM_ID, self.ALGORITHM_ID) - self.workflow_uuid = workflow.uuid - - def test_unknown_algorithm(self): - # Since we haven't overridden settings to configure the algorithms, - # the worker will not recognize the workflow's algorithm ID. - with self.assert_retry(train_classifiers, UnknownAlgorithm): - train_classifiers(self.workflow_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_skip_completed_workflow(self): - # Mark the grading workflow as complete - workflow = AITrainingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.mark_complete_and_save() - - # The training task should short-circuit immediately, skipping calls - # to get parameters for the task. - actual_call = ai_worker_api.get_training_task_params - patched = 'openassessment.assessment.worker.grading.ai_worker_api.get_training_task_params' - with mock.patch(patched) as mock_call: - mock_call.side_effect = actual_call - train_classifiers(self.workflow_uuid) - self.assertFalse(mock_call.called) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_check_complete_error(self): - with self.assert_retry(train_classifiers, AITrainingRequestError): - train_classifiers("no such workflow uuid") - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_unable_to_load_algorithm_class(self): - # The algorithm is defined in the settings, but the class does not exist. - self._set_algorithm_id(UNDEFINED_CLASS_ALGORITHM_ID) - with self.assert_retry(train_classifiers, AlgorithmLoadError): - train_classifiers(self.workflow_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_unable_to_find_algorithm_module(self): - # The algorithm is defined in the settings, but the module can't be loaded - self._set_algorithm_id(UNDEFINED_MODULE_ALGORITHM_ID) - with self.assert_retry(train_classifiers, AlgorithmLoadError): - train_classifiers(self.workflow_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - @mock.patch('openassessment.assessment.worker.training.ai_worker_api.get_training_task_params') - def test_get_training_task_params_api_error(self, mock_call): - mock_call.side_effect = AITrainingRequestError("Test error!") - with self.assert_retry(train_classifiers, AITrainingRequestError): - train_classifiers(self.workflow_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_invalid_training_example_error(self): - def _mutation(examples): # pylint: disable=C0111 - del examples[0]['scores'][u"ﻭɼค๓๓คɼ"] - self._assert_mutated_examples(_mutation) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_training_example_missing_key(self): - def _mutation(examples): # pylint: disable=C0111 - del examples[0]['scores'] - self._assert_mutated_examples(_mutation) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_training_example_non_numeric_score(self): - def _mutation(examples): # pylint: disable=C0111 - examples[0]['scores'][u"ﻭɼค๓๓คɼ"] = "not an integer" - self._assert_mutated_examples(_mutation) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_training_algorithm_error(self): - # Use a stub algorithm implementation that raises an exception during training - self._set_algorithm_id(ERROR_STUB_ALGORITHM_ID) - with self.assert_retry(train_classifiers, TrainingError): - train_classifiers(self.workflow_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - @mock.patch('openassessment.assessment.worker.training.ai_worker_api.create_classifiers') - def test_create_classifiers_api_error(self, mock_call): - mock_call.side_effect = AITrainingRequestError("Test error!") - with self.assert_retry(train_classifiers, AITrainingRequestError): - train_classifiers(self.workflow_uuid) - - def _set_algorithm_id(self, algorithm_id): - """ - Override the default algorithm ID for the training workflow. - - Args: - algorithm_id (unicode): The new algorithm ID - - Returns: - None - - """ - workflow = AITrainingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.algorithm_id = algorithm_id - workflow.save() - - def _assert_mutated_examples(self, mutate_func): - """ - Mutate the training examples returned by the API, - then check that we get the expected error. - - This *may* be a little paranoid :) - - Args: - mutate_func (callable): Function that accepts a single argument, - the list of example dictionaries. - - Raises: - AssertionError - - """ - params = ai_worker_api.get_training_task_params(self.workflow_uuid) - mutate_func(params['training_examples']) - - call_signature = 'openassessment.assessment.worker.training.ai_worker_api.get_training_task_params' - with mock.patch(call_signature) as mock_call: - mock_call.return_value = params - with self.assert_retry(train_classifiers, InvalidExample): - train_classifiers(self.workflow_uuid) - - -class AIGradingTaskTest(CeleryTaskTest): - """ - Tests for the grading task executed asynchronously by Celery workers. - """ - - # Classifier data - # Since this is controlled by the AI algorithm implementation, - # we could put anything here as long as it's JSON-serializable. - CLASSIFIERS = { - u"vøȼȺƀᵾłȺɍɏ": { - 'name': u'𝒕𝒆𝒔𝒕 𝒄𝒍𝒂𝒔𝒔𝒊𝒇𝒊𝒆𝒓', - 'data': u'Öḧ ḷëẗ ẗḧë ṡüṅ ḅëäẗ ḋöẅṅ üṗöṅ ṁÿ ḟäċë, ṡẗäṛṡ ẗö ḟïḷḷ ṁÿ ḋṛëäṁ"' - }, - u"ﻭɼค๓๓คɼ": { - 'name': u'𝒕𝒆𝒔𝒕 𝒄𝒍𝒂𝒔𝒔𝒊𝒇𝒊𝒆𝒓', - 'data': u"І ам а тѓаvэlэѓ оf ъотЂ тімэ аиↁ ѕрасэ, то ъэ шЂэѓэ І Ђаvэ ъээи" - } - } - - def setUp(self): - """ - Create a submission and grading workflow. - """ - # Create a submission - submission = sub_api.create_submission(STUDENT_ITEM, ANSWER) - self.submission_uuid = submission['uuid'] - - # Create a workflow for the submission - workflow = AIGradingWorkflow.start_workflow(self.submission_uuid, RUBRIC, ALGORITHM_ID) - self.workflow_uuid = workflow.uuid - - # Associate the workflow with classifiers - rubric = rubric_from_dict(RUBRIC) - classifier_set = AIClassifierSet.create_classifier_set( - self.CLASSIFIERS, rubric, ALGORITHM_ID, STUDENT_ITEM.get('course_id'), STUDENT_ITEM.get('item_id') - ) - workflow.classifier_set = classifier_set - workflow.save() - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_skip_completed_workflow(self): - # Mark the grading workflow as complete - workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.mark_complete_and_save() - - # The grading task should short-circuit immediately, skipping calls - # to get parameters for the task. - actual_call = ai_worker_api.get_grading_task_params - patched = 'openassessment.assessment.worker.grading.ai_worker_api.get_grading_task_params' - with mock.patch(patched) as mock_call: - mock_call.side_effect = actual_call - grade_essay(self.workflow_uuid) - self.assertFalse(mock_call.called) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_check_complete_error(self): - with self.assert_retry(grade_essay, AIGradingRequestError): - grade_essay("no such workflow uuid") - - @mock.patch('openassessment.assessment.api.ai_worker.create_assessment') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_algorithm_gives_invalid_score(self, mock_create_assessment): - # If an algorithm provides a score that isn't in the rubric, - # we should choose the closest valid score. - self._set_algorithm_id(INVALID_SCORE_ALGORITHM_ID) - - # The first score given by the algorithm should be below the minimum valid score - # The second score will be between two valid scores (0 and 1), rounding up - grade_essay(self.workflow_uuid) - expected_scores = { - u"vøȼȺƀᵾłȺɍɏ": 0, - u"ﻭɼค๓๓คɼ": 1 - } - mock_create_assessment.assert_called_with(self.workflow_uuid, expected_scores) - - # The third score will be between two valid scores (1 and 2), rounding down - # The final score will be greater than the maximum score - self._reset_workflow() - grade_essay(self.workflow_uuid) - expected_scores = { - u"vøȼȺƀᵾłȺɍɏ": 1, - u"ﻭɼค๓๓คɼ": 2 - } - mock_create_assessment.assert_called_with(self.workflow_uuid, expected_scores) - - @mock.patch('openassessment.assessment.worker.grading.ai_worker_api.get_grading_task_params') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_retrieve_params_error(self, mock_call): - mock_call.side_effect = AIGradingInternalError("Test error") - with self.assert_retry(grade_essay, AIGradingInternalError): - grade_essay(self.workflow_uuid) - - def test_unknown_algorithm_id_error(self): - # Since we're not overriding settings, the algorithm ID won't be recognized - with self.assert_retry(grade_essay, UnknownAlgorithm): - grade_essay(self.workflow_uuid) - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_algorithm_score_error(self): - self._set_algorithm_id(ERROR_STUB_ALGORITHM_ID) - with self.assert_retry(grade_essay, ScoreError): - grade_essay(self.workflow_uuid) - - @mock.patch('openassessment.assessment.worker.grading.ai_worker_api.create_assessment') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_create_assessment_error(self, mock_call): - mock_call.side_effect = AIGradingInternalError - with self.assert_retry(grade_essay, AIGradingInternalError): - grade_essay(self.workflow_uuid) - - @mock.patch('openassessment.assessment.worker.grading.ai_worker_api.get_grading_task_params') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_params_missing_criterion_for_valid_scores(self, mock_call): - mock_call.return_value = { - 'essay_text': 'test', - 'classifier_set': { - u"vøȼȺƀᵾłȺɍɏ": {}, - u"ﻭɼค๓๓คɼ": {} - }, - 'algorithm_id': ALGORITHM_ID, - 'valid_scores': {} - } - with self.assert_retry(grade_essay, AIGradingInternalError): - grade_essay(self.workflow_uuid) - - @mock.patch('openassessment.assessment.worker.grading.ai_worker_api.get_grading_task_params') - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_params_valid_scores_empty_list(self, mock_call): - mock_call.return_value = { - 'essay_text': 'test', - 'classifier_set': { - u"vøȼȺƀᵾłȺɍɏ": {}, - u"ﻭɼค๓๓คɼ": {} - }, - 'algorithm_id': ALGORITHM_ID, - 'valid_scores': { - u"vøȼȺƀᵾłȺɍɏ": [], - u"ﻭɼค๓๓คɼ": [0, 1, 2] - } - } - with self.assert_retry(grade_essay, AIGradingInternalError): - grade_essay(self.workflow_uuid) - - def _set_algorithm_id(self, algorithm_id): - """ - Override the default algorithm ID for the grading workflow. - - Args: - algorithm_id (unicode): The new algorithm ID - - Returns: - None - - """ - workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.algorithm_id = algorithm_id - workflow.save() - - def _reset_workflow(self): - """ - Reset the workflow so we can re-use it. - """ - workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid) - workflow.completed_at = None - workflow.assessment = None - workflow.save() diff --git a/openassessment/assessment/urls.py b/openassessment/assessment/urls.py index bd25e3a699..5e946e8905 100644 --- a/openassessment/assessment/urls.py +++ b/openassessment/assessment/urls.py @@ -1,9 +1,10 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url -urlpatterns = patterns( - 'openassessment.assessment.views', +from openassessment.assessment import views + +urlpatterns = [ url( r'^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$', - 'get_evaluations_for_student_item' + views.get_evaluations_for_student_item ), -) +] diff --git a/openassessment/assessment/views.py b/openassessment/assessment/views.py index 980fa658ed..5896f1a5bd 100644 --- a/openassessment/assessment/views.py +++ b/openassessment/assessment/views.py @@ -1,7 +1,8 @@ import logging -from django.contrib.auth.decorators import login_required +from django.contrib.auth.decorators import login_required from django.shortcuts import render_to_response + from openassessment.assessment.api.peer import get_assessments from submissions.api import SubmissionRequestError, get_submissions diff --git a/openassessment/assessment/worker/__init__.py b/openassessment/assessment/worker/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openassessment/assessment/worker/algorithm.py b/openassessment/assessment/worker/algorithm.py deleted file mode 100644 index aca374b56f..0000000000 --- a/openassessment/assessment/worker/algorithm.py +++ /dev/null @@ -1,376 +0,0 @@ -""" -Define the ML algorithms used to train text classifiers. -""" -try: - import cPickle as pickle -except ImportError: - import pickle - -from abc import ABCMeta, abstractmethod -from collections import namedtuple -import importlib -import traceback -import base64 -from django.conf import settings - - -DEFAULT_AI_ALGORITHMS = { - 'fake': 'openassessment.assessment.worker.algorithm.FakeAIAlgorithm', - 'ease': 'openassessment.assessment.worker.algorithm.EaseAIAlgorithm' -} - - -class AIAlgorithmError(Exception): - """ - An error occurred when using an AI algorithm. - Superclass for more specific errors below. - """ - pass - - -class UnknownAlgorithm(AIAlgorithmError): - """ - Algorithm ID not found in the configuration. - """ - def __init__(self, algorithm_id): - msg = u"Could not find algorithm \"{}\" in the configuration.".format(algorithm_id) - super(UnknownAlgorithm, self).__init__(msg) - - -class AlgorithmLoadError(AIAlgorithmError): - """ - Unable to load the algorithm class. - """ - def __init__(self, algorithm_id, algorithm_path): - msg = ( - u"Could not load algorithm \"{algorithm_id}\" from \"{path}\"" - ).format(algorithm_id=algorithm_id, path=algorithm_path) - super(AlgorithmLoadError, self).__init__(msg) - - -class TrainingError(AIAlgorithmError): - """ - An error occurred while training a classifier from example essays. - """ - pass - - -class ScoreError(AIAlgorithmError): - """ - An error occurred while scoring an essay. - """ - pass - - -class InvalidClassifier(ScoreError): - """ - The classifier could not be used by this algorithm to score an essay. - """ - pass - - -class AIAlgorithm(object): - """ - Abstract base class for a supervised ML text classification algorithm. - """ - __metaclass__ = ABCMeta - - # Example essay used as input to the training algorithm - # `text` is a unicode string representing a student essay submission. - # `score` is an integer score. - # Note that `score` is used as an arbitrary label, so you could - # have a set of examples with non-adjacent scores. - ExampleEssay = namedtuple('ExampleEssay', ['text', 'score']) - - @abstractmethod - def train_classifier(self, examples): - """ - Train a classifier based on example essays and scores. - - Args: - examples (list of AIAlgorithm.ExampleEssay): Example essays and scores. - - Returns: - JSON-serializable: The trained classifier. This MUST be JSON-serializable. - - Raises: - TrainingError: The classifier could not be trained successfully. - - """ - pass - - @abstractmethod - def score(self, text, classifier, cache): - """ - Score an essay using a classifier. - - Args: - text (unicode): The text to classify. - classifier (JSON-serializable): A classifier, using the same format - as `train_classifier()`. - cache (dict): An in-memory cache that persists until all criteria - in the rubric have been scored. - - Raises: - InvalidClassifier: The provided classifier cannot be used by this algorithm. - ScoreError: An error occurred while scoring. - - """ - pass - - @classmethod - def algorithm_for_id(cls, algorithm_id): - """ - Load an algorithm based on Django settings configuration. - - Args: - algorithm_id (unicode): The identifier for the algorithm, - which should be specified in Django settings. - - Returns: - AIAlgorithm - - Raises: - UnknownAlgorithm - - """ - algorithms = getattr(settings, "ORA2_AI_ALGORITHMS", DEFAULT_AI_ALGORITHMS) - cls_path = algorithms.get(algorithm_id) - - if cls_path is None: - raise UnknownAlgorithm(algorithm_id) - else: - module_path, _, name = cls_path.rpartition('.') - try: - algorithm_cls = getattr(importlib.import_module(module_path), name) - return algorithm_cls() - except (ImportError, ValueError, AttributeError): - raise AlgorithmLoadError(algorithm_id, cls_path) - - -class FakeAIAlgorithm(AIAlgorithm): - """ - Fake AI algorithm implementation that assigns scores randomly. - We use this for testing the pipeline independently of EASE. - """ - - def train_classifier(self, examples): - """ - Store the possible score labels, which will allow - us to deterministically choose scores for other essays. - """ - unique_sorted_scores = sorted(list(set(example.score for example in examples))) - return {'scores': unique_sorted_scores} - - def score(self, text, classifier, cache): - """ - Choose a score for the essay deterministically based on its length. - """ - if 'scores' not in classifier or len(classifier['scores']) == 0: - raise InvalidClassifier("Classifier must provide score labels") - else: - score_index = len(text) % len(classifier['scores']) - return classifier['scores'][score_index] - - -class EaseAIAlgorithm(AIAlgorithm): - """ - Wrapper for the EASE library. - See https://github.com/edx/ease for more information. - - Since EASE has many system dependencies, we don't include it explicitly - in edx-ora2 requirements. When testing locally, we use the fake - algorithm implementation instead. - """ - - def train_classifier(self, examples): - """ - Train a text classifier using the EASE library. - The classifier is serialized as a dictionary with keys: - * 'feature_extractor': The pickled feature extractor (transforms text into a numeric feature vector). - * 'score_classifier': The pickled classifier (uses the feature vector to assign scores to essays). - - Because we are using `pickle`, the serialized classifiers are unfortunately - tied to the particular version of ease/scikit-learn/numpy/scipy/nltk that we - have installed at the time of training. - - Args: - examples (list of AIAlgorithm.ExampleEssay): Example essays and scores. - - Returns: - dict: The serializable classifier. - - Raises: - TrainingError: The classifier could not be trained successfully. - - """ - feature_ext, classifier = self._train_classifiers(examples) - return self._serialize_classifiers(feature_ext, classifier) - - def score(self, text, classifier, cache): - """ - Score essays using EASE. - - Args: - text (unicode): The essay text to score. - classifier (dict): The serialized classifiers created during training. - cache (dict): An in-memory cache that persists until all criteria - in the rubric have been scored. - - Returns: - int - - Raises: - InvalidClassifier - ScoreError - - """ - try: - from ease.essay_set import EssaySet # pylint:disable=F0401 - except ImportError: - msg = u"Could not import EASE to grade essays." - raise ScoreError(msg) - - feature_extractor, score_classifier = self._deserialize_classifiers(classifier) - - # The following is a modified version of `ease.grade.grade()`, - # skipping things we don't use (cross-validation, feedback) - # and caching essay sets across criteria. This allows us to - # avoid some expensive NLTK operations, particularly tagging - # parts of speech. - try: - # Get the essay set from the cache or create it. - # Since all essays to be graded are assigned a dummy - # score of "0", we can safely re-use the essay set - # for each criterion in the rubric. - # EASE can't handle non-ASCII unicode, so we need - # to strip out non-ASCII chars. - essay_set = cache.get('grading_essay_set') - if essay_set is None: - essay_set = EssaySet(essaytype="test") - essay_set.add_essay(text.encode('ascii', 'ignore'), 0) - cache['grading_essay_set'] = essay_set - - # Extract features from the text - features = feature_extractor.gen_feats(essay_set) - - # Predict a score - return int(score_classifier.predict(features)[0]) - except: - msg = ( - u"An unexpected error occurred while using " - u"EASE to score an essay: {traceback}" - ).format(traceback=traceback.format_exc()) - raise ScoreError(msg) - - def _train_classifiers(self, examples): - """ - Use EASE to train classifiers. - - Args: - examples (list of AIAlgorithm.ExampleEssay): Example essays and scores. - - Returns: - tuple of `feature_extractor` (an `ease.feature_extractor.FeatureExtractor` object) - and `classifier` (a `sklearn.ensemble.GradientBoostingClassifier` object). - - Raises: - TrainingError: Could not load EASE or could not complete training. - - """ - try: - from ease.create import create # pylint: disable=F0401 - except ImportError: - msg = u"Could not import EASE to perform training." - raise TrainingError(msg) - - input_essays = [example.text for example in examples] - input_scores = [example.score for example in examples] - - try: - # Train the classifiers - # The third argument is the essay prompt, which EASE uses - # to check if an input essay is too similar to the prompt. - # Since we're not using this feature, we pass in an empty string. - results = create(input_essays, input_scores, "") - except: - msg = ( - u"An unexpected error occurred while using " - u"EASE to train classifiers: {traceback}" - ).format(traceback=traceback.format_exc()) - raise TrainingError(msg) - - if not results.get('success', False): - msg = ( - u"Errors occurred while training classifiers " - u"using EASE: {errors}" - ).format(errors=results.get('errors', [])) - raise TrainingError(msg) - - return results.get('feature_ext'), results.get('classifier') - - def _serialize_classifiers(self, feature_ext, classifier): - """ - Serialize the classifier objects. - - Args: - feature_extractor (ease.feature_extractor.FeatureExtractor) - classifier (sklearn.ensemble.GradientBoostingClassifier) - - Returns: - dict containing the pickled classifiers - - Raises: - TrainingError: Could not serialize the classifiers. - - """ - try: - return { - 'feature_extractor': base64.b64encode(pickle.dumps(feature_ext)), - 'score_classifier': base64.b64encode(pickle.dumps(classifier)), - } - except Exception as ex: - msg = ( - u"An error occurred while serializing the classifiers " - u"created by EASE: {ex}" - ).format(ex=ex) - raise TrainingError(msg) - - def _deserialize_classifiers(self, classifier_data): - """ - Deserialize the classifier objects. - - Args: - classifier_data (dict): The serialized classifiers. - - Returns: - tuple of `(feature_extractor, score_classifier)` - - Raises: - InvalidClassifier - - """ - if not isinstance(classifier_data, dict): - raise InvalidClassifier("Classifier must be a dictionary.") - - try: - classifier_str = classifier_data.get('feature_extractor').encode('utf-8') - feature_extractor = pickle.loads(base64.b64decode(classifier_str)) - except Exception as ex: - msg = ( - u"An error occurred while deserializing the " - u"EASE feature extractor: {ex}" - ).format(ex=ex) - raise InvalidClassifier(msg) - - try: - score_classifier_str = classifier_data.get('score_classifier').encode('utf-8') - score_classifier = pickle.loads(base64.b64decode(score_classifier_str)) - except Exception as ex: - msg = ( - u"An error occurred while deserializing the " - u"EASE score classifier: {ex}" - ).format(ex=ex) - raise InvalidClassifier(msg) - - return feature_extractor, score_classifier diff --git a/openassessment/assessment/worker/grading.py b/openassessment/assessment/worker/grading.py deleted file mode 100644 index 37440e3f92..0000000000 --- a/openassessment/assessment/worker/grading.py +++ /dev/null @@ -1,338 +0,0 @@ -""" -Asynchronous tasks for grading essays using text classifiers. -""" - -import datetime -from celery import task -from django.db import DatabaseError -from django.conf import settings -from celery.utils.log import get_task_logger -from dogapi import dog_stats_api -from openassessment.assessment.api import ai_worker as ai_worker_api -from openassessment.assessment.errors import ( - AIError, AIGradingInternalError, AIReschedulingInternalError, ANTICIPATED_CELERY_ERRORS -) -from .algorithm import AIAlgorithm, AIAlgorithmError -from openassessment.assessment.models.ai import AIGradingWorkflow - -MAX_RETRIES = 2 - -logger = get_task_logger(__name__) - -# If the Django settings define a low-priority queue, use that. -# Otherwise, use the default queue. -RESCHEDULE_TASK_QUEUE = getattr(settings, 'LOW_PRIORITY_QUEUE', None) - - -@task(max_retries=MAX_RETRIES) # pylint: disable=E1102 -@dog_stats_api.timed('openassessment.assessment.ai.grade_essay.time') -def grade_essay(workflow_uuid): - """ - Asynchronous task to grade an essay using a text classifier - (trained using a supervised ML algorithm). - - If the task could not be completed successfully, - it will be retried a few times; if it continues to fail, - it is left incomplete. Incomplate tasks can be rescheduled - manually through the AI API. - - Args: - workflow_uuid (str): The UUID of the workflow associated - with this grading task. - - Returns: - None - - Raises: - AIError: An error occurred while making an AI worker API call. - AIAlgorithmError: An error occurred while retrieving or using an AI algorithm. - - """ - # Short-circuit if the workflow is already marked complete - # This is an optimization, but grading tasks could still - # execute multiple times depending on when they get picked - # up by workers and marked complete. - try: - if ai_worker_api.is_grading_workflow_complete(workflow_uuid): - return - except AIError: - msg = ( - u"An unexpected error occurred while checking the " - u"completion of grading workflow with UUID {uuid}" - ).format(uuid=workflow_uuid) - logger.exception(msg) - raise grade_essay.retry() - - # Retrieve the task parameters - try: - params = ai_worker_api.get_grading_task_params(workflow_uuid) - essay_text = params['essay_text'] - classifier_set = params['classifier_set'] - algorithm_id = params['algorithm_id'] - valid_scores = params['valid_scores'] - except (AIError, KeyError): - msg = ( - u"An error occurred while retrieving the AI grading task " - u"parameters for the workflow with UUID {}" - ).format(workflow_uuid) - logger.exception(msg) - raise grade_essay.retry() - - # Validate that the we have valid scores for each criterion - for criterion_name in classifier_set.keys(): - msg = None - if criterion_name not in valid_scores: - msg = ( - u"Could not find {criterion} in the list of valid scores " - u"for grading workflow with UUID {uuid}" - ).format(criterion=criterion_name, uuid=workflow_uuid) - elif len(valid_scores[criterion_name]) == 0: - msg = ( - u"Valid scores for {criterion} is empty for " - u"grading workflow with UUID {uuid}" - ).format(criterion=criterion_name, uuid=workflow_uuid) - if msg: - logger.exception(msg) - raise AIGradingInternalError(msg) - - # Retrieve the AI algorithm - try: - algorithm = AIAlgorithm.algorithm_for_id(algorithm_id) - except AIAlgorithmError: - msg = ( - u"An error occurred while retrieving " - u"the algorithm ID (grading workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise grade_essay.retry() - - # Use the algorithm to evaluate the essay for each criterion - # Provide an in-memory cache so the algorithm can re-use - # results for multiple rubric criteria. - try: - cache = dict() - scores_by_criterion = { - criterion_name: _closest_valid_score( - algorithm.score(essay_text, classifier, cache), - valid_scores[criterion_name] - ) - for criterion_name, classifier in classifier_set.iteritems() - } - except AIAlgorithmError: - msg = ( - u"An error occurred while scoring essays using " - u"an AI algorithm (worker workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise grade_essay.retry() - - # Create the assessment and mark the workflow complete - try: - ai_worker_api.create_assessment(workflow_uuid, scores_by_criterion) - except AIError: - msg = ( - u"An error occurred while creating assessments " - u"for the AI grading workflow with UUID {uuid}. " - u"The assessment scores were: {scores}" - ).format(uuid=workflow_uuid, scores=scores_by_criterion) - logger.exception(msg) - raise grade_essay.retry() - - -@task(queue=RESCHEDULE_TASK_QUEUE, max_retries=MAX_RETRIES) # pylint: disable=E1102 -@dog_stats_api.timed('openassessment.assessment.ai.reschedule_grading_tasks.time') -def reschedule_grading_tasks(course_id, item_id): - """ - Reschedules all incomplete grading workflows with the specified parameters. - - Args: - course_id (unicode): The course item that we will be rerunning the rescheduling on. - item_id (unicode): The item that the rescheduling will be running on - - Raises: - AIReschedulingInternalError - AIGradingInternalError - """ - - # Logs the start of the rescheduling process and records the start time so that total time can be calculated later. - _log_start_reschedule_grading(course_id=course_id, item_id=item_id) - start_time = datetime.datetime.now() - - # Finds all incomplete grading workflows - try: - grading_workflows = AIGradingWorkflow.get_incomplete_workflows(course_id, item_id) - except (DatabaseError, AIGradingWorkflow.DoesNotExist) as ex: - msg = ( - u"An unexpected error occurred while retrieving all incomplete " - u"grading tasks for course_id: {cid} and item_id: {iid}: {ex}" - ).format(cid=course_id, iid=item_id, ex=ex) - logger.exception(msg) - raise AIReschedulingInternalError(msg) - - # Notes whether or not one or more operations failed. If they did, the process of rescheduling will be retried. - failures = 0 - - # A dictionary mapping tuples of (rubric, algorithm_id) to completed classifier sets. Used to avoid repeated - # queries which will return the same value. This loop implements a memoization of the the query. - maintained_classifiers = {} - - # Try to grade all incomplete grading workflows - for workflow in grading_workflows: - - # We will always go through the process of finding the most recent set of classifiers for an - # incomplete grading workflow. The rationale for this is that if we are ever rescheduling - # grading, we likely had classifiers which were not working. This way, we always take the last - # completed set. - - # Note that this solution will lead to failure if "Train Classifiers" and "Refinish Grading Tasks" - # are called in rapid succession. This is part of the reason this button is in the admin view. - - # Tries to find a set of classifiers that are already defined in our maintained_classifiers based on a - # description of the workflow in the form of a tuple (rubric, course_id, item_id, algorithm_id) - workflow_description = (workflow.rubric, course_id, item_id, workflow.algorithm_id) - found_classifiers = maintained_classifiers.get(workflow_description) - - # If no set of classifiers is found, we perform the query to try to find them. We take the most recent - # and add it to our dictionary of maintained classifiers for future reference. - if found_classifiers is None: - try: - found = workflow.assign_most_recent_classifier_set() - if found: - found_classifiers = workflow.classifier_set - maintained_classifiers[workflow_description] = found_classifiers - else: - msg = u"No applicable classifiers yet exist for essay with uuid='{}'".format(workflow.uuid) - logger.log(msg) - except DatabaseError as ex: - msg = ( - u"A Database error occurred while trying to assign classifiers to an essay with uuid='{id}'" - ).format(id=workflow.uuid) - logger.exception(msg) - - # If we found classifiers in our memoized lookup dictionary, we assign them and save. - else: - workflow.classifier_set = found_classifiers - try: - workflow.save() - logger.info( - u"Classifiers were successfully assigned to grading workflow with uuid={}".format(workflow.uuid) - ) - except DatabaseError as ex: - msg = ( - u"A Database error occurred while trying to save classifiers to an essay with uuid='{id}'" - ).format(id=workflow.uuid) - logger.exception(msg) - - if found_classifiers is not None: - - # Now we should (unless we had an exception above) have a classifier set. - # Try to schedule the grading - try: - grade_essay.apply_async(args=[workflow.uuid]) - logger.info( - u"Rescheduling of grading was successful for grading workflow with uuid='{}'".format(workflow.uuid) - ) - except ANTICIPATED_CELERY_ERRORS as ex: - msg = ( - u"An error occurred while try to grade essay with uuid='{id}': {ex}" - ).format(id=workflow.uuid, ex=ex) - logger.exception(msg) - failures += 1 - - # If we couldn't assign classifiers, we failed. - else: - failures += 1 - - # Logs the data from our rescheduling attempt - time_delta = datetime.datetime.now() - start_time - _log_complete_reschedule_grading( - course_id=course_id, item_id=item_id, seconds=time_delta.total_seconds(), success=(failures == 0) - ) - - # If one or more of these failed, we want to retry rescheduling. Note that this retry is executed in such a way - # that if it fails, an AIGradingInternalError will be raised with the number of failures on the last attempt (i.e. - # the total number of workflows matching these critera that still have left to be graded). - if failures > 0: - try: - raise AIGradingInternalError( - u"In an attempt to reschedule grading workflows, there were {} failures.".format(failures) - ) - except AIGradingInternalError as ex: - raise reschedule_grading_tasks.retry() - - -def _closest_valid_score(score, valid_scores): - """ - Return the closest valid score for a given score. - This is necessary, since rubric scores may be non-contiguous. - - Args: - score (int or float): The score assigned by the algorithm. - valid_scores (list of int): Valid scores for this criterion, - assumed to be sorted in ascending order. - - Returns: - int - - """ - # If the score is already valid, return it - if score in valid_scores: - return score - - # Otherwise, find the closest score in the list. - closest = valid_scores[0] - delta = abs(score - closest) - for valid in valid_scores[1:]: - new_delta = abs(score - valid) - if new_delta < delta: - closest = valid - delta = new_delta - return closest - - -def _log_start_reschedule_grading(course_id=None, item_id=None): - """ - Sends data about the rescheduling_grading task to datadog - - Args: - course_id (unicode): the course id to associate with the log start - item_id (unicode): the item id to tag with the log start - """ - tags = [ - u"course_id:{}".format(course_id), - u"item_id:{}".format(item_id), - ] - dog_stats_api.increment('openassessment.assessment.ai_task.AIRescheduleGrading.scheduled_count', tags=tags) - - msg = u"Rescheduling of incomplete grading tasks began for course_id={cid} and item_id={iid}" - logger.info(msg.format(cid=course_id, iid=item_id)) - - -def _log_complete_reschedule_grading(course_id=None, item_id=None, seconds=-1, success=False): - """ - Sends the total time the rescheduling of grading tasks took to datadog - (Just the time taken to reschedule tasks, not the time nescessary to complete them) - Note that this function may be invoked multiple times per call to reschedule_grading_tasks, - because the time for EACH ATTEMPT is taken (i.e. if we fail (by error) to schedule grading once, - we log the time elapsed before trying again.) - - Args: - course_id (unicode): the course_id to tag the task with - item_id (unicode): the item_id to tag the task with - seconds (int): the number of seconds that elapsed during the rescheduling task. - success (bool): indicates whether or not all attempts to reschedule were successful - """ - tags = [ - u"course_id:{}".format(course_id), - u"item_id:{}".format(item_id), - u"success:{}".format(success) - ] - - dog_stats_api.histogram('openassessment.assessment.ai_task.AIRescheduleGrading.turnaround_time', seconds, tags=tags) - dog_stats_api.increment('openassessment.assessment.ai_task.AIRescheduleGrading.completed_count', tags=tags) - - msg = u"Rescheduling of incomplete grading tasks for course_id={cid} and item_id={iid} completed in {s} seconds." - if not success: - msg += u" At least one grading task failed due to internal error." - msg.format(cid=course_id, iid=item_id, s=seconds) - logger.info(msg) diff --git a/openassessment/assessment/worker/training.py b/openassessment/assessment/worker/training.py deleted file mode 100644 index 6e600db48a..0000000000 --- a/openassessment/assessment/worker/training.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Asynchronous tasks for training classifiers from examples. -""" -import datetime -from collections import defaultdict -from celery import task -from celery.utils.log import get_task_logger -from dogapi import dog_stats_api -from django.conf import settings -from django.db import DatabaseError -from openassessment.assessment.api import ai_worker as ai_worker_api -from openassessment.assessment.errors import AIError, ANTICIPATED_CELERY_ERRORS -from .algorithm import AIAlgorithm, AIAlgorithmError -from .grading import reschedule_grading_tasks -from openassessment.assessment.errors.ai import AIGradingInternalError -from openassessment.assessment.models.ai import AITrainingWorkflow - -MAX_RETRIES = 2 - -logger = get_task_logger(__name__) - -# If the Django settings define a low-priority queue, use that. -# Otherwise, use the default queue. -TRAINING_TASK_QUEUE = getattr(settings, 'LOW_PRIORITY_QUEUE', None) -RESCHEDULE_TASK_QUEUE = getattr(settings, 'LOW_PRIORITY_QUEUE', None) - - -class InvalidExample(Exception): - """ - The example retrieved from the AI API had an invalid format. - """ - def __init__(self, example_dict, msg): - err_msg = u"Training example \"{example}\" is not valid: {msg}".format( - example=example_dict, - msg=msg - ) - super(InvalidExample, self).__init__(err_msg) - - -@task(queue=TRAINING_TASK_QUEUE, max_retries=MAX_RETRIES) # pylint: disable=E1102 -@dog_stats_api.timed('openassessment.assessment.ai.train_classifiers.time') -def train_classifiers(workflow_uuid): - """ - Asynchronous task to train classifiers for AI grading. - This task uses the AI API to retrieve task parameters - (algorithm ID and training examples) and upload - the trained classifiers. - - If the task could not be completed successfully, - it is retried a few times. If it continues to fail, - it is left incomplete. Since the AI API tracks all - training tasks in the database, incomplete tasks - can always be rescheduled manually later. - - Args: - workflow_uuid (str): The UUID of the workflow associated - with this training task. - - Returns: - None - - Raises: - AIError: An error occurred during a request to the AI API. - AIAlgorithmError: An error occurred while training the AI classifiers. - InvalidExample: The training examples provided by the AI API were not valid. - - """ - # Short-circuit if the workflow is already marked complete - # This is an optimization, but training tasks could still - # execute multiple times depending on when they get picked - # up by workers and marked complete. - try: - if ai_worker_api.is_training_workflow_complete(workflow_uuid): - return - except AIError: - msg = ( - u"An unexpected error occurred while checking the " - u"completion of training workflow with UUID {uuid}" - ).format(uuid=workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - - # Retrieve task parameters - try: - params = ai_worker_api.get_training_task_params(workflow_uuid) - examples = params['training_examples'] - algorithm_id = params['algorithm_id'] - course_id = params['course_id'] - item_id = params['item_id'] - except (AIError, KeyError): - msg = ( - u"An error occurred while retrieving AI training " - u"task parameters for the workflow with UUID {}" - ).format(workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - - # Retrieve the ML algorithm to use for training - # (based on task params and worker configuration) - try: - algorithm = AIAlgorithm.algorithm_for_id(algorithm_id) - except AIAlgorithmError: - msg = ( - u"An error occurred while loading the " - u"AI algorithm (training workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - except AIError: - msg = ( - u"An error occurred while retrieving " - u"the algorithm ID (training workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - - # Train a classifier for each criterion - # The AIAlgorithm subclass is responsible for ensuring that - # the trained classifiers are JSON-serializable. - try: - classifier_set = { - criterion_name: algorithm.train_classifier(examples_dict) - for criterion_name, examples_dict - in _examples_by_criterion(examples).iteritems() - } - except InvalidExample: - msg = ( - u"Training example format was not valid " - u"(training workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - except AIAlgorithmError: - msg = ( - u"An error occurred while training AI classifiers " - u"(training workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - - # Upload the classifiers - # (implicitly marks the workflow complete) - try: - ai_worker_api.create_classifiers(workflow_uuid, classifier_set) - except AIError: - msg = ( - u"An error occurred while uploading trained classifiers " - u"(training workflow UUID {})" - ).format(workflow_uuid) - logger.exception(msg) - raise train_classifiers.retry() - - # Upon successful completion of the creation of classifiers, we will try to automatically schedule any - # grading tasks for the same item. - try: - reschedule_grading_tasks.apply_async(args=[course_id, item_id]) - except AIGradingInternalError as ex: - msg = ( - u"An error occured while trying to regrade all ungraded assignments" - u"after classifiers were trained successfully: {}" - ).format(ex) - logger.exception(msg) - # Here we don't retry, because they will already retry once in the grading task. - raise - - -@task(queue=RESCHEDULE_TASK_QUEUE, max_retries=MAX_RETRIES) #pylint: disable=E1102 -@dog_stats_api.timed('openassessment.assessment.ai.reschedule_training_tasks.time') -def reschedule_training_tasks(course_id, item_id): - """ - Reschedules all incomplete training tasks - - Args: - course_id (unicode): The course that we are going to search for unfinished training workflows - item_id (unicode): The specific item within that course that we will reschedule unfinished workflows for - - Raises: - AIReschedulingInternalError - DatabaseError - """ - # Starts logging the details of the rescheduling - _log_start_reschedule_training(course_id=course_id, item_id=item_id) - start_time = datetime.datetime.now() - - # Run a query to find the incomplete training workflows - try: - training_workflows = AITrainingWorkflow.get_incomplete_workflows(course_id, item_id) - except (DatabaseError, AITrainingWorkflow.DoesNotExist) as ex: - msg = ( - u"An unexpected error occurred while retrieving all incomplete " - u"training tasks for course_id: {cid} and item_id: {iid}: {ex}" - ).format(cid=course_id, iid=item_id, ex=ex) - logger.exception(msg) - raise reschedule_training_tasks.retry() - - # Tries to train every workflow that has not completed. - for target_workflow in training_workflows: - try: - train_classifiers.apply_async(args=[target_workflow.uuid]) - logger.info( - u"Rescheduling of training was successful for workflow with uuid{}".format(target_workflow.uuid) - ) - except ANTICIPATED_CELERY_ERRORS as ex: - msg = ( - u"An unexpected error occurred while scheduling the task for training workflow with UUID {id}: {ex}" - ).format(id=target_workflow.uuid, ex=ex) - logger.exception(msg) - - time_delta = datetime.datetime.now() - start_time - _log_complete_reschedule_training( - course_id=course_id, item_id=item_id, seconds=time_delta.total_seconds(), success=False - ) - raise reschedule_training_tasks.retry() - - # Logs the total time to reschedule all training of classifiers if not logged beforehand by exception. - time_delta = datetime.datetime.now() - start_time - _log_complete_reschedule_training( - course_id=course_id, item_id=item_id, seconds=time_delta.total_seconds(), success=True - ) - -def _examples_by_criterion(examples): - """ - Transform the examples returned by the AI API into our internal format. - - Args: - examples (list): Training examples of the form returned by the AI API. - Each element of the list should be a dictionary with keys - 'text' (the essay text) and 'scores' (a dictionary mapping - criterion names to numeric scores). - - Returns: - dict: keys are the criteria names, and each value is list of `AIAlgorithm.ExampleEssay`s - - Raises: - InvalidExample: The provided training examples are not in a valid format. - - """ - internal_examples = defaultdict(list) - prev_criteria = None - - for example_dict in examples: - # Check that the example contains the expected keys - try: - scores_dict = example_dict['scores'] - text = unicode(example_dict['text']) - except KeyError: - raise InvalidExample(example_dict, u'Example dict must have keys "scores" and "text"') - - # Check that the criteria names are consistent across examples - if prev_criteria is None: - prev_criteria = set(scores_dict.keys()) - else: - if prev_criteria != set(scores_dict.keys()): - msg = ( - u"Example criteria do not match " - u"the previous example: {criteria}" - ).format(criteria=prev_criteria) - raise InvalidExample(example_dict, msg) - - for criterion_name, score in scores_dict.iteritems(): - try: - score = int(score) - except ValueError: - raise InvalidExample(example_dict, u"Example score is not an integer") - else: - internal_ex = AIAlgorithm.ExampleEssay(text, score) - internal_examples[criterion_name].append(internal_ex) - return internal_examples - - -def _log_start_reschedule_training(course_id=None, item_id=None): - """ - Sends data about the rescheduling_training task to datadog - - Args: - course_id (unicode): the course id to associate with the log start - item_id (unicode): the item id to tag with the log start - """ - tags = [ - u"course_id:{}".format(course_id), - u"item_id:{}".format(item_id), - ] - dog_stats_api.increment('openassessment.assessment.ai_task.AIRescheduleTraining.scheduled_count', tags) - - msg = u"Rescheduling of incomplete training tasks began for course_id={cid} and item_id={iid}" - logger.info(msg.format(cid=course_id, iid=item_id)) - - -def _log_complete_reschedule_training(course_id=None, item_id=None, seconds=-1, success=False): - """ - Sends the total time the rescheduling of training tasks took to datadog - Note that this function may be invoked multiple times per call to reschedule_training_tasks, - because the time for EACH ATTEMPT is taken (i.e. if we fail (by error) to schedule training once, - we log the time elapsed before trying again.) - - Args: - course_id (unicode): the course_id to tag the task with - item_id (unicode): the item_id to tag the task with - seconds (int): the number of seconds that elapsed during the rescheduling task. - success (bool): indicates whether or not all attempts to reschedule were successful - """ - tags = [ - u"course_id:{}".format(course_id), - u"item_id:{}".format(item_id), - u"success:{}".format(success) - ] - - dog_stats_api.histogram('openassessment.assessment.ai_task.AIRescheduleTraining.turnaround_time', seconds,tags) - dog_stats_api.increment('openassessment.assessment.ai_task.AIRescheduleTraining.completed_count', tags) - - msg = u"Rescheduling of incomplete training tasks for course_id={cid} and item_id={iid} completed in {s} seconds." - if not success: - msg += u" At least one rescheduling task failed due to internal error." - msg.format(cid=course_id, iid=item_id, s=seconds) - logger.info(msg) diff --git a/openassessment/data.py b/openassessment/data.py index 8389ced349..25eed6fd1c 100755 --- a/openassessment/data.py +++ b/openassessment/data.py @@ -1,14 +1,15 @@ """ Aggregate data for openassessment. """ +from collections import defaultdict import csv import json -from collections import defaultdict from django.conf import settings -from submissions import api as sub_api + +from openassessment.assessment.models import Assessment, AssessmentFeedback, AssessmentPart from openassessment.workflow.models import AssessmentWorkflow -from openassessment.assessment.models import Assessment, AssessmentPart, AssessmentFeedback +from submissions import api as sub_api class CsvWriter(object): diff --git a/openassessment/fileupload/backends/base.py b/openassessment/fileupload/backends/base.py index ce9b899641..1d26c0234a 100644 --- a/openassessment/fileupload/backends/base.py +++ b/openassessment/fileupload/backends/base.py @@ -2,8 +2,7 @@ from django.conf import settings -from ..exceptions import FileUploadInternalError -from ..exceptions import FileUploadRequestError +from ..exceptions import FileUploadInternalError, FileUploadRequestError class Settings(object): diff --git a/openassessment/fileupload/backends/django_storage.py b/openassessment/fileupload/backends/django_storage.py index 5a6c61b36d..8711fb4a81 100644 --- a/openassessment/fileupload/backends/django_storage.py +++ b/openassessment/fileupload/backends/django_storage.py @@ -1,9 +1,10 @@ import os -from .base import BaseBackend -from django.core.files.storage import default_storage from django.core.files.base import ContentFile -from django.core.urlresolvers import reverse +from django.core.files.storage import default_storage +from django.core.urlresolvers import reverse_lazy + +from .base import BaseBackend class Backend(BaseBackend): @@ -14,7 +15,7 @@ def get_upload_url(self, key, content_type): """ Return the URL pointing to the ORA2 django storage upload endpoint. """ - return reverse("openassessment-django-storage", kwargs={'key': key}) + return reverse_lazy("openassessment-django-storage", kwargs={'key': key}) def get_download_url(self, key): """ diff --git a/openassessment/fileupload/backends/filesystem.py b/openassessment/fileupload/backends/filesystem.py index 8606a6849e..afe612f456 100644 --- a/openassessment/fileupload/backends/filesystem.py +++ b/openassessment/fileupload/backends/filesystem.py @@ -1,11 +1,11 @@ -from .base import BaseBackend -from .. import exceptions - from django.conf import settings import django.core.cache -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse_lazy from django.utils.encoding import smart_text +from .. import exceptions +from .base import BaseBackend + class Backend(BaseBackend): """ @@ -47,7 +47,7 @@ def remove_file(self, key): def _get_url(self, key): key_name = self._get_key_name(key) - url = reverse("openassessment-filesystem-storage", kwargs={'key': key_name}) + url = reverse_lazy("openassessment-filesystem-storage", kwargs={'key': key_name}) return url diff --git a/openassessment/fileupload/backends/s3.py b/openassessment/fileupload/backends/s3.py index 3608339833..880ed00a64 100644 --- a/openassessment/fileupload/backends/s3.py +++ b/openassessment/fileupload/backends/s3.py @@ -1,9 +1,12 @@ -import boto import logging + +import boto3 + from django.conf import settings -from .base import BaseBackend from ..exceptions import FileUploadInternalError +from .base import BaseBackend + logger = logging.getLogger("openassessment.fileupload.api") @@ -12,15 +15,16 @@ class Backend(BaseBackend): def get_upload_url(self, key, content_type): bucket_name, key_name = self._retrieve_parameters(key) try: - conn = _connect_to_s3() - upload_url = conn.generate_url( - expires_in=self.UPLOAD_URL_TIMEOUT, - method='PUT', - bucket=bucket_name, - key=key_name, - headers={'Content-Length': '5242880', 'Content-Type': content_type} + client = _connect_to_s3() + return client.generate_presigned_url( + ExpiresIn=self.UPLOAD_URL_TIMEOUT, + ClientMethod='put_object', + Params={ + 'Bucket': bucket_name, + 'Key': key_name + }, + HttpMethod="PUT" ) - return upload_url except Exception as ex: logger.exception( u"An internal exception occurred while generating an upload URL." @@ -30,10 +34,16 @@ def get_upload_url(self, key, content_type): def get_download_url(self, key): bucket_name, key_name = self._retrieve_parameters(key) try: - conn = _connect_to_s3() - bucket = conn.get_bucket(bucket_name) - s3_key = bucket.get_key(key_name) - return s3_key.generate_url(expires_in=self.DOWNLOAD_URL_TIMEOUT) if s3_key else "" + client = _connect_to_s3() + return client.generate_presigned_url( + ExpiresIn=self.DOWNLOAD_URL_TIMEOUT, + ClientMethod='get_object', + Params={ + 'Bucket': bucket_name, + 'Key': key_name + }, + HttpMethod="GET" + ) except Exception as ex: logger.exception( u"An internal exception occurred while generating a download URL." @@ -42,15 +52,16 @@ def get_download_url(self, key): def remove_file(self, key): bucket_name, key_name = self._retrieve_parameters(key) - conn = _connect_to_s3() - bucket = conn.get_bucket(bucket_name) - s3_key = bucket.get_key(key_name) - - if s3_key: - bucket.delete_key(s3_key) + client = _connect_to_s3() + resp = client.delete_objects( + Bucket=bucket_name, + Delete={ + 'Objects': [{'Key':key_name}] + } + ) + if 'Deleted' in resp and any(key_name == deleted_dict['Key'] for deleted_dict in resp['Deleted']): return True - else: - return False + return False def _connect_to_s3(): @@ -65,7 +76,8 @@ def _connect_to_s3(): aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) - return boto.connect_s3( + return boto3.client( + 's3', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key ) diff --git a/openassessment/fileupload/backends/swift.py b/openassessment/fileupload/backends/swift.py index 566204b1b4..4a222f2dbe 100644 --- a/openassessment/fileupload/backends/swift.py +++ b/openassessment/fileupload/backends/swift.py @@ -12,15 +12,21 @@ ''' import logging -from django.conf import settings -import swiftclient import urlparse + import requests +import swiftclient + +from django.conf import settings -from .base import BaseBackend from ..exceptions import FileUploadInternalError +from .base import BaseBackend + logger = logging.getLogger("openassessment.fileupload.api") +# prefix paths with current version, in case we need to roll it at some point +SWIFT_BACKEND_VERSION = 1 + class Backend(BaseBackend): """ @@ -32,10 +38,11 @@ def get_upload_url(self, key, content_type): key, url = get_settings() try: temp_url = swiftclient.utils.generate_temp_url( - path='%s/%s/%s' % (url.path, bucket_name, key_name), + path='/v%s%s/%s/%s' % (SWIFT_BACKEND_VERSION, url.path, bucket_name, key_name), key=key, method='PUT', - seconds=self.UPLOAD_URL_TIMEOUT) + seconds=self.UPLOAD_URL_TIMEOUT + ) return '%s://%s%s' % (url.scheme, url.netloc, temp_url) except Exception as ex: logger.exception( @@ -48,10 +55,11 @@ def get_download_url(self, key): key, url = get_settings() try: temp_url = swiftclient.utils.generate_temp_url( - path='%s/%s/%s' % (url.path, bucket_name, key_name), + path='/v%s%s/%s/%s' % (SWIFT_BACKEND_VERSION, url.path, bucket_name, key_name), key=key, method='GET', - seconds=self.DOWNLOAD_URL_TIMEOUT) + seconds=self.DOWNLOAD_URL_TIMEOUT + ) download_url = '%s://%s%s' % (url.scheme, url.netloc, temp_url) response = requests.get(download_url) return download_url if response.status_code == 200 else "" diff --git a/openassessment/fileupload/tests/test_api.py b/openassessment/fileupload/tests/test_api.py index 02f5c32307..8b22358ee6 100644 --- a/openassessment/fileupload/tests/test_api.py +++ b/openassessment/fileupload/tests/test_api.py @@ -1,29 +1,25 @@ # -*- coding: utf-8 -*- -import boto -from boto.s3.key import Key -import ddt - import json -from mock import patch, Mock import os import shutil import tempfile import urllib from urlparse import urlparse +import boto3 +import ddt +from mock import Mock, patch +from moto import mock_s3 +from nose.tools import raises + from django.conf import settings +from django.contrib.auth import get_user_model +from django.core.urlresolvers import reverse_lazy from django.test import TestCase from django.test.utils import override_settings -from django.core.urlresolvers import reverse -from django.contrib.auth import get_user_model - -from moto import mock_s3 -from mock import patch -from nose.tools import raises -from openassessment.fileupload import api -from openassessment.fileupload import exceptions +from openassessment.fileupload import api, exceptions, urls from openassessment.fileupload import views_filesystem as views from openassessment.fileupload.backends.base import Settings as FileUploadSettings from openassessment.fileupload.backends.filesystem import get_cache as get_filesystem_cache @@ -39,8 +35,8 @@ class TestFileUploadService(TestCase): FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" ) def test_get_upload_url(self): - conn = boto.connect_s3() - conn.create_bucket('mybucket') + s3 = boto3.resource('s3') + s3.create_bucket(Bucket='mybucket') uploadUrl = api.get_upload_url("foo", "bar") self.assertIn("https://mybucket.s3.amazonaws.com/submissions_attachments/foo", uploadUrl) @@ -51,11 +47,9 @@ def test_get_upload_url(self): FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" ) def test_get_download_url(self): - conn = boto.connect_s3() - bucket = conn.create_bucket('mybucket') - key = Key(bucket) - key.key = "submissions_attachments/foo" - key.set_contents_from_string("How d'ya do?") + s3 = boto3.resource('s3') + s3.create_bucket(Bucket='mybucket') + s3.Object('mybucket', 'submissions_attachments/foo').put(Body="How d'ya do?") downloadUrl = api.get_download_url("foo") self.assertIn("https://mybucket.s3.amazonaws.com/submissions_attachments/foo", downloadUrl) @@ -66,11 +60,9 @@ def test_get_download_url(self): FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" ) def test_remove_file(self): - conn = boto.connect_s3() - bucket = conn.create_bucket('mybucket') - key = Key(bucket) - key.key = "submissions_attachments/foo" - key.set_contents_from_string("Test") + s3 = boto3.resource('s3') + s3.create_bucket(Bucket='mybucket') + s3.Object('mybucket', 'submissions_attachments/foo').put(Body="Test") result = api.remove_file("foo") self.assertTrue(result) result = api.remove_file("foo") @@ -90,7 +82,7 @@ def test_get_upload_url_no_key(self): AWS_SECRET_ACCESS_KEY='bizbaz', FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" ) - @patch.object(boto, 'connect_s3') + @patch.object(boto3, 'client') @raises(exceptions.FileUploadInternalError) def test_get_upload_url_error(self, mock_s3): mock_s3.side_effect = Exception("Oh noes") @@ -102,7 +94,7 @@ def test_get_upload_url_error(self, mock_s3): AWS_SECRET_ACCESS_KEY='bizbaz', FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" ) - @patch.object(boto, 'connect_s3') + @patch.object(boto3, 'client') @raises(exceptions.FileUploadInternalError, mock_s3) def test_get_download_url_error(self, mock_s3): mock_s3.side_effect = Exception("Oh noes") @@ -272,7 +264,7 @@ def test_download_content_with_no_content_type(self): self.assertEqual('application/octet-stream', download_response["Content-Type"]) def test_upload_with_unauthorized_key(self): - upload_url = reverse("openassessment-filesystem-storage", kwargs={'key': self.key_name}) + upload_url = reverse_lazy("openassessment-filesystem-storage", kwargs={'key': self.key_name}) cache_before_request = get_filesystem_cache().get(self.key_name) upload_response = self.client.put(upload_url, data=self.content.read(), content_type=self.content_type) @@ -282,7 +274,7 @@ def test_upload_with_unauthorized_key(self): self.assertIsNone(cache_after_request) def test_download_url_with_unauthorized_key(self): - download_url = reverse("openassessment-filesystem-storage", kwargs={'key': self.key_name}) + download_url = reverse_lazy("openassessment-filesystem-storage", kwargs={'key': self.key_name}) views.save_to_file(self.key_name, "uploaded content") download_response = self.client.get(download_url) @@ -327,7 +319,7 @@ def _verify_url(self, url): result = urlparse(url) self.assertEqual(result.scheme, u'http') self.assertEqual(result.netloc, u'www.example.com:12345') - self.assertEqual(result.path, u'/bucket_name/submissions_attachments/foo') + self.assertEqual(result.path, u'/v1/bucket_name/submissions_attachments/foo') self.assertIn(result.params, 'temp_url_sig=') self.assertIn(result.params, 'temp_url_expires=') diff --git a/openassessment/fileupload/urls.py b/openassessment/fileupload/urls.py index fe58afb9fa..eb23bbd2d3 100644 --- a/openassessment/fileupload/urls.py +++ b/openassessment/fileupload/urls.py @@ -1,11 +1,8 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url -urlpatterns = patterns( - 'openassessment.fileupload.views_django_storage', - url(r'^django/(?P.+)/$', 'django_storage', name='openassessment-django-storage'), -) +from openassessment.fileupload import views_django_storage, views_filesystem -urlpatterns += patterns( - 'openassessment.fileupload.views_filesystem', - url(r'^(?P.+)/$', 'filesystem_storage', name='openassessment-filesystem-storage'), -) +urlpatterns = [ + url(r'^django/(?P.+)/$', views_django_storage.django_storage, name='openassessment-django-storage'), + url(r'^(?P.+)/$', views_filesystem.filesystem_storage, name='openassessment-filesystem-storage'), +] diff --git a/openassessment/fileupload/views_filesystem.py b/openassessment/fileupload/views_filesystem.py index efc48e2e21..f3d620393e 100644 --- a/openassessment/fileupload/views_filesystem.py +++ b/openassessment/fileupload/views_filesystem.py @@ -3,13 +3,13 @@ import os from django.conf import settings -from django.shortcuts import HttpResponse, Http404 +from django.shortcuts import Http404, HttpResponse from django.utils import timezone from django.views.decorators.http import require_http_methods from . import exceptions -from .backends.filesystem import is_upload_url_available, is_download_url_available from .backends.base import Settings +from .backends.filesystem import is_download_url_available, is_upload_url_available @require_http_methods(["PUT", "GET"]) diff --git a/openassessment/management/commands/collect_ora2_data.py b/openassessment/management/commands/collect_ora2_data.py index 9f688c8d31..0a3a7772df 100644 --- a/openassessment/management/commands/collect_ora2_data.py +++ b/openassessment/management/commands/collect_ora2_data.py @@ -7,7 +7,6 @@ Generates the same format as the instructor dashboard downloads. """ import csv -from optparse import make_option import os from django.core.management.base import BaseCommand, CommandError @@ -21,25 +20,34 @@ class Command(BaseCommand): """ help = ("Usage: collect_ora2_data --output-dir=") - args = "" - option_list = BaseCommand.option_list + ( - make_option('-o', '--output-dir', - action='store', dest='output_dir', default=None, - help="Write output to a directory rather than stdout"), - make_option('-n', '--file-name', - action='store', dest='file_name', default=None, - help="Write CSV file to the given name"), - ) + def add_arguments(self, parser): + parser.add_argument('course_id', nargs='+', type=unicode) + parser.add_argument( + '-o', + '--output-dir', + action='store', + dest='output_dir', + default=None, + help="Write output to a directory rather than stdout" + ) + parser.add_argument( + '-n', + '--file-name', + action='store', + dest='file_name', + default=None, + help="Write CSV file to the given name" + ) def handle(self, *args, **options): """ Run the command. """ - if not args: + if not options['course_id']: raise CommandError("Course ID must be specified to fetch data") - course_id = args[0] + course_id = options['course_id'] if options['file_name']: file_name = options['file_name'] diff --git a/openassessment/management/commands/create_oa_submissions.py b/openassessment/management/commands/create_oa_submissions.py index dd68cf594e..d3f387eb26 100644 --- a/openassessment/management/commands/create_oa_submissions.py +++ b/openassessment/management/commands/create_oa_submissions.py @@ -1,14 +1,17 @@ """ Create dummy submissions and assessments for testing. """ -from uuid import uuid4 import copy -from django.core.management.base import BaseCommand, CommandError +from uuid import uuid4 + import loremipsum -from submissions import api as sub_api -from openassessment.workflow import api as workflow_api + +from django.core.management.base import BaseCommand, CommandError + from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api +from openassessment.workflow import api as workflow_api +from submissions import api as sub_api STEPS = ['peer', 'self'] diff --git a/openassessment/management/commands/performance_test_for_submissions.py b/openassessment/management/commands/performance_test_for_submissions.py deleted file mode 100644 index 8ffe82be86..0000000000 --- a/openassessment/management/commands/performance_test_for_submissions.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -Gives the time taken by - find_active_assessments - get_submission_for_review - get_submission_for_over_grading - methods for particular set of workflows. -""" -import random -import datetime - -from django.core.management.base import BaseCommand - -from openassessment.assessment.models import PeerWorkflow - - -class Command(BaseCommand): - """ - Note the time taken by queries. - """ - - help = ("Test the performance for " - "find_active_assessments, " - "get_submission_for_review & " - "get_submission_for_over_grading" - "methods.") - - def __init__(self, *args, **kwargs): - super(Command, self).__init__(*args, **kwargs) - - def handle(self, *args, **options): - """ - Execute the command. - - Args: - None - """ - peer_workflow_count = PeerWorkflow.objects.filter(submission_uuid__isnull=False).count() - - peer_workflow_ids = [random.randint(1, peer_workflow_count) for num in range(100)] - peer_workflows = list(PeerWorkflow.objects.filter(id__in=peer_workflow_ids)) - - pw_dt_before = datetime.datetime.now() - - for peer_workflow in peer_workflows: - peer_workflow.find_active_assessments() - - pw_dt_after = datetime.datetime.now() - time_taken = pw_dt_after - pw_dt_before - - print "Time taken by (find_active_assessments) method Is: %s " % time_taken - - #### get_submission_for_review #### - - pw_dt_before = datetime.datetime.now() - - for peer_workflow in peer_workflows: - peer_workflow.get_submission_for_review(2) - - pw_dt_after = datetime.datetime.now() - time_taken = pw_dt_after - pw_dt_before - - print "Time taken by (get_submission_for_review) method Is: %s " % time_taken - - #### get_submission_for_over_grading #### - - pw_dt_before = datetime.datetime.now() - - for peer_workflow in peer_workflows: - peer_workflow.get_submission_for_over_grading() - - pw_dt_after = datetime.datetime.now() - time_taken = pw_dt_after - pw_dt_before - - print "Time taken by (get_submission_for_over_grading) method Is: %s " % time_taken diff --git a/openassessment/management/commands/simulate_ai_grading_error.py b/openassessment/management/commands/simulate_ai_grading_error.py deleted file mode 100644 index 591e2760ec..0000000000 --- a/openassessment/management/commands/simulate_ai_grading_error.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Simulate failure of the worker AI grading tasks. -When the workers fail to successfully complete AI grading, -the AI grading workflow in the database will never be marked complete. - -To simulate the error condition, therefore, we create incomplete -AI grading workflows without scheduling a grading task. - -To recover, a staff member can reschedule incomplete grading tasks. - -""" -from django.core.management.base import BaseCommand, CommandError -from submissions import api as sub_api -from openassessment.assessment.models import AIGradingWorkflow, AIClassifierSet -from openassessment.assessment.serializers import rubric_from_dict -from openassessment.assessment.worker.algorithm import AIAlgorithm - - -class Command(BaseCommand): - """ - Create submissions and AI incomplete grading workflows. - """ - - help = ( - u"Simulate failure of the worker AI grading tasks " - u"by creating incomplete AI grading workflows in the database." - ) - - args = ' ' - - RUBRIC_OPTIONS = [ - { - "order_num": 0, - "name": u"poor", - "explanation": u"Poor job!", - "points": 0, - }, - { - "order_num": 1, - "name": u"good", - "explanation": u"Good job!", - "points": 1, - } - ] - - RUBRIC = { - 'prompts': [{"description": u"Test prompt"}], - 'criteria': [ - { - "order_num": 0, - "name": u"vocabulary", - "prompt": u"Vocabulary", - "options": RUBRIC_OPTIONS - }, - { - "order_num": 1, - "name": u"grammar", - "prompt": u"Grammar", - "options": RUBRIC_OPTIONS - } - ] - } - - EXAMPLES = { - "vocabulary": [ - AIAlgorithm.ExampleEssay( - text=u"World Food Day is celebrated every year around the world on 16 October in honor " - u"of the date of the founding of the Food and Agriculture " - u"Organization of the United Nations in 1945.", - score=0 - ), - AIAlgorithm.ExampleEssay( - text=u"Since 1981, World Food Day has adopted a different theme each year " - u"in order to highlight areas needed for action and provide a common focus.", - score=1 - ), - ], - "grammar": [ - AIAlgorithm.ExampleEssay( - text=u"Most of the themes revolve around agriculture because only investment in agriculture ", - score=0 - ), - AIAlgorithm.ExampleEssay( - text=u"In spite of the importance of agriculture as the driving force " - u"in the economies of many developing countries, this " - u"vital sector is frequently starved of investment.", - score=1 - ) - ] - } - - STUDENT_ID = u'test_student' - ANSWER = {"text": 'test answer'} - - def handle(self, *args, **options): - """ - Execute the command. - - Args: - course_id (unicode): The ID of the course to create submissions/workflows in. - item_id (unicode): The ID of the problem in the course. - num_submissions (int): The number of submissions/workflows to create. - algorithm_id (unicode): The ID of the ML algorithm to use ("fake" or "ease") - - Raises: - CommandError - - """ - if len(args) < 4: - raise CommandError(u"Usage: simulate_ai_grading_error {}".format(self.args)) - - # Parse arguments - course_id = args[0].decode('utf-8') - item_id = args[1].decode('utf-8') - num_submissions = int(args[2]) - algorithm_id = args[3].decode('utf-8') - - # Create the rubric model - rubric = rubric_from_dict(self.RUBRIC) - - # Train classifiers - print u"Training classifiers using {algorithm_id}...".format(algorithm_id=algorithm_id) - algorithm = AIAlgorithm.algorithm_for_id(algorithm_id) - classifier_data = { - criterion_name: algorithm.train_classifier(example) - for criterion_name, example in self.EXAMPLES.iteritems() - } - print u"Successfully trained classifiers." - - # Create the classifier set - classifier_set = AIClassifierSet.create_classifier_set( - classifier_data, rubric, algorithm_id, course_id, item_id - ) - print u"Successfully created classifier set with id {}".format(classifier_set.pk) - - # Create submissions and grading workflows - for num in range(num_submissions): - student_item = { - 'course_id': course_id, - 'item_id': item_id, - 'item_type': 'openassessment', - 'student_id': "{base}_{num}".format(base=self.STUDENT_ID, num=num) - } - submission = sub_api.create_submission(student_item, self.ANSWER) - workflow = AIGradingWorkflow.start_workflow( - submission['uuid'], self.RUBRIC, algorithm_id - ) - workflow.classifier_set = classifier_set - workflow.save() - print u"{num}: Created incomplete grading workflow with UUID {uuid}".format( - num=num, uuid=workflow.uuid - ) diff --git a/openassessment/management/commands/upload_oa_data.py b/openassessment/management/commands/upload_oa_data.py index f78f538d92..a14a251eee 100644 --- a/openassessment/management/commands/upload_oa_data.py +++ b/openassessment/management/commands/upload_oa_data.py @@ -1,17 +1,19 @@ """ Generate CSV files for submission and assessment data, then upload to S3. """ -import sys +import datetime import os import os.path -import datetime import shutil -import tempfile +import sys import tarfile -import boto -from boto.s3.key import Key -from django.core.management.base import BaseCommand, CommandError +import tempfile + +import boto3 + from django.conf import settings +from django.core.management.base import BaseCommand, CommandError + from openassessment.data import CsvWriter @@ -135,16 +137,24 @@ def _upload(self, course_id, file_path, s3_bucket): # environment vars or configuration files instead. aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) - conn = boto.connect_s3( + client = boto3.client( + 's3', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key ) + bucket = client.create_bucket(Bucket=s3_bucket) - bucket = conn.get_bucket(s3_bucket) key_name = os.path.join(course_id, os.path.split(file_path)[1]) - key = Key(bucket=bucket, name=key_name) - key.set_contents_from_filename(file_path) - url = key.generate_url(self.URL_EXPIRATION_HOURS * 3600) + client.put_object(Bucket=s3_bucket, Key=key_name, Body=open(file_path, 'rb')) + url = client.generate_presigned_url( + ExpiresIn=self.URL_EXPIRATION_HOURS * 3600, + ClientMethod='get_object', + Params={ + 'Bucket': s3_bucket, + 'Key': key_name + }, + HttpMethod="GET" + ) # Store the key and url in the history self._history.append({'key': key_name, 'url': url}) diff --git a/openassessment/management/tests/test_create_oa_submissions.py b/openassessment/management/tests/test_create_oa_submissions.py index 8a6b5267db..79070007d1 100644 --- a/openassessment/management/tests/test_create_oa_submissions.py +++ b/openassessment/management/tests/test_create_oa_submissions.py @@ -2,11 +2,12 @@ Tests for the management command that creates dummy submissions. """ -from submissions import api as sub_api +from django.test import TestCase + from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api from openassessment.management.commands import create_oa_submissions -from django.test import TestCase +from submissions import api as sub_api class CreateSubmissionsTest(TestCase): diff --git a/openassessment/management/tests/test_simulate_ai_grading_error.py b/openassessment/management/tests/test_simulate_ai_grading_error.py deleted file mode 100644 index 2994f73b52..0000000000 --- a/openassessment/management/tests/test_simulate_ai_grading_error.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tests for the simulate AI grading error management command. -""" - -from django.test.utils import override_settings -from openassessment.test_utils import CacheResetTest -from openassessment.management.commands import simulate_ai_grading_error -from openassessment.assessment.models import AIGradingWorkflow -from openassessment.assessment.worker.grading import grade_essay - - -class SimulateAIGradingErrorTest(CacheResetTest): - """ - Tests for the simulate AI grading error management command. - """ - - COURSE_ID = u"TɘꙅT ↄoUᴙꙅɘ" - ITEM_ID = u"𝖙𝖊𝖘𝖙 𝖎𝖙𝖊𝖒" - NUM_SUBMISSIONS = 20 - - AI_ALGORITHMS = { - "fake": "openassessment.assessment.worker.algorithm.FakeAIAlgorithm" - } - - @override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS) - def test_simulate_ai_grading_error(self): - # Run the command - cmd = simulate_ai_grading_error.Command() - cmd.handle( - self.COURSE_ID.encode('utf-8'), - self.ITEM_ID.encode('utf-8'), - self.NUM_SUBMISSIONS, - "fake" - ) - - # Check that the correct number of incomplete workflows - # were created. These workflows should still have - # a classifier set, though, because otherwise they - # wouldn't have been scheduled for grading - # (that is, the submissions were made before classifier - # training completed). - incomplete_workflows = AIGradingWorkflow.objects.filter( - classifier_set__isnull=False, - completed_at__isnull=True - ) - num_errors = incomplete_workflows.count() - self.assertEqual(self.NUM_SUBMISSIONS, num_errors) - - # Verify that we can complete the workflows successfully - # (that is, make sure the classifier data is valid) - # We're calling a Celery task method here, - # but we're NOT using `apply_async`, so this will - # execute synchronously. - for workflow in incomplete_workflows: - grade_essay(workflow.uuid) - - # Now there should be no incomplete workflows - remaining_incomplete = AIGradingWorkflow.objects.filter( - classifier_set__isnull=False, - completed_at__isnull=True - ).count() - self.assertEqual(remaining_incomplete, 0) diff --git a/openassessment/management/tests/test_upload_oa_data.py b/openassessment/management/tests/test_upload_oa_data.py index f7a28bac8c..9c9a2018b3 100644 --- a/openassessment/management/tests/test_upload_oa_data.py +++ b/openassessment/management/tests/test_upload_oa_data.py @@ -2,12 +2,13 @@ """ Tests for management command that uploads submission/assessment data. """ -from StringIO import StringIO import tarfile -import boto + +import boto3 import moto -from openassessment.test_utils import CacheResetTest + from openassessment.management.commands import upload_oa_data +from openassessment.test_utils import CacheResetTest from openassessment.workflow import api as workflow_api from submissions import api as sub_api @@ -29,8 +30,8 @@ class UploadDataTest(CacheResetTest): @moto.mock_s3 def test_upload(self): # Create an S3 bucket using the fake S3 implementation - conn = boto.connect_s3() - conn.create_bucket(self.BUCKET_NAME) + s3 = boto3.resource('s3') + s3.create_bucket(Bucket=self.BUCKET_NAME) # Create some submissions to ensure that we cover # the progress indicator code. @@ -53,12 +54,10 @@ def test_upload(self): # Retrieve the uploaded file from the fake S3 implementation self.assertEqual(len(cmd.history), 1) - bucket = conn.get_all_buckets()[0] - key = bucket.get_key(cmd.history[0]['key']) - contents = StringIO(key.get_contents_as_string()) + s3.Object(self.BUCKET_NAME, cmd.history[0]['key']).download_file("tmp-test-file.tar.gz") # Expect that the contents contain all the expected CSV files - with tarfile.open(mode="r:gz", fileobj=contents) as tar: + with tarfile.open("tmp-test-file.tar.gz", mode="r:gz") as tar: file_sizes = { member.name: member.size for member in tar.getmembers() @@ -69,4 +68,4 @@ def test_upload(self): # Expect that we generated a URL for the bucket url = cmd.history[0]['url'] - self.assertIn("https://{}".format(self.BUCKET_NAME), url) + self.assertIn("https://s3.amazonaws.com/{}".format(self.BUCKET_NAME), url) diff --git a/openassessment/templates/openassessmentblock/edit/oa_edit.html b/openassessment/templates/openassessmentblock/edit/oa_edit.html index ab69d0d26b..efde4e24b5 100644 --- a/openassessment/templates/openassessmentblock/edit/oa_edit.html +++ b/openassessment/templates/openassessmentblock/edit/oa_edit.html @@ -181,11 +181,7 @@

{% trans "Rubric Change Impacts Settings

- {% if 'example_based_assessment' in editor_assessments_order %} - {% trans "In this assignment, you can include learner training, peer assessment, self assessment, example based assessment, and staff assessment steps. Select the steps that you want below, and then drag them into the order you want. If you include an example based assessment step, it must precede all other steps. If you include a learner training step, it must precede peer and self assessment steps. If you include a staff assessment step, it must be the final step. After you release an ORA assignment, you cannot change the type and number of assessment steps." %} - {% else %} - {% trans "In this assignment, you can include learner training, peer assessment, self assessment, and staff assessment steps. Select the steps that you want below, and then drag them into the order you want. If you include a learner training step, it must precede all other steps. If you include a staff assessment step, it must be the final step. After you release an ORA assignment, you cannot change the type and number of assessment steps." %} - {% endif %} + {% trans "In this assignment, you can include learner training, peer assessment, self assessment, and staff assessment steps. Select the steps that you want below, and then drag them into the order you want. If you include a learner training step, it must precede all other steps. If you include a staff assessment step, it must be the final step. After you release an ORA assignment, you cannot change the type and number of assessment steps." %}

    {% for assessment in editor_assessments_order %} diff --git a/openassessment/templates/openassessmentblock/edit/oa_edit_example_based_assessment.html b/openassessment/templates/openassessmentblock/edit/oa_edit_example_based_assessment.html deleted file mode 100644 index a1bd8b11f7..0000000000 --- a/openassessment/templates/openassessmentblock/edit/oa_edit_example_based_assessment.html +++ /dev/null @@ -1,22 +0,0 @@ -{% load i18n %} -{% spaceless %} -
  1. -
    -
    - - -
    -
    -

    - {% trans "An algorithm assesses learners' responses by comparing the responses to pre-assessed sample responses that the instructor provides."%} -

    -
    -

    - {% trans "Enter one or more sample responses that you've created, and then specify the options that you would choose for each criterion in your rubric. Note that you must add your rubric to the Rubric tab before you can complete this step." %} -

    - -
    -
    -
  2. -{% endspaceless %} diff --git a/openassessment/templates/openassessmentblock/staff_area/oa_staff_area.html b/openassessment/templates/openassessmentblock/staff_area/oa_staff_area.html index b3750ad7d2..d6cfe8e6a2 100644 --- a/openassessment/templates/openassessmentblock/staff_area/oa_staff_area.html +++ b/openassessment/templates/openassessmentblock/staff_area/oa_staff_area.html @@ -33,51 +33,6 @@

    - {% if display_schedule_training %} -
    - {% if classifierset %} - - - - - - - - - - - - - - - - - - - - - - - - -
    {% trans "Classifier set" %}
    {% trans "Field" %}{% trans "Value" %}
    {% trans "Created at" %}{{ classifierset.created_at }}
    {% trans "Algorithm ID" %}{{ classifierset.algorithm_id }}
    {% trans "Course ID" %}{{ classifierset.course_id }}
    {% trans "Item ID" %}{{ classifierset.item_id }}
    - {% else %} - {% trans "No classifiers are available for this problem" %} - {% endif %} -
    - -
    - -
    -
    - {% endif %} - - {% if display_reschedule_unfinished_tasks %} -
    - -
    -
    - {% endif %} diff --git a/openassessment/templates/openassessmentblock/staff_area/oa_student_info.html b/openassessment/templates/openassessmentblock/staff_area/oa_student_info.html index ceb9a91aa4..b436c4a35b 100644 --- a/openassessment/templates/openassessmentblock/staff_area/oa_student_info.html +++ b/openassessment/templates/openassessmentblock/staff_area/oa_student_info.html @@ -68,11 +68,6 @@

    {% include "openassessmentblock/staff_area/oa_student_info_assessment_detail.html" with class_type="self" assessments=self_assessment %} {% endif %} - {% if example_based_assessment %} - {% trans "Example-Based Assessment" as translated_title %} - {% include "openassessmentblock/staff_area/oa_student_info_assessment_detail.html" with class_type="example_based" assessments=example_based_assessment %} - {% endif %} - {% if staff_assessment %} {% trans "Staff Assessment for This Learner" as translated_title %} {% include "openassessmentblock/staff_area/oa_student_info_assessment_detail.html" with class_type="staff" assessments=staff_assessment %} diff --git a/openassessment/test_utils.py b/openassessment/test_utils.py index 534241fcbf..529f0f7ff2 100644 --- a/openassessment/test_utils.py +++ b/openassessment/test_utils.py @@ -3,16 +3,11 @@ """ from django.core.cache import cache from django.test import TestCase, TransactionTestCase -from openassessment.assessment.models.ai import ( - CLASSIFIERS_CACHE_IN_MEM, CLASSIFIERS_CACHE_IN_FILE -) def _clear_all_caches(): """Clear the default cache and any custom caches.""" cache.clear() - CLASSIFIERS_CACHE_IN_MEM.clear() - CLASSIFIERS_CACHE_IN_FILE.clear() class CacheResetTest(TestCase): diff --git a/openassessment/tests/factories.py b/openassessment/tests/factories.py index baf9faf556..9f6bbc9813 100644 --- a/openassessment/tests/factories.py +++ b/openassessment/tests/factories.py @@ -4,9 +4,8 @@ import factory from factory.django import DjangoModelFactory -from openassessment.assessment.models import ( - Assessment, AssessmentPart, Rubric, Criterion, CriterionOption, AssessmentFeedbackOption, AssessmentFeedback -) +from openassessment.assessment.models import (Assessment, AssessmentFeedback, AssessmentFeedbackOption, AssessmentPart, + Criterion, CriterionOption, Rubric) class RubricFactory(DjangoModelFactory): diff --git a/openassessment/tests/test_data.py b/openassessment/tests/test_data.py index 9b8bd3f6c1..d45e803651 100644 --- a/openassessment/tests/test_data.py +++ b/openassessment/tests/test_data.py @@ -3,23 +3,24 @@ Tests for openassessment data aggregation. """ -import os.path - from StringIO import StringIO import csv -from django.core.management import call_command +import os.path + import ddt -from submissions import api as sub_api + +from django.core.management import call_command + +import openassessment.assessment.api.peer as peer_api +from openassessment.data import CsvWriter, OraAggregateData from openassessment.test_utils import TransactionCacheResetTest from openassessment.tests.factories import * # pylint: disable=wildcard-import from openassessment.workflow import api as workflow_api -from openassessment.data import CsvWriter, OraAggregateData -import openassessment.assessment.api.peer as peer_api - +from submissions import api as sub_api COURSE_ID = "Test_Course" -STUDENT_ID = "Student" +STUDENT_ID = u"Student" SCORER_ID = "Scorer" @@ -82,8 +83,8 @@ FEEDBACK_OPTIONS = { "feedback_text": FEEDBACK_TEXT, "options": [ - 'I disliked this assessment', - 'I felt this assessment was unfair', + u'I disliked this assessment', + u'I felt this assessment was unfair', ] } @@ -363,6 +364,7 @@ class TestOraAggregateDataIntegration(TransactionCacheResetTest): def setUp(self): super(TestOraAggregateDataIntegration, self).setUp() + self.maxDiff = None # Create submissions and assessments self.submission = self._create_submission(STUDENT_ITEM) self.scorer_submission = self._create_submission(SCORER_ITEM) @@ -370,6 +372,7 @@ def setUp(self): self.possible_points = 2 peer_api.get_submission_to_assess(self.scorer_submission['uuid'], 1) self.assessment = self._create_assessment(self.scorer_submission['uuid']) + self.assertEqual(self.assessment['parts'][0]['criterion']['label'], "criterion_1") sub_api.set_score(self.submission['uuid'], self.earned_points, self.possible_points) self.score = sub_api.get_score(STUDENT_ITEM) @@ -470,15 +473,15 @@ def test_collect_ora2_data(self): ), u"Assessment #{id}\n-- {label}: {option_label} ({points})\n".format( id=self.assessment['id'], - label=self.assessment['parts'][1]['criterion']['label'], - option_label=self.assessment['parts'][1]['criterion']['options'][0]['label'], - points=self.assessment['parts'][1]['criterion']['options'][0]['points'], + label=self.assessment['parts'][0]['criterion']['label'], + option_label=self.assessment['parts'][0]['criterion']['options'][0]['label'], + points=self.assessment['parts'][0]['criterion']['options'][0]['points'], ) + u"-- {label}: {option_label} ({points})\n-- feedback: {feedback}\n".format( - label=self.assessment['parts'][0]['criterion']['label'], - option_label=self.assessment['parts'][0]['criterion']['options'][1]['label'], - points=self.assessment['parts'][0]['criterion']['options'][1]['points'], - feedback=self.assessment['parts'][0]['feedback'], + label=self.assessment['parts'][1]['criterion']['label'], + option_label=self.assessment['parts'][1]['criterion']['options'][1]['label'], + points=self.assessment['parts'][1]['criterion']['options'][1]['points'], + feedback=self.assessment['parts'][1]['feedback'], ), self.score['created_at'], self.score['points_earned'], @@ -532,19 +535,19 @@ def test_collect_ora2_responses(self): self.assertIn(item_id2, data) self.assertIn(item_id3, data) for item in [ITEM_ID, item_id2, item_id3]: - self.assertEqual({'total', 'training', 'peer', 'self', 'staff', 'waiting', 'done', 'ai', 'cancelled'}, + self.assertEqual({'total', 'training', 'peer', 'self', 'staff', 'waiting', 'done', 'cancelled'}, set(data[item].keys())) self.assertEqual(data[ITEM_ID], { 'total': 2, 'training': 0, 'peer': 2, 'self': 0, 'staff': 0, 'waiting': 0, - 'done': 0, 'ai': 0, 'cancelled': 0 + 'done': 0, 'cancelled': 0 }) self.assertEqual(data[item_id2], { 'total': 2, 'training': 0, 'peer': 1, 'self': 1, 'staff': 0, 'waiting': 0, - 'done': 0, 'ai': 0, 'cancelled': 0 + 'done': 0, 'cancelled': 0 }) self.assertEqual(data[item_id3], { 'total': 3, 'training': 0, 'peer': 1, 'self': 2, 'staff': 0, 'waiting': 0, - 'done': 0, 'ai': 0, 'cancelled': 0 + 'done': 0, 'cancelled': 0 }) data = OraAggregateData.collect_ora2_responses(COURSE_ID, ['staff', 'peer']) diff --git a/openassessment/workflow/api.py b/openassessment/workflow/api.py index 34bdf2566a..2ca1f25c30 100644 --- a/openassessment/workflow/api.py +++ b/openassessment/workflow/api.py @@ -8,12 +8,11 @@ from openassessment.assessment.errors import PeerAssessmentError, PeerAssessmentInternalError from submissions import api as sub_api + +from .errors import (AssessmentWorkflowError, AssessmentWorkflowInternalError, AssessmentWorkflowNotFoundError, + AssessmentWorkflowRequestError) from .models import AssessmentWorkflow, AssessmentWorkflowCancellation -from .serializers import AssessmentWorkflowSerializer, AssessmentWorkflowCancellationSerializer -from .errors import ( - AssessmentWorkflowError, AssessmentWorkflowInternalError, - AssessmentWorkflowRequestError, AssessmentWorkflowNotFoundError -) +from .serializers import AssessmentWorkflowCancellationSerializer, AssessmentWorkflowSerializer logger = logging.getLogger(__name__) diff --git a/openassessment/workflow/migrations/0001_initial.py b/openassessment/workflow/migrations/0001_initial.py index 1e86deb3e3..864eb39092 100644 --- a/openassessment/workflow/migrations/0001_initial.py +++ b/openassessment/workflow/migrations/0001_initial.py @@ -4,8 +4,8 @@ from django.db import migrations, models import django.utils.timezone + import model_utils.fields -import django_extensions.db.fields class Migration(migrations.Migration): @@ -23,7 +23,7 @@ class Migration(migrations.Migration): ('status', model_utils.fields.StatusField(default=b'peer', max_length=100, verbose_name='status', no_check_for_status=True, choices=[(b'peer', b'peer'), (b'ai', b'ai'), (b'self', b'self'), (b'training', b'training'), (b'waiting', b'waiting'), (b'done', b'done'), (b'cancelled', b'cancelled')])), ('status_changed', model_utils.fields.MonitorField(default=django.utils.timezone.now, verbose_name='status changed', monitor='status')), ('submission_uuid', models.CharField(unique=True, max_length=36, db_index=True)), - ('uuid', django_extensions.db.fields.UUIDField(db_index=True, unique=True, version=1, editable=False, blank=True)), + ('uuid', models.UUIDField(db_index=True, unique=True, editable=False, blank=True)), ('course_id', models.CharField(max_length=255, db_index=True)), ('item_id', models.CharField(max_length=255, db_index=True)), ], diff --git a/openassessment/workflow/migrations/0002_remove_django_extensions.py b/openassessment/workflow/migrations/0002_remove_django_extensions.py new file mode 100644 index 0000000000..8fd6fa02e4 --- /dev/null +++ b/openassessment/workflow/migrations/0002_remove_django_extensions.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import uuid + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='assessmentworkflow', + name='uuid', + field=models.UUIDField(default=uuid.uuid4, unique=True, db_index=True), + ), + ] diff --git a/openassessment/workflow/models.py b/openassessment/workflow/models.py index 1463903557..9447eee114 100644 --- a/openassessment/workflow/models.py +++ b/openassessment/workflow/models.py @@ -9,20 +9,23 @@ ./manage.py schemamigration openassessment.workflow --auto """ -import logging import importlib +import logging +from uuid import uuid4 + from django.conf import settings -from django.db import models, transaction, DatabaseError +from django.db import DatabaseError, models, transaction from django.dispatch import receiver -from django_extensions.db.fields import UUIDField from django.utils.timezone import now + from model_utils import Choices from model_utils.models import StatusModel, TimeStampedModel -from submissions import api as sub_api + from openassessment.assessment.errors.base import AssessmentError from openassessment.assessment.signals import assessment_complete_signal -from .errors import AssessmentApiLoadError, AssessmentWorkflowError, AssessmentWorkflowInternalError +from submissions import api as sub_api +from .errors import AssessmentApiLoadError, AssessmentWorkflowError, AssessmentWorkflowInternalError logger = logging.getLogger('openassessment.workflow.models') @@ -36,7 +39,6 @@ 'peer': 'openassessment.assessment.api.peer', 'self': 'openassessment.assessment.api.self', 'training': 'openassessment.assessment.api.student_training', - 'ai': 'openassessment.assessment.api.ai', } ASSESSMENT_API_DICT = getattr( settings, 'ORA2_ASSESSMENTS', @@ -77,7 +79,7 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel): # We then use that score as the student's overall score. # This Django setting is a list of assessment steps (defined in `settings.ORA2_ASSESSMENTS`) # in descending priority order. - DEFAULT_ASSESSMENT_SCORE_PRIORITY = ['peer', 'self', 'ai'] + DEFAULT_ASSESSMENT_SCORE_PRIORITY = ['peer', 'self'] ASSESSMENT_SCORE_PRIORITY = getattr( settings, 'ORA2_ASSESSMENT_SCORE_PRIORITY', DEFAULT_ASSESSMENT_SCORE_PRIORITY @@ -86,7 +88,7 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel): STAFF_ANNOTATION_TYPE = "staff_defined" submission_uuid = models.CharField(max_length=36, db_index=True, unique=True) - uuid = UUIDField(version=1, db_index=True, unique=True) + uuid = models.UUIDField(db_index=True, unique=True, default=uuid4) # These values are used to find workflows for a particular item # in a course without needing to look up the submissions for that item. @@ -98,6 +100,7 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel): class Meta: ordering = ["-created"] # TODO: In migration, need a non-unique index on (course_id, item_id, status) + app_label = "workflow" def __init__(self, *args, **kwargs): super(AssessmentWorkflow, self).__init__(*args, **kwargs) @@ -154,7 +157,7 @@ def start_workflow(cls, submission_uuid, step_names, on_init_params): item_id=submission_dict['student_item']['item_id'] ) workflow_steps = [ - AssessmentWorkflowStep( + AssessmentWorkflowStep.objects.create( workflow=workflow, name=step, order_num=i ) for i, step in enumerate(step_names) @@ -396,12 +399,14 @@ def _get_steps(self): except AssessmentWorkflowStep.DoesNotExist: for step in list(self.steps.all()): step.order_num += 1 + staff_step, _ = AssessmentWorkflowStep.objects.get_or_create( + name=self.STATUS.staff, + order_num=0, + assessment_completed_at=now(), + workflow=self, + ) self.steps.add( - AssessmentWorkflowStep( - name=self.STATUS.staff, - order_num=0, - assessment_completed_at=now(), - ) + staff_step ) # Do not return steps that are not recognized in the AssessmentWorkflow. @@ -624,6 +629,7 @@ class AssessmentWorkflowStep(models.Model): class Meta: ordering = ["workflow", "order_num"] + app_label = "workflow" def is_submitter_complete(self): """ @@ -760,6 +766,7 @@ class AssessmentWorkflowCancellation(models.Model): class Meta: ordering = ["created_at", "id"] + app_label = "workflow" def __repr__(self): return ( diff --git a/openassessment/workflow/serializers.py b/openassessment/workflow/serializers.py index bef556ca21..5fd137db82 100644 --- a/openassessment/workflow/serializers.py +++ b/openassessment/workflow/serializers.py @@ -3,6 +3,7 @@ scope of the ORA2 APIs. """ from rest_framework import serializers + from openassessment.workflow.models import AssessmentWorkflow, AssessmentWorkflowCancellation diff --git a/openassessment/workflow/test/data/assessments.json b/openassessment/workflow/test/data/assessments.json index ac83ddd337..718ff2b6cf 100644 --- a/openassessment/workflow/test/data/assessments.json +++ b/openassessment/workflow/test/data/assessments.json @@ -57,66 +57,5 @@ }, "self": {} } - }, - "ai": { - "steps": ["ai"], - "requirements": { - "ai": {} - } - }, - "ai_peer": { - "steps": ["ai", "peer"], - "requirements": { - "ai": {}, - "peer": { - "must_grade": 5, - "must_be_graded_by": 3 - } - } - }, - "ai_training_peer": { - "steps": ["ai", "training", "peer"], - "requirements": { - "ai": {}, - "training": { - "num_required": 2 - }, - "peer": { - "must_grade": 5, - "must_be_graded_by": 3 - } - } - }, - "ai_self": { - "steps": ["ai", "self"], - "requirements": { - "ai": {}, - "self": {} - } - }, - "ai_peer_self": { - "steps": ["ai", "peer", "self"], - "requirements": { - "ai": {}, - "peer": { - "must_grade": 5, - "must_be_graded_by": 3 - }, - "self": {} - } - }, - "ai_training_peer_self": { - "steps": ["ai", "training", "peer", "self"], - "requirements": { - "ai": {}, - "training": { - "num_required": 2 - }, - "peer": { - "must_grade": 5, - "must_be_graded_by": 3 - }, - "self": {} - } } -} \ No newline at end of file +} diff --git a/openassessment/workflow/test/test_api.py b/openassessment/workflow/test/test_api.py index 8edb381799..0fe94efaec 100644 --- a/openassessment/workflow/test/test_api.py +++ b/openassessment/workflow/test/test_api.py @@ -1,23 +1,16 @@ -from django.db import DatabaseError -from django.test.utils import override_settings import ddt from mock import patch from nose.tools import raises -from openassessment.test_utils import CacheResetTest +from django.db import DatabaseError +from django.test.utils import override_settings -from submissions.models import Submission -import openassessment.workflow.api as workflow_api -from openassessment.assessment.api import ai as ai_api -from openassessment.assessment.errors import AIError from openassessment.assessment.models import PeerWorkflow, StudentTrainingWorkflow -import submissions.api as sub_api -from openassessment.assessment.api import peer as peer_api -from openassessment.assessment.api import self as self_api -from openassessment.assessment.api import staff as staff_api -from openassessment.workflow.models import AssessmentWorkflow, AssessmentApiLoadError +from openassessment.test_utils import CacheResetTest +import openassessment.workflow.api as workflow_api from openassessment.workflow.errors import AssessmentWorkflowInternalError - +from openassessment.workflow.models import AssessmentWorkflow +import submissions.api as sub_api RUBRIC_DICT = { "criteria": [ @@ -35,15 +28,6 @@ ANSWER_1 = {"text": "Shoot Hot Rod"} ANSWER_2 = {"text": "Ultra Magnus fumble"} -ALGORITHM_ID = "Ease" - -ON_INIT_PARAMS = { - 'ai': { - 'rubric': RUBRIC_DICT, - 'algorithm_id': ALGORITHM_ID, - } -} - ITEM_1 = { "student_id": "Optimus Prime 001", "item_id": "Matrix of Leadership", @@ -68,7 +52,7 @@ def test_create_workflow(self, data): if "ai" in data["steps"]: first_step = data["steps"][1] if len(data["steps"]) > 1 else "waiting" submission = sub_api.create_submission(ITEM_1, ANSWER_1) - workflow = workflow_api.create_workflow(submission["uuid"], data["steps"], ON_INIT_PARAMS) + workflow = workflow_api.create_workflow(submission["uuid"], data["steps"]) workflow_keys = set(workflow.keys()) self.assertEqual( @@ -134,14 +118,14 @@ def test_assessment_module_rollback_update_workflow(self): self.assertTrue(peer_workflows) - with patch.object(peer_api, 'submitter_is_finished') as mock_peer_submit: + with patch('openassessment.assessment.api.peer.submitter_is_finished') as mock_peer_submit: mock_peer_submit.return_value = True workflow = workflow_api.get_workflow_for_submission( submission["uuid"], requirements ) self.assertEquals("self", workflow['status']) - with patch.object(self_api, 'submitter_is_finished') as mock_self_submit: + with patch('openassessment.assessment.api.self.submitter_is_finished') as mock_self_submit: mock_self_submit.return_value = True workflow = workflow_api.get_workflow_for_submission( submission["uuid"], requirements @@ -151,7 +135,7 @@ def test_assessment_module_rollback_update_workflow(self): def test_update_peer_workflow(self): submission = sub_api.create_submission(ITEM_1, ANSWER_1) - workflow = workflow_api.create_workflow(submission["uuid"], ["training", "peer"], ON_INIT_PARAMS) + workflow = workflow_api.create_workflow(submission["uuid"], ["training", "peer"]) StudentTrainingWorkflow.create_workflow(submission_uuid=submission["uuid"]) requirements = { "training": { @@ -193,69 +177,38 @@ def test_update_peer_workflow(self): @ddt.file_data('data/assessments.json') def test_need_valid_submission_uuid(self, data): # submission doesn't exist - with self.assertRaises(workflow_api.AssessmentWorkflowRequestError): - workflow = workflow_api.create_workflow("xxxxxxxxxxx", data["steps"], ON_INIT_PARAMS) + with self.assertRaises(workflow_api.AssessmentWorkflowInternalError): + _ = workflow_api.create_workflow("xxxxxxxxxxx", data["steps"]) # submission_uuid is the wrong type with self.assertRaises(workflow_api.AssessmentWorkflowRequestError): - workflow = workflow_api.create_workflow(123, data["steps"], ON_INIT_PARAMS) - - @patch.object(ai_api, 'assessment_is_finished') - @patch.object(ai_api, 'get_score') - def test_ai_score_set(self, mock_score, mock_is_finished): - submission = sub_api.create_submission(ITEM_1, ANSWER_2) - mock_is_finished.return_value = True - score = {"points_earned": 7, "points_possible": 10} - mock_score.return_value = score - workflow_api.create_workflow(submission["uuid"], ["ai"], ON_INIT_PARAMS) - workflow = workflow_api.get_workflow_for_submission(submission["uuid"], {}) - self.assertEquals(workflow["score"]["points_earned"], score["points_earned"]) - self.assertEquals(workflow["score"]["points_possible"], score["points_possible"]) - - @ddt.data((RUBRIC_DICT, None), (None, ALGORITHM_ID)) - @ddt.unpack - @raises(workflow_api.AssessmentWorkflowInternalError) - def test_create_ai_workflow_no_rubric(self, rubric, algorithm_id): - submission = sub_api.create_submission(ITEM_1, ANSWER_1) - on_init_params = { - 'ai': { - 'rubric': rubric, - 'algorithm_id': algorithm_id, - } - } - workflow_api.create_workflow(submission["uuid"], ["ai"], on_init_params) - - @patch.object(ai_api, 'on_init') - @raises(workflow_api.AssessmentWorkflowInternalError) - def test_ai_on_init_failures(self, mock_on_init): - mock_on_init.side_effect = AIError("Kaboom!") - submission = sub_api.create_submission(ITEM_1, ANSWER_2) - workflow_api.create_workflow(submission["uuid"], ["ai"], ON_INIT_PARAMS) + _ = workflow_api.create_workflow(123, data["steps"]) - @patch.object(Submission.objects, 'get') + @patch('submissions.models.Submission.objects.get') @ddt.file_data('data/assessments.json') @raises(workflow_api.AssessmentWorkflowInternalError) def test_unexpected_submissions_errors_wrapped(self, data, mock_get): mock_get.side_effect = Exception("Kaboom!") - workflow_api.create_workflow("zzzzzzzzzzzzzzz", data["steps"], ON_INIT_PARAMS) + workflow_api.create_workflow("zzzzzzzzzzzzzzz", data["steps"]) - @patch.object(AssessmentWorkflow.objects, 'create') + @patch('openassessment.workflow.models.AssessmentWorkflow.objects.create') @ddt.file_data('data/assessments.json') @raises(workflow_api.AssessmentWorkflowInternalError) def test_unexpected_workflow_errors_wrapped(self, data, mock_create): mock_create.side_effect = DatabaseError("Kaboom!") submission = sub_api.create_submission(ITEM_1, ANSWER_2) - workflow_api.create_workflow(submission["uuid"], data["steps"], ON_INIT_PARAMS) + workflow_api.create_workflow(submission["uuid"], data["steps"]) - @patch.object(PeerWorkflow.objects, 'get_or_create') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get_or_create') @raises(workflow_api.AssessmentWorkflowInternalError) def test_unexpected_peer_workflow_errors_wrapped(self, mock_create): mock_create.side_effect = DatabaseError("Kaboom!") submission = sub_api.create_submission(ITEM_1, ANSWER_2) - workflow_api.create_workflow(submission["uuid"], ["peer", "self"], ON_INIT_PARAMS) + workflow_api.create_workflow(submission["uuid"], ["peer", "self"]) - @patch.object(staff_api, 'get_score') - @patch.object(PeerWorkflow.objects, 'get') + @patch('openassessment.assessment.api.staff.get_score') + @patch('openassessment.assessment.models.peer.PeerWorkflow.objects.get_or_create') + @raises(workflow_api.AssessmentWorkflowInternalError) def test_no_peer_assessment_error_handled(self, mock_get_workflow, mock_get_staff_score): """ Tests to verify that, given a problem that requires the peer step and a submission associated with a workflow @@ -287,7 +240,7 @@ def test_no_peer_assessment_error_handled(self, mock_get_workflow, mock_get_staf override_submitter_requirements=True ) - @patch.object(AssessmentWorkflow.objects, 'get') + @patch('openassessment.workflow.models.AssessmentWorkflow.objects.get') @ddt.file_data('data/assessments.json') @raises(workflow_api.AssessmentWorkflowInternalError) def test_unexpected_exception_wrapped(self, data, mock_create): @@ -302,13 +255,13 @@ def test_get_assessment_workflow_expected_errors(self, data): with self.assertRaises(workflow_api.AssessmentWorkflowRequestError): workflow_api.get_workflow_for_submission(123, data["requirements"]) - @patch.object(Submission.objects, 'get') + @patch('submissions.models.Submission.objects.get') @ddt.file_data('data/assessments.json') @raises(workflow_api.AssessmentWorkflowInternalError) def test_unexpected_workflow_get_errors_wrapped(self, data, mock_get): mock_get.side_effect = Exception("Kaboom!") submission = sub_api.create_submission(ITEM_1, "We talk TV!") - workflow = workflow_api.create_workflow(submission["uuid"], data["steps"], ON_INIT_PARAMS) + workflow = workflow_api.create_workflow(submission["uuid"], data["steps"]) workflow_api.get_workflow_for_submission(workflow["uuid"], {}) def test_preexisting_workflow(self): @@ -416,7 +369,7 @@ def test_unable_to_load_api(self): }, "test answer") with self.assertRaises(AssessmentWorkflowInternalError): - workflow_api.create_workflow(submission['uuid'], ['self'], ON_INIT_PARAMS) + workflow_api.create_workflow(submission['uuid'], ['self']) def test_cancel_the_assessment_workflow(self): # Create the submission and assessment workflow. @@ -556,7 +509,7 @@ def _create_workflow_with_status( "item_type": "openassessment", }, answer) - workflow = workflow_api.create_workflow(submission['uuid'], steps, ON_INIT_PARAMS) + workflow = workflow_api.create_workflow(submission['uuid'], steps) workflow_model = AssessmentWorkflow.objects.get(uuid=workflow['uuid']) workflow_model.status = status workflow_model.save() diff --git a/openassessment/workflow/test/test_signals.py b/openassessment/workflow/test/test_signals.py index 2c22b1ff4d..8361b7cda8 100644 --- a/openassessment/workflow/test/test_signals.py +++ b/openassessment/workflow/test/test_signals.py @@ -1,14 +1,16 @@ """ Tests for Django signals and receivers defined by the workflow API. """ +import ddt import mock + from django.db import DatabaseError -import ddt -from submissions import api as sub_api + +from openassessment.assessment.signals import assessment_complete_signal from openassessment.test_utils import CacheResetTest from openassessment.workflow import api as workflow_api from openassessment.workflow.models import AssessmentWorkflow -from openassessment.assessment.signals import assessment_complete_signal +from submissions import api as sub_api @ddt.ddt @@ -54,7 +56,7 @@ def test_update_signal_updates_workflow(self): mock_update.assert_called_once_with(None) @ddt.data(DatabaseError, IOError) - @mock.patch.object(AssessmentWorkflow.objects, 'get') + @mock.patch('openassessment.workflow.models.AssessmentWorkflow.objects.get') def test_errors(self, error, mock_call): # Start a workflow for the submission workflow_api.create_workflow(self.submission_uuid, ['self']) diff --git a/openassessment/xblock/grade_mixin.py b/openassessment/xblock/grade_mixin.py index 1fe3e32f67..62ea4dc628 100644 --- a/openassessment/xblock/grade_mixin.py +++ b/openassessment/xblock/grade_mixin.py @@ -2,21 +2,19 @@ Grade step in the OpenAssessment XBlock. """ import copy + from lazy import lazy +from xblock.core import XBlock from django.utils.translation import ugettext as _ -from xblock.core import XBlock - -from openassessment.assessment.api import ai as ai_api +from data_conversion import create_submission_dict from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api from openassessment.assessment.api import staff as staff_api -from openassessment.assessment.errors import SelfAssessmentError, PeerAssessmentError +from openassessment.assessment.errors import PeerAssessmentError, SelfAssessmentError from submissions import api as sub_api -from data_conversion import create_submission_dict - class GradeMixin(object): """Grade Mixin introduces all handlers for displaying grades @@ -91,7 +89,6 @@ def render_grade_complete(self, workflow): submission_uuid = workflow['submission_uuid'] staff_assessment = None - example_based_assessment = None self_assessment = None feedback = None peer_assessments = [] @@ -111,11 +108,6 @@ def render_grade_complete(self, workflow): self_api.get_assessment(submission_uuid) ) - if "example-based-assessment" in assessment_steps: - example_based_assessment = self._assessment_grade_context( - ai_api.get_latest_assessment(submission_uuid) - ) - raw_staff_assessment = staff_api.get_latest_staff_assessment(submission_uuid) if raw_staff_assessment: staff_assessment = self._assessment_grade_context(raw_staff_assessment) @@ -141,7 +133,6 @@ def render_grade_complete(self, workflow): submission_uuid, peer_assessments=peer_assessments, self_assessment=self_assessment, - example_based_assessment=example_based_assessment, staff_assessment=staff_assessment, ), 'file_upload_type': self.file_upload_type, @@ -219,7 +210,7 @@ def submit_feedback(self, data, suffix=''): return {'success': True, 'msg': self._(u"Feedback saved.")} def grade_details( - self, submission_uuid, peer_assessments, self_assessment, example_based_assessment, staff_assessment, + self, submission_uuid, peer_assessments, self_assessment, staff_assessment, is_staff=False ): """ @@ -229,7 +220,6 @@ def grade_details( submission_uuid (str): The id of the submission being graded. peer_assessments (list of dict): Serialized assessment models from the peer API. self_assessment (dict): Serialized assessment model from the self API - example_based_assessment (dict): Serialized assessment model from the example-based API staff_assessment (dict): Serialized assessment model from the staff API is_staff (bool): True if the grade details are being displayed to staff, else False. Default value is False (meaning grade details are being shown to the learner). @@ -268,7 +258,10 @@ def has_feedback(assessments): Returns True if at least one assessment has feedback. """ return any( - assessment.get('feedback', None) or has_feedback(assessment.get('individual_assessments', [])) + ( + assessment and + (assessment.get('feedback', None) or has_feedback(assessment.get('individual_assessments', []))) + ) for assessment in assessments ) @@ -279,8 +272,6 @@ def has_feedback(assessments): median_scores = staff_api.get_assessment_scores_by_criteria(submission_uuid) elif "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores(submission_uuid) - elif "example-based-assessment" in assessment_steps: - median_scores = ai_api.get_assessment_scores_by_criteria(submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria(submission_uuid) @@ -293,7 +284,6 @@ def has_feedback(assessments): assessment_steps, staff_assessment, peer_assessments, - example_based_assessment, self_assessment, is_staff=is_staff, ) @@ -322,7 +312,7 @@ def has_feedback(assessments): def _graded_assessments( self, submission_uuid, criterion, assessment_steps, staff_assessment, peer_assessments, - example_based_assessment, self_assessment, is_staff=False + self_assessment, is_staff=False ): """ Returns an array of assessments with their associated grades. @@ -364,9 +354,6 @@ def _get_assessment_part(title, feedback_title, part_criterion_name, assessment) } else: peer_assessment_part = None - example_based_assessment_part = _get_assessment_part( - _('Example-Based Grade'), _('Example-Based Comments'), criterion_name, example_based_assessment - ) self_assessment_part = _get_assessment_part( _('Self Assessment Grade') if is_staff else _('Your Self Assessment'), _('Your Comments'), # This is only used in the LMS student-facing view @@ -380,8 +367,6 @@ def _get_assessment_part(title, feedback_title, part_criterion_name, assessment) assessments.append(staff_assessment_part) if peer_assessment_part: assessments.append(peer_assessment_part) - if example_based_assessment_part: - assessments.append(example_based_assessment_part) if self_assessment_part: assessments.append(self_assessment_part) @@ -389,7 +374,7 @@ def _get_assessment_part(title, feedback_title, part_criterion_name, assessment) if len(assessments) > 0: first_assessment = assessments[0] option = first_assessment['option'] - if option: + if option and option.get('points'): first_assessment['points'] = option['points'] return assessments diff --git a/openassessment/xblock/leaderboard_mixin.py b/openassessment/xblock/leaderboard_mixin.py index 1b24036be1..3fc923d3f7 100644 --- a/openassessment/xblock/leaderboard_mixin.py +++ b/openassessment/xblock/leaderboard_mixin.py @@ -1,15 +1,15 @@ """ Leaderboard step in the OpenAssessment XBlock. """ -from django.utils.translation import ugettext as _ from xblock.core import XBlock -from submissions import api as sub_api +from django.utils.translation import ugettext as _ -from openassessment.assessment.errors import SelfAssessmentError, PeerAssessmentError +from openassessment.assessment.errors import PeerAssessmentError, SelfAssessmentError from openassessment.fileupload import api as file_upload_api from openassessment.fileupload.exceptions import FileUploadError from openassessment.xblock.data_conversion import create_submission_dict +from submissions import api as sub_api class LeaderboardMixin(object): diff --git a/openassessment/xblock/lms_mixin.py b/openassessment/xblock/lms_mixin.py index 7c2b112061..a0253d760a 100644 --- a/openassessment/xblock/lms_mixin.py +++ b/openassessment/xblock/lms_mixin.py @@ -2,7 +2,7 @@ Fields and methods used by the LMS and Studio. """ -from xblock.fields import String, Float, Scope, DateTime +from xblock.fields import DateTime, Float, Scope, String class LmsCompatibilityMixin(object): diff --git a/openassessment/xblock/message_mixin.py b/openassessment/xblock/message_mixin.py index 4280060d26..b41d513eea 100644 --- a/openassessment/xblock/message_mixin.py +++ b/openassessment/xblock/message_mixin.py @@ -5,7 +5,6 @@ import datetime as dt import pytz - from xblock.core import XBlock diff --git a/openassessment/xblock/openassessmentblock.py b/openassessment/xblock/openassessmentblock.py index 22d1ef100d..23e2f31829 100644 --- a/openassessment/xblock/openassessmentblock.py +++ b/openassessment/xblock/openassessmentblock.py @@ -5,40 +5,37 @@ import json import logging import os -import pkg_resources +from lazy import lazy +import pkg_resources import pytz - -from django.conf import settings -from django.template.context import Context -from django.template.loader import get_template from webob import Response -from lazy import lazy - from xblock.core import XBlock -from xblock.fields import List, Scope, String, Boolean, Integer +from xblock.fields import Boolean, Integer, List, Scope, String from xblock.fragment import Fragment +from django.conf import settings +from django.template.loader import get_template + +from openassessment.workflow.errors import AssessmentWorkflowError +from openassessment.xblock.course_items_listing_mixin import CourseItemsListingMixin +from openassessment.xblock.data_conversion import create_prompts_list, create_rubric_dict, update_assessments_format +from openassessment.xblock.defaults import * # pylint: disable=wildcard-import, unused-wildcard-import from openassessment.xblock.grade_mixin import GradeMixin from openassessment.xblock.leaderboard_mixin import LeaderboardMixin -from openassessment.xblock.defaults import * # pylint: disable=wildcard-import, unused-wildcard-import +from openassessment.xblock.lms_mixin import LmsCompatibilityMixin from openassessment.xblock.message_mixin import MessageMixin from openassessment.xblock.peer_assessment_mixin import PeerAssessmentMixin -from openassessment.xblock.lms_mixin import LmsCompatibilityMixin +from openassessment.xblock.resolve_dates import DISTANT_FUTURE, DISTANT_PAST, parse_date_value, resolve_dates from openassessment.xblock.self_assessment_mixin import SelfAssessmentMixin -from openassessment.xblock.submission_mixin import SubmissionMixin -from openassessment.xblock.studio_mixin import StudioMixin -from openassessment.xblock.xml import parse_from_xml, serialize_content_to_xml from openassessment.xblock.staff_area_mixin import StaffAreaMixin -from openassessment.xblock.workflow_mixin import WorkflowMixin from openassessment.xblock.staff_assessment_mixin import StaffAssessmentMixin -from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.xblock.student_training_mixin import StudentTrainingMixin +from openassessment.xblock.studio_mixin import StudioMixin +from openassessment.xblock.submission_mixin import SubmissionMixin from openassessment.xblock.validation import validator -from openassessment.xblock.resolve_dates import resolve_dates, parse_date_value, DISTANT_PAST, DISTANT_FUTURE -from openassessment.xblock.data_conversion import create_prompts_list, create_rubric_dict, update_assessments_format -from openassessment.xblock.course_items_listing_mixin import CourseItemsListingMixin - +from openassessment.xblock.workflow_mixin import WorkflowMixin +from openassessment.xblock.xml import parse_from_xml, serialize_content_to_xml logger = logging.getLogger(__name__) @@ -83,7 +80,6 @@ VALID_ASSESSMENT_TYPES = [ "student-training", - "example-based-assessment", "peer-assessment", "self-assessment", "staff-assessment" @@ -491,8 +487,7 @@ def _create_fragment(self, template, context_dict, initialize_js_func, additiona Creates a fragment for display. """ - context = Context(context_dict) - fragment = Fragment(template.render(context)) + fragment = Fragment(template.render(context_dict)) if additional_css is None: additional_css = [] @@ -645,10 +640,6 @@ def workbench_scenarios(): "OpenAssessmentBlock Unicode", load('static/xml/unicode.xml') ), - ( - "OpenAssessmentBlock Example Based Rubric", - load('static/xml/example_based_example.xml') - ), ( "OpenAssessmentBlock Poverty Rubric", load('static/xml/poverty_rubric_example.xml') @@ -825,8 +816,7 @@ def render_assessment(self, path, context_dict=None): context_dict = {} template = get_template(path) - context = Context(context_dict) - return Response(template.render(context), content_type='application/html', charset='UTF-8') + return Response(template.render(context_dict), content_type='application/html', charset='UTF-8') def add_xml_to_node(self, node): """ @@ -844,7 +834,7 @@ def render_error(self, error_msg): Returns: Response: A response object with an HTML body. """ - context = Context({'error_msg': error_msg}) + context = {'error_msg': error_msg} template = get_template('openassessmentblock/oa_error.html') return Response(template.render(context), content_type='application/html', charset='UTF-8') diff --git a/openassessment/xblock/peer_assessment_mixin.py b/openassessment/xblock/peer_assessment_mixin.py index 772109e5b4..b462fd5d17 100644 --- a/openassessment/xblock/peer_assessment_mixin.py +++ b/openassessment/xblock/peer_assessment_mixin.py @@ -9,15 +9,15 @@ from xblock.core import XBlock from openassessment.assessment.api import peer as peer_api -from openassessment.assessment.errors import ( - PeerAssessmentRequestError, PeerAssessmentInternalError, PeerAssessmentWorkflowError -) +from openassessment.assessment.errors import (PeerAssessmentInternalError, PeerAssessmentRequestError, + PeerAssessmentWorkflowError) from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.xblock.defaults import DEFAULT_RUBRIC_FEEDBACK_TEXT -from .data_conversion import create_rubric_dict + +from .data_conversion import (clean_criterion_feedback, create_rubric_dict, create_submission_dict, + verify_assessment_parameters) from .resolve_dates import DISTANT_FUTURE from .user_data import get_user_preferences -from .data_conversion import clean_criterion_feedback, create_submission_dict, verify_assessment_parameters logger = logging.getLogger(__name__) diff --git a/openassessment/xblock/resolve_dates.py b/openassessment/xblock/resolve_dates.py index d5c1deda05..cbfb062ee2 100644 --- a/openassessment/xblock/resolve_dates.py +++ b/openassessment/xblock/resolve_dates.py @@ -2,8 +2,9 @@ Resolve unspecified dates and date strings to datetimes. """ import datetime as dt -import pytz + from dateutil.parser import parse as parse_date +import pytz class InvalidDateFormat(Exception): diff --git a/openassessment/xblock/schema.py b/openassessment/xblock/schema.py index b6c1640e26..b2d4f74181 100644 --- a/openassessment/xblock/schema.py +++ b/openassessment/xblock/schema.py @@ -4,7 +4,7 @@ import dateutil from pytz import utc -from voluptuous import Schema, Required, All, Any, Range, In, Invalid +from voluptuous import All, Any, In, Invalid, Range, Required, Schema def utf8_validator(value): @@ -66,7 +66,6 @@ def datetime_validator(value): VALID_ASSESSMENT_TYPES = [ u'peer-assessment', u'self-assessment', - u'example-based-assessment', u'student-training', u'staff-assessment', ] diff --git a/openassessment/xblock/self_assessment_mixin.py b/openassessment/xblock/self_assessment_mixin.py index 901cf7bd1f..747fbc71ed 100644 --- a/openassessment/xblock/self_assessment_mixin.py +++ b/openassessment/xblock/self_assessment_mixin.py @@ -1,15 +1,16 @@ import logging -from xblock.core import XBlock from webob import Response +from xblock.core import XBlock from openassessment.assessment.api import self as self_api from openassessment.workflow import api as workflow_api from submissions import api as submission_api + +from .data_conversion import (clean_criterion_feedback, create_rubric_dict, create_submission_dict, + verify_assessment_parameters) from .resolve_dates import DISTANT_FUTURE from .user_data import get_user_preferences -from .data_conversion import (clean_criterion_feedback, create_submission_dict, - create_rubric_dict, verify_assessment_parameters) logger = logging.getLogger(__name__) diff --git a/openassessment/xblock/staff_area_mixin.py b/openassessment/xblock/staff_area_mixin.py index 13e7179e68..dcfc40f5f6 100644 --- a/openassessment/xblock/staff_area_mixin.py +++ b/openassessment/xblock/staff_area_mixin.py @@ -7,25 +7,18 @@ import logging from xblock.core import XBlock -from openassessment.assessment.errors import ( - PeerAssessmentInternalError, -) -from openassessment.workflow.errors import ( - AssessmentWorkflowError, AssessmentWorkflowInternalError -) -from openassessment.assessment.errors.ai import AIError -from openassessment.xblock.resolve_dates import DISTANT_PAST, DISTANT_FUTURE -from openassessment.xblock.data_conversion import ( - create_rubric_dict, convert_training_examples_list_to_dict, create_submission_dict -) -from submissions import api as submission_api + from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api -from openassessment.assessment.api import ai as ai_api -from openassessment.workflow import api as workflow_api from openassessment.assessment.api import staff as staff_api -from .user_data import get_user_preferences +from openassessment.assessment.errors import PeerAssessmentInternalError +from openassessment.workflow import api as workflow_api +from openassessment.workflow.errors import AssessmentWorkflowError, AssessmentWorkflowInternalError +from openassessment.xblock.data_conversion import create_submission_dict +from openassessment.xblock.resolve_dates import DISTANT_FUTURE, DISTANT_PAST +from submissions import api as submission_api +from .user_data import get_user_preferences logger = logging.getLogger(__name__) @@ -127,24 +120,6 @@ def get_staff_path_and_context(self): context['status_counts'] = status_counts context['num_submissions'] = num_submissions - # Show the schedule training button if example based assessment is - # configured, and the current user has admin privileges. - example_based_assessment = self.get_assessment_module('example-based-assessment') - display_ai_staff_info = ( - self.is_admin and - bool(example_based_assessment) and - not self.in_studio_preview - ) - context['display_schedule_training'] = display_ai_staff_info - context['display_reschedule_unfinished_tasks'] = display_ai_staff_info - if display_ai_staff_info: - context['classifierset'] = ai_api.get_classifier_set_info( - create_rubric_dict(self.prompts, self.rubric_criteria_with_labels), - example_based_assessment['algorithm_id'], - student_item['course_id'], - student_item['item_id'] - ) - # Include Latex setting context['allow_latex'] = self.allow_latex @@ -152,9 +127,6 @@ def get_staff_path_and_context(self): context['step_dates'] = list() for step in ['submission'] + self.assessment_steps: - if step == 'example-based-assessment': - continue - # Get the dates as a student would see them __, __, start_date, due_date = self.is_closed(step=step, course_staff=False) @@ -187,42 +159,6 @@ def get_staff_assessment_statistics_context(course_id, item_id): 'staff_assessment_in_progress': grading_stats['in-progress'] } - @XBlock.json_handler - @require_global_admin("SCHEDULE_TRAINING") - def schedule_training(self, data, suffix=''): # pylint: disable=W0613 - """ - Schedule a new training task for example-based grading. - """ - assessment = self.get_assessment_module('example-based-assessment') - student_item_dict = self.get_student_item_dict() - - if assessment: - examples = assessment["examples"] - try: - workflow_uuid = ai_api.train_classifiers( - create_rubric_dict(self.prompts, self.rubric_criteria_with_labels), - convert_training_examples_list_to_dict(examples), - student_item_dict.get('course_id'), - student_item_dict.get('item_id'), - assessment["algorithm_id"] - ) - return { - 'success': True, - 'workflow_uuid': workflow_uuid, - 'msg': self._(u"Training scheduled with new Workflow UUID: {uuid}".format(uuid=workflow_uuid)) - } - except AIError as err: - return { - 'success': False, - 'msg': self._(u"An error occurred scheduling classifier training: {error}".format(error=err)) - } - - else: - return { - 'success': False, - 'msg': self._(u"Example Based Assessment is not configured for this location.") - } - @XBlock.handler @require_course_staff("STUDENT_INFO") def render_student_info(self, data, suffix=''): # pylint: disable=W0613 @@ -389,9 +325,6 @@ def add_submission_context(self, submission_uuid, context): """ assessment_steps = self.assessment_steps - example_based_assessment = None - example_based_assessment_grade_context = None - self_assessment = None self_assessment_grade_context = None @@ -423,11 +356,6 @@ def add_submission_context(self, submission_uuid, context): if grade_exists: self_assessment_grade_context = self._assessment_grade_context(self_assessment) - if "example-based-assessment" in assessment_steps: - example_based_assessment = ai_api.get_latest_assessment(submission_uuid) - if grade_exists: - example_based_assessment_grade_context = self._assessment_grade_context(example_based_assessment) - if grade_exists: if staff_assessment: staff_assessment_grade_context = self._assessment_grade_context(staff_assessment) @@ -436,7 +364,6 @@ def add_submission_context(self, submission_uuid, context): submission_uuid, peer_assessments_grade_context, self_assessment_grade_context, - example_based_assessment_grade_context, staff_assessment_grade_context, is_staff=True, ) @@ -444,7 +371,6 @@ def add_submission_context(self, submission_uuid, context): workflow_cancellation = self.get_workflow_cancellation_info(submission_uuid) context.update({ - 'example_based_assessment': [example_based_assessment] if example_based_assessment else None, 'self_assessment': [self_assessment] if self_assessment else None, 'peer_assessments': peer_assessments, 'staff_assessment': [staff_assessment] if staff_assessment else None, @@ -455,50 +381,11 @@ def add_submission_context(self, submission_uuid, context): 'workflow_cancellation': workflow_cancellation, }) - if peer_assessments or self_assessment or example_based_assessment or staff_assessment: + if peer_assessments or self_assessment or staff_assessment: max_scores = peer_api.get_rubric_max_scores(submission_uuid) for criterion in context["rubric_criteria"]: criterion["total_value"] = max_scores[criterion["name"]] - @XBlock.json_handler - @require_global_admin("RESCHEDULE_TASKS") - def reschedule_unfinished_tasks(self, data, suffix=''): # pylint: disable=W0613 - """ - Wrapper which invokes the API call for rescheduling grading tasks. - - Checks that the requester is an administrator that is not in studio-preview mode, - and that the api-call returns without error. If it returns with an error, (any - exception), the appropriate JSON serializable dictionary with success conditions - is passed back. - - Args: - data (not used) - suffix (not used) - - Return: - Json serilaizable dict with the following elements: - 'success': (bool) Indicates whether or not the tasks were rescheduled successfully - 'msg': The response to the server (could be error message or success message) - """ - # Identifies the course and item that will need to be re-run - student_item_dict = self.get_student_item_dict() - course_id = student_item_dict.get('course_id') - item_id = student_item_dict.get('item_id') - - try: - # Note that we only want to recschdule grading tasks, but maintain the potential functionallity - # within the API to also reschedule training tasks. - ai_api.reschedule_unfinished_tasks(course_id=course_id, item_id=item_id, task_type=u"grade") - return { - 'success': True, - 'msg': self._(u"All AI tasks associated with this item have been rescheduled successfully.") - } - except AIError as ex: - return { - 'success': False, - 'msg': self._(u"An error occurred while rescheduling tasks: {}".format(ex)) - } - def clear_student_state(self, user_id, course_id, item_id, requesting_user_id): """ This xblock method is called (from our LMS runtime, which defines this method signature) to clear student state diff --git a/openassessment/xblock/staff_assessment_mixin.py b/openassessment/xblock/staff_assessment_mixin.py index e6bf255a40..2f7959f3ab 100644 --- a/openassessment/xblock/staff_assessment_mixin.py +++ b/openassessment/xblock/staff_assessment_mixin.py @@ -3,17 +3,14 @@ """ import logging -from staff_area_mixin import require_course_staff from xblock.core import XBlock from openassessment.assessment.api import staff as staff_api +from openassessment.assessment.errors import StaffAssessmentInternalError, StaffAssessmentRequestError from openassessment.workflow import api as workflow_api -from openassessment.assessment.errors import ( - StaffAssessmentRequestError, StaffAssessmentInternalError -) +from staff_area_mixin import require_course_staff -from .data_conversion import create_rubric_dict -from .data_conversion import clean_criterion_feedback, verify_assessment_parameters +from .data_conversion import clean_criterion_feedback, create_rubric_dict, verify_assessment_parameters logger = logging.getLogger(__name__) diff --git a/openassessment/xblock/static/css/openassessment-ltr.css b/openassessment/xblock/static/css/openassessment-ltr.css index 34be941dab..2b3b95b5f0 100644 --- a/openassessment/xblock/static/css/openassessment-ltr.css +++ b/openassessment/xblock/static/css/openassessment-ltr.css @@ -1,3 +1,3 @@ -html{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,*:before,*:after{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}.wrapper--xblock{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock main,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary{display:block}.wrapper--xblock audio,.wrapper--xblock canvas,.wrapper--xblock progress,.wrapper--xblock video{display:inline-block;vertical-align:baseline}.wrapper--xblock audio:not([controls]){display:none;height:0}.wrapper--xblock [hidden],.wrapper--xblock template{display:none}.wrapper--xblock a{background:transparent}.wrapper--xblock abbr[title]{border-bottom:1px dotted}.wrapper--xblock b,.wrapper--xblock strong{font-weight:bold}.wrapper--xblock dfn{font-style:italic}.wrapper--xblock h1{font-size:2em;margin:0.67em 0}.wrapper--xblock mark{background:#ff0;color:#000}.wrapper--xblock small{font-size:80%}.wrapper--xblock sub,.wrapper--xblock sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.wrapper--xblock sup{top:-0.5em}.wrapper--xblock sub{bottom:-0.25em}.wrapper--xblock img{border:0}.wrapper--xblock svg:not(:root){overflow:hidden}.wrapper--xblock figure{margin:1em 40px}.wrapper--xblock hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}.wrapper--xblock pre{overflow:auto}.wrapper--xblock code,.wrapper--xblock kbd,.wrapper--xblock pre,.wrapper--xblock samp{font-family:monospace, monospace;font-size:1em}.wrapper--xblock button,.wrapper--xblock input,.wrapper--xblock optgroup,.wrapper--xblock select,.wrapper--xblock textarea{color:inherit;font:inherit;margin:0}.wrapper--xblock button{overflow:visible}.wrapper--xblock button,.wrapper--xblock select{text-transform:none}.wrapper--xblock button,.wrapper--xblock html input[type="button"],.wrapper--xblock input[type="reset"],.wrapper--xblock input[type="submit"]{-webkit-appearance:button;cursor:pointer}.wrapper--xblock button[disabled],.wrapper--xblock html input[disabled]{cursor:default}.wrapper--xblock button[disabled]{opacity:0.5}.wrapper--xblock button::-moz-focus-inner,.wrapper--xblock input::-moz-focus-inner{border:0;padding:0}.wrapper--xblock input{line-height:normal}.wrapper--xblock input[type="checkbox"],.wrapper--xblock input[type="radio"]{box-sizing:border-box;padding:0}.wrapper--xblock input[type="number"]::-webkit-inner-spin-button,.wrapper--xblock input[type="number"]::-webkit-outer-spin-button{height:auto}.wrapper--xblock input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}.wrapper--xblock input[type="search"]::-webkit-search-cancel-button,.wrapper--xblock input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}.wrapper--xblock fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em 0.625em}.wrapper--xblock legend{border:0;padding:0}.wrapper--xblock textarea{overflow:auto}.wrapper--xblock optgroup{font-weight:bold}.wrapper--xblock table{border-collapse:collapse;border-spacing:0}.wrapper--xblock td,.wrapper--xblock th{padding:0}.wrapper--xblock *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wrapper--xblock div,.wrapper--xblock span,.wrapper--xblock applet,.wrapper--xblock object,.wrapper--xblock iframe,.wrapper--xblock h1,.wrapper--xblock h2,.wrapper--xblock h3,.wrapper--xblock h4,.wrapper--xblock h5,.wrapper--xblock h6,.wrapper--xblock p,.wrapper--xblock blockquote,.wrapper--xblock pre,.wrapper--xblock a,.wrapper--xblock abbr,.wrapper--xblock acronym,.wrapper--xblock address,.wrapper--xblock big,.wrapper--xblock cite,.wrapper--xblock code,.wrapper--xblock del,.wrapper--xblock dfn,.wrapper--xblock em,.wrapper--xblock img,.wrapper--xblock ins,.wrapper--xblock kbd,.wrapper--xblock q,.wrapper--xblock s,.wrapper--xblock samp,.wrapper--xblock small,.wrapper--xblock strike,.wrapper--xblock strong,.wrapper--xblock sub,.wrapper--xblock sup,.wrapper--xblock tt,.wrapper--xblock var,.wrapper--xblock b,.wrapper--xblock u,.wrapper--xblock i,.wrapper--xblock center,.wrapper--xblock dl,.wrapper--xblock dt,.wrapper--xblock dd,.wrapper--xblock ol,.wrapper--xblock ul,.wrapper--xblock li,.wrapper--xblock fieldset,.wrapper--xblock form,.wrapper--xblock label,.wrapper--xblock legend,.wrapper--xblock table,.wrapper--xblock caption,.wrapper--xblock tbody,.wrapper--xblock tfoot,.wrapper--xblock thead,.wrapper--xblock tr,.wrapper--xblock th,.wrapper--xblock td,.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock canvas,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock menu,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary,.wrapper--xblock time,.wrapper--xblock mark,.wrapper--xblock audio,.wrapper--xblock video{margin:0;vertical-align:baseline;border:0;padding:0;font-weight:400}.wrapper--xblock abbr[title]{border-bottom:none;text-decoration:none;cursor:help}.list--actions:last-child,.list--docs:last-child,.list--steps:last-child,.list--fields:last-child,.list--controls:last-child,.list--help:last-child,.list--options:last-child,.nav--wizard:last-child,.list--tips:last-child,.field--group:last-child,.wrapper--xblock .field--radio .option:last-child,.wrapper--xblock .field--checkbox .option:last-child,.wrapper--xblock .message .message__content p:last-child,.openassessment .staff-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:last-child,.step--student-training .student-training__assessment .assessment__rubric__question:last-child,.openassessment .staff-assessment__assessment .question__answers .answer:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:last-child,.step--student-training .student-training__assessment .question__answers .answer:last-child,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:last-child,.step--student-training .student-training__assessment .question__answers .answer--feedback:last-child,.openassessment .ui-staff__content__section:last-child,.openassessment .openassessment__steps__step .step__content__section:last-child,.openassessment .submission__answer__part__prompt__copy>:last-child,.openassessment .submission__peer-evaluations__questions .question:last-child,.openassessment .submission__peer-evaluations__questions .question__answers:last-child,.openassessment .answer--feedback .feedback:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0;margin-right:0;border-right:none;padding-right:0}.list--actions,.list--docs,.list--steps,.list--fields,.list--controls,.list--help,.list--options,.nav--wizard,.list--tips,.field--group,.openassessment .openassessment__steps,.openassessment .step--peer-assessment .list--peer-assessments,.openassessment .submission__peer-evaluations__questions,.openassessment .submission__peer-evaluations__questions .question__answers,.openassessment .answer--feedback .feedback{list-style:none;margin:0;padding:0;text-indent:0}.sr,hr.divider,.nav--contents,.system__element,.openassessment .submission__answer__part__prompt__title,.openassessment .step--response .response__submission__content label{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-hint{cursor:pointer}.is--hidden,.wrapper--xblock .is--transitioning{pointer-events:none;outline:none}.is--hidden{display:none;visibility:hidden}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder,.wrapper--xblock .message--error{-webkit-transition:opacity 0.5s ease-in-out;-moz-transition:opacity 0.5s ease-in-out;transition:opacity 0.5s ease-in-out}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{-webkit-transition:color 0.5s ease-in-out;-moz-transition:color 0.5s ease-in-out;transition:color 0.5s ease-in-out}.openassessment .ui-hint:after{box-shadow:0 1px 2px 0 rgba(3,3,3,0.4)}@-webkit-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-moz-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-o-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}.wrapper--xblock .problem__header{margin-bottom:20px;color:#414141}.wrapper--xblock{background:transparent}.wrapper--xblock strong,.wrapper--xblock .emphasis,.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload,.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea,.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label,.wrapper--xblock .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-info__title__copy,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value,.openassessment .staff-assessment__display__header span,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .step--peer-assessment .peer-assessment__display__header span,.openassessment .step--grade .step__title .grade__value__title .grade__value__earned,.openassessment .step--grade .step__title .grade__value__title .grade__value__potential,.openassessment .submission__peer-evaluations__questions .question__score__value,.openassessment .submission__peer-evaluations__questions .question__score__potential,.openassessment .submission__peer-evaluations__questions .question__score__potential .unit,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .submission__peer-evaluations__questions .answer__value__value,.openassessment .answer--feedback .feedback__source,.staff-info__student .staff-info__student__report .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-weight:600}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder,.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder,.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder,.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder,.openassessment .openassessment__title{font-weight:400}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source,.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after,#openassessment-editor .openassessment_container_guide{font-size:12px;line-height:18px}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub,.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload,.openassessment .openassessment__steps__step .step__status .icon{font-size:14px;line-height:21px}.wrapper--xblock .problem__header,.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions,.openassessment .list--actions .action--submit .icon{font-size:16px;line-height:24px}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon,.openassessment .submission__peer-evaluations__questions .question__title .icon,.openassessment .submission__feedback__title .icon{font-size:18px;line-height:27px}.wrapper--xblock .is--transitioning .transition__status .icon{font-size:22px;line-height:33px}.wrapper--xblock .problem__header,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{text-transform:uppercase;letter-spacing:0.05rem}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .problem__header{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__content a,.openassessment .submission__answer__part__prompt__copy a{-webkit-transition:color 0.5s ease-in-out,border 0.5s ease-in-out;-moz-transition:color 0.5s ease-in-out,border 0.5s ease-in-out;transition:color 0.5s ease-in-out,border 0.5s ease-in-out;border-bottom:1px solid transparent;color:#0075b4;text-decoration:none}.wrapper--xblock .message .message__content a:hover,.openassessment .submission__answer__part__prompt__copy a:hover,.wrapper--xblock .message .message__content a:focus,.openassessment .submission__answer__part__prompt__copy a:focus{color:#065683;border-color:#065683}.wrapper--xblock .message .message__content a:active,.openassessment .submission__answer__part__prompt__copy a:active,.wrapper--xblock .message .message__content a.is--current,.openassessment .submission__answer__part__prompt__copy a.is--current{color:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;-moz-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;display:inline-block;cursor:pointer;text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username .icon,.openassessment .step--response .response__submission__content .action--save .icon,.openassessment .step--response .response__submission__actions .action--save .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save .icon,.openassessment .step--self-assessment .self-assessment__assessment .action--save .icon,.openassessment .action--submit .icon,.step--response .action--upload .icon{display:inline-block;vertical-align:middle}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{border-radius:4px}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .ui-slidable__container .ui-slidable,.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit{padding:10px 40px;background:#0075b4;border:1px solid #0075b4;color:#fff}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .list--actions .action--submit:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .list--actions .action--submit:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .list--actions .action--submit:focus{background:#065683;border-color:#065683}.openassessment .openassessment_student_info_form .current.action--submit-username,.openassessment .list--actions .current.action--submit,.openassessment .openassessment_student_info_form .active.action--submit-username,.openassessment .list--actions .active.action--submit{background:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form [disabled].action--submit-username,.openassessment .list--actions [disabled].action--submit{color:#b4b3b3;border-color:#b4b3b3;background:#f5f5f5;opacity:1}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{padding:10px 40px;background:#fff;border:1px solid #065683;color:#065683}.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{background:#0075b4;border-color:#0075b4;color:#fff}.openassessment .step--response .response__submission__content .current.action--save,.openassessment .step--response .response__submission__actions .current.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .current.action--save,.openassessment .step--self-assessment .self-assessment__assessment .current.action--save,.openassessment .current.action--submit,.step--response .current.action--upload,.openassessment .step--response .response__submission__content .active.action--save,.openassessment .step--response .response__submission__actions .active.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .active.action--save,.openassessment .step--self-assessment .self-assessment__assessment .active.action--save,.openassessment .active.action--submit,.step--response .active.action--upload{background:#fff;border-color:#065683;color:#065683}.openassessment .step--response .response__submission__content [disabled].action--save,.openassessment .step--response .response__submission__actions [disabled].action--save,.openassessment .step--peer-assessment .peer-assessment__assessment [disabled].action--save,.openassessment .step--self-assessment .self-assessment__assessment [disabled].action--save,.openassessment [disabled].action--submit,.step--response [disabled].action--upload{background:#fff;border-color:#b4b3b3;color:#b4b3b3;opacity:1}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder{opacity:0.0}.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea{border:1px solid #b9c1c9;padding:10px;color:#414141}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:focus,.wrapper--xblock input[type="email"]:focus,.wrapper--xblock input[type="password"]:focus,.wrapper--xblock textarea:focus{box-shadow:0 0 6px 0 rgba(29,157,217,0.4),inset 0 0 4px 0 rgba(0,0,0,0.15);border-color:#000}.wrapper--xblock .field{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .field-group__label{display:block;margin-bottom:10px}.wrapper--xblock .field textarea{width:100%}.wrapper--xblock .field .tip{display:block;color:#414141}.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label{margin-bottom:10px}.wrapper--xblock .field-group__label{margin-bottom:10px}.wrapper--xblock .field--radio .option,.wrapper--xblock .field--checkbox .option{margin-bottom:5px}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__input,.wrapper--xblock .field--checkbox .option__label{display:inline-block;vertical-align:middle}.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label{margin-bottom:0;color:#414141}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--checkbox .option__input{margin-right:10px}.wrapper--xblock .field--radio .option__input:checked+.option__label,.wrapper--xblock .field--checkbox .option__input:checked+.option__label{color:#000}.wrapper--xblock .message{margin-bottom:20px;border-radius:2px;padding:20px 20px;background:#f5f6f7}.wrapper--xblock .message .message__title{margin-bottom:5px;border-bottom:2px solid #d9d9d9;padding-bottom:5px}.wrapper--xblock .message .message__content{color:#414141}.wrapper--xblock .message .message__content p{margin-bottom:10px}.wrapper--xblock .message--error{background:#feeced}.wrapper--xblock .message--error .message__title{color:#414141;border-bottom-color:#b20610}.wrapper--xblock .message--warning{background:#fff9eb}.wrapper--xblock .message--warning .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--complete,.wrapper--xblock .step--student-training .message--correct,.step--student-training .wrapper--xblock .message--correct{background:#ecfaec}.wrapper--xblock .message--complete .message__title,.wrapper--xblock .step--student-training .message--correct .message__title,.step--student-training .wrapper--xblock .message--correct .message__title{color:#414141;border-bottom-color:#008100}.wrapper--xblock .message--incomplete,.wrapper--xblock .step--student-training .message--incorrect,.step--student-training .wrapper--xblock .message--incorrect{background:#fff9eb}.wrapper--xblock .message--incomplete .message__title,.wrapper--xblock .step--student-training .message--incorrect .message__title,.step--student-training .wrapper--xblock .message--incorrect .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--error{display:none;opacity:0.0}.wrapper--xblock .has--error .message--error{display:block;opacity:1.0}.wrapper--xblock .message--inline{padding:10px 20px;background:#5e5e5e}.wrapper--xblock .message--inline .message__title{margin-bottom:0;border:none;padding-bottom:0;color:#fff}.wrapper--xblock .message--inline.message--error{background:#b20610}.wrapper--xblock .message--inline.message--error .message__content{color:#fff}.wrapper--xblock .message--inline.message--warning{background:#ffc01f}.wrapper--xblock .message--inline.message--warning .message__content{color:#111}.wrapper--xblock .message--inline.message--warning .message__title{color:#111}.wrapper--xblock .is--transitioning{padding:40px 40px;background:#f5f6f7}.wrapper--xblock .is--transitioning .transition__status{position:relative;top:50%;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%);text-align:center}.wrapper--xblock .is--transitioning .transition__status .wrapper--anim{display:block;-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.wrapper--xblock .is--transitioning .transition__status .icon,.wrapper--xblock .is--transitioning .transition__status .copy{display:block;color:#414141}.wrapper--xblock .is--transitioning .transition__status .icon{margin-bottom:10px}.wrapper--grid{*zoom:1;max-width:100%;margin-left:auto;margin-right:auto}.wrapper--grid:before,.wrapper--grid:after{content:" ";display:table}.wrapper--grid:after{clear:both}/*! Hint.css - v1.3.1 - 2013-11-23 +html{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,*:before,*:after{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}.wrapper--xblock{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock main,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary{display:block}.wrapper--xblock audio,.wrapper--xblock canvas,.wrapper--xblock progress,.wrapper--xblock video{display:inline-block;vertical-align:baseline}.wrapper--xblock audio:not([controls]){display:none;height:0}.wrapper--xblock [hidden],.wrapper--xblock template{display:none}.wrapper--xblock a{background:transparent}.wrapper--xblock abbr[title]{border-bottom:1px dotted}.wrapper--xblock b,.wrapper--xblock strong{font-weight:bold}.wrapper--xblock dfn{font-style:italic}.wrapper--xblock h1{font-size:2em;margin:0.67em 0}.wrapper--xblock mark{background:#ff0;color:#000}.wrapper--xblock small{font-size:80%}.wrapper--xblock sub,.wrapper--xblock sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.wrapper--xblock sup{top:-0.5em}.wrapper--xblock sub{bottom:-0.25em}.wrapper--xblock img{border:0}.wrapper--xblock svg:not(:root){overflow:hidden}.wrapper--xblock figure{margin:1em 40px}.wrapper--xblock hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}.wrapper--xblock pre{overflow:auto}.wrapper--xblock code,.wrapper--xblock kbd,.wrapper--xblock pre,.wrapper--xblock samp{font-family:monospace, monospace;font-size:1em}.wrapper--xblock button,.wrapper--xblock input,.wrapper--xblock optgroup,.wrapper--xblock select,.wrapper--xblock textarea{color:inherit;font:inherit;margin:0}.wrapper--xblock button{overflow:visible}.wrapper--xblock button,.wrapper--xblock select{text-transform:none}.wrapper--xblock button,.wrapper--xblock html input[type="button"],.wrapper--xblock input[type="reset"],.wrapper--xblock input[type="submit"]{-webkit-appearance:button;cursor:pointer}.wrapper--xblock button[disabled],.wrapper--xblock html input[disabled]{cursor:default}.wrapper--xblock button[disabled]{opacity:0.5}.wrapper--xblock button::-moz-focus-inner,.wrapper--xblock input::-moz-focus-inner{border:0;padding:0}.wrapper--xblock input{line-height:normal}.wrapper--xblock input[type="checkbox"],.wrapper--xblock input[type="radio"]{box-sizing:border-box;padding:0}.wrapper--xblock input[type="number"]::-webkit-inner-spin-button,.wrapper--xblock input[type="number"]::-webkit-outer-spin-button{height:auto}.wrapper--xblock input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}.wrapper--xblock input[type="search"]::-webkit-search-cancel-button,.wrapper--xblock input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}.wrapper--xblock fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em .625em}.wrapper--xblock legend{border:0;padding:0}.wrapper--xblock textarea{overflow:auto}.wrapper--xblock optgroup{font-weight:bold}.wrapper--xblock table{border-collapse:collapse;border-spacing:0}.wrapper--xblock td,.wrapper--xblock th{padding:0}.wrapper--xblock *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wrapper--xblock div,.wrapper--xblock span,.wrapper--xblock applet,.wrapper--xblock object,.wrapper--xblock iframe,.wrapper--xblock h1,.wrapper--xblock h2,.wrapper--xblock h3,.wrapper--xblock h4,.wrapper--xblock h5,.wrapper--xblock h6,.wrapper--xblock p,.wrapper--xblock blockquote,.wrapper--xblock pre,.wrapper--xblock a,.wrapper--xblock abbr,.wrapper--xblock acronym,.wrapper--xblock address,.wrapper--xblock big,.wrapper--xblock cite,.wrapper--xblock code,.wrapper--xblock del,.wrapper--xblock dfn,.wrapper--xblock em,.wrapper--xblock img,.wrapper--xblock ins,.wrapper--xblock kbd,.wrapper--xblock q,.wrapper--xblock s,.wrapper--xblock samp,.wrapper--xblock small,.wrapper--xblock strike,.wrapper--xblock strong,.wrapper--xblock sub,.wrapper--xblock sup,.wrapper--xblock tt,.wrapper--xblock var,.wrapper--xblock b,.wrapper--xblock u,.wrapper--xblock i,.wrapper--xblock center,.wrapper--xblock dl,.wrapper--xblock dt,.wrapper--xblock dd,.wrapper--xblock ol,.wrapper--xblock ul,.wrapper--xblock li,.wrapper--xblock fieldset,.wrapper--xblock form,.wrapper--xblock label,.wrapper--xblock legend,.wrapper--xblock table,.wrapper--xblock caption,.wrapper--xblock tbody,.wrapper--xblock tfoot,.wrapper--xblock thead,.wrapper--xblock tr,.wrapper--xblock th,.wrapper--xblock td,.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock canvas,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock menu,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary,.wrapper--xblock time,.wrapper--xblock mark,.wrapper--xblock audio,.wrapper--xblock video{margin:0;vertical-align:baseline;border:0;padding:0;font-weight:400}.wrapper--xblock abbr[title]{border-bottom:none;text-decoration:none;cursor:help}.list--actions:last-child,.list--docs:last-child,.list--steps:last-child,.list--fields:last-child,.list--controls:last-child,.list--help:last-child,.list--options:last-child,.nav--wizard:last-child,.list--tips:last-child,.field--group:last-child,.wrapper--xblock .field--radio .option:last-child,.wrapper--xblock .field--checkbox .option:last-child,.wrapper--xblock .message .message__content p:last-child,.openassessment .staff-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:last-child,.step--student-training .student-training__assessment .assessment__rubric__question:last-child,.openassessment .staff-assessment__assessment .question__answers .answer:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:last-child,.step--student-training .student-training__assessment .question__answers .answer:last-child,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:last-child,.step--student-training .student-training__assessment .question__answers .answer--feedback:last-child,.openassessment .ui-staff__content__section:last-child,.openassessment .openassessment__steps__step .step__content__section:last-child,.openassessment .submission__answer__part__prompt__copy>:last-child,.openassessment .submission__peer-evaluations__questions .question:last-child,.openassessment .submission__peer-evaluations__questions .question__answers:last-child,.openassessment .answer--feedback .feedback:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0;margin-right:0;border-right:none;padding-right:0}.list--actions,.list--docs,.list--steps,.list--fields,.list--controls,.list--help,.list--options,.nav--wizard,.list--tips,.field--group,.openassessment .openassessment__steps,.openassessment .step--peer-assessment .list--peer-assessments,.openassessment .submission__peer-evaluations__questions,.openassessment .submission__peer-evaluations__questions .question__answers,.openassessment .answer--feedback .feedback{list-style:none;margin:0;padding:0;text-indent:0}.sr,hr.divider,.nav--contents,.system__element,.openassessment .submission__answer__part__prompt__title,.openassessment .step--response .response__submission__content label{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-hint{cursor:pointer}.is--hidden,.wrapper--xblock .is--transitioning{pointer-events:none;outline:none}.is--hidden{display:none;visibility:hidden}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder,.wrapper--xblock .message--error{-webkit-transition:opacity .5s ease-in-out;-moz-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{-webkit-transition:color .5s ease-in-out;-moz-transition:color .5s ease-in-out;transition:color .5s ease-in-out}.openassessment .ui-hint:after{box-shadow:0 1px 2px 0 rgba(3,3,3,0.4)}@-webkit-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-moz-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-o-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}.wrapper--xblock .problem__header{margin-bottom:20px;color:#414141}.wrapper--xblock{background:transparent}.wrapper--xblock strong,.wrapper--xblock .emphasis,.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload,.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea,.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label,.wrapper--xblock .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-info__title__copy,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value,.openassessment .staff-assessment__display__header span,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .step--peer-assessment .peer-assessment__display__header span,.openassessment .step--grade .step__title .grade__value__title .grade__value__earned,.openassessment .step--grade .step__title .grade__value__title .grade__value__potential,.openassessment .submission__peer-evaluations__questions .question__score__value,.openassessment .submission__peer-evaluations__questions .question__score__potential,.openassessment .submission__peer-evaluations__questions .question__score__potential .unit,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .submission__peer-evaluations__questions .answer__value__value,.openassessment .answer--feedback .feedback__source,.staff-info__student .staff-info__student__report .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-weight:600}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder,.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder,.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder,.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder,.openassessment .openassessment__title{font-weight:400}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source,.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after,#openassessment-editor .openassessment_container_guide{font-size:12px;line-height:18px}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub,.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload,.openassessment .openassessment__steps__step .step__status .icon{font-size:14px;line-height:21px}.wrapper--xblock .problem__header,.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions,.openassessment .list--actions .action--submit .icon{font-size:16px;line-height:24px}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon,.openassessment .submission__peer-evaluations__questions .question__title .icon,.openassessment .submission__feedback__title .icon{font-size:18px;line-height:27px}.wrapper--xblock .is--transitioning .transition__status .icon{font-size:22px;line-height:33px}.wrapper--xblock .problem__header,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{text-transform:uppercase;letter-spacing:0.05rem}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .problem__header{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__content a,.openassessment .submission__answer__part__prompt__copy a{-webkit-transition:color .5s ease-in-out,border .5s ease-in-out;-moz-transition:color .5s ease-in-out,border .5s ease-in-out;transition:color .5s ease-in-out,border .5s ease-in-out;border-bottom:1px solid transparent;color:#0075b4;text-decoration:none}.wrapper--xblock .message .message__content a:hover,.openassessment .submission__answer__part__prompt__copy a:hover,.wrapper--xblock .message .message__content a:focus,.openassessment .submission__answer__part__prompt__copy a:focus{color:#065683;border-color:#065683}.wrapper--xblock .message .message__content a:active,.openassessment .submission__answer__part__prompt__copy a:active,.wrapper--xblock .message .message__content a.is--current,.openassessment .submission__answer__part__prompt__copy a.is--current{color:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;-moz-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;display:inline-block;cursor:pointer;text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username .icon,.openassessment .step--response .response__submission__content .action--save .icon,.openassessment .step--response .response__submission__actions .action--save .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save .icon,.openassessment .step--self-assessment .self-assessment__assessment .action--save .icon,.openassessment .action--submit .icon,.step--response .action--upload .icon{display:inline-block;vertical-align:middle}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{border-radius:4px}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .ui-slidable__container .ui-slidable,.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit{padding:10px 40px;background:#0075b4;border:1px solid #0075b4;color:#fff}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .list--actions .action--submit:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .list--actions .action--submit:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .list--actions .action--submit:focus{background:#065683;border-color:#065683}.openassessment .openassessment_student_info_form .current.action--submit-username,.openassessment .list--actions .current.action--submit,.openassessment .openassessment_student_info_form .active.action--submit-username,.openassessment .list--actions .active.action--submit{background:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form [disabled].action--submit-username,.openassessment .list--actions [disabled].action--submit{color:#b4b3b3;border-color:#b4b3b3;background:#f5f5f5;opacity:1}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{padding:10px 40px;background:#fff;border:1px solid #065683;color:#065683}.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{background:#0075b4;border-color:#0075b4;color:#fff}.openassessment .step--response .response__submission__content .current.action--save,.openassessment .step--response .response__submission__actions .current.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .current.action--save,.openassessment .step--self-assessment .self-assessment__assessment .current.action--save,.openassessment .current.action--submit,.step--response .current.action--upload,.openassessment .step--response .response__submission__content .active.action--save,.openassessment .step--response .response__submission__actions .active.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .active.action--save,.openassessment .step--self-assessment .self-assessment__assessment .active.action--save,.openassessment .active.action--submit,.step--response .active.action--upload{background:#fff;border-color:#065683;color:#065683}.openassessment .step--response .response__submission__content [disabled].action--save,.openassessment .step--response .response__submission__actions [disabled].action--save,.openassessment .step--peer-assessment .peer-assessment__assessment [disabled].action--save,.openassessment .step--self-assessment .self-assessment__assessment [disabled].action--save,.openassessment [disabled].action--submit,.step--response [disabled].action--upload{background:#fff;border-color:#b4b3b3;color:#b4b3b3;opacity:1}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder{opacity:0.0}.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea{border:1px solid #b9c1c9;padding:10px;color:#414141}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:focus,.wrapper--xblock input[type="email"]:focus,.wrapper--xblock input[type="password"]:focus,.wrapper--xblock textarea:focus{box-shadow:0 0 6px 0 rgba(29,157,217,0.4),inset 0 0 4px 0 rgba(0,0,0,0.15);border-color:#000}.wrapper--xblock .field{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .field-group__label{display:block;margin-bottom:10px}.wrapper--xblock .field textarea{width:100%}.wrapper--xblock .field .tip{display:block;color:#414141}.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label{margin-bottom:10px}.wrapper--xblock .field-group__label{margin-bottom:10px}.wrapper--xblock .field--radio .option,.wrapper--xblock .field--checkbox .option{margin-bottom:5px}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__input,.wrapper--xblock .field--checkbox .option__label{display:inline-block;vertical-align:middle}.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label{margin-bottom:0;color:#414141}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--checkbox .option__input{margin-right:10px}.wrapper--xblock .field--radio .option__input:checked+.option__label,.wrapper--xblock .field--checkbox .option__input:checked+.option__label{color:#000}.wrapper--xblock .message{margin-bottom:20px;border-radius:2px;padding:20px 20px;background:#f5f6f7}.wrapper--xblock .message .message__title{margin-bottom:5px;border-bottom:2px solid #d9d9d9;padding-bottom:5px}.wrapper--xblock .message .message__content{color:#414141}.wrapper--xblock .message .message__content p{margin-bottom:10px}.wrapper--xblock .message--error{background:#feeced}.wrapper--xblock .message--error .message__title{color:#414141;border-bottom-color:#b20610}.wrapper--xblock .message--warning{background:#fff9eb}.wrapper--xblock .message--warning .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--complete,.wrapper--xblock .step--student-training .message--correct,.step--student-training .wrapper--xblock .message--correct{background:#ecfaec}.wrapper--xblock .message--complete .message__title,.wrapper--xblock .step--student-training .message--correct .message__title,.step--student-training .wrapper--xblock .message--correct .message__title{color:#414141;border-bottom-color:#008100}.wrapper--xblock .message--incomplete,.wrapper--xblock .step--student-training .message--incorrect,.step--student-training .wrapper--xblock .message--incorrect{background:#fff9eb}.wrapper--xblock .message--incomplete .message__title,.wrapper--xblock .step--student-training .message--incorrect .message__title,.step--student-training .wrapper--xblock .message--incorrect .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--error{display:none;opacity:0.0}.wrapper--xblock .has--error .message--error{display:block;opacity:1.0}.wrapper--xblock .message--inline{padding:10px 20px;background:#5e5e5e}.wrapper--xblock .message--inline .message__title{margin-bottom:0;border:none;padding-bottom:0;color:#fff}.wrapper--xblock .message--inline.message--error{background:#b20610}.wrapper--xblock .message--inline.message--error .message__content{color:#fff}.wrapper--xblock .message--inline.message--warning{background:#ffc01f}.wrapper--xblock .message--inline.message--warning .message__content{color:#111}.wrapper--xblock .message--inline.message--warning .message__title{color:#111}.wrapper--xblock .is--transitioning{padding:40px 40px;background:#f5f6f7}.wrapper--xblock .is--transitioning .transition__status{position:relative;top:50%;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%);text-align:center}.wrapper--xblock .is--transitioning .transition__status .wrapper--anim{display:block;-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.wrapper--xblock .is--transitioning .transition__status .icon,.wrapper--xblock .is--transitioning .transition__status .copy{display:block;color:#414141}.wrapper--xblock .is--transitioning .transition__status .icon{margin-bottom:10px}.wrapper--grid{*zoom:1;max-width:100%;margin-left:auto;margin-right:auto}.wrapper--grid:before,.wrapper--grid:after{content:" ";display:table}.wrapper--grid:after{clear:both}/*! Hint.css - v1.3.1 - 2013-11-23 * http://kushagragour.in/lab/hint/ -* Copyright (c) 2013 Kushagra Gour; Licensed MIT */.hint,[data-hint]{position:relative;display:inline-block}.hint:before,.hint:after,[data-hint]:before,[data-hint]:after{position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);visibility:hidden;opacity:0;z-index:1000000;pointer-events:none;-webkit-transition:0.3s ease;-moz-transition:0.3s ease;transition:0.3s ease}.hint:hover:before,.hint:hover:after,.hint:focus:before,.hint:focus:after,[data-hint]:hover:before,[data-hint]:hover:after,[data-hint]:focus:before,[data-hint]:focus:after{visibility:visible;opacity:1}.hint:before,[data-hint]:before{content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1000001}.hint:after,[data-hint]:after{content:attr(data-hint);background:#383838;color:white;text-shadow:0 -1px 0px black;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;box-shadow:4px 4px 8px rgba(0,0,0,0.3)}.hint--top:before{border-top-color:#383838}.hint--bottom:before{border-bottom-color:#383838}.hint--left:before{border-left-color:#383838}.hint--right:before{border-right-color:#383838}.hint--top:before{margin-bottom:-12px}.hint--top:after{margin-left:-18px}.hint--top:before,.hint--top:after{bottom:100%;left:50%}.hint--top:hover:after,.hint--top:hover:before,.hint--top:focus:after,.hint--top:focus:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--bottom:before{margin-top:-12px}.hint--bottom:after{margin-left:-18px}.hint--bottom:before,.hint--bottom:after{top:100%;left:50%}.hint--bottom:hover:after,.hint--bottom:hover:before,.hint--bottom:focus:after,.hint--bottom:focus:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--right:before{margin-left:-12px;margin-bottom:-6px}.hint--right:after{margin-bottom:-14px}.hint--right:before,.hint--right:after{left:100%;bottom:50%}.hint--right:hover:after,.hint--right:hover:before,.hint--right:focus:after,.hint--right:focus:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--left:before{margin-right:-12px;margin-bottom:-6px}.hint--left:after{margin-bottom:-14px}.hint--left:before,.hint--left:after{right:100%;bottom:50%}.hint--left:hover:after,.hint--left:hover:before,.hint--left:focus:after,.hint--left:focus:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--error:after{background-color:#b34e4d;text-shadow:0 -1px 0px #592726}.hint--error.hint--top:before{border-top-color:#b34e4d}.hint--error.hint--bottom:before{border-bottom-color:#b34e4d}.hint--error.hint--left:before{border-left-color:#b34e4d}.hint--error.hint--right:before{border-right-color:#b34e4d}.hint--warning:after{background-color:#c09854;text-shadow:0 -1px 0px #6c5328}.hint--warning.hint--top:before{border-top-color:#c09854}.hint--warning.hint--bottom:before{border-bottom-color:#c09854}.hint--warning.hint--left:before{border-left-color:#c09854}.hint--warning.hint--right:before{border-right-color:#c09854}.hint--info:after{background-color:#3986ac;text-shadow:0 -1px 0px #193b4d}.hint--info.hint--top:before{border-top-color:#3986ac}.hint--info.hint--bottom:before{border-bottom-color:#3986ac}.hint--info.hint--left:before{border-left-color:#3986ac}.hint--info.hint--right:before{border-right-color:#3986ac}.hint--success:after{background-color:#458746;text-shadow:0 -1px 0px #1a321a}.hint--success.hint--top:before{border-top-color:#458746}.hint--success.hint--bottom:before{border-bottom-color:#458746}.hint--success.hint--left:before{border-left-color:#458746}.hint--success.hint--right:before{border-right-color:#458746}.hint--always:after,.hint--always:before{opacity:1;visibility:visible}.hint--always.hint--top:after,.hint--always.hint--top:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--bottom:after,.hint--always.hint--bottom:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--left:after,.hint--always.hint--left:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--always.hint--right:after,.hint--always.hint--right:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--rounded:after{border-radius:4px}.hint--bounce:before,.hint--bounce:after{-webkit-transition:opacity 0.3s ease,visibility 0.3s ease,-webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);-moz-transition:opacity 0.3s ease,visibility 0.3s ease,-moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);transition:opacity 0.3s ease,visibility 0.3s ease,transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24)}.openassessment .openassessment__steps__step{-webkit-transition:all 0.5s ease-in-out;-moz-transition:all 0.5s ease-in-out;transition:all 0.5s ease-in-out;-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;box-shadow:0 1px 1px 0 rgba(3,3,3,0.05);margin-bottom:20px;border:1px solid #d9d9d9;border-top:5px solid #d9d9d9;padding:10px 20px;background:#fff}.openassessment .openassessment__steps__step:last-child{margin-bottom:0}.openassessment .openassessment__steps__step:hover,.openassessment .openassessment__steps__step:focus{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}.openassessment .has--error.openassessment__steps__step:hover,.openassessment .has--error.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--showing.openassessment__steps__step{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);box-shadow:0 1px 1px 0 rgba(3,3,3,0.1)}.openassessment .is--loading.openassessment__steps__step{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);box-shadow:0 1px 1px 0 rgba(3,3,3,0.05)}.openassessment .is--empty.openassessment__steps__step:hover,.openassessment .is--empty.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--unavailable.openassessment__steps__step{border-top-color:#d9d9d9}.openassessment .is--unavailable.openassessment__steps__step:hover,.openassessment .is--unavailable.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--in-progress.openassessment__steps__step{border-top-color:#0075b4}.openassessment .is--complete.openassessment__steps__step{border-top-color:#008100}.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .has--error.openassessment__steps__step{border-top-color:#b20610}.openassessment .is--closed.openassessment__steps__step,.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .staff-assessment__display,.openassessment .step--peer-assessment .peer-assessment__display,.openassessment .step--self-assessment .self-assessment__display,.openassessment .submission__peer-evaluations,.openassessment .submission__feedback,.step--student-training .student-training__display{margin-bottom:20px}.openassessment .submission__feedback__title{margin-bottom:10px;border-bottom:2px solid #d9d9d9;padding-bottom:10px}.openassessment .staff-assessment__display__response p,.openassessment .step--peer-assessment .peer-assessment__display__response p,.openassessment .step--self-assessment .self-assessment__display__response p,.openassessment .submission__answer__display__content p,.openassessment .self-assessment__display__image p,.openassessment .peer-assessment__display__image p,.openassessment .staff-assessment__display__image p,.openassessment .submission__answer__display__image p,.openassessment .submission__peer-evaluations__questions p,.openassessment .submission__feeedback__content p,.openassessment .submission__feedback__actions p,.step--student-training .student-training__display__response p{margin-bottom:20px}.openassessment .staff-assessment__display__response p:last-child,.openassessment .step--peer-assessment .peer-assessment__display__response p:last-child,.openassessment .step--self-assessment .self-assessment__display__response p:last-child,.openassessment .submission__answer__display__content p:last-child,.openassessment .self-assessment__display__image p:last-child,.openassessment .peer-assessment__display__image p:last-child,.openassessment .staff-assessment__display__image p:last-child,.openassessment .submission__answer__display__image p:last-child,.openassessment .submission__peer-evaluations__questions p:last-child,.openassessment .submission__feeedback__content p:last-child,.openassessment .submission__feedback__actions p:last-child,.step--student-training .student-training__display__response p:last-child{margin-bottom:0}.openassessment .staff-assessment__display__response,.openassessment .submission__answer__part__prompt,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.step--student-training .student-training__display__response{box-shadow:inset 0 1px 2px 1px rgba(3,3,3,0.1);padding:20px 20px;background:#fdfdfd}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value,.openassessment .staff-assessment__display__response,.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--response .response__submission__content .submission__answer__part__text textarea,.openassessment .step--response .response__submission__content .submission__file__description textarea,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .submission__feedback__fields textarea,.step--student-training .student-training__display__response,.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{max-height:300px;overflow-y:scroll}.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label{color:#414141;display:inline-block;vertical-align:middle;margin-left:10px}.openassessment .step--response .response__submission__content .response__submission__label .list--actions,.openassessment .step--response .response__submission__actions .response__submission__label .list--actions,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label .list--actions,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label .list--actions{text-align:left}.openassessment .step--response .response__submission__content .list--actions__item,.openassessment .step--response .response__submission__actions .list--actions__item,.openassessment .step--peer-assessment .peer-assessment__assessment .list--actions__item,.openassessment .step--self-assessment .self-assessment__assessment .list--actions__item{text-align:left;margin-top:5px}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:block;margin-bottom:10px;min-width:215px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}@media screen and (min-width: 1300px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}.openassessment .staff-assessment__assessment .assessment__rubric__question,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question,.step--student-training .student-training__assessment .assessment__rubric__question{*zoom:1;margin-bottom:40px}.openassessment .staff-assessment__assessment .assessment__rubric__question:before,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:before,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:before,.step--student-training .student-training__assessment .assessment__rubric__question:before,.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{content:" ";display:table}.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{clear:both}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title,.step--student-training .student-training__assessment .assessment__rubric__question .question__title{border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy{margin-left:10px;white-space:pre-wrap;display:inline}.openassessment .staff-assessment__assessment .question__answers,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers,.openassessment .step--self-assessment .self-assessment__assessment .question__answers,.step--student-training .student-training__assessment .question__answers{margin-top:20px;margin-bottom:20px;margin-left:20px}.openassessment .staff-assessment__assessment .question__answers .answer,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer,.step--student-training .student-training__assessment .question__answers .answer,.openassessment .staff-assessment__assessment .question__answers .answer--feedback,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback,.step--student-training .student-training__assessment .question__answers .answer--feedback{*zoom:1;display:block;margin-bottom:10px;border-bottom:1px solid #f5f6f7;padding-bottom:10px}.openassessment .staff-assessment__assessment .question__answers .answer:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:before,.step--student-training .student-training__assessment .question__answers .answer:before,.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:before,.step--student-training .student-training__assessment .question__answers .answer--feedback:before,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{content:" ";display:table}.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{clear:both}.openassessment .staff-assessment__assessment .question__answers .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input,.step--student-training .student-training__assessment .question__answers .wrapper--input{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{margin-bottom:0;color:#414141}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value{margin-right:10px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label{color:#000}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{display:block}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:left;display:block;margin-right:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:left;display:block;margin-right:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:left;display:block;margin-right:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-right:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{display:block}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{margin-bottom:10px;white-space:pre-wrap;margin-right:20px;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:left;display:block;margin-right:3.57866%;width:74.10533%;float:left;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:left;display:block;margin-right:3.57866%;width:74.10533%;float:left;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:left;display:block;margin-right:3.57866%;width:74.10533%;float:left;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-right:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:after,.step--student-training .student-training__assessment .question__answers .answer__tip:after{content:""}.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:right;text-align:right}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:right;text-align:right}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:right;text-align:right}}.openassessment .staff-assessment__assessment .question__answers .answer__points__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points__label,.step--student-training .student-training__assessment .question__answers .answer__points__label{display:inline-block;margin-left:5px;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:4.82916%;width:100%}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__label,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__label{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value{min-height:100px;margin-right:0}.has--hints{overflow:visible}.ui-hint .icon{margin-left:5px}.ui-hint:hover,.ui-hint:focus{color:#065683}.ui-hint:after{left:0;display:block;white-space:normal;width:200px;background:rgba(3,3,3,0.75)}.ui-hint.hint--top:after{left:0;margin-left:-20px}.openassessment .wrapper--ui-staff{box-shadow:inset 0 1px -2px 1px rgba(3,3,3,0.4);margin-top:40px;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;border:1px solid #891c4e;border-top:5px solid #b62568;padding:20px 20px;background:#2c2f36;display:none}.openassessment .ui-staff .ui-slidable__content{padding:10px 10px}.openassessment .ui-staff__title{color:#fff}.openassessment .ui-staff__subtitle{font-size:16px;color:#fff !important;margin-bottom:10px}.openassessment .ui-staff__subtitle span{font-weight:inherit}.openassessment .ui-staff__content{margin-top:20px;color:#fff}.openassessment .ui-staff__content__section{padding-bottom:10px}.openassessment .ui-staff__content__section .wrapper--input,.openassessment .ui-staff__content__section .wrapper--content{padding:20px 20px;background-color:#fff;color:#414141}.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value{display:inline-block;vertical-align:center;color:#fff}.openassessment .staff-info__summary .label{margin-right:10px}.openassessment .openassessment_student_info_form{margin-bottom:10px}.openassessment .openassessment_student_info_form .action--submit-username{margin-left:0}.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table{-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;word-break:initial}.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title{text-align:left;color:#fff;margin-bottom:10px}.openassessment .staff-info__status__table .label,.openassessment .staff-info__classifierset__table .label{color:#fff}.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value{color:#fff}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td,.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td{border:1px solid rgba(255,255,255,0.25);padding:10px 10px}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td[scope],.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td[scope]{text-align:left}.openassessment .wrapper--staff-assessment{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .staff-assessment__display__header{*zoom:1}.openassessment .staff-assessment__display__header:before,.openassessment .staff-assessment__display__header:after{content:" ";display:table}.openassessment .staff-assessment__display__header:after{clear:both}.openassessment .staff-assessment__display__header .staff-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .staff-assessment__display__response{color:#414141}.openassessment .staff-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-left:0;white-space:pre-wrap}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .ui-staff .staff__grade__control{border-top:5px solid #d9d9d9;background:#fff;background-color:#fff}.openassessment .ui-staff .staff__grade__control .staff__grade__header{margin-bottom:0}.openassessment .ui-staff .staff__grade__control .staff__grade__title{text-align:left;float:none;padding:0 20px;display:block;width:100%}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form{font-size:16px;background-color:#fff;text-transform:none;letter-spacing:normal;padding:0}.openassessment .ui-staff .staff__grade__control .staff__grade__status{display:inline-block;padding:0 20px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:right;position:relative;top:-40px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:right;position:relative;top:-40px}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:right;position:relative;top:-40px}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{border-radius:2px;padding:5px 10px;background:#d9d9d9;line-height:0}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy{color:#414141}.openassessment .ui-staff .staff__grade__control .submit_assessment--action{display:inline}.openassessment .ui-staff .staff__grade__control .staff__grade__content{padding:0}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .comments__label{color:#414141}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .cancel_submission_comments{width:100%;min-height:100px;text-align:left}.openassessment .staff-info__student__report .staff-info__final__grade__table{margin-top:10px}.wrapper--openassessment{position:relative}.openassessment{direction:ltr}.openassessment .openassessment__title{*zoom:1;display:block}.openassessment .openassessment__title:before,.openassessment .openassessment__title:after{content:" ";display:table}.openassessment .openassessment__title:after{clear:both}.openassessment .openassessment__message{*zoom:1;display:block;background:none;padding:0}.openassessment .openassessment__message:before,.openassessment .openassessment__message:after{content:" ";display:table}.openassessment .openassessment__message:after{clear:both}.openassessment .list--actions{margin-bottom:10px;text-align:center}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .list--actions{text-align:right}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .list--actions{text-align:right}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .list--actions{text-align:right}}@media screen and (min-width: 1300px){.openassessment .list--actions{text-align:right}}.openassessment .list--actions.has--error .message{margin-bottom:20px;text-align:left}.openassessment .list--actions .action--submit{margin-left:10px;margin-top:10px}.openassessment .list--actions .action--submit .copy,.openassessment .list--actions .action--submit .icon{display:inline}.openassessment .list--actions .action--submit .fa-caret-right:before{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}.openassessment .openassessment__steps{counter-reset:item}.openassessment .openassessment__steps__step .step__title{display:block;margin-bottom:10px;width:100%;margin-bottom:0}.openassessment .openassessment__steps__step .step__title .step__counter,.openassessment .openassessment__steps__step .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-left:20px;border-left:1px solid #d9d9d9;padding-left:20px}@media screen and (max-width: 529px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-left:0;border-left:none;padding-left:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-left:0;border-left:none;padding-left:0}}.openassessment .openassessment__steps__step .step__title .step__counter{display:none}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:none;content:counter(item, decimal);counter-increment:item;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .openassessment__steps__step .step__title .step__deadline{display:block}.openassessment .openassessment__steps__step .step__title .step__deadline{color:#414141}.openassessment .openassessment__steps__step .step__status{display:inline;margin-top:5px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:right;position:relative;top:-12px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:right;position:relative;top:-12px}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:right;position:relative;top:-12px}}.openassessment .openassessment__steps__step .step__status .step__status__value{border-radius:2px;padding:4px 10px;background:#d9d9d9;position:relative}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}.openassessment .openassessment__steps__step .step__status .icon{display:inline-block;vertical-align:baseline;margin-right:10px}.openassessment .openassessment__steps__step .step__status .copy{color:#414141}.openassessment .openassessment__steps__step .wrapper--step__content{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .openassessment__steps__step .step__message{*zoom:1;display:block;margin-bottom:20px}.openassessment .openassessment__steps__step .step__message:before,.openassessment .openassessment__steps__step .step__message:after{content:" ";display:table}.openassessment .openassessment__steps__step .step__message:after{clear:both}.openassessment .openassessment__steps__step .step__instruction{margin-bottom:20px;color:#414141}.openassessment .openassessment__steps__step .step__content{margin-bottom:20px}.openassessment .openassessment__steps__step .step__content__section{margin-bottom:40px}.openassessment .openassessment__steps__step.is--loading .step__header{padding-bottom:0;border-bottom:none;margin-bottom:0}.openassessment .openassessment__steps__step.is--loading .step__counter:before,.openassessment .openassessment__steps__step.is--loading .step__label,.openassessment .openassessment__steps__step.is--loading .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--loading .step__status__value .wrapper--anim{-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.openassessment .openassessment__steps__step.is--loading .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__counter:before{border-color:#ffc01f;color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__status__value{background:#ffc01f}.openassessment .openassessment__steps__step.is--incomplete .step__status__value .copy,.openassessment .openassessment__steps__step.is--incomplete .step__status__value .icon{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__counter:before,.openassessment .openassessment__steps__step.is--unavailable .step__label,.openassessment .openassessment__steps__step.is--unavailable .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__counter:before,.openassessment .openassessment__steps__step.is--waiting .step__label,.openassessment .openassessment__steps__step.is--waiting .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__title .grade__value__title{color:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__instruction,.openassessment .openassessment__steps__step.is--waiting .grade__value__description{color:#414141}.openassessment .openassessment__steps__step.is--complete .step__counter:before{color:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value{background:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--complete .step__status__value .copy,.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--complete button{opacity:1}.openassessment .openassessment__steps__step.is--in-progress .step__counter:before{color:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value{background:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .copy,.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--in-progress button{opacity:1}.openassessment .openassessment__steps__step.has--error .step__counter:before{border-color:rgba(178,6,16,0.5);color:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value{background:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value .copy,.openassessment .openassessment__steps__step.has--error .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.has--error .step__label{color:#b20610}.openassessment .ui-slidable__container{-webkit-transition:color 0.25s ease-in;-moz-transition:color 0.25s ease-in;transition:color 0.25s ease-in}.openassessment .ui-slidable__container .ui-slidable__content{width:100%}.openassessment .ui-slidable__container .icon{-webkit-transition:all 0.25s ease-in-out 0s;-moz-transition:all 0.25s ease-in-out 0s;transition:all 0.25s ease-in-out 0s;-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}.openassessment .ui-slidable__container.is--initially--collapsed .ui-slidable__content{display:none}.openassessment .ui-slidable__container.is--showing .ui-slidable .icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%;-ms-transform-origin:50% 50%;-o-transform-origin:50% 50%;transform-origin:50% 50%}.openassessment .ui-slidable__container.is--showing .ui-slidable__container:not(.is--showing) .icon{-webkit-transition:all 0.25s ease-in-out 0s;-moz-transition:all 0.25s ease-in-out 0s;transition:all 0.25s ease-in-out 0s;-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}.openassessment .ui-slidable__container .ui-slidable{background:none;border-radius:5px;padding:0px}.openassessment .ui-slidable__container .ui-slidable:active,.openassessment .ui-slidable__container .ui-slidable:focus:not(:disabled),.openassessment .ui-slidable__container .ui-slidable:hover:not(:disabled){background:none;border:none;box-shadow:none}.openassessment .ui-slidable__container .ui-slidable:not([disabled]){color:#0075b4}.openassessment .ui-slidable__container .ui-slidable:not([disabled]):hover,.openassessment .ui-slidable__container .ui-slidable:not([disabled]):focus{color:#065683}.openassessment .ui-slidable__container .ui-slidable .wrapper--copy{text-align:left}.openassessment .submission__answer__part__prompt{position:relative;margin:10px 0;border:1px solid #d9d9d9;border-left:10px solid #b9c1c9;padding-left:30px}.openassessment .submission__answer__part__prompt__copy{color:#414141}.openassessment .submission__answer__part__prompt__copy>*{margin-bottom:20px}.openassessment .submission__answer__part__text__value{margin:10px 0}.openassessment .submission__answer__part__text__value:not(textarea){border:1px solid #b9c1c9;background-color:#fff;padding:20px}.openassessment .submission__file__description__label{margin-bottom:5px}.openassessment .submission__answer__file__block{margin-bottom:8px}.openassessment .submission__img__preview{float:left;margin-right:10px}.openassessment .step--response .response__submission{padding:10px 10px 20px 10px;background:#f5f6f7;border-radius:2px}.openassessment .step--response .response__submission__content{margin-bottom:20px}.openassessment .step--response .response__submission__content .files__descriptions{display:none}.openassessment .step--response .response__submission__content .files__descriptions .submission__file__description{padding-bottom:10px}.openassessment .step--response .response__submission__content .submission__answer__part__text textarea{min-height:200px}.openassessment .step--response .response__submission__content .submission__file__description textarea{min-height:80px;width:70%}.openassessment .step--response .response__submission__content .tip{padding:5px 10px;background:#b9c1c9;color:#fff}.openassessment .step--peer-assessment .peer-assessment__display__header{*zoom:1}.openassessment .step--peer-assessment .peer-assessment__display__header:before,.openassessment .step--peer-assessment .peer-assessment__display__header:after{content:" ";display:table}.openassessment .step--peer-assessment .peer-assessment__display__header:after{clear:both}.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--peer-assessment .peer-assessment__display__response{color:#414141}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-left:0;white-space:pre-wrap}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .step--self-assessment .self-assessment__display__header{*zoom:1}.openassessment .step--self-assessment .self-assessment__display__header:before,.openassessment .step--self-assessment .self-assessment__display__header:after{content:" ";display:table}.openassessment .step--self-assessment .self-assessment__display__header:after{clear:both}.openassessment .step--self-assessment .self-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--self-assessment .self-assessment__display__response{color:#414141}.openassessment .step--self-assessment .self-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--self-assessment .self-assessment__assessment .question__answers{overflow:visible}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-left:0;white-space:pre-wrap}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}@media screen and (max-width: 529px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:7.42297%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:4.82916%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}.openassessment .step--grade .step__title .step__counter,.openassessment .step--grade .step__title .step__counter:before{display:none}.openassessment .step--grade .step__title .wrapper--copy{margin-left:0;border-left:none;padding-left:0}.openassessment .step--grade .step__title .step__label,.openassessment .step--grade .step__title .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--grade .step__title .step__label{margin-right:10px}.openassessment .step--grade .grade__value__description{margin-bottom:20px;color:#414141}.openassessment .submission__answer__display__title{margin-bottom:10px;color:#414141}.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image{padding:10px 10px 20px 10px;border-radius:2px;list-style:none}.openassessment .submission__answer__display__content .submission__answer__part,.openassessment .self-assessment__display__image .submission__answer__part,.openassessment .peer-assessment__display__image .submission__answer__part,.openassessment .staff-assessment__display__image .submission__answer__part,.openassessment .submission__answer__display__image .submission__answer__part{margin:30px 0px 0px}.openassessment .submission__answer__display__content .submission__answer__part:first-child,.openassessment .self-assessment__display__image .submission__answer__part:first-child,.openassessment .peer-assessment__display__image .submission__answer__part:first-child,.openassessment .staff-assessment__display__image .submission__answer__part:first-child,.openassessment .submission__answer__display__image .submission__answer__part:first-child{margin-top:0px}.openassessment .submission__peer-evaluations__title{margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions{margin-bottom:10px}.openassessment .submission__peer-evaluations__questions:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0}.openassessment .submission__peer-evaluations__questions .question{margin-bottom:30px}.openassessment .submission__peer-evaluations__questions .question__title{*zoom:1;margin-bottom:20px;border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .submission__peer-evaluations__questions .question__title:before,.openassessment .submission__peer-evaluations__questions .question__title:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__title:after{clear:both}.openassessment .submission__peer-evaluations__questions .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__peer-evaluations__questions .question__title__copy{margin-left:10px;white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .question__score{float:right;margin-left:20px}.openassessment .submission__peer-evaluations__questions .question__score__value{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__value:after{content:"/";margin-left:5px;color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential .unit{margin-left:5px}.openassessment .submission__peer-evaluations__questions .question__answers{*zoom:1;display:block;overflow:visible}.openassessment .submission__peer-evaluations__questions .question__answers:before,.openassessment .submission__peer-evaluations__questions .question__answers:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__answers:after{clear:both}.openassessment .submission__peer-evaluations__questions .answer{margin-bottom:20px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:4.82916%;width:47.58542%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n+1){clear:left}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:left}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:left}}@media screen and (min-width: 1300px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:left}}.openassessment .submission__peer-evaluations__questions .answer.self-assessment{border-left:5px solid #f5f6f7;padding-left:20px}.openassessment .submission__peer-evaluations__questions .answer.self-assessment .answer__source{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__source{display:block;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value{display:block;margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value .answer__value__raw{white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .answer__value__value{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:hover,.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:focus{color:#065683}.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value{display:block;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .answer--feedback{float:left;display:block;margin-right:4.82916%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .answer--feedback{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .answer--feedback{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .answer--feedback{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}.openassessment .answer--feedback .answer--feedback__title__copy{margin-left:10px}.openassessment .answer--feedback .answer--feedback__content{margin-top:5px}.openassessment .answer--feedback .feedback{margin-bottom:20px}.openassessment .answer--feedback .feedback__source{display:block;color:#414141}.openassessment .answer--feedback .feedback__value{display:block}.openassessment .answer--feedback .feedback__value .feedback__value__raw{white-space:pre-wrap}.openassessment .submission__feedback__title{border-bottom-color:solid #414141;color:#414141}.openassessment .submission__feedback__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__feedback__title__copy{margin-left:10px}.openassessment .submission__feeedback__content{margin-bottom:20px}.openassessment .submission__feedback__instructions{margin-bottom:20px;color:#414141}.openassessment .submission__feedback__elements{padding:20px 20px 20px 20px;background:#f5f6f7;border-radius:2px}.openassessment .submission__feedback__fields{margin-bottom:20px}.openassessment .submission__feedback__fields .field{margin-bottom:30px}.openassessment .submission__feedback__fields .field:last-child{margin-bottom:0}.openassessment .submission__feedback__fields select{display:block}.openassessment .submission__feedback__fields textarea{min-height:200px}.openassessment .submission__feedback__actions{padding-top:0}.openassessment .submission__feedback__actions .list--actions{padding:0}.openassessment .action--submit{margin-right:10px}.open-response-assessment-block table{table-layout:fixed}.open-response-assessment-block .open-response-assessment-msg{margin-bottom:20px}.open-response-assessment-block .open-response-assessment-content,.open-response-assessment-block .open-response-assessment-item{display:none}.open-response-assessment-block .open-response-assessment-summary td{border:none}.open-response-assessment-block .open-response-assessment-item-breadcrumbs{padding-bottom:20px}.open-response-assessment-block .open-response-assessment-item-breadcrumbs a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table th{background-color:#ffffff;text-align:left}.open-response-assessment-block .open-response-assessment-main-table th button{background:none;border:none;box-shadow:none;outline:none;font-size:14px}.open-response-assessment-block .open-response-assessment-main-table th a{color:#000000 !important}.open-response-assessment-block .open-response-assessment-main-table td a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table tbody tr:nth-child(odd){background-color:#f9f9f9}.open-response-assessment-block .ora-summary-value,.open-response-assessment-block .backgrid{text-overflow:ellipsis;overflow:hidden}.open-response-assessment-block .ora-summary-title{color:#0079bc;font-weight:bold;text-transform:uppercase;font-size:0.8em}.open-response-assessment-block .ora-summary-value{font-size:1.5em;padding-top:3px}.open-response-assessment-block .backgrid .peer,.open-response-assessment-block .backgrid .self,.open-response-assessment-block .backgrid .staff,.open-response-assessment-block .backgrid .waiting,.open-response-assessment-block .backgrid .training{width:80px}.open-response-assessment-block .backgrid .total{width:130px}.open-response-assessment-block .backgrid .done{width:160px}.view--placeholder{background:transparent;margin:40px 40px}.wrapper--openassessment .wrapper--staff-area{width:100%;overflow:auto;margin-top:20px;padding-top:20px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar{position:relative;text-align:right;margin:0 0 8px;padding:10px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{padding:5px 10px;font-size:12px;border-radius:5px;text-transform:uppercase;color:#414141;background-color:rgba(3,3,3,0.05)}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active{color:white;background-color:#b62568}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active:hover{background-color:#921e53}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button:hover{color:white;background-color:#0075b4}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff{margin-top:0}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button{margin:0;padding:0;border:0;float:right;background-color:#2c2f36;font-size:12px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button:hover{color:#fff}.staff-info__student .label{color:#fff;margin-bottom:10px}.staff-info__student .label input{display:block;padding-top:0;padding-bottom:0}.staff-info__student .title{color:#fff;margin-bottom:10px}.staff-info__student .title--sub{color:#fff;margin-top:10px;margin-bottom:10px}.staff-info__student .student__answer__display__content{border:1px solid rgba(255,255,255,0.25);padding:10px 10px;margin-bottom:10px}.staff-info__student .staff-info__student__report{list-style-type:none}.staff-info__student .staff-info__student__report .title{margin-top:10px;border-top:1px solid #fff;padding:10px 20px 10px 0}.staff-info__student .staff-info__student__report .title span{font-weight:inherit}.staff-info__student .value{width:50%}.step--student-training .student-training__display__header{*zoom:1}.step--student-training .student-training__display__header:before,.step--student-training .student-training__display__header:after{content:" ";display:table}.step--student-training .student-training__display__header:after{clear:both}.step--student-training .student-training__display__title{margin-bottom:10px;color:#414141}.step--student-training .student-training__display__response{color:#414141}.step--student-training .student-training__assessment .assessment__fields{margin-bottom:20px}.step--student-training .student-training__assessment .question__answers{overflow:visible}.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.step--student-training .message--correct{margin-top:20px}.step--student-training .message--correct.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--correct.is--hidden .step__header{border-bottom:none}.step--student-training .message--incorrect{margin-top:20px}.step--student-training .message--incorrect.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--incorrect.is--hidden .step__header{border-bottom:none}#openassessment-editor{margin-bottom:0;height:100%}#openassessment-editor .openassessment_editor_content_and_tabs{width:100%;height:calc(100% - 55px)}#openassessment-editor #oa_editor_window_title{float:left}#openassessment-editor input[type="number"]{background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;width:45%}#openassessment-editor #openassessment_editor_header{background-color:#e5e5e5;width:100%;top:0;height:42px}#openassessment-editor #openassessment_editor_header .editor_tabs{width:35%;min-width:190px;max-width:360px;float:right}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab{float:right;padding:0;width:29%;margin:0px 2%;height:35px;-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px;box-shadow:none;border:0;text-align:center}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab a{padding:8px 0 0 0;width:100%;text-transform:uppercase;outline-color:transparent}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab{background:#e5e5e5 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab a{color:#424243}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab{background:#626365 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover a,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab a{color:#fff}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab:hover{background-color:rgba(164,165,168,0.75)}#openassessment-editor #openassessment_validation_alert{width:100%;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;background-color:#323232;border-bottom:3px solid #c0ac00;padding:10px;position:absolute;z-index:10;max-height:200px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon{position:absolute;top:35%;font-size:200%;left:3%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon:before{font-family:FontAwesome;content:"\f071";display:inline-block;color:#c0ac00;float:left;height:0;width:0}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header{width:85%;margin:0 5% 0 10%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_title{width:auto;color:white}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_message{font-size:80%;color:darkgray}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close{display:inline-block;position:absolute;top:0px;right:0px;color:#e9e9e9;text-align:center;margin:5px 10px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close [class^="icon"]{width:auto;margin:0;padding:2px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close:hover{color:#80a9cc}#openassessment-editor .oa_editor_content_wrapper{height:calc(100% - 42px);width:100%;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;border:1px solid #838486;background-color:white;overflow-y:scroll;position:absolute;bottom:0;z-index:11;transition:height 1s ease-in-out 0;-webkit-transition:height 1s ease-in-out 0;-moz-transition:height 1s ease-in-out 0}#openassessment-editor #openassessment_rubric_editor{width:100%;height:100%}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper label{width:25%;text-align:left}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper input{width:45%;min-width:100px}#openassessment-editor #openassessment_step_select_description{padding:10px 10px 0 10px;font-size:80%;border-top:1px solid #838486;margin:7.5px}#openassessment-editor .openassessment_assessment_module_settings_editor.openassessment_shrink_for_drag{height:40px}#openassessment-editor #ai_training_examples{margin:5px 20px 10px 10px;height:300px;width:calc(100% - 20px);resize:none;font:inherit;line-height:110%;font-size:80%;padding:5px 10px}#openassessment-editor .openassessment_assessment_module_settings_editor{padding:0 15px 0 0;margin:10px;border:1px solid #dbdbdc;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;position:relative;background-color:white}#openassessment-editor .openassessment_assessment_module_settings_editor .drag-handle{position:absolute;background:center rgba(164,165,168,0.25);display:block;top:0px;right:0px;z-index:10;width:15px;height:100%;border-left:1px solid #dbdbdc;cursor:move;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description_closed{padding:5px 15px 5px 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description{padding:5px 15px 0 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor.ui-sortable-helper{opacity:50%;border-color:#009fe6}#openassessment-editor .openassessment_assessment_module_settings_editor:hover{border-color:#80a9cc}#openassessment-editor .openassessment_assessment_module_settings_editor:hover .drag-handle{background-color:#009fe6;border-color:#009fe6;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_description{font-size:75%;margin:0}#openassessment-editor .openassessment_inclusion_wrapper{background-color:rgba(164,165,168,0.25);margin:0;padding:7px 5px 7px 13px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px}#openassessment-editor .openassessment_inclusion_wrapper label{cursor:pointer;margin-left:10px}#openassessment-editor .openassessment_inclusion_wrapper:hover{background-color:rgba(164,165,168,0.5)}#openassessment-editor label{padding-right:10px}#openassessment-editor .xblock_actions{background-color:#c8c9cb;position:absolute;width:100%;bottom:0}#openassessment-editor .openassessment_tab_instructions{background-color:rgba(164,165,168,0.25);border-bottom:1px solid #838486;padding:10px}#openassessment-editor .openassessment_container_header{margin:10px 10px;padding-bottom:5px;border-bottom:1px solid;overflow:auto;color:#626365}#openassessment-editor .action.expand-collapse{float:left}#openassessment-editor .action.expand-collapse .ui-toggle-expansion{color:#626365;cursor:pointer}#openassessment-editor .action.expand-collapse .ui-toggle-expansion:hover{color:#838486;cursor:pointer}#openassessment-editor .openassessment_container_header_title_box{float:left;width:80%;display:inline-block}#openassessment-editor .openassessment_container_header_title{text-transform:uppercase;cursor:default;padding-top:2px}#openassessment-editor .openassessment_container_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove{cursor:pointer;float:right}#openassessment-editor .openassessment_container_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#openassessment-editor .openassessment_container_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2{text-transform:uppercase;font-size:80%;float:right;display:inline-block;padding:3px 8px 3px 13px}#openassessment-editor .openassessment_container_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove:hover{background-color:#626365;border-radius:4px;color:white}#openassessment-editor .openassessment_container_add_button{background-color:white;border:1px solid;border-radius:4px;text-align:center;color:#009fe6;padding:10px;margin:15px 10px}#openassessment-editor .openassessment_container_add_button h6:before{font-family:"FontAwesome";display:inline-block;margin-left:5px;margin-right:10px;width:auto;height:auto;content:"\f067"}#openassessment-editor .openassessment_container_add_button.openassessment_highlighted_field{color:red;border-width:2px}#openassessment-editor .openassessment_container_add_button:hover{color:white;background-color:#009fe6;cursor:pointer}#openassessment-editor .openassessment_container_remove_button.is--disabled,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove,#openassessment-editor .openassessment_container_remove_button.is--disabled:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove:hover,#openassessment-editor .openassessment_container_add_button.is--disabled,#openassessment-editor .openassessment_container_add_button.is--disabled:hover{color:#c8c9cb;background-color:transparent;cursor:auto}#openassessment-editor #oa_prompts_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description{width:100%;min-height:100px;resize:none;border:1px solid #b2b2b2;border-radius:4px;padding:10px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;overflow:auto;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option{text-align:center;border:1px solid;color:#009fe6;background-color:white;padding:7.5px;margin:10px 10px 10px 20px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;cursor:pointer}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option h2:before{font-family:FontAwesome;content:"\f067";display:inline-block;margin:0 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option:hover{background-color:#009fe6;color:white}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry{padding-right:0;margin-right:10px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label{padding:0;margin:0;width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label input{font-size:11px;float:right;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt{padding:10px;width:70%;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_header{background-color:#c8c9cb;padding:5px;margin:10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label{width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label select{float:right;margin-right:24%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_direction label{margin-left:15px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option{padding:5px 5px 5px 15px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header{background-color:rgba(164,165,168,0.25);padding:5px 5px 5px 10px;margin:5px 5px 8px 5px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_title{cursor:default;float:left;padding:2.5px 0 0 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper{width:30%;border-top:none;padding:10px 5px 10px 0px;float:left;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper .wrapper-comp-setting{min-width:150px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label{width:100%;padding-left:calc((100% - 150px)/2);margin:0;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label input{min-width:50px;padding:10px;float:right;width:30%;font-size:11px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper{float:left;width:70%;padding:10px 10px 10px 20px;border-top:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label{width:100%;vertical-align:middle;padding:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label input{padding:10px;font-size:11px;width:56%;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper{padding:15px 5px 0px 20px;width:100%;display:inline-block;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label{width:100%;text-align:left;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea{padding:10px;height:70px;width:69.5%;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{height:70px;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_input_styling,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper{padding:0}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper #openassessment_rubric_feedback_header{background-color:rgba(164,165,168,0.25);padding:7.5px 7.5px 7.5px 15px;margin:10px 10px 0 10px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper label{padding-top:10px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{padding:10px;width:70%;float:right}#openassessment-editor hr{background-color:transparent;color:#424243;height:1px;border:0px;clear:both}.modal-lg.modal-window.confirm.openassessment_modal_window{height:80%;top:10%;min-width:600px}.openassessment_full_height.edit-xblock-modal,.openassessment_full_height.xblock-editor,.openassessment_full_height.xblock.xblock-studio_view.xblock-initialized,[class*="view-"] .modal-lg.modal-editor .openassessment_full_height.modal-content{height:100%}#openassessment-editor .openassessment_highlighted_field{border-color:red;border-width:2px}#student_training_settings_editor .openassessment_training_example{padding:5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header{margin:10px;padding:5px;border-bottom:1px solid;overflow:auto;color:#626365}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_header_title{text-transform:uppercase;width:50%;display:inline-block;float:left;padding-top:2px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove{padding:3px 5px 3px 7px;cursor:pointer;border-radius:4px;float:right}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2{text-transform:uppercase;font-size:80%;float:right;display:inline-block}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove:hover{background-color:lightgray}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body{padding:0 15px 15px 15px;position:relative;overflow:scroll}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper{width:58%;display:inline-block;position:absolute;left:15px;height:100%}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper textarea{min-height:90px;height:calc(100% - 35px);resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;padding:6px 8px 8px 8px;background-color:white;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c;margin:5px 15px 0 10px;width:calc(100% - 25px)}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric{width:40%;display:inline-block;float:right;min-height:150px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry{margin:0;padding:10px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry .wrapper-comp-setting{min-width:100px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label{width:100%;margin-left:0}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label select{width:60%;float:right}#student_training_settings_editor .openassessment_add_training_example{color:#009fe6;text-align:center;padding:5px;border:1px solid;border-radius:4px;margin:10px}#student_training_settings_editor .openassessment_add_training_example:hover{color:white;background-color:#009fe6;cursor:pointer}#student_training_settings_editor .openassessment_add_training_example:before{font-family:"FontAwesome";display:inline-block;width:auto;height:auto;content:"\f067";margin:0 10px}#openassessment-editor .wrapper-comp-settings .list-input.settings-list .field.comp-setting-entry select{padding:5px}.step--response .action--upload{text-align:center;float:right;display:inline-block;margin:10px 0;box-shadow:none}.step--response .file--upload{margin:20px 10px}.self-assessment__display__header .self-assessment__display__title,.peer-assessment__display__header .peer-assessment__display__title,.staff-assessment__display__header .staff-assessment__display__title,.submission__answer__display .submission__answer__display__title{margin:10px 0}.self-assessment__display__image,.peer-assessment__display__image,.staff-assessment__display__image,.submission__answer__display__image{max-height:400px;text-align:left;overflow:auto}.self-assessment__display__image img,.peer-assessment__display__image img,.staff-assessment__display__image img,.submission__answer__display__image img{max-height:100%;max-width:100%}.submission__answer__display__image .submission--image{max-height:250px;max-width:100%}.openassessment .step--leaderboard{font-family:"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif}.openassessment .step--leaderboard .step__title .step__counter,.openassessment .step--leaderboard .step__title .step__counter:before{display:none}.openassessment .step--leaderboard .step__title .wrapper--copy{margin-left:0;padding-left:0;border-left:0}.openassessment .step--leaderboard .step__label,.openassessment .step--leaderboard .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--leaderboard .step__label{margin-right:10px}.openassessment .step--leaderboard .list.leaderboard__score__list{list-style-type:none}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item{margin:15px 0}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__list__number{display:inline-block;background:#626365;color:white;padding:5px 5px 3px 5px;font-size:16px;min-width:35px;text-align:center;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__image{max-height:150px;max-width:100%;margin-right:20px;margin-bottom:20px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__title{font-size:15px;color:#414141;text-transform:uppercase;display:inline-block;padding-left:15px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__answer{border-top:2px solid #626365;box-shadow:inset 0 0 3px 1px rgba(10,10,10,0.1);padding:10px;max-height:200px;overflow-y:scroll;font-size:14px}.wrapper--xblock .openassessment h3.staff__grade__title{font-size:inherit;margin-bottom:5px}.openassessment .self-assessment__display__header,.openassessment .peer-assessment__display__header,.openassessment .step__header,.openassessment .staff-assessment__display__header,.openassessment .submission__answer__display__header,.openassessment .student-training__display__header{margin-bottom:0 !important;border-radius:0 !important;padding-bottom:2px !important;font-size:initial !important;border-bottom:none !important}.openassessment span{line-height:normal}.openassessment h2{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__deadline{color:#414141}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title{text-align:left;float:none;margin-top:0 !important}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__label{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{content:counter(item, decimal);counter-increment:item;color:#414141}.openassessment .step__status__value{line-height:0}.openassessment .leaderboard__answer .submission__answer__display__content .submission__answer__part__text__title,.openassessment .leaderboard__answer .self-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .peer-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .staff-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .submission__answer__display__image .submission__answer__part__text__title{text-align:left;float:none;font-size:inherit;margin:0}.openassessment .list--actions{list-style:none !important;padding-left:0 !important;text-indent:0 !important}.openassessment .list--actions li{margin-bottom:0 !important;margin-top:5px !important}.openassessment .list--actions button span,.openassessment .list--actions .button span,.openassessment .list--actions .action span{color:inherit}.openassessment span{color:inherit}.openassessment .step--peer-assessment .list--peer-assessments{list-style:none outside none !important;padding-left:0}.openassessment .self-assessment__display__title,.openassessment .peer-assessment__display__title{margin-bottom:10px !important;color:#414141 !important}.openassessment .question__answers{padding-left:0}.openassessment .question__title.ui-slidable .question__title__copy{color:inherit !important}.openassessment .step--grade .step__title{width:100% !important}.openassessment .action--submit,.openassessment .action--save,.openassessment .action--upload{text-shadow:none !important;box-shadow:none !important}.openassessment .message p{color:inherit}.openassessment .icon{font-style:normal}.openassessment .step__title .step__deadline{letter-spacing:normal;text-transform:none}.openassessment .openassessment__steps__step .step__header .step__title{margin-bottom:0}.openassessment .is--collapsed .question__answers{overflow:auto}.openassessment .wrapper--staff-assessment .student__answer__display__content p{color:inherit} +* Copyright (c) 2013 Kushagra Gour; Licensed MIT */.hint,[data-hint]{position:relative;display:inline-block}.hint:before,.hint:after,[data-hint]:before,[data-hint]:after{position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);visibility:hidden;opacity:0;z-index:1000000;pointer-events:none;-webkit-transition:0.3s ease;-moz-transition:0.3s ease;transition:0.3s ease}.hint:hover:before,.hint:hover:after,.hint:focus:before,.hint:focus:after,[data-hint]:hover:before,[data-hint]:hover:after,[data-hint]:focus:before,[data-hint]:focus:after{visibility:visible;opacity:1}.hint:before,[data-hint]:before{content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1000001}.hint:after,[data-hint]:after{content:attr(data-hint);background:#383838;color:white;text-shadow:0 -1px 0px black;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;box-shadow:4px 4px 8px rgba(0,0,0,0.3)}.hint--top:before{border-top-color:#383838}.hint--bottom:before{border-bottom-color:#383838}.hint--left:before{border-left-color:#383838}.hint--right:before{border-right-color:#383838}.hint--top:before{margin-bottom:-12px}.hint--top:after{margin-left:-18px}.hint--top:before,.hint--top:after{bottom:100%;left:50%}.hint--top:hover:after,.hint--top:hover:before,.hint--top:focus:after,.hint--top:focus:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--bottom:before{margin-top:-12px}.hint--bottom:after{margin-left:-18px}.hint--bottom:before,.hint--bottom:after{top:100%;left:50%}.hint--bottom:hover:after,.hint--bottom:hover:before,.hint--bottom:focus:after,.hint--bottom:focus:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--right:before{margin-left:-12px;margin-bottom:-6px}.hint--right:after{margin-bottom:-14px}.hint--right:before,.hint--right:after{left:100%;bottom:50%}.hint--right:hover:after,.hint--right:hover:before,.hint--right:focus:after,.hint--right:focus:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--left:before{margin-right:-12px;margin-bottom:-6px}.hint--left:after{margin-bottom:-14px}.hint--left:before,.hint--left:after{right:100%;bottom:50%}.hint--left:hover:after,.hint--left:hover:before,.hint--left:focus:after,.hint--left:focus:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--error:after{background-color:#b34e4d;text-shadow:0 -1px 0px #592726}.hint--error.hint--top:before{border-top-color:#b34e4d}.hint--error.hint--bottom:before{border-bottom-color:#b34e4d}.hint--error.hint--left:before{border-left-color:#b34e4d}.hint--error.hint--right:before{border-right-color:#b34e4d}.hint--warning:after{background-color:#c09854;text-shadow:0 -1px 0px #6c5328}.hint--warning.hint--top:before{border-top-color:#c09854}.hint--warning.hint--bottom:before{border-bottom-color:#c09854}.hint--warning.hint--left:before{border-left-color:#c09854}.hint--warning.hint--right:before{border-right-color:#c09854}.hint--info:after{background-color:#3986ac;text-shadow:0 -1px 0px #193b4d}.hint--info.hint--top:before{border-top-color:#3986ac}.hint--info.hint--bottom:before{border-bottom-color:#3986ac}.hint--info.hint--left:before{border-left-color:#3986ac}.hint--info.hint--right:before{border-right-color:#3986ac}.hint--success:after{background-color:#458746;text-shadow:0 -1px 0px #1a321a}.hint--success.hint--top:before{border-top-color:#458746}.hint--success.hint--bottom:before{border-bottom-color:#458746}.hint--success.hint--left:before{border-left-color:#458746}.hint--success.hint--right:before{border-right-color:#458746}.hint--always:after,.hint--always:before{opacity:1;visibility:visible}.hint--always.hint--top:after,.hint--always.hint--top:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--bottom:after,.hint--always.hint--bottom:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--left:after,.hint--always.hint--left:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--always.hint--right:after,.hint--always.hint--right:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--rounded:after{border-radius:4px}.hint--bounce:before,.hint--bounce:after{-webkit-transition:opacity 0.3s ease,visibility 0.3s ease,-webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);-moz-transition:opacity 0.3s ease,visibility 0.3s ease,-moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);transition:opacity 0.3s ease,visibility 0.3s ease,transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24)}.openassessment .openassessment__steps__step{-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;transition:all .5s ease-in-out;-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;box-shadow:0 1px 1px 0 rgba(3,3,3,0.05);margin-bottom:20px;border:1px solid #d9d9d9;border-top:5px solid #d9d9d9;padding:10px 20px;background:#fff}.openassessment .openassessment__steps__step:last-child{margin-bottom:0}.openassessment .openassessment__steps__step:hover,.openassessment .openassessment__steps__step:focus{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}.openassessment .has--error.openassessment__steps__step:hover,.openassessment .has--error.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--showing.openassessment__steps__step{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);box-shadow:0 1px 1px 0 rgba(3,3,3,0.1)}.openassessment .is--loading.openassessment__steps__step{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);box-shadow:0 1px 1px 0 rgba(3,3,3,0.05)}.openassessment .is--empty.openassessment__steps__step:hover,.openassessment .is--empty.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--unavailable.openassessment__steps__step{border-top-color:#d9d9d9}.openassessment .is--unavailable.openassessment__steps__step:hover,.openassessment .is--unavailable.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--in-progress.openassessment__steps__step{border-top-color:#0075b4}.openassessment .is--complete.openassessment__steps__step{border-top-color:#008100}.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .has--error.openassessment__steps__step{border-top-color:#b20610}.openassessment .is--closed.openassessment__steps__step,.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .staff-assessment__display,.openassessment .step--peer-assessment .peer-assessment__display,.openassessment .step--self-assessment .self-assessment__display,.openassessment .submission__peer-evaluations,.openassessment .submission__feedback,.step--student-training .student-training__display{margin-bottom:20px}.openassessment .submission__feedback__title{margin-bottom:10px;border-bottom:2px solid #d9d9d9;padding-bottom:10px}.openassessment .staff-assessment__display__response p,.openassessment .step--peer-assessment .peer-assessment__display__response p,.openassessment .step--self-assessment .self-assessment__display__response p,.openassessment .submission__answer__display__content p,.openassessment .self-assessment__display__image p,.openassessment .peer-assessment__display__image p,.openassessment .staff-assessment__display__image p,.openassessment .submission__answer__display__image p,.openassessment .submission__peer-evaluations__questions p,.openassessment .submission__feeedback__content p,.openassessment .submission__feedback__actions p,.step--student-training .student-training__display__response p{margin-bottom:20px}.openassessment .staff-assessment__display__response p:last-child,.openassessment .step--peer-assessment .peer-assessment__display__response p:last-child,.openassessment .step--self-assessment .self-assessment__display__response p:last-child,.openassessment .submission__answer__display__content p:last-child,.openassessment .self-assessment__display__image p:last-child,.openassessment .peer-assessment__display__image p:last-child,.openassessment .staff-assessment__display__image p:last-child,.openassessment .submission__answer__display__image p:last-child,.openassessment .submission__peer-evaluations__questions p:last-child,.openassessment .submission__feeedback__content p:last-child,.openassessment .submission__feedback__actions p:last-child,.step--student-training .student-training__display__response p:last-child{margin-bottom:0}.openassessment .staff-assessment__display__response,.openassessment .submission__answer__part__prompt,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.step--student-training .student-training__display__response{box-shadow:inset 0 1px 2px 1px rgba(3,3,3,0.1);padding:20px 20px;background:#fdfdfd}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value,.openassessment .staff-assessment__display__response,.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--response .response__submission__content .submission__answer__part__text textarea,.openassessment .step--response .response__submission__content .submission__file__description textarea,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .submission__feedback__fields textarea,.step--student-training .student-training__display__response,.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{max-height:300px;overflow-y:scroll}.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label{color:#414141;display:inline-block;vertical-align:middle;margin-left:10px}.openassessment .step--response .response__submission__content .response__submission__label .list--actions,.openassessment .step--response .response__submission__actions .response__submission__label .list--actions,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label .list--actions,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label .list--actions{text-align:left}.openassessment .step--response .response__submission__content .list--actions__item,.openassessment .step--response .response__submission__actions .list--actions__item,.openassessment .step--peer-assessment .peer-assessment__assessment .list--actions__item,.openassessment .step--self-assessment .self-assessment__assessment .list--actions__item{text-align:left;margin-top:5px}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:block;margin-bottom:10px;min-width:215px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}@media screen and (min-width: 1300px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-right:10px;margin-bottom:0}}.openassessment .staff-assessment__assessment .assessment__rubric__question,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question,.step--student-training .student-training__assessment .assessment__rubric__question{*zoom:1;margin-bottom:40px}.openassessment .staff-assessment__assessment .assessment__rubric__question:before,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:before,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:before,.step--student-training .student-training__assessment .assessment__rubric__question:before,.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{content:" ";display:table}.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{clear:both}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title,.step--student-training .student-training__assessment .assessment__rubric__question .question__title{border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy{margin-left:10px;white-space:pre-wrap;display:inline}.openassessment .staff-assessment__assessment .question__answers,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers,.openassessment .step--self-assessment .self-assessment__assessment .question__answers,.step--student-training .student-training__assessment .question__answers{margin-top:20px;margin-bottom:20px;margin-left:20px}.openassessment .staff-assessment__assessment .question__answers .answer,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer,.step--student-training .student-training__assessment .question__answers .answer,.openassessment .staff-assessment__assessment .question__answers .answer--feedback,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback,.step--student-training .student-training__assessment .question__answers .answer--feedback{*zoom:1;display:block;margin-bottom:10px;border-bottom:1px solid #f5f6f7;padding-bottom:10px}.openassessment .staff-assessment__assessment .question__answers .answer:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:before,.step--student-training .student-training__assessment .question__answers .answer:before,.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:before,.step--student-training .student-training__assessment .question__answers .answer--feedback:before,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{content:" ";display:table}.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{clear:both}.openassessment .staff-assessment__assessment .question__answers .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input,.step--student-training .student-training__assessment .question__answers .wrapper--input{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{margin-bottom:0;color:#414141}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value{margin-right:10px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label{color:#000}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{display:block}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:left;display:block;margin-right:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:left;display:block;margin-right:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:left;display:block;margin-right:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-right:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{display:block}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{margin-bottom:10px;white-space:pre-wrap;margin-right:20px;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:left;display:block;margin-right:3.57866%;width:74.10533%;float:left;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:left;display:block;margin-right:3.57866%;width:74.10533%;float:left;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:left;display:block;margin-right:3.57866%;width:74.10533%;float:left;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-right:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:after,.step--student-training .student-training__assessment .question__answers .answer__tip:after{content:""}.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:right;text-align:right}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:right;text-align:right}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:right;text-align:right}}.openassessment .staff-assessment__assessment .question__answers .answer__points__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points__label,.step--student-training .student-training__assessment .question__answers .answer__points__label{display:inline-block;margin-left:5px;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:4.82916%;width:100%}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:left;display:block;margin-right:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-right:0}}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__label,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__label{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value{min-height:100px;margin-right:0}.has--hints{overflow:visible}.ui-hint .icon{margin-left:5px}.ui-hint:hover,.ui-hint:focus{color:#065683}.ui-hint:after{left:0;display:block;white-space:normal;width:200px;background:rgba(3,3,3,0.75)}.ui-hint.hint--top:after{left:0;margin-left:-20px}.openassessment .wrapper--ui-staff{box-shadow:inset 0 1px -2px 1px rgba(3,3,3,0.4);margin-top:40px;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;border:1px solid #891c4e;border-top:5px solid #b62568;padding:20px 20px;background:#2c2f36;display:none}.openassessment .ui-staff .ui-slidable__content{padding:10px 10px}.openassessment .ui-staff__title{color:#fff}.openassessment .ui-staff__subtitle{font-size:16px;color:#fff !important;margin-bottom:10px}.openassessment .ui-staff__subtitle span{font-weight:inherit}.openassessment .ui-staff__content{margin-top:20px;color:#fff}.openassessment .ui-staff__content__section{padding-bottom:10px}.openassessment .ui-staff__content__section .wrapper--input,.openassessment .ui-staff__content__section .wrapper--content{padding:20px 20px;background-color:#fff;color:#414141}.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value{display:inline-block;vertical-align:center;color:#fff}.openassessment .staff-info__summary .label{margin-right:10px}.openassessment .openassessment_student_info_form{margin-bottom:10px}.openassessment .openassessment_student_info_form .action--submit-username{margin-left:0}.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table{-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;word-break:initial}.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title{text-align:left;color:#fff;margin-bottom:10px}.openassessment .staff-info__status__table .label,.openassessment .staff-info__classifierset__table .label{color:#fff}.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value{color:#fff}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td,.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td{border:1px solid rgba(255,255,255,0.25);padding:10px 10px}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td[scope],.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td[scope]{text-align:left}.openassessment .wrapper--staff-assessment{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .staff-assessment__display__header{*zoom:1}.openassessment .staff-assessment__display__header:before,.openassessment .staff-assessment__display__header:after{content:" ";display:table}.openassessment .staff-assessment__display__header:after{clear:both}.openassessment .staff-assessment__display__header .staff-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .staff-assessment__display__response{color:#414141}.openassessment .staff-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-left:0;white-space:pre-wrap}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .ui-staff .staff__grade__control{border-top:5px solid #d9d9d9;background:#fff;background-color:#fff}.openassessment .ui-staff .staff__grade__control .staff__grade__header{margin-bottom:0}.openassessment .ui-staff .staff__grade__control .staff__grade__title{text-align:left;float:none;padding:0 20px;display:block;width:100%}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form{font-size:16px;background-color:#fff;text-transform:none;letter-spacing:normal;padding:0}.openassessment .ui-staff .staff__grade__control .staff__grade__status{display:inline-block;padding:0 20px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:right;position:relative;top:-40px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:right;position:relative;top:-40px}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:right;position:relative;top:-40px}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{border-radius:2px;padding:5px 10px;background:#d9d9d9;line-height:0}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy{color:#414141}.openassessment .ui-staff .staff__grade__control .submit_assessment--action{display:inline}.openassessment .ui-staff .staff__grade__control .staff__grade__content{padding:0}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .comments__label{color:#414141}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .cancel_submission_comments{width:100%;min-height:100px;text-align:left}.openassessment .staff-info__student__report .staff-info__final__grade__table{margin-top:10px}.wrapper--openassessment{position:relative}.openassessment{direction:ltr}.openassessment .openassessment__title{*zoom:1;display:block}.openassessment .openassessment__title:before,.openassessment .openassessment__title:after{content:" ";display:table}.openassessment .openassessment__title:after{clear:both}.openassessment .openassessment__message{*zoom:1;display:block;background:none;padding:0}.openassessment .openassessment__message:before,.openassessment .openassessment__message:after{content:" ";display:table}.openassessment .openassessment__message:after{clear:both}.openassessment .list--actions{margin-bottom:10px;text-align:center}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .list--actions{text-align:right}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .list--actions{text-align:right}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .list--actions{text-align:right}}@media screen and (min-width: 1300px){.openassessment .list--actions{text-align:right}}.openassessment .list--actions.has--error .message{margin-bottom:20px;text-align:left}.openassessment .list--actions .action--submit{margin-left:10px;margin-top:10px}.openassessment .list--actions .action--submit .copy,.openassessment .list--actions .action--submit .icon{display:inline}.openassessment .list--actions .action--submit .fa-caret-right:before{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}.openassessment .openassessment__steps{counter-reset:item}.openassessment .openassessment__steps__step .step__title{display:block;margin-bottom:10px;width:100%;margin-bottom:0}.openassessment .openassessment__steps__step .step__title .step__counter,.openassessment .openassessment__steps__step .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-left:20px;border-left:1px solid #d9d9d9;padding-left:20px}@media screen and (max-width: 529px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-left:0;border-left:none;padding-left:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-left:0;border-left:none;padding-left:0}}.openassessment .openassessment__steps__step .step__title .step__counter{display:none}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:none;content:counter(item, decimal);counter-increment:item;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .openassessment__steps__step .step__title .step__deadline{display:block}.openassessment .openassessment__steps__step .step__title .step__deadline{color:#414141}.openassessment .openassessment__steps__step .step__status{display:inline;margin-top:5px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:right;position:relative;top:-12px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:right;position:relative;top:-12px}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:right;position:relative;top:-12px}}.openassessment .openassessment__steps__step .step__status .step__status__value{border-radius:2px;padding:4px 10px;background:#d9d9d9;position:relative}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}.openassessment .openassessment__steps__step .step__status .icon{display:inline-block;vertical-align:baseline;margin-right:10px}.openassessment .openassessment__steps__step .step__status .copy{color:#414141}.openassessment .openassessment__steps__step .wrapper--step__content{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .openassessment__steps__step .step__message{*zoom:1;display:block;margin-bottom:20px}.openassessment .openassessment__steps__step .step__message:before,.openassessment .openassessment__steps__step .step__message:after{content:" ";display:table}.openassessment .openassessment__steps__step .step__message:after{clear:both}.openassessment .openassessment__steps__step .step__instruction{margin-bottom:20px;color:#414141}.openassessment .openassessment__steps__step .step__content{margin-bottom:20px}.openassessment .openassessment__steps__step .step__content__section{margin-bottom:40px}.openassessment .openassessment__steps__step.is--loading .step__header{padding-bottom:0;border-bottom:none;margin-bottom:0}.openassessment .openassessment__steps__step.is--loading .step__counter:before,.openassessment .openassessment__steps__step.is--loading .step__label,.openassessment .openassessment__steps__step.is--loading .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--loading .step__status__value .wrapper--anim{-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.openassessment .openassessment__steps__step.is--loading .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__counter:before{border-color:#ffc01f;color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__status__value{background:#ffc01f}.openassessment .openassessment__steps__step.is--incomplete .step__status__value .copy,.openassessment .openassessment__steps__step.is--incomplete .step__status__value .icon{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__counter:before,.openassessment .openassessment__steps__step.is--unavailable .step__label,.openassessment .openassessment__steps__step.is--unavailable .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__counter:before,.openassessment .openassessment__steps__step.is--waiting .step__label,.openassessment .openassessment__steps__step.is--waiting .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__title .grade__value__title{color:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__instruction,.openassessment .openassessment__steps__step.is--waiting .grade__value__description{color:#414141}.openassessment .openassessment__steps__step.is--complete .step__counter:before{color:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value{background:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--complete .step__status__value .copy,.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--complete button{opacity:1}.openassessment .openassessment__steps__step.is--in-progress .step__counter:before{color:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value{background:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .copy,.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--in-progress button{opacity:1}.openassessment .openassessment__steps__step.has--error .step__counter:before{border-color:rgba(178,6,16,0.5);color:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value{background:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value .copy,.openassessment .openassessment__steps__step.has--error .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.has--error .step__label{color:#b20610}.openassessment .ui-slidable__container{-webkit-transition:color .25s ease-in;-moz-transition:color .25s ease-in;transition:color .25s ease-in}.openassessment .ui-slidable__container .ui-slidable__content{width:100%}.openassessment .ui-slidable__container .icon{-webkit-transition:all .25s ease-in-out 0s;-moz-transition:all .25s ease-in-out 0s;transition:all .25s ease-in-out 0s;-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}.openassessment .ui-slidable__container.is--initially--collapsed .ui-slidable__content{display:none}.openassessment .ui-slidable__container.is--showing .ui-slidable .icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%;-ms-transform-origin:50% 50%;-o-transform-origin:50% 50%;transform-origin:50% 50%}.openassessment .ui-slidable__container.is--showing .ui-slidable__container:not(.is--showing) .icon{-webkit-transition:all .25s ease-in-out 0s;-moz-transition:all .25s ease-in-out 0s;transition:all .25s ease-in-out 0s;-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}.openassessment .ui-slidable__container .ui-slidable{background:none;border-radius:5px;padding:0px}.openassessment .ui-slidable__container .ui-slidable:active,.openassessment .ui-slidable__container .ui-slidable:focus:not(:disabled),.openassessment .ui-slidable__container .ui-slidable:hover:not(:disabled){background:none;border:none;box-shadow:none}.openassessment .ui-slidable__container .ui-slidable:not([disabled]){color:#0075b4}.openassessment .ui-slidable__container .ui-slidable:not([disabled]):hover,.openassessment .ui-slidable__container .ui-slidable:not([disabled]):focus{color:#065683}.openassessment .ui-slidable__container .ui-slidable .wrapper--copy{text-align:left}.openassessment .submission__answer__part__prompt{position:relative;margin:10px 0;border:1px solid #d9d9d9;border-left:10px solid #b9c1c9;padding-left:30px}.openassessment .submission__answer__part__prompt__copy{color:#414141}.openassessment .submission__answer__part__prompt__copy>*{margin-bottom:20px}.openassessment .submission__answer__part__text__value{margin:10px 0}.openassessment .submission__answer__part__text__value:not(textarea){border:1px solid #b9c1c9;background-color:#fff;padding:20px}.openassessment .submission__file__description__label{margin-bottom:5px}.openassessment .submission__answer__file__block{margin-bottom:8px}.openassessment .submission__img__preview{float:left;margin-right:10px}.openassessment .step--response .response__submission{padding:10px 10px 20px 10px;background:#f5f6f7;border-radius:2px}.openassessment .step--response .response__submission__content{margin-bottom:20px}.openassessment .step--response .response__submission__content .files__descriptions{display:none}.openassessment .step--response .response__submission__content .files__descriptions .submission__file__description{padding-bottom:10px}.openassessment .step--response .response__submission__content .submission__answer__part__text textarea{min-height:200px}.openassessment .step--response .response__submission__content .submission__file__description textarea{min-height:80px;width:70%}.openassessment .step--response .response__submission__content .tip{padding:5px 10px;background:#b9c1c9;color:#fff}.openassessment .step--peer-assessment .peer-assessment__display__header{*zoom:1}.openassessment .step--peer-assessment .peer-assessment__display__header:before,.openassessment .step--peer-assessment .peer-assessment__display__header:after{content:" ";display:table}.openassessment .step--peer-assessment .peer-assessment__display__header:after{clear:both}.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--peer-assessment .peer-assessment__display__response{color:#414141}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-left:0;white-space:pre-wrap}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .step--self-assessment .self-assessment__display__header{*zoom:1}.openassessment .step--self-assessment .self-assessment__display__header:before,.openassessment .step--self-assessment .self-assessment__display__header:after{content:" ";display:table}.openassessment .step--self-assessment .self-assessment__display__header:after{clear:both}.openassessment .step--self-assessment .self-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--self-assessment .self-assessment__display__response{color:#414141}.openassessment .step--self-assessment .self-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--self-assessment .self-assessment__assessment .question__answers{overflow:visible}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-left:0;white-space:pre-wrap}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}@media screen and (max-width: 529px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:7.42297%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:4.82916%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .step--grade .step__title{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-right:0}}.openassessment .step--grade .step__title .step__counter,.openassessment .step--grade .step__title .step__counter:before{display:none}.openassessment .step--grade .step__title .wrapper--copy{margin-left:0;border-left:none;padding-left:0}.openassessment .step--grade .step__title .step__label,.openassessment .step--grade .step__title .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--grade .step__title .step__label{margin-right:10px}.openassessment .step--grade .grade__value__description{margin-bottom:20px;color:#414141}.openassessment .submission__answer__display__title{margin-bottom:10px;color:#414141}.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image{padding:10px 10px 20px 10px;border-radius:2px;list-style:none}.openassessment .submission__answer__display__content .submission__answer__part,.openassessment .self-assessment__display__image .submission__answer__part,.openassessment .peer-assessment__display__image .submission__answer__part,.openassessment .staff-assessment__display__image .submission__answer__part,.openassessment .submission__answer__display__image .submission__answer__part{margin:30px 0px 0px}.openassessment .submission__answer__display__content .submission__answer__part:first-child,.openassessment .self-assessment__display__image .submission__answer__part:first-child,.openassessment .peer-assessment__display__image .submission__answer__part:first-child,.openassessment .staff-assessment__display__image .submission__answer__part:first-child,.openassessment .submission__answer__display__image .submission__answer__part:first-child{margin-top:0px}.openassessment .submission__peer-evaluations__title{margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions{margin-bottom:10px}.openassessment .submission__peer-evaluations__questions:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0}.openassessment .submission__peer-evaluations__questions .question{margin-bottom:30px}.openassessment .submission__peer-evaluations__questions .question__title{*zoom:1;margin-bottom:20px;border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .submission__peer-evaluations__questions .question__title:before,.openassessment .submission__peer-evaluations__questions .question__title:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__title:after{clear:both}.openassessment .submission__peer-evaluations__questions .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__peer-evaluations__questions .question__title__copy{margin-left:10px;white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .question__score{float:right;margin-left:20px}.openassessment .submission__peer-evaluations__questions .question__score__value{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__value:after{content:"/";margin-left:5px;color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential .unit{margin-left:5px}.openassessment .submission__peer-evaluations__questions .question__answers{*zoom:1;display:block;overflow:visible}.openassessment .submission__peer-evaluations__questions .question__answers:before,.openassessment .submission__peer-evaluations__questions .question__answers:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__answers:after{clear:both}.openassessment .submission__peer-evaluations__questions .answer{margin-bottom:20px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:4.82916%;width:47.58542%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n+1){clear:left}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:left}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:left}}@media screen and (min-width: 1300px){.openassessment .submission__peer-evaluations__questions .answer{float:left;display:block;margin-right:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-right:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:left}}.openassessment .submission__peer-evaluations__questions .answer.self-assessment{border-left:5px solid #f5f6f7;padding-left:20px}.openassessment .submission__peer-evaluations__questions .answer.self-assessment .answer__source{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__source{display:block;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value{display:block;margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value .answer__value__raw{white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .answer__value__value{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:hover,.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:focus{color:#065683}.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value{display:block;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .answer--feedback{float:left;display:block;margin-right:4.82916%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .answer--feedback{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .answer--feedback{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}@media screen and (min-width: 1300px){.openassessment .answer--feedback{float:left;display:block;margin-right:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-right:0}}.openassessment .answer--feedback .answer--feedback__title__copy{margin-left:10px}.openassessment .answer--feedback .answer--feedback__content{margin-top:5px}.openassessment .answer--feedback .feedback{margin-bottom:20px}.openassessment .answer--feedback .feedback__source{display:block;color:#414141}.openassessment .answer--feedback .feedback__value{display:block}.openassessment .answer--feedback .feedback__value .feedback__value__raw{white-space:pre-wrap}.openassessment .submission__feedback__title{border-bottom-color:solid #414141;color:#414141}.openassessment .submission__feedback__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__feedback__title__copy{margin-left:10px}.openassessment .submission__feeedback__content{margin-bottom:20px}.openassessment .submission__feedback__instructions{margin-bottom:20px;color:#414141}.openassessment .submission__feedback__elements{padding:20px 20px 20px 20px;background:#f5f6f7;border-radius:2px}.openassessment .submission__feedback__fields{margin-bottom:20px}.openassessment .submission__feedback__fields .field{margin-bottom:30px}.openassessment .submission__feedback__fields .field:last-child{margin-bottom:0}.openassessment .submission__feedback__fields select{display:block}.openassessment .submission__feedback__fields textarea{min-height:200px}.openassessment .submission__feedback__actions{padding-top:0}.openassessment .submission__feedback__actions .list--actions{padding:0}.openassessment .action--submit{margin-right:10px}.open-response-assessment-block table{table-layout:fixed}.open-response-assessment-block .open-response-assessment-msg{margin-bottom:20px}.open-response-assessment-block .open-response-assessment-content,.open-response-assessment-block .open-response-assessment-item{display:none}.open-response-assessment-block .open-response-assessment-summary td{border:none}.open-response-assessment-block .open-response-assessment-item-breadcrumbs{padding-bottom:20px}.open-response-assessment-block .open-response-assessment-item-breadcrumbs a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table th{background-color:#ffffff;text-align:left}.open-response-assessment-block .open-response-assessment-main-table th button{background:none;border:none;box-shadow:none;outline:none;font-size:14px}.open-response-assessment-block .open-response-assessment-main-table th a{color:#000000 !important}.open-response-assessment-block .open-response-assessment-main-table td a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table tbody tr:nth-child(odd){background-color:#f9f9f9}.open-response-assessment-block .ora-summary-value,.open-response-assessment-block .backgrid{text-overflow:ellipsis;overflow:hidden}.open-response-assessment-block .ora-summary-title{color:#0079bc;font-weight:bold;text-transform:uppercase;font-size:0.8em}.open-response-assessment-block .ora-summary-value{font-size:1.5em;padding-top:3px}.open-response-assessment-block .backgrid .peer,.open-response-assessment-block .backgrid .self,.open-response-assessment-block .backgrid .staff,.open-response-assessment-block .backgrid .waiting,.open-response-assessment-block .backgrid .training{width:80px}.open-response-assessment-block .backgrid .total{width:130px}.open-response-assessment-block .backgrid .done{width:160px}.view--placeholder{background:transparent;margin:40px 40px}.wrapper--openassessment .wrapper--staff-area{width:100%;overflow:auto;margin-top:20px;padding-top:20px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar{position:relative;text-align:right;margin:0 0 8px;padding:10px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{padding:5px 10px;font-size:12px;border-radius:5px;text-transform:uppercase;color:#414141;background-color:rgba(3,3,3,0.05)}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active{color:white;background-color:#b62568}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active:hover{background-color:#921e53}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button:hover{color:white;background-color:#0075b4}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff{margin-top:0}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button{margin:0;padding:0;border:0;float:right;background-color:#2c2f36;font-size:12px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button:hover{color:#fff}.staff-info__student .label{color:#fff;margin-bottom:10px}.staff-info__student .label input{display:block;padding-top:0;padding-bottom:0}.staff-info__student .title{color:#fff;margin-bottom:10px}.staff-info__student .title--sub{color:#fff;margin-top:10px;margin-bottom:10px}.staff-info__student .student__answer__display__content{border:1px solid rgba(255,255,255,0.25);padding:10px 10px;margin-bottom:10px}.staff-info__student .staff-info__student__report{list-style-type:none}.staff-info__student .staff-info__student__report .title{margin-top:10px;border-top:1px solid #fff;padding:10px 20px 10px 0}.staff-info__student .staff-info__student__report .title span{font-weight:inherit}.staff-info__student .value{width:50%}.step--student-training .student-training__display__header{*zoom:1}.step--student-training .student-training__display__header:before,.step--student-training .student-training__display__header:after{content:" ";display:table}.step--student-training .student-training__display__header:after{clear:both}.step--student-training .student-training__display__title{margin-bottom:10px;color:#414141}.step--student-training .student-training__display__response{color:#414141}.step--student-training .student-training__assessment .assessment__fields{margin-bottom:20px}.step--student-training .student-training__assessment .question__answers{overflow:visible}.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.step--student-training .message--correct{margin-top:20px}.step--student-training .message--correct.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--correct.is--hidden .step__header{border-bottom:none}.step--student-training .message--incorrect{margin-top:20px}.step--student-training .message--incorrect.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--incorrect.is--hidden .step__header{border-bottom:none}#openassessment-editor{margin-bottom:0;height:100%}#openassessment-editor .openassessment_editor_content_and_tabs{width:100%;height:calc(100% - 55px)}#openassessment-editor #oa_editor_window_title{float:left}#openassessment-editor input[type="number"]{background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;width:45%}#openassessment-editor #openassessment_editor_header{background-color:#e5e5e5;width:100%;top:0;height:42px}#openassessment-editor #openassessment_editor_header .editor_tabs{width:35%;min-width:190px;max-width:360px;float:right}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab{float:right;padding:0;width:29%;margin:0px 2%;height:35px;-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px;box-shadow:none;border:0;text-align:center}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab a{padding:8px 0 0 0;width:100%;text-transform:uppercase;outline-color:transparent}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab{background:#e5e5e5 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab a{color:#424243}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab{background:#626365 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover a,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab a{color:#fff}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab:hover{background-color:rgba(164,165,168,0.75)}#openassessment-editor #openassessment_validation_alert{width:100%;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;background-color:#323232;border-bottom:3px solid #c0ac00;padding:10px;position:absolute;z-index:10;max-height:200px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon{position:absolute;top:35%;font-size:200%;left:3%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon:before{font-family:FontAwesome;content:"\f071";display:inline-block;color:#c0ac00;float:left;height:0;width:0}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header{width:85%;margin:0 5% 0 10%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_title{width:auto;color:white}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_message{font-size:80%;color:darkgray}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close{display:inline-block;position:absolute;top:0px;right:0px;color:#e9e9e9;text-align:center;margin:5px 10px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close [class^="icon"]{width:auto;margin:0;padding:2px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close:hover{color:#80a9cc}#openassessment-editor .oa_editor_content_wrapper{height:calc(100% - 42px);width:100%;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;border:1px solid #838486;background-color:white;overflow-y:scroll;position:absolute;bottom:0;z-index:11;transition:height 1s ease-in-out 0;-webkit-transition:height 1s ease-in-out 0;-moz-transition:height 1s ease-in-out 0}#openassessment-editor #openassessment_rubric_editor{width:100%;height:100%}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper label{width:25%;text-align:left}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper input{width:45%;min-width:100px}#openassessment-editor #openassessment_step_select_description{padding:10px 10px 0 10px;font-size:80%;border-top:1px solid #838486;margin:7.5px}#openassessment-editor .openassessment_assessment_module_settings_editor.openassessment_shrink_for_drag{height:40px}#openassessment-editor #ai_training_examples{margin:5px 20px 10px 10px;height:300px;width:calc(100% - 20px);resize:none;font:inherit;line-height:110%;font-size:80%;padding:5px 10px}#openassessment-editor .openassessment_assessment_module_settings_editor{padding:0 15px 0 0;margin:10px;border:1px solid #dbdbdc;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;position:relative;background-color:white}#openassessment-editor .openassessment_assessment_module_settings_editor .drag-handle{position:absolute;background:center rgba(164,165,168,0.25);display:block;top:0px;right:0px;z-index:10;width:15px;height:100%;border-left:1px solid #dbdbdc;cursor:move;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description_closed{padding:5px 15px 5px 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description{padding:5px 15px 0 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor.ui-sortable-helper{opacity:50%;border-color:#009fe6}#openassessment-editor .openassessment_assessment_module_settings_editor:hover{border-color:#80a9cc}#openassessment-editor .openassessment_assessment_module_settings_editor:hover .drag-handle{background-color:#009fe6;border-color:#009fe6;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_description{font-size:75%;margin:0}#openassessment-editor .openassessment_inclusion_wrapper{background-color:rgba(164,165,168,0.25);margin:0;padding:7px 5px 7px 13px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px}#openassessment-editor .openassessment_inclusion_wrapper label{cursor:pointer;margin-left:10px}#openassessment-editor .openassessment_inclusion_wrapper:hover{background-color:rgba(164,165,168,0.5)}#openassessment-editor label{padding-right:10px}#openassessment-editor .xblock_actions{background-color:#c8c9cb;position:absolute;width:100%;bottom:0}#openassessment-editor .openassessment_tab_instructions{background-color:rgba(164,165,168,0.25);border-bottom:1px solid #838486;padding:10px}#openassessment-editor .openassessment_container_header{margin:10px 10px;padding-bottom:5px;border-bottom:1px solid;overflow:auto;color:#626365}#openassessment-editor .action.expand-collapse{float:left}#openassessment-editor .action.expand-collapse .ui-toggle-expansion{color:#626365;cursor:pointer}#openassessment-editor .action.expand-collapse .ui-toggle-expansion:hover{color:#838486;cursor:pointer}#openassessment-editor .openassessment_container_header_title_box{float:left;width:80%;display:inline-block}#openassessment-editor .openassessment_container_header_title{text-transform:uppercase;cursor:default;padding-top:2px}#openassessment-editor .openassessment_container_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove{cursor:pointer;float:right}#openassessment-editor .openassessment_container_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#openassessment-editor .openassessment_container_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2{text-transform:uppercase;font-size:80%;float:right;display:inline-block;padding:3px 8px 3px 13px}#openassessment-editor .openassessment_container_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove:hover{background-color:#626365;border-radius:4px;color:white}#openassessment-editor .openassessment_container_add_button{background-color:white;border:1px solid;border-radius:4px;text-align:center;color:#009fe6;padding:10px;margin:15px 10px}#openassessment-editor .openassessment_container_add_button h6:before{font-family:"FontAwesome";display:inline-block;margin-left:5px;margin-right:10px;width:auto;height:auto;content:"\f067"}#openassessment-editor .openassessment_container_add_button.openassessment_highlighted_field{color:red;border-width:2px}#openassessment-editor .openassessment_container_add_button:hover{color:white;background-color:#009fe6;cursor:pointer}#openassessment-editor .openassessment_container_remove_button.is--disabled,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove,#openassessment-editor .openassessment_container_remove_button.is--disabled:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove:hover,#openassessment-editor .openassessment_container_add_button.is--disabled,#openassessment-editor .openassessment_container_add_button.is--disabled:hover{color:#c8c9cb;background-color:transparent;cursor:auto}#openassessment-editor #oa_prompts_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description{width:100%;min-height:100px;resize:none;border:1px solid #b2b2b2;border-radius:4px;padding:10px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;overflow:auto;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option{text-align:center;border:1px solid;color:#009fe6;background-color:white;padding:7.5px;margin:10px 10px 10px 20px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;cursor:pointer}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option h2:before{font-family:FontAwesome;content:"\f067";display:inline-block;margin:0 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option:hover{background-color:#009fe6;color:white}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry{padding-right:0;margin-right:10px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label{padding:0;margin:0;width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label input{font-size:11px;float:right;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt{padding:10px;width:70%;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_header{background-color:#c8c9cb;padding:5px;margin:10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label{width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label select{float:right;margin-right:24%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_direction label{margin-left:15px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option{padding:5px 5px 5px 15px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header{background-color:rgba(164,165,168,0.25);padding:5px 5px 5px 10px;margin:5px 5px 8px 5px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_title{cursor:default;float:left;padding:2.5px 0 0 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper{width:30%;border-top:none;padding:10px 5px 10px 0px;float:left;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper .wrapper-comp-setting{min-width:150px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label{width:100%;padding-left:calc((100% - 150px)/2);margin:0;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label input{min-width:50px;padding:10px;float:right;width:30%;font-size:11px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper{float:left;width:70%;padding:10px 10px 10px 20px;border-top:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label{width:100%;vertical-align:middle;padding:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label input{padding:10px;font-size:11px;width:56%;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper{padding:15px 5px 0px 20px;width:100%;display:inline-block;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label{width:100%;text-align:left;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea{padding:10px;height:70px;width:69.5%;float:right}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{height:70px;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_input_styling,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper{padding:0}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper #openassessment_rubric_feedback_header{background-color:rgba(164,165,168,0.25);padding:7.5px 7.5px 7.5px 15px;margin:10px 10px 0 10px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper label{padding-top:10px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{padding:10px;width:70%;float:right}#openassessment-editor hr{background-color:transparent;color:#424243;height:1px;border:0px;clear:both}.modal-lg.modal-window.confirm.openassessment_modal_window{height:80%;top:10%;min-width:600px}.openassessment_full_height.edit-xblock-modal,.openassessment_full_height.xblock-editor,.openassessment_full_height.xblock.xblock-studio_view.xblock-initialized,[class*="view-"] .modal-lg.modal-editor .openassessment_full_height.modal-content{height:100%}#openassessment-editor .openassessment_highlighted_field{border-color:red;border-width:2px}#student_training_settings_editor .openassessment_training_example{padding:5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header{margin:10px;padding:5px;border-bottom:1px solid;overflow:auto;color:#626365}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_header_title{text-transform:uppercase;width:50%;display:inline-block;float:left;padding-top:2px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove{padding:3px 5px 3px 7px;cursor:pointer;border-radius:4px;float:right}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2{text-transform:uppercase;font-size:80%;float:right;display:inline-block}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove:hover{background-color:lightgray}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body{padding:0 15px 15px 15px;position:relative;overflow:scroll}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper{width:58%;display:inline-block;position:absolute;left:15px;height:100%}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper textarea{min-height:90px;height:calc(100% - 35px);resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;padding:6px 8px 8px 8px;background-color:white;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c;margin:5px 15px 0 10px;width:calc(100% - 25px)}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric{width:40%;display:inline-block;float:right;min-height:150px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry{margin:0;padding:10px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry .wrapper-comp-setting{min-width:100px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label{width:100%;margin-left:0}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label select{width:60%;float:right}#student_training_settings_editor .openassessment_add_training_example{color:#009fe6;text-align:center;padding:5px;border:1px solid;border-radius:4px;margin:10px}#student_training_settings_editor .openassessment_add_training_example:hover{color:white;background-color:#009fe6;cursor:pointer}#student_training_settings_editor .openassessment_add_training_example:before{font-family:"FontAwesome";display:inline-block;width:auto;height:auto;content:"\f067";margin:0 10px}#openassessment-editor .wrapper-comp-settings .list-input.settings-list .field.comp-setting-entry select{padding:5px}.step--response .action--upload{text-align:center;float:right;display:inline-block;margin:10px 0;box-shadow:none}.step--response .file--upload{margin:20px 10px}.self-assessment__display__header .self-assessment__display__title,.peer-assessment__display__header .peer-assessment__display__title,.staff-assessment__display__header .staff-assessment__display__title,.submission__answer__display .submission__answer__display__title{margin:10px 0}.self-assessment__display__image,.peer-assessment__display__image,.staff-assessment__display__image,.submission__answer__display__image{max-height:400px;text-align:left;overflow:auto}.self-assessment__display__image img,.peer-assessment__display__image img,.staff-assessment__display__image img,.submission__answer__display__image img{max-height:100%;max-width:100%}.submission__answer__display__image .submission--image{max-height:250px;max-width:100%}.openassessment .step--leaderboard{font-family:"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif}.openassessment .step--leaderboard .step__title .step__counter,.openassessment .step--leaderboard .step__title .step__counter:before{display:none}.openassessment .step--leaderboard .step__title .wrapper--copy{margin-left:0;padding-left:0;border-left:0}.openassessment .step--leaderboard .step__label,.openassessment .step--leaderboard .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--leaderboard .step__label{margin-right:10px}.openassessment .step--leaderboard .list.leaderboard__score__list{list-style-type:none}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item{margin:15px 0}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__list__number{display:inline-block;background:#626365;color:white;padding:5px 5px 3px 5px;font-size:16px;min-width:35px;text-align:center;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__image{max-height:150px;max-width:100%;margin-right:20px;margin-bottom:20px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__title{font-size:15px;color:#414141;text-transform:uppercase;display:inline-block;padding-left:15px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__answer{border-top:2px solid #626365;box-shadow:inset 0 0 3px 1px rgba(10,10,10,0.1);padding:10px;max-height:200px;overflow-y:scroll;font-size:14px}.wrapper--xblock .openassessment h3.staff__grade__title{font-size:inherit;margin-bottom:5px}.openassessment .self-assessment__display__header,.openassessment .peer-assessment__display__header,.openassessment .step__header,.openassessment .staff-assessment__display__header,.openassessment .submission__answer__display__header,.openassessment .student-training__display__header{margin-bottom:0 !important;border-radius:0 !important;padding-bottom:2px !important;font-size:initial !important;border-bottom:none !important}.openassessment span{line-height:normal}.openassessment h2{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__deadline{color:#414141}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title{text-align:left;float:none;margin-top:0 !important}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__label{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{content:counter(item, decimal);counter-increment:item;color:#414141}.openassessment .step__status__value{line-height:0}.openassessment .leaderboard__answer .submission__answer__display__content .submission__answer__part__text__title,.openassessment .leaderboard__answer .self-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .peer-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .staff-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .submission__answer__display__image .submission__answer__part__text__title{text-align:left;float:none;font-size:inherit;margin:0}.openassessment .list--actions{list-style:none !important;padding-left:0 !important;text-indent:0 !important}.openassessment .list--actions li{margin-bottom:0 !important;margin-top:5px !important}.openassessment .list--actions button span,.openassessment .list--actions .button span,.openassessment .list--actions .action span{color:inherit}.openassessment span{color:inherit}.openassessment .step--peer-assessment .list--peer-assessments{list-style:none outside none !important;padding-left:0}.openassessment .self-assessment__display__title,.openassessment .peer-assessment__display__title{margin-bottom:10px !important;color:#414141 !important}.openassessment .question__answers{padding-left:0}.openassessment .question__title.ui-slidable .question__title__copy{color:inherit !important}.openassessment .step--grade .step__title{width:100% !important}.openassessment .action--submit,.openassessment .action--save,.openassessment .action--upload{text-shadow:none !important;box-shadow:none !important}.openassessment .message p{color:inherit}.openassessment .icon{font-style:normal}.openassessment .step__title .step__deadline{letter-spacing:normal;text-transform:none}.openassessment .openassessment__steps__step .step__header .step__title{margin-bottom:0}.openassessment .is--collapsed .question__answers{overflow:auto}.openassessment .wrapper--staff-assessment .student__answer__display__content p{color:inherit} diff --git a/openassessment/xblock/static/css/openassessment-rtl.css b/openassessment/xblock/static/css/openassessment-rtl.css index e120985d34..b119dceb47 100644 --- a/openassessment/xblock/static/css/openassessment-rtl.css +++ b/openassessment/xblock/static/css/openassessment-rtl.css @@ -1,3 +1,3 @@ -html{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,*:before,*:after{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}.wrapper--xblock{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock main,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary{display:block}.wrapper--xblock audio,.wrapper--xblock canvas,.wrapper--xblock progress,.wrapper--xblock video{display:inline-block;vertical-align:baseline}.wrapper--xblock audio:not([controls]){display:none;height:0}.wrapper--xblock [hidden],.wrapper--xblock template{display:none}.wrapper--xblock a{background:transparent}.wrapper--xblock abbr[title]{border-bottom:1px dotted}.wrapper--xblock b,.wrapper--xblock strong{font-weight:bold}.wrapper--xblock dfn{font-style:italic}.wrapper--xblock h1{font-size:2em;margin:0.67em 0}.wrapper--xblock mark{background:#ff0;color:#000}.wrapper--xblock small{font-size:80%}.wrapper--xblock sub,.wrapper--xblock sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.wrapper--xblock sup{top:-0.5em}.wrapper--xblock sub{bottom:-0.25em}.wrapper--xblock img{border:0}.wrapper--xblock svg:not(:root){overflow:hidden}.wrapper--xblock figure{margin:1em 40px}.wrapper--xblock hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}.wrapper--xblock pre{overflow:auto}.wrapper--xblock code,.wrapper--xblock kbd,.wrapper--xblock pre,.wrapper--xblock samp{font-family:monospace, monospace;font-size:1em}.wrapper--xblock button,.wrapper--xblock input,.wrapper--xblock optgroup,.wrapper--xblock select,.wrapper--xblock textarea{color:inherit;font:inherit;margin:0}.wrapper--xblock button{overflow:visible}.wrapper--xblock button,.wrapper--xblock select{text-transform:none}.wrapper--xblock button,.wrapper--xblock html input[type="button"],.wrapper--xblock input[type="reset"],.wrapper--xblock input[type="submit"]{-webkit-appearance:button;cursor:pointer}.wrapper--xblock button[disabled],.wrapper--xblock html input[disabled]{cursor:default}.wrapper--xblock button[disabled]{opacity:0.5}.wrapper--xblock button::-moz-focus-inner,.wrapper--xblock input::-moz-focus-inner{border:0;padding:0}.wrapper--xblock input{line-height:normal}.wrapper--xblock input[type="checkbox"],.wrapper--xblock input[type="radio"]{box-sizing:border-box;padding:0}.wrapper--xblock input[type="number"]::-webkit-inner-spin-button,.wrapper--xblock input[type="number"]::-webkit-outer-spin-button{height:auto}.wrapper--xblock input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}.wrapper--xblock input[type="search"]::-webkit-search-cancel-button,.wrapper--xblock input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}.wrapper--xblock fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em 0.625em}.wrapper--xblock legend{border:0;padding:0}.wrapper--xblock textarea{overflow:auto}.wrapper--xblock optgroup{font-weight:bold}.wrapper--xblock table{border-collapse:collapse;border-spacing:0}.wrapper--xblock td,.wrapper--xblock th{padding:0}.wrapper--xblock *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wrapper--xblock div,.wrapper--xblock span,.wrapper--xblock applet,.wrapper--xblock object,.wrapper--xblock iframe,.wrapper--xblock h1,.wrapper--xblock h2,.wrapper--xblock h3,.wrapper--xblock h4,.wrapper--xblock h5,.wrapper--xblock h6,.wrapper--xblock p,.wrapper--xblock blockquote,.wrapper--xblock pre,.wrapper--xblock a,.wrapper--xblock abbr,.wrapper--xblock acronym,.wrapper--xblock address,.wrapper--xblock big,.wrapper--xblock cite,.wrapper--xblock code,.wrapper--xblock del,.wrapper--xblock dfn,.wrapper--xblock em,.wrapper--xblock img,.wrapper--xblock ins,.wrapper--xblock kbd,.wrapper--xblock q,.wrapper--xblock s,.wrapper--xblock samp,.wrapper--xblock small,.wrapper--xblock strike,.wrapper--xblock strong,.wrapper--xblock sub,.wrapper--xblock sup,.wrapper--xblock tt,.wrapper--xblock var,.wrapper--xblock b,.wrapper--xblock u,.wrapper--xblock i,.wrapper--xblock center,.wrapper--xblock dl,.wrapper--xblock dt,.wrapper--xblock dd,.wrapper--xblock ol,.wrapper--xblock ul,.wrapper--xblock li,.wrapper--xblock fieldset,.wrapper--xblock form,.wrapper--xblock label,.wrapper--xblock legend,.wrapper--xblock table,.wrapper--xblock caption,.wrapper--xblock tbody,.wrapper--xblock tfoot,.wrapper--xblock thead,.wrapper--xblock tr,.wrapper--xblock th,.wrapper--xblock td,.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock canvas,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock menu,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary,.wrapper--xblock time,.wrapper--xblock mark,.wrapper--xblock audio,.wrapper--xblock video{margin:0;vertical-align:baseline;border:0;padding:0;font-weight:400}.wrapper--xblock abbr[title]{border-bottom:none;text-decoration:none;cursor:help}.list--actions:last-child,.list--docs:last-child,.list--steps:last-child,.list--fields:last-child,.list--controls:last-child,.list--help:last-child,.list--options:last-child,.nav--wizard:last-child,.list--tips:last-child,.field--group:last-child,.wrapper--xblock .field--radio .option:last-child,.wrapper--xblock .field--checkbox .option:last-child,.wrapper--xblock .message .message__content p:last-child,.openassessment .staff-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:last-child,.step--student-training .student-training__assessment .assessment__rubric__question:last-child,.openassessment .staff-assessment__assessment .question__answers .answer:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:last-child,.step--student-training .student-training__assessment .question__answers .answer:last-child,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:last-child,.step--student-training .student-training__assessment .question__answers .answer--feedback:last-child,.openassessment .ui-staff__content__section:last-child,.openassessment .openassessment__steps__step .step__content__section:last-child,.openassessment .submission__answer__part__prompt__copy>:last-child,.openassessment .submission__peer-evaluations__questions .question:last-child,.openassessment .submission__peer-evaluations__questions .question__answers:last-child,.openassessment .answer--feedback .feedback:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0;margin-right:0;border-right:none;padding-right:0}.list--actions,.list--docs,.list--steps,.list--fields,.list--controls,.list--help,.list--options,.nav--wizard,.list--tips,.field--group,.openassessment .openassessment__steps,.openassessment .step--peer-assessment .list--peer-assessments,.openassessment .submission__peer-evaluations__questions,.openassessment .submission__peer-evaluations__questions .question__answers,.openassessment .answer--feedback .feedback{list-style:none;margin:0;padding:0;text-indent:0}.sr,hr.divider,.nav--contents,.system__element,.openassessment .submission__answer__part__prompt__title,.openassessment .step--response .response__submission__content label{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-hint{cursor:pointer}.is--hidden,.wrapper--xblock .is--transitioning{pointer-events:none;outline:none}.is--hidden{display:none;visibility:hidden}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder,.wrapper--xblock .message--error{-webkit-transition:opacity 0.5s ease-in-out;-moz-transition:opacity 0.5s ease-in-out;transition:opacity 0.5s ease-in-out}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{-webkit-transition:color 0.5s ease-in-out;-moz-transition:color 0.5s ease-in-out;transition:color 0.5s ease-in-out}.openassessment .ui-hint:after{box-shadow:0 1px 2px 0 rgba(3,3,3,0.4)}@-webkit-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-moz-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-o-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}.wrapper--xblock .problem__header{margin-bottom:20px;color:#414141}.wrapper--xblock{background:transparent}.wrapper--xblock strong,.wrapper--xblock .emphasis,.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload,.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea,.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label,.wrapper--xblock .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-info__title__copy,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value,.openassessment .staff-assessment__display__header span,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .step--peer-assessment .peer-assessment__display__header span,.openassessment .step--grade .step__title .grade__value__title .grade__value__earned,.openassessment .step--grade .step__title .grade__value__title .grade__value__potential,.openassessment .submission__peer-evaluations__questions .question__score__value,.openassessment .submission__peer-evaluations__questions .question__score__potential,.openassessment .submission__peer-evaluations__questions .question__score__potential .unit,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .submission__peer-evaluations__questions .answer__value__value,.openassessment .answer--feedback .feedback__source,.staff-info__student .staff-info__student__report .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-weight:600}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder,.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder,.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder,.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder,.openassessment .openassessment__title{font-weight:400}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source,.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after,#openassessment-editor .openassessment_container_guide{font-size:12px;line-height:18px}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub,.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload,.openassessment .openassessment__steps__step .step__status .icon{font-size:14px;line-height:21px}.wrapper--xblock .problem__header,.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions,.openassessment .list--actions .action--submit .icon{font-size:16px;line-height:24px}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon,.openassessment .submission__peer-evaluations__questions .question__title .icon,.openassessment .submission__feedback__title .icon{font-size:18px;line-height:27px}.wrapper--xblock .is--transitioning .transition__status .icon{font-size:22px;line-height:33px}.wrapper--xblock .problem__header,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{text-transform:uppercase;letter-spacing:0.05rem}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .problem__header{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__content a,.openassessment .submission__answer__part__prompt__copy a{-webkit-transition:color 0.5s ease-in-out,border 0.5s ease-in-out;-moz-transition:color 0.5s ease-in-out,border 0.5s ease-in-out;transition:color 0.5s ease-in-out,border 0.5s ease-in-out;border-bottom:1px solid transparent;color:#0075b4;text-decoration:none}.wrapper--xblock .message .message__content a:hover,.openassessment .submission__answer__part__prompt__copy a:hover,.wrapper--xblock .message .message__content a:focus,.openassessment .submission__answer__part__prompt__copy a:focus{color:#065683;border-color:#065683}.wrapper--xblock .message .message__content a:active,.openassessment .submission__answer__part__prompt__copy a:active,.wrapper--xblock .message .message__content a.is--current,.openassessment .submission__answer__part__prompt__copy a.is--current{color:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;-moz-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;display:inline-block;cursor:pointer;text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username .icon,.openassessment .step--response .response__submission__content .action--save .icon,.openassessment .step--response .response__submission__actions .action--save .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save .icon,.openassessment .step--self-assessment .self-assessment__assessment .action--save .icon,.openassessment .action--submit .icon,.step--response .action--upload .icon{display:inline-block;vertical-align:middle}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{border-radius:4px}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .ui-slidable__container .ui-slidable,.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit{padding:10px 40px;background:#0075b4;border:1px solid #0075b4;color:#fff}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .list--actions .action--submit:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .list--actions .action--submit:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .list--actions .action--submit:focus{background:#065683;border-color:#065683}.openassessment .openassessment_student_info_form .current.action--submit-username,.openassessment .list--actions .current.action--submit,.openassessment .openassessment_student_info_form .active.action--submit-username,.openassessment .list--actions .active.action--submit{background:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form [disabled].action--submit-username,.openassessment .list--actions [disabled].action--submit{color:#b4b3b3;border-color:#b4b3b3;background:#f5f5f5;opacity:1}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{padding:10px 40px;background:#fff;border:1px solid #065683;color:#065683}.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{background:#0075b4;border-color:#0075b4;color:#fff}.openassessment .step--response .response__submission__content .current.action--save,.openassessment .step--response .response__submission__actions .current.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .current.action--save,.openassessment .step--self-assessment .self-assessment__assessment .current.action--save,.openassessment .current.action--submit,.step--response .current.action--upload,.openassessment .step--response .response__submission__content .active.action--save,.openassessment .step--response .response__submission__actions .active.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .active.action--save,.openassessment .step--self-assessment .self-assessment__assessment .active.action--save,.openassessment .active.action--submit,.step--response .active.action--upload{background:#fff;border-color:#065683;color:#065683}.openassessment .step--response .response__submission__content [disabled].action--save,.openassessment .step--response .response__submission__actions [disabled].action--save,.openassessment .step--peer-assessment .peer-assessment__assessment [disabled].action--save,.openassessment .step--self-assessment .self-assessment__assessment [disabled].action--save,.openassessment [disabled].action--submit,.step--response [disabled].action--upload{background:#fff;border-color:#b4b3b3;color:#b4b3b3;opacity:1}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder{opacity:0.0}.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea{border:1px solid #b9c1c9;padding:10px;color:#414141}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:focus,.wrapper--xblock input[type="email"]:focus,.wrapper--xblock input[type="password"]:focus,.wrapper--xblock textarea:focus{box-shadow:0 0 6px 0 rgba(29,157,217,0.4),inset 0 0 4px 0 rgba(0,0,0,0.15);border-color:#000}.wrapper--xblock .field{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .field-group__label{display:block;margin-bottom:10px}.wrapper--xblock .field textarea{width:100%}.wrapper--xblock .field .tip{display:block;color:#414141}.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label{margin-bottom:10px}.wrapper--xblock .field-group__label{margin-bottom:10px}.wrapper--xblock .field--radio .option,.wrapper--xblock .field--checkbox .option{margin-bottom:5px}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__input,.wrapper--xblock .field--checkbox .option__label{display:inline-block;vertical-align:middle}.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label{margin-bottom:0;color:#414141}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--checkbox .option__input{margin-left:10px}.wrapper--xblock .field--radio .option__input:checked+.option__label,.wrapper--xblock .field--checkbox .option__input:checked+.option__label{color:#000}.wrapper--xblock .message{margin-bottom:20px;border-radius:2px;padding:20px 20px;background:#f5f6f7}.wrapper--xblock .message .message__title{margin-bottom:5px;border-bottom:2px solid #d9d9d9;padding-bottom:5px}.wrapper--xblock .message .message__content{color:#414141}.wrapper--xblock .message .message__content p{margin-bottom:10px}.wrapper--xblock .message--error{background:#feeced}.wrapper--xblock .message--error .message__title{color:#414141;border-bottom-color:#b20610}.wrapper--xblock .message--warning{background:#fff9eb}.wrapper--xblock .message--warning .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--complete,.wrapper--xblock .step--student-training .message--correct,.step--student-training .wrapper--xblock .message--correct{background:#ecfaec}.wrapper--xblock .message--complete .message__title,.wrapper--xblock .step--student-training .message--correct .message__title,.step--student-training .wrapper--xblock .message--correct .message__title{color:#414141;border-bottom-color:#008100}.wrapper--xblock .message--incomplete,.wrapper--xblock .step--student-training .message--incorrect,.step--student-training .wrapper--xblock .message--incorrect{background:#fff9eb}.wrapper--xblock .message--incomplete .message__title,.wrapper--xblock .step--student-training .message--incorrect .message__title,.step--student-training .wrapper--xblock .message--incorrect .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--error{display:none;opacity:0.0}.wrapper--xblock .has--error .message--error{display:block;opacity:1.0}.wrapper--xblock .message--inline{padding:10px 20px;background:#5e5e5e}.wrapper--xblock .message--inline .message__title{margin-bottom:0;border:none;padding-bottom:0;color:#fff}.wrapper--xblock .message--inline.message--error{background:#b20610}.wrapper--xblock .message--inline.message--error .message__content{color:#fff}.wrapper--xblock .message--inline.message--warning{background:#ffc01f}.wrapper--xblock .message--inline.message--warning .message__content{color:#111}.wrapper--xblock .message--inline.message--warning .message__title{color:#111}.wrapper--xblock .is--transitioning{padding:40px 40px;background:#f5f6f7}.wrapper--xblock .is--transitioning .transition__status{position:relative;top:50%;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%);text-align:center}.wrapper--xblock .is--transitioning .transition__status .wrapper--anim{display:block;-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.wrapper--xblock .is--transitioning .transition__status .icon,.wrapper--xblock .is--transitioning .transition__status .copy{display:block;color:#414141}.wrapper--xblock .is--transitioning .transition__status .icon{margin-bottom:10px}.wrapper--grid{*zoom:1;max-width:100%;margin-left:auto;margin-right:auto}.wrapper--grid:before,.wrapper--grid:after{content:" ";display:table}.wrapper--grid:after{clear:both}/*! Hint.css - v1.3.1 - 2013-11-23 +html{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,*:before,*:after{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}.wrapper--xblock{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock main,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary{display:block}.wrapper--xblock audio,.wrapper--xblock canvas,.wrapper--xblock progress,.wrapper--xblock video{display:inline-block;vertical-align:baseline}.wrapper--xblock audio:not([controls]){display:none;height:0}.wrapper--xblock [hidden],.wrapper--xblock template{display:none}.wrapper--xblock a{background:transparent}.wrapper--xblock abbr[title]{border-bottom:1px dotted}.wrapper--xblock b,.wrapper--xblock strong{font-weight:bold}.wrapper--xblock dfn{font-style:italic}.wrapper--xblock h1{font-size:2em;margin:0.67em 0}.wrapper--xblock mark{background:#ff0;color:#000}.wrapper--xblock small{font-size:80%}.wrapper--xblock sub,.wrapper--xblock sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.wrapper--xblock sup{top:-0.5em}.wrapper--xblock sub{bottom:-0.25em}.wrapper--xblock img{border:0}.wrapper--xblock svg:not(:root){overflow:hidden}.wrapper--xblock figure{margin:1em 40px}.wrapper--xblock hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}.wrapper--xblock pre{overflow:auto}.wrapper--xblock code,.wrapper--xblock kbd,.wrapper--xblock pre,.wrapper--xblock samp{font-family:monospace, monospace;font-size:1em}.wrapper--xblock button,.wrapper--xblock input,.wrapper--xblock optgroup,.wrapper--xblock select,.wrapper--xblock textarea{color:inherit;font:inherit;margin:0}.wrapper--xblock button{overflow:visible}.wrapper--xblock button,.wrapper--xblock select{text-transform:none}.wrapper--xblock button,.wrapper--xblock html input[type="button"],.wrapper--xblock input[type="reset"],.wrapper--xblock input[type="submit"]{-webkit-appearance:button;cursor:pointer}.wrapper--xblock button[disabled],.wrapper--xblock html input[disabled]{cursor:default}.wrapper--xblock button[disabled]{opacity:0.5}.wrapper--xblock button::-moz-focus-inner,.wrapper--xblock input::-moz-focus-inner{border:0;padding:0}.wrapper--xblock input{line-height:normal}.wrapper--xblock input[type="checkbox"],.wrapper--xblock input[type="radio"]{box-sizing:border-box;padding:0}.wrapper--xblock input[type="number"]::-webkit-inner-spin-button,.wrapper--xblock input[type="number"]::-webkit-outer-spin-button{height:auto}.wrapper--xblock input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}.wrapper--xblock input[type="search"]::-webkit-search-cancel-button,.wrapper--xblock input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}.wrapper--xblock fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em .625em}.wrapper--xblock legend{border:0;padding:0}.wrapper--xblock textarea{overflow:auto}.wrapper--xblock optgroup{font-weight:bold}.wrapper--xblock table{border-collapse:collapse;border-spacing:0}.wrapper--xblock td,.wrapper--xblock th{padding:0}.wrapper--xblock *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wrapper--xblock div,.wrapper--xblock span,.wrapper--xblock applet,.wrapper--xblock object,.wrapper--xblock iframe,.wrapper--xblock h1,.wrapper--xblock h2,.wrapper--xblock h3,.wrapper--xblock h4,.wrapper--xblock h5,.wrapper--xblock h6,.wrapper--xblock p,.wrapper--xblock blockquote,.wrapper--xblock pre,.wrapper--xblock a,.wrapper--xblock abbr,.wrapper--xblock acronym,.wrapper--xblock address,.wrapper--xblock big,.wrapper--xblock cite,.wrapper--xblock code,.wrapper--xblock del,.wrapper--xblock dfn,.wrapper--xblock em,.wrapper--xblock img,.wrapper--xblock ins,.wrapper--xblock kbd,.wrapper--xblock q,.wrapper--xblock s,.wrapper--xblock samp,.wrapper--xblock small,.wrapper--xblock strike,.wrapper--xblock strong,.wrapper--xblock sub,.wrapper--xblock sup,.wrapper--xblock tt,.wrapper--xblock var,.wrapper--xblock b,.wrapper--xblock u,.wrapper--xblock i,.wrapper--xblock center,.wrapper--xblock dl,.wrapper--xblock dt,.wrapper--xblock dd,.wrapper--xblock ol,.wrapper--xblock ul,.wrapper--xblock li,.wrapper--xblock fieldset,.wrapper--xblock form,.wrapper--xblock label,.wrapper--xblock legend,.wrapper--xblock table,.wrapper--xblock caption,.wrapper--xblock tbody,.wrapper--xblock tfoot,.wrapper--xblock thead,.wrapper--xblock tr,.wrapper--xblock th,.wrapper--xblock td,.wrapper--xblock article,.wrapper--xblock aside,.wrapper--xblock canvas,.wrapper--xblock details,.wrapper--xblock figcaption,.wrapper--xblock figure,.wrapper--xblock footer,.wrapper--xblock header,.wrapper--xblock hgroup,.wrapper--xblock menu,.wrapper--xblock nav,.wrapper--xblock section,.wrapper--xblock summary,.wrapper--xblock time,.wrapper--xblock mark,.wrapper--xblock audio,.wrapper--xblock video{margin:0;vertical-align:baseline;border:0;padding:0;font-weight:400}.wrapper--xblock abbr[title]{border-bottom:none;text-decoration:none;cursor:help}.list--actions:last-child,.list--docs:last-child,.list--steps:last-child,.list--fields:last-child,.list--controls:last-child,.list--help:last-child,.list--options:last-child,.nav--wizard:last-child,.list--tips:last-child,.field--group:last-child,.wrapper--xblock .field--radio .option:last-child,.wrapper--xblock .field--checkbox .option:last-child,.wrapper--xblock .message .message__content p:last-child,.openassessment .staff-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:last-child,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:last-child,.step--student-training .student-training__assessment .assessment__rubric__question:last-child,.openassessment .staff-assessment__assessment .question__answers .answer:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:last-child,.step--student-training .student-training__assessment .question__answers .answer:last-child,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:last-child,.step--student-training .student-training__assessment .question__answers .answer--feedback:last-child,.openassessment .ui-staff__content__section:last-child,.openassessment .openassessment__steps__step .step__content__section:last-child,.openassessment .submission__answer__part__prompt__copy>:last-child,.openassessment .submission__peer-evaluations__questions .question:last-child,.openassessment .submission__peer-evaluations__questions .question__answers:last-child,.openassessment .answer--feedback .feedback:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0;margin-right:0;border-right:none;padding-right:0}.list--actions,.list--docs,.list--steps,.list--fields,.list--controls,.list--help,.list--options,.nav--wizard,.list--tips,.field--group,.openassessment .openassessment__steps,.openassessment .step--peer-assessment .list--peer-assessments,.openassessment .submission__peer-evaluations__questions,.openassessment .submission__peer-evaluations__questions .question__answers,.openassessment .answer--feedback .feedback{list-style:none;margin:0;padding:0;text-indent:0}.sr,hr.divider,.nav--contents,.system__element,.openassessment .submission__answer__part__prompt__title,.openassessment .step--response .response__submission__content label{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-hint{cursor:pointer}.is--hidden,.wrapper--xblock .is--transitioning{pointer-events:none;outline:none}.is--hidden{display:none;visibility:hidden}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder,.wrapper--xblock .message--error{-webkit-transition:opacity .5s ease-in-out;-moz-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{-webkit-transition:color .5s ease-in-out;-moz-transition:color .5s ease-in-out;transition:color .5s ease-in-out}.openassessment .ui-hint:after{box-shadow:0 1px 2px 0 rgba(3,3,3,0.4)}@-webkit-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-moz-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@-o-keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}@keyframes pulse{0%{opacity:0.50}50%{opacity:1.0}100%{opacity:0.50}}.wrapper--xblock .problem__header{margin-bottom:20px;color:#414141}.wrapper--xblock{background:transparent}.wrapper--xblock strong,.wrapper--xblock .emphasis,.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload,.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea,.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label,.wrapper--xblock .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-info__title__copy,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value,.openassessment .staff-assessment__display__header span,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .step--peer-assessment .peer-assessment__display__header span,.openassessment .step--grade .step__title .grade__value__title .grade__value__earned,.openassessment .step--grade .step__title .grade__value__title .grade__value__potential,.openassessment .submission__peer-evaluations__questions .question__score__value,.openassessment .submission__peer-evaluations__questions .question__score__potential,.openassessment .submission__peer-evaluations__questions .question__score__potential .unit,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .submission__peer-evaluations__questions .answer__value__value,.openassessment .answer--feedback .feedback__source,.staff-info__student .staff-info__student__report .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-weight:600}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder,.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder,.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder,.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder,.openassessment .openassessment__title{font-weight:400}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source,.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after,#openassessment-editor .openassessment_container_guide{font-size:12px;line-height:18px}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub,.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload,.openassessment .openassessment__steps__step .step__status .icon{font-size:14px;line-height:21px}.wrapper--xblock .problem__header,.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions,.openassessment .list--actions .action--submit .icon{font-size:16px;line-height:24px}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon,.openassessment .submission__peer-evaluations__questions .question__title .icon,.openassessment .submission__feedback__title .icon{font-size:18px;line-height:27px}.wrapper--xblock .is--transitioning .transition__status .icon{font-size:22px;line-height:33px}.wrapper--xblock .problem__header,.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{text-transform:uppercase;letter-spacing:0.05rem}.wrapper--xblock .is--transitioning .transition__status .copy,.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .step--grade .step__title .grade__value__title,.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value,.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title,.openassessment .openassessment__steps__step .step__title .step__counter:before,.openassessment .submission__peer-evaluations__questions .answer__value__value,.staff-info__student .title,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .problem__header{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__title,.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy,.openassessment .ui-staff__title,.openassessment .ui-staff__subtitle,.openassessment .staff-assessment__display__header .staff-assessment__display__title,.openassessment .submission__answer__part__text__title,.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title,.openassessment .step--self-assessment .self-assessment__display__title,.openassessment .submission__answer__display__title,.openassessment .submission__peer-evaluations__title,.openassessment .submission__peer-evaluations__questions .question__title__copy,.openassessment .submission__feedback__title,.openassessment .submission__feedback__title__copy,.step--student-training .student-training__display__title,.openassessment .openassessment__steps__step .step__title .step__deadline,.staff-info__student .title--sub{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .submission__peer-evaluations__questions .answer__source,.openassessment .answer--feedback .feedback__source{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .openassessment__steps__step .step__instruction,.openassessment .submission__answer__part__prompt__copy,.openassessment .step--grade .grade__value__description,.openassessment .step--grade .grade__summary,.openassessment .submission__feedback__instructions{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field label,.wrapper--xblock .field .label,.wrapper--xblock .field .field-group__label,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label,.wrapper--xblock .message .message__content,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label,.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table,.openassessment .staff-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image,.openassessment .answer--feedback .feedback__value,.step--student-training .student-training__display__response{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .tip,.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points,.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy,.openassessment .openassessment__steps__step .step__status .copy,.openassessment .step--response .response__submission__content .tip,.openassessment .submission__peer-evaluations__questions .question__score,.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points,.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value,.openassessment .staff-info__status__table th,.openassessment .staff-info__classifierset__table th,.openassessment .ui-hint:after{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .list--actions .action--submit,.openassessment .answer--feedback .answer--feedback__title,.openassessment .action--submit,.step--response .action--upload{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .message .message__content a,.openassessment .submission__answer__part__prompt__copy a{-webkit-transition:color .5s ease-in-out,border .5s ease-in-out;-moz-transition:color .5s ease-in-out,border .5s ease-in-out;transition:color .5s ease-in-out,border .5s ease-in-out;border-bottom:1px solid transparent;color:#0075b4;text-decoration:none}.wrapper--xblock .message .message__content a:hover,.openassessment .submission__answer__part__prompt__copy a:hover,.wrapper--xblock .message .message__content a:focus,.openassessment .submission__answer__part__prompt__copy a:focus{color:#065683;border-color:#065683}.wrapper--xblock .message .message__content a:active,.openassessment .submission__answer__part__prompt__copy a:active,.wrapper--xblock .message .message__content a.is--current,.openassessment .submission__answer__part__prompt__copy a.is--current{color:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;-moz-transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;transition:color 0.25s ease-in-out,background 0.25s ease-in-out,box-shadow 0.25s ease-in-out;display:inline-block;cursor:pointer;text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{text-decoration:none}.openassessment .openassessment_student_info_form .action--submit-username .icon,.openassessment .step--response .response__submission__content .action--save .icon,.openassessment .step--response .response__submission__actions .action--save .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save .icon,.openassessment .step--self-assessment .self-assessment__assessment .action--save .icon,.openassessment .action--submit .icon,.step--response .action--upload .icon{display:inline-block;vertical-align:middle}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit,.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{border-radius:4px}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form,.openassessment .ui-slidable__container .ui-slidable,.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.openassessment .openassessment_student_info_form .action--submit-username,.openassessment .list--actions .action--submit{padding:10px 40px;background:#0075b4;border:1px solid #0075b4;color:#fff}.openassessment .openassessment_student_info_form .action--submit-username:hover,.openassessment .list--actions .action--submit:hover,.openassessment .openassessment_student_info_form .action--submit-username:active,.openassessment .list--actions .action--submit:active,.openassessment .openassessment_student_info_form .action--submit-username:focus,.openassessment .list--actions .action--submit:focus{background:#065683;border-color:#065683}.openassessment .openassessment_student_info_form .current.action--submit-username,.openassessment .list--actions .current.action--submit,.openassessment .openassessment_student_info_form .active.action--submit-username,.openassessment .list--actions .active.action--submit{background:#0075b4;border-color:#0075b4}.openassessment .openassessment_student_info_form [disabled].action--submit-username,.openassessment .list--actions [disabled].action--submit{color:#b4b3b3;border-color:#b4b3b3;background:#f5f5f5;opacity:1}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save,.openassessment .action--submit,.step--response .action--upload{padding:10px 40px;background:#fff;border:1px solid #065683;color:#065683}.openassessment .step--response .response__submission__content .action--save:hover,.openassessment .step--response .response__submission__actions .action--save:hover,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:hover,.openassessment .step--self-assessment .self-assessment__assessment .action--save:hover,.openassessment .action--submit:hover,.step--response .action--upload:hover,.openassessment .step--response .response__submission__content .action--save:active,.openassessment .step--response .response__submission__actions .action--save:active,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:active,.openassessment .step--self-assessment .self-assessment__assessment .action--save:active,.openassessment .action--submit:active,.step--response .action--upload:active,.openassessment .step--response .response__submission__content .action--save:focus,.openassessment .step--response .response__submission__actions .action--save:focus,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save:focus,.openassessment .step--self-assessment .self-assessment__assessment .action--save:focus,.openassessment .action--submit:focus,.step--response .action--upload:focus{background:#0075b4;border-color:#0075b4;color:#fff}.openassessment .step--response .response__submission__content .current.action--save,.openassessment .step--response .response__submission__actions .current.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .current.action--save,.openassessment .step--self-assessment .self-assessment__assessment .current.action--save,.openassessment .current.action--submit,.step--response .current.action--upload,.openassessment .step--response .response__submission__content .active.action--save,.openassessment .step--response .response__submission__actions .active.action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .active.action--save,.openassessment .step--self-assessment .self-assessment__assessment .active.action--save,.openassessment .active.action--submit,.step--response .active.action--upload{background:#fff;border-color:#065683;color:#065683}.openassessment .step--response .response__submission__content [disabled].action--save,.openassessment .step--response .response__submission__actions [disabled].action--save,.openassessment .step--peer-assessment .peer-assessment__assessment [disabled].action--save,.openassessment .step--self-assessment .self-assessment__assessment [disabled].action--save,.openassessment [disabled].action--submit,.step--response [disabled].action--upload{background:#fff;border-color:#b4b3b3;color:#b4b3b3;opacity:1}.wrapper--xblock [placeholder]:focus::-webkit-input-placeholder{opacity:0.0}.wrapper--xblock input[type="text"],.wrapper--xblock input[type="email"],.wrapper--xblock input[type="password"],.wrapper--xblock textarea{border:1px solid #b9c1c9;padding:10px;color:#414141}.wrapper--xblock input[type="text"]::-webkit-input-placeholder,.wrapper--xblock input[type="email"]::-webkit-input-placeholder,.wrapper--xblock input[type="password"]::-webkit-input-placeholder,.wrapper--xblock textarea::-webkit-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-moz-placeholder,.wrapper--xblock input[type="email"]:-moz-placeholder,.wrapper--xblock input[type="password"]:-moz-placeholder,.wrapper--xblock textarea:-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]::-moz-placeholder,.wrapper--xblock input[type="email"]::-moz-placeholder,.wrapper--xblock input[type="password"]::-moz-placeholder,.wrapper--xblock textarea::-moz-placeholder{color:#414141}.wrapper--xblock input[type="text"]:-ms-input-placeholder,.wrapper--xblock input[type="email"]:-ms-input-placeholder,.wrapper--xblock input[type="password"]:-ms-input-placeholder,.wrapper--xblock textarea:-ms-input-placeholder{color:#414141}.wrapper--xblock input[type="text"]:focus,.wrapper--xblock input[type="email"]:focus,.wrapper--xblock input[type="password"]:focus,.wrapper--xblock textarea:focus{box-shadow:0 0 6px 0 rgba(29,157,217,0.4),inset 0 0 4px 0 rgba(0,0,0,0.15);border-color:#000}.wrapper--xblock .field{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.wrapper--xblock .field .field-group__label{display:block;margin-bottom:10px}.wrapper--xblock .field textarea{width:100%}.wrapper--xblock .field .tip{display:block;color:#414141}.wrapper--xblock .field--textarea .label,.wrapper--xblock .field--textarea label{margin-bottom:10px}.wrapper--xblock .field-group__label{margin-bottom:10px}.wrapper--xblock .field--radio .option,.wrapper--xblock .field--checkbox .option{margin-bottom:5px}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__input,.wrapper--xblock .field--checkbox .option__label{display:inline-block;vertical-align:middle}.wrapper--xblock .field--radio .option__label,.wrapper--xblock .field--checkbox .option__label{margin-bottom:0;color:#414141}.wrapper--xblock .field--radio .option__input,.wrapper--xblock .field--checkbox .option__input{margin-left:10px}.wrapper--xblock .field--radio .option__input:checked+.option__label,.wrapper--xblock .field--checkbox .option__input:checked+.option__label{color:#000}.wrapper--xblock .message{margin-bottom:20px;border-radius:2px;padding:20px 20px;background:#f5f6f7}.wrapper--xblock .message .message__title{margin-bottom:5px;border-bottom:2px solid #d9d9d9;padding-bottom:5px}.wrapper--xblock .message .message__content{color:#414141}.wrapper--xblock .message .message__content p{margin-bottom:10px}.wrapper--xblock .message--error{background:#feeced}.wrapper--xblock .message--error .message__title{color:#414141;border-bottom-color:#b20610}.wrapper--xblock .message--warning{background:#fff9eb}.wrapper--xblock .message--warning .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--complete,.wrapper--xblock .step--student-training .message--correct,.step--student-training .wrapper--xblock .message--correct{background:#ecfaec}.wrapper--xblock .message--complete .message__title,.wrapper--xblock .step--student-training .message--correct .message__title,.step--student-training .wrapper--xblock .message--correct .message__title{color:#414141;border-bottom-color:#008100}.wrapper--xblock .message--incomplete,.wrapper--xblock .step--student-training .message--incorrect,.step--student-training .wrapper--xblock .message--incorrect{background:#fff9eb}.wrapper--xblock .message--incomplete .message__title,.wrapper--xblock .step--student-training .message--incorrect .message__title,.step--student-training .wrapper--xblock .message--incorrect .message__title{color:#414141;border-bottom-color:#ffc01f}.wrapper--xblock .message--error{display:none;opacity:0.0}.wrapper--xblock .has--error .message--error{display:block;opacity:1.0}.wrapper--xblock .message--inline{padding:10px 20px;background:#5e5e5e}.wrapper--xblock .message--inline .message__title{margin-bottom:0;border:none;padding-bottom:0;color:#fff}.wrapper--xblock .message--inline.message--error{background:#b20610}.wrapper--xblock .message--inline.message--error .message__content{color:#fff}.wrapper--xblock .message--inline.message--warning{background:#ffc01f}.wrapper--xblock .message--inline.message--warning .message__content{color:#111}.wrapper--xblock .message--inline.message--warning .message__title{color:#111}.wrapper--xblock .is--transitioning{padding:40px 40px;background:#f5f6f7}.wrapper--xblock .is--transitioning .transition__status{position:relative;top:50%;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%);text-align:center}.wrapper--xblock .is--transitioning .transition__status .wrapper--anim{display:block;-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.wrapper--xblock .is--transitioning .transition__status .icon,.wrapper--xblock .is--transitioning .transition__status .copy{display:block;color:#414141}.wrapper--xblock .is--transitioning .transition__status .icon{margin-bottom:10px}.wrapper--grid{*zoom:1;max-width:100%;margin-left:auto;margin-right:auto}.wrapper--grid:before,.wrapper--grid:after{content:" ";display:table}.wrapper--grid:after{clear:both}/*! Hint.css - v1.3.1 - 2013-11-23 * http://kushagragour.in/lab/hint/ -* Copyright (c) 2013 Kushagra Gour; Licensed MIT */.hint,[data-hint]{position:relative;display:inline-block}.hint:before,.hint:after,[data-hint]:before,[data-hint]:after{position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);visibility:hidden;opacity:0;z-index:1000000;pointer-events:none;-webkit-transition:0.3s ease;-moz-transition:0.3s ease;transition:0.3s ease}.hint:hover:before,.hint:hover:after,.hint:focus:before,.hint:focus:after,[data-hint]:hover:before,[data-hint]:hover:after,[data-hint]:focus:before,[data-hint]:focus:after{visibility:visible;opacity:1}.hint:before,[data-hint]:before{content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1000001}.hint:after,[data-hint]:after{content:attr(data-hint);background:#383838;color:white;text-shadow:0 -1px 0px black;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;box-shadow:4px 4px 8px rgba(0,0,0,0.3)}.hint--top:before{border-top-color:#383838}.hint--bottom:before{border-bottom-color:#383838}.hint--left:before{border-left-color:#383838}.hint--right:before{border-right-color:#383838}.hint--top:before{margin-bottom:-12px}.hint--top:after{margin-left:-18px}.hint--top:before,.hint--top:after{bottom:100%;left:50%}.hint--top:hover:after,.hint--top:hover:before,.hint--top:focus:after,.hint--top:focus:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--bottom:before{margin-top:-12px}.hint--bottom:after{margin-left:-18px}.hint--bottom:before,.hint--bottom:after{top:100%;left:50%}.hint--bottom:hover:after,.hint--bottom:hover:before,.hint--bottom:focus:after,.hint--bottom:focus:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--right:before{margin-left:-12px;margin-bottom:-6px}.hint--right:after{margin-bottom:-14px}.hint--right:before,.hint--right:after{left:100%;bottom:50%}.hint--right:hover:after,.hint--right:hover:before,.hint--right:focus:after,.hint--right:focus:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--left:before{margin-right:-12px;margin-bottom:-6px}.hint--left:after{margin-bottom:-14px}.hint--left:before,.hint--left:after{right:100%;bottom:50%}.hint--left:hover:after,.hint--left:hover:before,.hint--left:focus:after,.hint--left:focus:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--error:after{background-color:#b34e4d;text-shadow:0 -1px 0px #592726}.hint--error.hint--top:before{border-top-color:#b34e4d}.hint--error.hint--bottom:before{border-bottom-color:#b34e4d}.hint--error.hint--left:before{border-left-color:#b34e4d}.hint--error.hint--right:before{border-right-color:#b34e4d}.hint--warning:after{background-color:#c09854;text-shadow:0 -1px 0px #6c5328}.hint--warning.hint--top:before{border-top-color:#c09854}.hint--warning.hint--bottom:before{border-bottom-color:#c09854}.hint--warning.hint--left:before{border-left-color:#c09854}.hint--warning.hint--right:before{border-right-color:#c09854}.hint--info:after{background-color:#3986ac;text-shadow:0 -1px 0px #193b4d}.hint--info.hint--top:before{border-top-color:#3986ac}.hint--info.hint--bottom:before{border-bottom-color:#3986ac}.hint--info.hint--left:before{border-left-color:#3986ac}.hint--info.hint--right:before{border-right-color:#3986ac}.hint--success:after{background-color:#458746;text-shadow:0 -1px 0px #1a321a}.hint--success.hint--top:before{border-top-color:#458746}.hint--success.hint--bottom:before{border-bottom-color:#458746}.hint--success.hint--left:before{border-left-color:#458746}.hint--success.hint--right:before{border-right-color:#458746}.hint--always:after,.hint--always:before{opacity:1;visibility:visible}.hint--always.hint--top:after,.hint--always.hint--top:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--bottom:after,.hint--always.hint--bottom:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--left:after,.hint--always.hint--left:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--always.hint--right:after,.hint--always.hint--right:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--rounded:after{border-radius:4px}.hint--bounce:before,.hint--bounce:after{-webkit-transition:opacity 0.3s ease,visibility 0.3s ease,-webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);-moz-transition:opacity 0.3s ease,visibility 0.3s ease,-moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);transition:opacity 0.3s ease,visibility 0.3s ease,transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24)}.openassessment .openassessment__steps__step{-webkit-transition:all 0.5s ease-in-out;-moz-transition:all 0.5s ease-in-out;transition:all 0.5s ease-in-out;-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;box-shadow:0 1px 1px 0 rgba(3,3,3,0.05);margin-bottom:20px;border:1px solid #d9d9d9;border-top:5px solid #d9d9d9;padding:10px 20px;background:#fff}.openassessment .openassessment__steps__step:last-child{margin-bottom:0}.openassessment .openassessment__steps__step:hover,.openassessment .openassessment__steps__step:focus{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}.openassessment .has--error.openassessment__steps__step:hover,.openassessment .has--error.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--showing.openassessment__steps__step{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);box-shadow:0 1px 1px 0 rgba(3,3,3,0.1)}.openassessment .is--loading.openassessment__steps__step{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);box-shadow:0 1px 1px 0 rgba(3,3,3,0.05)}.openassessment .is--empty.openassessment__steps__step:hover,.openassessment .is--empty.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--unavailable.openassessment__steps__step{border-top-color:#d9d9d9}.openassessment .is--unavailable.openassessment__steps__step:hover,.openassessment .is--unavailable.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--in-progress.openassessment__steps__step{border-top-color:#0075b4}.openassessment .is--complete.openassessment__steps__step{border-top-color:#008100}.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .has--error.openassessment__steps__step{border-top-color:#b20610}.openassessment .is--closed.openassessment__steps__step,.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .staff-assessment__display,.openassessment .step--peer-assessment .peer-assessment__display,.openassessment .step--self-assessment .self-assessment__display,.openassessment .submission__peer-evaluations,.openassessment .submission__feedback,.step--student-training .student-training__display{margin-bottom:20px}.openassessment .submission__feedback__title{margin-bottom:10px;border-bottom:2px solid #d9d9d9;padding-bottom:10px}.openassessment .staff-assessment__display__response p,.openassessment .step--peer-assessment .peer-assessment__display__response p,.openassessment .step--self-assessment .self-assessment__display__response p,.openassessment .submission__answer__display__content p,.openassessment .self-assessment__display__image p,.openassessment .peer-assessment__display__image p,.openassessment .staff-assessment__display__image p,.openassessment .submission__answer__display__image p,.openassessment .submission__peer-evaluations__questions p,.openassessment .submission__feeedback__content p,.openassessment .submission__feedback__actions p,.step--student-training .student-training__display__response p{margin-bottom:20px}.openassessment .staff-assessment__display__response p:last-child,.openassessment .step--peer-assessment .peer-assessment__display__response p:last-child,.openassessment .step--self-assessment .self-assessment__display__response p:last-child,.openassessment .submission__answer__display__content p:last-child,.openassessment .self-assessment__display__image p:last-child,.openassessment .peer-assessment__display__image p:last-child,.openassessment .staff-assessment__display__image p:last-child,.openassessment .submission__answer__display__image p:last-child,.openassessment .submission__peer-evaluations__questions p:last-child,.openassessment .submission__feeedback__content p:last-child,.openassessment .submission__feedback__actions p:last-child,.step--student-training .student-training__display__response p:last-child{margin-bottom:0}.openassessment .staff-assessment__display__response,.openassessment .submission__answer__part__prompt,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.step--student-training .student-training__display__response{box-shadow:inset 0 1px 2px 1px rgba(3,3,3,0.1);padding:20px 20px;background:#fdfdfd}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value,.openassessment .staff-assessment__display__response,.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--response .response__submission__content .submission__answer__part__text textarea,.openassessment .step--response .response__submission__content .submission__file__description textarea,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .submission__feedback__fields textarea,.step--student-training .student-training__display__response,.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{max-height:300px;overflow-y:scroll}.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label{color:#414141;display:inline-block;vertical-align:middle;margin-right:10px}.openassessment .step--response .response__submission__content .response__submission__label .list--actions,.openassessment .step--response .response__submission__actions .response__submission__label .list--actions,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label .list--actions,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label .list--actions{text-align:right}.openassessment .step--response .response__submission__content .list--actions__item,.openassessment .step--response .response__submission__actions .list--actions__item,.openassessment .step--peer-assessment .peer-assessment__assessment .list--actions__item,.openassessment .step--self-assessment .self-assessment__assessment .list--actions__item{text-align:right;margin-top:5px}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:block;margin-bottom:10px;min-width:215px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}@media screen and (min-width: 1300px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}.openassessment .staff-assessment__assessment .assessment__rubric__question,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question,.step--student-training .student-training__assessment .assessment__rubric__question{*zoom:1;margin-bottom:40px}.openassessment .staff-assessment__assessment .assessment__rubric__question:before,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:before,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:before,.step--student-training .student-training__assessment .assessment__rubric__question:before,.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{content:" ";display:table}.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{clear:both}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title,.step--student-training .student-training__assessment .assessment__rubric__question .question__title{border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy{margin-right:10px;white-space:pre-wrap;display:inline}.openassessment .staff-assessment__assessment .question__answers,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers,.openassessment .step--self-assessment .self-assessment__assessment .question__answers,.step--student-training .student-training__assessment .question__answers{margin-top:20px;margin-bottom:20px;margin-right:20px}.openassessment .staff-assessment__assessment .question__answers .answer,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer,.step--student-training .student-training__assessment .question__answers .answer,.openassessment .staff-assessment__assessment .question__answers .answer--feedback,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback,.step--student-training .student-training__assessment .question__answers .answer--feedback{*zoom:1;display:block;margin-bottom:10px;border-bottom:1px solid #f5f6f7;padding-bottom:10px}.openassessment .staff-assessment__assessment .question__answers .answer:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:before,.step--student-training .student-training__assessment .question__answers .answer:before,.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:before,.step--student-training .student-training__assessment .question__answers .answer--feedback:before,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{content:" ";display:table}.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{clear:both}.openassessment .staff-assessment__assessment .question__answers .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input,.step--student-training .student-training__assessment .question__answers .wrapper--input{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{margin-bottom:0;color:#414141}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value{margin-left:10px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label{color:#000}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{display:block}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:right;display:block;margin-left:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:right;display:block;margin-left:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:right;display:block;margin-left:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-left:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{display:block}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{margin-bottom:10px;white-space:pre-wrap;margin-left:20px;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:right;display:block;margin-left:3.57866%;width:74.10533%;float:right;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:right;display:block;margin-left:3.57866%;width:74.10533%;float:right;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:right;display:block;margin-left:3.57866%;width:74.10533%;float:right;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-left:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:after,.step--student-training .student-training__assessment .question__answers .answer__tip:after{content:""}.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:left;text-align:left}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:left;text-align:left}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:left;text-align:left}}.openassessment .staff-assessment__assessment .question__answers .answer__points__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points__label,.step--student-training .student-training__assessment .question__answers .answer__points__label{display:inline-block;margin-right:5px;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:4.82916%;width:100%}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__label,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__label{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value{min-height:100px;margin-left:0}.has--hints{overflow:visible}.ui-hint .icon{margin-right:5px}.ui-hint:hover,.ui-hint:focus{color:#065683}.ui-hint:after{right:0;display:block;white-space:normal;width:200px;background:rgba(3,3,3,0.75)}.ui-hint.hint--top:after{right:0;margin-right:-20px}.openassessment .wrapper--ui-staff{box-shadow:inset 0 1px -2px 1px rgba(3,3,3,0.4);margin-top:40px;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;border:1px solid #891c4e;border-top:5px solid #b62568;padding:20px 20px;background:#2c2f36;display:none}.openassessment .ui-staff .ui-slidable__content{padding:10px 10px}.openassessment .ui-staff__title{color:#fff}.openassessment .ui-staff__subtitle{font-size:16px;color:#fff !important;margin-bottom:10px}.openassessment .ui-staff__subtitle span{font-weight:inherit}.openassessment .ui-staff__content{margin-top:20px;color:#fff}.openassessment .ui-staff__content__section{padding-bottom:10px}.openassessment .ui-staff__content__section .wrapper--input,.openassessment .ui-staff__content__section .wrapper--content{padding:20px 20px;background-color:#fff;color:#414141}.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value{display:inline-block;vertical-align:center;color:#fff}.openassessment .staff-info__summary .label{margin-left:10px}.openassessment .openassessment_student_info_form{margin-bottom:10px}.openassessment .openassessment_student_info_form .action--submit-username{margin-right:0}.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table{-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;word-break:initial}.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title{text-align:right;color:#fff;margin-bottom:10px}.openassessment .staff-info__status__table .label,.openassessment .staff-info__classifierset__table .label{color:#fff}.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value{color:#fff}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td,.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td{border:1px solid rgba(255,255,255,0.25);padding:10px 10px}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td[scope],.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td[scope]{text-align:right}.openassessment .wrapper--staff-assessment{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .staff-assessment__display__header{*zoom:1}.openassessment .staff-assessment__display__header:before,.openassessment .staff-assessment__display__header:after{content:" ";display:table}.openassessment .staff-assessment__display__header:after{clear:both}.openassessment .staff-assessment__display__header .staff-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .staff-assessment__display__response{color:#414141}.openassessment .staff-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-right:0;white-space:pre-wrap}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .ui-staff .staff__grade__control{border-top:5px solid #d9d9d9;background:#fff;background-color:#fff}.openassessment .ui-staff .staff__grade__control .staff__grade__header{margin-bottom:0}.openassessment .ui-staff .staff__grade__control .staff__grade__title{text-align:right;float:none;padding:0 20px;display:block;width:100%}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form{font-size:16px;background-color:#fff;text-transform:none;letter-spacing:normal;padding:0}.openassessment .ui-staff .staff__grade__control .staff__grade__status{display:inline-block;padding:0 20px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:left;position:relative;top:-40px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:left;position:relative;top:-40px}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:left;position:relative;top:-40px}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{border-radius:2px;padding:5px 10px;background:#d9d9d9;line-height:0}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy{color:#414141}.openassessment .ui-staff .staff__grade__control .submit_assessment--action{display:inline}.openassessment .ui-staff .staff__grade__control .staff__grade__content{padding:0}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .comments__label{color:#414141}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .cancel_submission_comments{width:100%;min-height:100px;text-align:left}.openassessment .staff-info__student__report .staff-info__final__grade__table{margin-top:10px}.wrapper--openassessment{position:relative}.openassessment{direction:rtl}.openassessment .openassessment__title{*zoom:1;display:block}.openassessment .openassessment__title:before,.openassessment .openassessment__title:after{content:" ";display:table}.openassessment .openassessment__title:after{clear:both}.openassessment .openassessment__message{*zoom:1;display:block;background:none;padding:0}.openassessment .openassessment__message:before,.openassessment .openassessment__message:after{content:" ";display:table}.openassessment .openassessment__message:after{clear:both}.openassessment .list--actions{margin-bottom:10px;text-align:center}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .list--actions{text-align:left}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .list--actions{text-align:left}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .list--actions{text-align:left}}@media screen and (min-width: 1300px){.openassessment .list--actions{text-align:left}}.openassessment .list--actions.has--error .message{margin-bottom:20px;text-align:right}.openassessment .list--actions .action--submit{margin-right:10px;margin-top:10px}.openassessment .list--actions .action--submit .copy,.openassessment .list--actions .action--submit .icon{display:inline}.openassessment .list--actions .action--submit .fa-caret-right:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.openassessment .openassessment__steps{counter-reset:item}.openassessment .openassessment__steps__step .step__title{display:block;margin-bottom:10px;width:100%;margin-bottom:0}.openassessment .openassessment__steps__step .step__title .step__counter,.openassessment .openassessment__steps__step .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-right:20px;border-right:1px solid #d9d9d9;padding-right:20px}@media screen and (max-width: 529px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-right:0;border-right:none;padding-right:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-right:0;border-right:none;padding-right:0}}.openassessment .openassessment__steps__step .step__title .step__counter{display:none}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:none;content:counter(item, decimal);counter-increment:item;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .openassessment__steps__step .step__title .step__deadline{display:block}.openassessment .openassessment__steps__step .step__title .step__deadline{color:#414141}.openassessment .openassessment__steps__step .step__status{display:inline;margin-top:5px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:left;position:relative;top:-12px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:left;position:relative;top:-12px}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:left;position:relative;top:-12px}}.openassessment .openassessment__steps__step .step__status .step__status__value{border-radius:2px;padding:4px 10px;background:#d9d9d9;position:relative}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}.openassessment .openassessment__steps__step .step__status .icon{display:inline-block;vertical-align:baseline;margin-left:10px}.openassessment .openassessment__steps__step .step__status .copy{color:#414141}.openassessment .openassessment__steps__step .wrapper--step__content{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .openassessment__steps__step .step__message{*zoom:1;display:block;margin-bottom:20px}.openassessment .openassessment__steps__step .step__message:before,.openassessment .openassessment__steps__step .step__message:after{content:" ";display:table}.openassessment .openassessment__steps__step .step__message:after{clear:both}.openassessment .openassessment__steps__step .step__instruction{margin-bottom:20px;color:#414141}.openassessment .openassessment__steps__step .step__content{margin-bottom:20px}.openassessment .openassessment__steps__step .step__content__section{margin-bottom:40px}.openassessment .openassessment__steps__step.is--loading .step__header{padding-bottom:0;border-bottom:none;margin-bottom:0}.openassessment .openassessment__steps__step.is--loading .step__counter:before,.openassessment .openassessment__steps__step.is--loading .step__label,.openassessment .openassessment__steps__step.is--loading .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--loading .step__status__value .wrapper--anim{-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.openassessment .openassessment__steps__step.is--loading .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__counter:before{border-color:#ffc01f;color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__status__value{background:#ffc01f}.openassessment .openassessment__steps__step.is--incomplete .step__status__value .copy,.openassessment .openassessment__steps__step.is--incomplete .step__status__value .icon{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__counter:before,.openassessment .openassessment__steps__step.is--unavailable .step__label,.openassessment .openassessment__steps__step.is--unavailable .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__counter:before,.openassessment .openassessment__steps__step.is--waiting .step__label,.openassessment .openassessment__steps__step.is--waiting .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__title .grade__value__title{color:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__instruction,.openassessment .openassessment__steps__step.is--waiting .grade__value__description{color:#414141}.openassessment .openassessment__steps__step.is--complete .step__counter:before{color:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value{background:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--complete .step__status__value .copy,.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--complete button{opacity:1}.openassessment .openassessment__steps__step.is--in-progress .step__counter:before{color:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value{background:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .copy,.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--in-progress button{opacity:1}.openassessment .openassessment__steps__step.has--error .step__counter:before{border-color:rgba(178,6,16,0.5);color:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value{background:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value .copy,.openassessment .openassessment__steps__step.has--error .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.has--error .step__label{color:#b20610}.openassessment .ui-slidable__container{-webkit-transition:color 0.25s ease-in;-moz-transition:color 0.25s ease-in;transition:color 0.25s ease-in}.openassessment .ui-slidable__container .ui-slidable__content{width:100%}.openassessment .ui-slidable__container .icon{-webkit-transition:all 0.25s ease-in-out 0s;-moz-transition:all 0.25s ease-in-out 0s;transition:all 0.25s ease-in-out 0s;-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.openassessment .ui-slidable__container.is--initially--collapsed .ui-slidable__content{display:none}.openassessment .ui-slidable__container.is--showing .ui-slidable .icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%;-ms-transform-origin:50% 50%;-o-transform-origin:50% 50%;transform-origin:50% 50%}.openassessment .ui-slidable__container.is--showing .ui-slidable__container:not(.is--showing) .icon{-webkit-transition:all 0.25s ease-in-out 0s;-moz-transition:all 0.25s ease-in-out 0s;transition:all 0.25s ease-in-out 0s;-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.openassessment .ui-slidable__container .ui-slidable{background:none;border-radius:5px;padding:0px}.openassessment .ui-slidable__container .ui-slidable:active,.openassessment .ui-slidable__container .ui-slidable:focus:not(:disabled),.openassessment .ui-slidable__container .ui-slidable:hover:not(:disabled){background:none;border:none;box-shadow:none}.openassessment .ui-slidable__container .ui-slidable:not([disabled]){color:#0075b4}.openassessment .ui-slidable__container .ui-slidable:not([disabled]):hover,.openassessment .ui-slidable__container .ui-slidable:not([disabled]):focus{color:#065683}.openassessment .ui-slidable__container .ui-slidable .wrapper--copy{text-align:left}.openassessment .submission__answer__part__prompt{position:relative;margin:10px 0;border:1px solid #d9d9d9;border-right:10px solid #b9c1c9;padding-right:30px}.openassessment .submission__answer__part__prompt__copy{color:#414141}.openassessment .submission__answer__part__prompt__copy>*{margin-bottom:20px}.openassessment .submission__answer__part__text__value{margin:10px 0}.openassessment .submission__answer__part__text__value:not(textarea){border:1px solid #b9c1c9;background-color:#fff;padding:20px}.openassessment .submission__file__description__label{margin-bottom:5px}.openassessment .submission__answer__file__block{margin-bottom:8px}.openassessment .submission__img__preview{float:left;margin-right:10px}.openassessment .step--response .response__submission{padding:10px 10px 20px 10px;background:#f5f6f7;border-radius:2px}.openassessment .step--response .response__submission__content{margin-bottom:20px}.openassessment .step--response .response__submission__content .files__descriptions{display:none}.openassessment .step--response .response__submission__content .files__descriptions .submission__file__description{padding-bottom:10px}.openassessment .step--response .response__submission__content .submission__answer__part__text textarea{min-height:200px}.openassessment .step--response .response__submission__content .submission__file__description textarea{min-height:80px;width:70%}.openassessment .step--response .response__submission__content .tip{padding:5px 10px;background:#b9c1c9;color:#fff}.openassessment .step--peer-assessment .peer-assessment__display__header{*zoom:1}.openassessment .step--peer-assessment .peer-assessment__display__header:before,.openassessment .step--peer-assessment .peer-assessment__display__header:after{content:" ";display:table}.openassessment .step--peer-assessment .peer-assessment__display__header:after{clear:both}.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--peer-assessment .peer-assessment__display__response{color:#414141}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-right:0;white-space:pre-wrap}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .step--self-assessment .self-assessment__display__header{*zoom:1}.openassessment .step--self-assessment .self-assessment__display__header:before,.openassessment .step--self-assessment .self-assessment__display__header:after{content:" ";display:table}.openassessment .step--self-assessment .self-assessment__display__header:after{clear:both}.openassessment .step--self-assessment .self-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--self-assessment .self-assessment__display__response{color:#414141}.openassessment .step--self-assessment .self-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--self-assessment .self-assessment__assessment .question__answers{overflow:visible}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-right:0;white-space:pre-wrap}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}@media screen and (max-width: 529px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:7.42297%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:4.82916%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}.openassessment .step--grade .step__title .step__counter,.openassessment .step--grade .step__title .step__counter:before{display:none}.openassessment .step--grade .step__title .wrapper--copy{margin-right:0;border-right:none;padding-right:0}.openassessment .step--grade .step__title .step__label,.openassessment .step--grade .step__title .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--grade .step__title .step__label{margin-left:10px}.openassessment .step--grade .grade__value__description{margin-bottom:20px;color:#414141}.openassessment .submission__answer__display__title{margin-bottom:10px;color:#414141}.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image{padding:10px 10px 20px 10px;border-radius:2px;list-style:none}.openassessment .submission__answer__display__content .submission__answer__part,.openassessment .self-assessment__display__image .submission__answer__part,.openassessment .peer-assessment__display__image .submission__answer__part,.openassessment .staff-assessment__display__image .submission__answer__part,.openassessment .submission__answer__display__image .submission__answer__part{margin:30px 0px 0px}.openassessment .submission__answer__display__content .submission__answer__part:first-child,.openassessment .self-assessment__display__image .submission__answer__part:first-child,.openassessment .peer-assessment__display__image .submission__answer__part:first-child,.openassessment .staff-assessment__display__image .submission__answer__part:first-child,.openassessment .submission__answer__display__image .submission__answer__part:first-child{margin-top:0px}.openassessment .submission__peer-evaluations__title{margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions{margin-bottom:10px}.openassessment .submission__peer-evaluations__questions:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0}.openassessment .submission__peer-evaluations__questions .question{margin-bottom:30px}.openassessment .submission__peer-evaluations__questions .question__title{*zoom:1;margin-bottom:20px;border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .submission__peer-evaluations__questions .question__title:before,.openassessment .submission__peer-evaluations__questions .question__title:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__title:after{clear:both}.openassessment .submission__peer-evaluations__questions .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__peer-evaluations__questions .question__title__copy{margin-right:10px;white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .question__score{float:left;margin-right:20px}.openassessment .submission__peer-evaluations__questions .question__score__value{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__value:after{content:"/";margin-right:5px;color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential .unit{margin-right:5px}.openassessment .submission__peer-evaluations__questions .question__answers{*zoom:1;display:block;overflow:visible}.openassessment .submission__peer-evaluations__questions .question__answers:before,.openassessment .submission__peer-evaluations__questions .question__answers:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__answers:after{clear:both}.openassessment .submission__peer-evaluations__questions .answer{margin-bottom:20px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:4.82916%;width:47.58542%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n+1){clear:right}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:right}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:right}}@media screen and (min-width: 1300px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:right}}.openassessment .submission__peer-evaluations__questions .answer.self-assessment{border-right:5px solid #f5f6f7;padding-right:20px}.openassessment .submission__peer-evaluations__questions .answer.self-assessment .answer__source{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__source{display:block;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value{display:block;margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value .answer__value__raw{white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .answer__value__value{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:hover,.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:focus{color:#065683}.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value{display:block;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .answer--feedback{float:right;display:block;margin-left:4.82916%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .answer--feedback{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .answer--feedback{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .answer--feedback{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}.openassessment .answer--feedback .answer--feedback__title__copy{margin-right:10px}.openassessment .answer--feedback .answer--feedback__content{margin-top:5px}.openassessment .answer--feedback .feedback{margin-bottom:20px}.openassessment .answer--feedback .feedback__source{display:block;color:#414141}.openassessment .answer--feedback .feedback__value{display:block}.openassessment .answer--feedback .feedback__value .feedback__value__raw{white-space:pre-wrap}.openassessment .submission__feedback__title{border-bottom-color:solid #414141;color:#414141}.openassessment .submission__feedback__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__feedback__title__copy{margin-right:10px}.openassessment .submission__feeedback__content{margin-bottom:20px}.openassessment .submission__feedback__instructions{margin-bottom:20px;color:#414141}.openassessment .submission__feedback__elements{padding:20px 20px 20px 20px;background:#f5f6f7;border-radius:2px}.openassessment .submission__feedback__fields{margin-bottom:20px}.openassessment .submission__feedback__fields .field{margin-bottom:30px}.openassessment .submission__feedback__fields .field:last-child{margin-bottom:0}.openassessment .submission__feedback__fields select{display:block}.openassessment .submission__feedback__fields textarea{min-height:200px}.openassessment .submission__feedback__actions{padding-top:0}.openassessment .submission__feedback__actions .list--actions{padding:0}.openassessment .action--submit{margin-left:10px}.open-response-assessment-block table{table-layout:fixed}.open-response-assessment-block .open-response-assessment-msg{margin-bottom:20px}.open-response-assessment-block .open-response-assessment-content,.open-response-assessment-block .open-response-assessment-item{display:none}.open-response-assessment-block .open-response-assessment-summary td{border:none}.open-response-assessment-block .open-response-assessment-item-breadcrumbs{padding-bottom:20px}.open-response-assessment-block .open-response-assessment-item-breadcrumbs a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table th{background-color:#ffffff;text-align:left}.open-response-assessment-block .open-response-assessment-main-table th button{background:none;border:none;box-shadow:none;outline:none;font-size:14px}.open-response-assessment-block .open-response-assessment-main-table th a{color:#000000 !important}.open-response-assessment-block .open-response-assessment-main-table td a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table tbody tr:nth-child(odd){background-color:#f9f9f9}.open-response-assessment-block .ora-summary-value,.open-response-assessment-block .backgrid{text-overflow:ellipsis;overflow:hidden}.open-response-assessment-block .ora-summary-title{color:#0079bc;font-weight:bold;text-transform:uppercase;font-size:0.8em}.open-response-assessment-block .ora-summary-value{font-size:1.5em;padding-top:3px}.open-response-assessment-block .backgrid .peer,.open-response-assessment-block .backgrid .self,.open-response-assessment-block .backgrid .staff,.open-response-assessment-block .backgrid .waiting,.open-response-assessment-block .backgrid .training{width:80px}.open-response-assessment-block .backgrid .total{width:130px}.open-response-assessment-block .backgrid .done{width:160px}.view--placeholder{background:transparent;margin:40px 40px}.wrapper--openassessment .wrapper--staff-area{width:100%;overflow:auto;margin-top:20px;padding-top:20px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar{position:relative;text-align:right;margin:0 0 8px;padding:10px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{padding:5px 10px;font-size:12px;border-radius:5px;text-transform:uppercase;color:#414141;background-color:rgba(3,3,3,0.05)}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active{color:white;background-color:#b62568}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active:hover{background-color:#921e53}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button:hover{color:white;background-color:#0075b4}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff{margin-top:0}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button{margin:0;padding:0;border:0;float:right;background-color:#2c2f36;font-size:12px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button:hover{color:#fff}.staff-info__student .label{color:#fff;margin-bottom:10px}.staff-info__student .label input{display:block;padding-top:0;padding-bottom:0}.staff-info__student .title{color:#fff;margin-bottom:10px}.staff-info__student .title--sub{color:#fff;margin-top:10px;margin-bottom:10px}.staff-info__student .student__answer__display__content{border:1px solid rgba(255,255,255,0.25);padding:10px 10px;margin-bottom:10px}.staff-info__student .staff-info__student__report{list-style-type:none}.staff-info__student .staff-info__student__report .title{margin-top:10px;border-top:1px solid #fff;padding:10px 20px 10px 0}.staff-info__student .staff-info__student__report .title span{font-weight:inherit}.staff-info__student .value{width:50%}.step--student-training .student-training__display__header{*zoom:1}.step--student-training .student-training__display__header:before,.step--student-training .student-training__display__header:after{content:" ";display:table}.step--student-training .student-training__display__header:after{clear:both}.step--student-training .student-training__display__title{margin-bottom:10px;color:#414141}.step--student-training .student-training__display__response{color:#414141}.step--student-training .student-training__assessment .assessment__fields{margin-bottom:20px}.step--student-training .student-training__assessment .question__answers{overflow:visible}.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.step--student-training .message--correct{margin-top:20px}.step--student-training .message--correct.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--correct.is--hidden .step__header{border-bottom:none}.step--student-training .message--incorrect{margin-top:20px}.step--student-training .message--incorrect.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--incorrect.is--hidden .step__header{border-bottom:none}#openassessment-editor{margin-bottom:0;height:100%}#openassessment-editor .openassessment_editor_content_and_tabs{width:100%;height:calc(100% - 55px)}#openassessment-editor #oa_editor_window_title{float:right}#openassessment-editor input[type="number"]{background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;width:45%}#openassessment-editor #openassessment_editor_header{background-color:#e5e5e5;width:100%;top:0;height:42px}#openassessment-editor #openassessment_editor_header .editor_tabs{width:35%;min-width:190px;max-width:360px;float:left}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab{float:left;padding:0;width:29%;margin:0px 2%;height:35px;-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px;box-shadow:none;border:0;text-align:center}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab a{padding:8px 0 0 0;width:100%;text-transform:uppercase;outline-color:transparent}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab{background:#e5e5e5 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab a{color:#424243}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab{background:#626365 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover a,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab a{color:#fff}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab:hover{background-color:rgba(164,165,168,0.75)}#openassessment-editor #openassessment_validation_alert{width:100%;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;background-color:#323232;border-bottom:3px solid #c0ac00;padding:10px;position:absolute;z-index:10;max-height:200px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon{position:absolute;top:35%;font-size:200%;right:3%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon:before{font-family:FontAwesome;content:"\f071";display:inline-block;color:#c0ac00;float:right;height:0;width:0}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header{width:85%;margin:0 10% 0 5%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_title{width:auto;color:white}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_message{font-size:80%;color:darkgray}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close{display:inline-block;position:absolute;top:0px;left:0px;color:#e9e9e9;text-align:center;margin:5px 10px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close [class^="icon"]{width:auto;margin:0;padding:2px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close:hover{color:#80a9cc}#openassessment-editor .oa_editor_content_wrapper{height:calc(100% - 42px);width:100%;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;border:1px solid #838486;background-color:white;overflow-y:scroll;position:absolute;bottom:0;z-index:11;transition:height 1s ease-in-out 0;-webkit-transition:height 1s ease-in-out 0;-moz-transition:height 1s ease-in-out 0}#openassessment-editor #openassessment_rubric_editor{width:100%;height:100%}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper label{width:25%;text-align:right}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper input{width:45%;min-width:100px}#openassessment-editor #openassessment_step_select_description{padding:10px 10px 0 10px;font-size:80%;border-top:1px solid #838486;margin:7.5px}#openassessment-editor .openassessment_assessment_module_settings_editor.openassessment_shrink_for_drag{height:40px}#openassessment-editor #ai_training_examples{margin:5px 10px 10px 20px;height:300px;width:calc(100% - 20px);resize:none;font:inherit;line-height:110%;font-size:80%;padding:5px 10px}#openassessment-editor .openassessment_assessment_module_settings_editor{padding:0 0 0 15px;margin:10px;border:1px solid #dbdbdc;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;position:relative;background-color:white}#openassessment-editor .openassessment_assessment_module_settings_editor .drag-handle{position:absolute;background:center rgba(164,165,168,0.25);display:block;top:0px;left:0px;z-index:10;width:15px;height:100%;border-right:1px solid #dbdbdc;cursor:move;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description_closed{padding:5px 15px 5px 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description{padding:5px 15px 0 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor.ui-sortable-helper{opacity:50%;border-color:#009fe6}#openassessment-editor .openassessment_assessment_module_settings_editor:hover{border-color:#80a9cc}#openassessment-editor .openassessment_assessment_module_settings_editor:hover .drag-handle{background-color:#009fe6;border-color:#009fe6;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_description{font-size:75%;margin:0}#openassessment-editor .openassessment_inclusion_wrapper{background-color:rgba(164,165,168,0.25);margin:0;padding:7px 13px 7px 5px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px}#openassessment-editor .openassessment_inclusion_wrapper label{cursor:pointer;margin-right:10px}#openassessment-editor .openassessment_inclusion_wrapper:hover{background-color:rgba(164,165,168,0.5)}#openassessment-editor label{padding-left:10px}#openassessment-editor .xblock_actions{background-color:#c8c9cb;position:absolute;width:100%;bottom:0}#openassessment-editor .openassessment_tab_instructions{background-color:rgba(164,165,168,0.25);border-bottom:1px solid #838486;padding:10px}#openassessment-editor .openassessment_container_header{margin:10px 10px;padding-bottom:5px;border-bottom:1px solid;overflow:auto;color:#626365}#openassessment-editor .action.expand-collapse{float:right}#openassessment-editor .action.expand-collapse .ui-toggle-expansion{color:#626365;cursor:pointer}#openassessment-editor .action.expand-collapse .ui-toggle-expansion:hover{color:#838486;cursor:pointer}#openassessment-editor .openassessment_container_header_title_box{float:right;width:80%;display:inline-block}#openassessment-editor .openassessment_container_header_title{text-transform:uppercase;cursor:default;padding-top:2px}#openassessment-editor .openassessment_container_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove{cursor:pointer;float:left}#openassessment-editor .openassessment_container_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#openassessment-editor .openassessment_container_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2{text-transform:uppercase;font-size:80%;float:left;display:inline-block;padding:3px 8px 3px 13px}#openassessment-editor .openassessment_container_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove:hover{background-color:#626365;border-radius:4px;color:white}#openassessment-editor .openassessment_container_add_button{background-color:white;border:1px solid;border-radius:4px;text-align:center;color:#009fe6;padding:10px;margin:15px 10px}#openassessment-editor .openassessment_container_add_button h6:before{font-family:"FontAwesome";display:inline-block;margin-right:5px;margin-left:10px;width:auto;height:auto;content:"\f067"}#openassessment-editor .openassessment_container_add_button.openassessment_highlighted_field{color:red;border-width:2px}#openassessment-editor .openassessment_container_add_button:hover{color:white;background-color:#009fe6;cursor:pointer}#openassessment-editor .openassessment_container_remove_button.is--disabled,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove,#openassessment-editor .openassessment_container_remove_button.is--disabled:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove:hover,#openassessment-editor .openassessment_container_add_button.is--disabled,#openassessment-editor .openassessment_container_add_button.is--disabled:hover{color:#c8c9cb;background-color:transparent;cursor:auto}#openassessment-editor #oa_prompts_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description{width:100%;min-height:100px;resize:none;border:1px solid #b2b2b2;border-radius:4px;padding:10px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;overflow:auto;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option{text-align:center;border:1px solid;color:#009fe6;background-color:white;padding:7.5px;margin:10px 20px 10px 10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;cursor:pointer}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option h2:before{font-family:FontAwesome;content:"\f067";display:inline-block;margin:0 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option:hover{background-color:#009fe6;color:white}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry{padding-left:0;margin-left:10px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label{padding:0;margin:0;width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label input{font-size:11px;float:left;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt{padding:10px;width:70%;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_header{background-color:#c8c9cb;padding:5px;margin:10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label{width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label select{float:left;margin-left:24%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_direction label{margin-right:15px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option{padding:5px 15px 5px 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header{background-color:rgba(164,165,168,0.25);padding:5px 10px 5px 5px;margin:5px 5px 8px 5px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_title{cursor:default;float:right;padding:2.5px 5px 0 0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper{width:30%;border-top:none;padding:10px 0px 10px 5px;float:right;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper .wrapper-comp-setting{min-width:150px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label{width:100%;padding-right:calc((100% - 150px)/2);margin:0;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label input{min-width:50px;padding:10px;float:left;width:30%;font-size:11px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper{float:right;width:70%;padding:10px 20px 10px 10px;border-top:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label{width:100%;vertical-align:middle;padding:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label input{padding:10px;font-size:11px;width:56%;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper{padding:15px 20px 0px 5px;width:100%;display:inline-block;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label{width:100%;text-align:right;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea{padding:10px;height:70px;width:69.5%;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{height:70px;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_input_styling,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper{padding:0}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper #openassessment_rubric_feedback_header{background-color:rgba(164,165,168,0.25);padding:7.5px 15px 7.5px 7.5px;margin:10px 10px 0 10px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper label{padding-top:10px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{padding:10px;width:70%;float:left}#openassessment-editor hr{background-color:transparent;color:#424243;height:1px;border:0px;clear:both}.modal-lg.modal-window.confirm.openassessment_modal_window{height:80%;top:10%;min-width:600px}.openassessment_full_height.edit-xblock-modal,.openassessment_full_height.xblock-editor,.openassessment_full_height.xblock.xblock-studio_view.xblock-initialized,[class*="view-"] .modal-lg.modal-editor .openassessment_full_height.modal-content{height:100%}#openassessment-editor .openassessment_highlighted_field{border-color:red;border-width:2px}#student_training_settings_editor .openassessment_training_example{padding:5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header{margin:10px;padding:5px;border-bottom:1px solid;overflow:auto;color:#626365}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_header_title{text-transform:uppercase;width:50%;display:inline-block;float:right;padding-top:2px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove{padding:3px 7px 3px 5px;cursor:pointer;border-radius:4px;float:left}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2{text-transform:uppercase;font-size:80%;float:left;display:inline-block}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove:hover{background-color:lightgray}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body{padding:0 15px 15px 15px;position:relative;overflow:scroll}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper{width:58%;display:inline-block;position:absolute;right:15px;height:100%}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper textarea{min-height:90px;height:calc(100% - 35px);resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;padding:6px 8px 8px 8px;background-color:white;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c;margin:5px 10px 0 15px;width:calc(100% - 25px)}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric{width:40%;display:inline-block;float:left;min-height:150px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry{margin:0;padding:10px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry .wrapper-comp-setting{min-width:100px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label{width:100%;margin-right:0}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label select{width:60%;float:left}#student_training_settings_editor .openassessment_add_training_example{color:#009fe6;text-align:center;padding:5px;border:1px solid;border-radius:4px;margin:10px}#student_training_settings_editor .openassessment_add_training_example:hover{color:white;background-color:#009fe6;cursor:pointer}#student_training_settings_editor .openassessment_add_training_example:before{font-family:"FontAwesome";display:inline-block;width:auto;height:auto;content:"\f067";margin:0 10px}#openassessment-editor .wrapper-comp-settings .list-input.settings-list .field.comp-setting-entry select{padding:5px}.step--response .action--upload{text-align:center;float:left;display:inline-block;margin:10px 0;box-shadow:none}.step--response .file--upload{margin:20px 10px}.self-assessment__display__header .self-assessment__display__title,.peer-assessment__display__header .peer-assessment__display__title,.staff-assessment__display__header .staff-assessment__display__title,.submission__answer__display .submission__answer__display__title{margin:10px 0}.self-assessment__display__image,.peer-assessment__display__image,.staff-assessment__display__image,.submission__answer__display__image{max-height:400px;text-align:right;overflow:auto}.self-assessment__display__image img,.peer-assessment__display__image img,.staff-assessment__display__image img,.submission__answer__display__image img{max-height:100%;max-width:100%}.submission__answer__display__image .submission--image{max-height:250px;max-width:100%}.openassessment .step--leaderboard{font-family:"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif}.openassessment .step--leaderboard .step__title .step__counter,.openassessment .step--leaderboard .step__title .step__counter:before{display:none}.openassessment .step--leaderboard .step__title .wrapper--copy{margin-right:0;padding-right:0;border-right:0}.openassessment .step--leaderboard .step__label,.openassessment .step--leaderboard .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--leaderboard .step__label{margin-left:10px}.openassessment .step--leaderboard .list.leaderboard__score__list{list-style-type:none}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item{margin:15px 0}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__list__number{display:inline-block;background:#626365;color:white;padding:5px 5px 3px 5px;font-size:16px;min-width:35px;text-align:center;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__image{max-height:150px;max-width:100%;margin-left:20px;margin-bottom:20px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__title{font-size:15px;color:#414141;text-transform:uppercase;display:inline-block;padding-right:15px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__answer{border-top:2px solid #626365;box-shadow:inset 0 0 3px 1px rgba(10,10,10,0.1);padding:10px;max-height:200px;overflow-y:scroll;font-size:14px}.wrapper--xblock .openassessment h3.staff__grade__title{font-size:inherit;margin-bottom:5px}.openassessment .self-assessment__display__header,.openassessment .peer-assessment__display__header,.openassessment .step__header,.openassessment .staff-assessment__display__header,.openassessment .submission__answer__display__header,.openassessment .student-training__display__header{margin-bottom:0 !important;border-radius:0 !important;padding-bottom:2px !important;font-size:initial !important;border-bottom:none !important}.openassessment span{line-height:normal}.openassessment h2{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__deadline{color:#414141}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title{text-align:right;float:none;margin-top:0 !important}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__label{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{content:counter(item, decimal);counter-increment:item;color:#414141}.openassessment .step__status__value{line-height:0}.openassessment .leaderboard__answer .submission__answer__display__content .submission__answer__part__text__title,.openassessment .leaderboard__answer .self-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .peer-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .staff-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .submission__answer__display__image .submission__answer__part__text__title{text-align:right;float:none;font-size:inherit;margin:0}.openassessment .list--actions{list-style:none !important;padding-right:0 !important;text-indent:0 !important}.openassessment .list--actions li{margin-bottom:0 !important;margin-top:5px !important}.openassessment .list--actions button span,.openassessment .list--actions .button span,.openassessment .list--actions .action span{color:inherit}.openassessment span{color:inherit}.openassessment .step--peer-assessment .list--peer-assessments{list-style:none outside none !important;padding-right:0}.openassessment .self-assessment__display__title,.openassessment .peer-assessment__display__title{margin-bottom:10px !important;color:#414141 !important}.openassessment .question__answers{padding-right:0}.openassessment .question__title.ui-slidable .question__title__copy{color:inherit !important}.openassessment .step--grade .step__title{width:100% !important}.openassessment .action--submit,.openassessment .action--save,.openassessment .action--upload{text-shadow:none !important;box-shadow:none !important}.openassessment .message p{color:inherit}.openassessment .icon{font-style:normal}.openassessment .step__title .step__deadline{letter-spacing:normal;text-transform:none}.openassessment .openassessment__steps__step .step__header .step__title{margin-bottom:0}.openassessment .is--collapsed .question__answers{overflow:auto}.openassessment .wrapper--staff-assessment .student__answer__display__content p{color:inherit} +* Copyright (c) 2013 Kushagra Gour; Licensed MIT */.hint,[data-hint]{position:relative;display:inline-block}.hint:before,.hint:after,[data-hint]:before,[data-hint]:after{position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);visibility:hidden;opacity:0;z-index:1000000;pointer-events:none;-webkit-transition:0.3s ease;-moz-transition:0.3s ease;transition:0.3s ease}.hint:hover:before,.hint:hover:after,.hint:focus:before,.hint:focus:after,[data-hint]:hover:before,[data-hint]:hover:after,[data-hint]:focus:before,[data-hint]:focus:after{visibility:visible;opacity:1}.hint:before,[data-hint]:before{content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1000001}.hint:after,[data-hint]:after{content:attr(data-hint);background:#383838;color:white;text-shadow:0 -1px 0px black;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;box-shadow:4px 4px 8px rgba(0,0,0,0.3)}.hint--top:before{border-top-color:#383838}.hint--bottom:before{border-bottom-color:#383838}.hint--left:before{border-left-color:#383838}.hint--right:before{border-right-color:#383838}.hint--top:before{margin-bottom:-12px}.hint--top:after{margin-left:-18px}.hint--top:before,.hint--top:after{bottom:100%;left:50%}.hint--top:hover:after,.hint--top:hover:before,.hint--top:focus:after,.hint--top:focus:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--bottom:before{margin-top:-12px}.hint--bottom:after{margin-left:-18px}.hint--bottom:before,.hint--bottom:after{top:100%;left:50%}.hint--bottom:hover:after,.hint--bottom:hover:before,.hint--bottom:focus:after,.hint--bottom:focus:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--right:before{margin-left:-12px;margin-bottom:-6px}.hint--right:after{margin-bottom:-14px}.hint--right:before,.hint--right:after{left:100%;bottom:50%}.hint--right:hover:after,.hint--right:hover:before,.hint--right:focus:after,.hint--right:focus:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--left:before{margin-right:-12px;margin-bottom:-6px}.hint--left:after{margin-bottom:-14px}.hint--left:before,.hint--left:after{right:100%;bottom:50%}.hint--left:hover:after,.hint--left:hover:before,.hint--left:focus:after,.hint--left:focus:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--error:after{background-color:#b34e4d;text-shadow:0 -1px 0px #592726}.hint--error.hint--top:before{border-top-color:#b34e4d}.hint--error.hint--bottom:before{border-bottom-color:#b34e4d}.hint--error.hint--left:before{border-left-color:#b34e4d}.hint--error.hint--right:before{border-right-color:#b34e4d}.hint--warning:after{background-color:#c09854;text-shadow:0 -1px 0px #6c5328}.hint--warning.hint--top:before{border-top-color:#c09854}.hint--warning.hint--bottom:before{border-bottom-color:#c09854}.hint--warning.hint--left:before{border-left-color:#c09854}.hint--warning.hint--right:before{border-right-color:#c09854}.hint--info:after{background-color:#3986ac;text-shadow:0 -1px 0px #193b4d}.hint--info.hint--top:before{border-top-color:#3986ac}.hint--info.hint--bottom:before{border-bottom-color:#3986ac}.hint--info.hint--left:before{border-left-color:#3986ac}.hint--info.hint--right:before{border-right-color:#3986ac}.hint--success:after{background-color:#458746;text-shadow:0 -1px 0px #1a321a}.hint--success.hint--top:before{border-top-color:#458746}.hint--success.hint--bottom:before{border-bottom-color:#458746}.hint--success.hint--left:before{border-left-color:#458746}.hint--success.hint--right:before{border-right-color:#458746}.hint--always:after,.hint--always:before{opacity:1;visibility:visible}.hint--always.hint--top:after,.hint--always.hint--top:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--bottom:after,.hint--always.hint--bottom:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--left:after,.hint--always.hint--left:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--always.hint--right:after,.hint--always.hint--right:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--rounded:after{border-radius:4px}.hint--bounce:before,.hint--bounce:after{-webkit-transition:opacity 0.3s ease,visibility 0.3s ease,-webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);-moz-transition:opacity 0.3s ease,visibility 0.3s ease,-moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);transition:opacity 0.3s ease,visibility 0.3s ease,transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24)}.openassessment .openassessment__steps__step{-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;transition:all .5s ease-in-out;-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;box-shadow:0 1px 1px 0 rgba(3,3,3,0.05);margin-bottom:20px;border:1px solid #d9d9d9;border-top:5px solid #d9d9d9;padding:10px 20px;background:#fff}.openassessment .openassessment__steps__step:last-child{margin-bottom:0}.openassessment .openassessment__steps__step:hover,.openassessment .openassessment__steps__step:focus{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}.openassessment .has--error.openassessment__steps__step:hover,.openassessment .has--error.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--showing.openassessment__steps__step{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);box-shadow:0 1px 1px 0 rgba(3,3,3,0.1)}.openassessment .is--loading.openassessment__steps__step{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99);box-shadow:0 1px 1px 0 rgba(3,3,3,0.05)}.openassessment .is--empty.openassessment__steps__step:hover,.openassessment .is--empty.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--unavailable.openassessment__steps__step{border-top-color:#d9d9d9}.openassessment .is--unavailable.openassessment__steps__step:hover,.openassessment .is--unavailable.openassessment__steps__step:focus{-webkit-transform:scale(0.99);-moz-transform:scale(0.99);-ms-transform:scale(0.99);-o-transform:scale(0.99);transform:scale(0.99)}.openassessment .is--in-progress.openassessment__steps__step{border-top-color:#0075b4}.openassessment .is--complete.openassessment__steps__step{border-top-color:#008100}.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .has--error.openassessment__steps__step{border-top-color:#b20610}.openassessment .is--closed.openassessment__steps__step,.openassessment .is--incomplete.openassessment__steps__step{border-top-color:#ffc01f}.openassessment .staff-assessment__display,.openassessment .step--peer-assessment .peer-assessment__display,.openassessment .step--self-assessment .self-assessment__display,.openassessment .submission__peer-evaluations,.openassessment .submission__feedback,.step--student-training .student-training__display{margin-bottom:20px}.openassessment .submission__feedback__title{margin-bottom:10px;border-bottom:2px solid #d9d9d9;padding-bottom:10px}.openassessment .staff-assessment__display__response p,.openassessment .step--peer-assessment .peer-assessment__display__response p,.openassessment .step--self-assessment .self-assessment__display__response p,.openassessment .submission__answer__display__content p,.openassessment .self-assessment__display__image p,.openassessment .peer-assessment__display__image p,.openassessment .staff-assessment__display__image p,.openassessment .submission__answer__display__image p,.openassessment .submission__peer-evaluations__questions p,.openassessment .submission__feeedback__content p,.openassessment .submission__feedback__actions p,.step--student-training .student-training__display__response p{margin-bottom:20px}.openassessment .staff-assessment__display__response p:last-child,.openassessment .step--peer-assessment .peer-assessment__display__response p:last-child,.openassessment .step--self-assessment .self-assessment__display__response p:last-child,.openassessment .submission__answer__display__content p:last-child,.openassessment .self-assessment__display__image p:last-child,.openassessment .peer-assessment__display__image p:last-child,.openassessment .staff-assessment__display__image p:last-child,.openassessment .submission__answer__display__image p:last-child,.openassessment .submission__peer-evaluations__questions p:last-child,.openassessment .submission__feeedback__content p:last-child,.openassessment .submission__feedback__actions p:last-child,.step--student-training .student-training__display__response p:last-child{margin-bottom:0}.openassessment .staff-assessment__display__response,.openassessment .submission__answer__part__prompt,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--self-assessment .self-assessment__display__response,.step--student-training .student-training__display__response{box-shadow:inset 0 1px 2px 1px rgba(3,3,3,0.1);padding:20px 20px;background:#fdfdfd}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value,.openassessment .staff-assessment__display__response,.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--response .response__submission__content .submission__answer__part__text textarea,.openassessment .step--response .response__submission__content .submission__file__description textarea,.openassessment .step--peer-assessment .peer-assessment__display__response,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .step--self-assessment .self-assessment__display__response,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea,.openassessment .submission__feedback__fields textarea,.step--student-training .student-training__display__response,.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{max-height:300px;overflow-y:scroll}.openassessment .step--response .response__submission__content .response__submission__label,.openassessment .step--response .response__submission__actions .response__submission__label,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label{color:#414141;display:inline-block;vertical-align:middle;margin-right:10px}.openassessment .step--response .response__submission__content .response__submission__label .list--actions,.openassessment .step--response .response__submission__actions .response__submission__label .list--actions,.openassessment .step--peer-assessment .peer-assessment__assessment .response__submission__label .list--actions,.openassessment .step--self-assessment .self-assessment__assessment .response__submission__label .list--actions{text-align:right}.openassessment .step--response .response__submission__content .list--actions__item,.openassessment .step--response .response__submission__actions .list--actions__item,.openassessment .step--peer-assessment .peer-assessment__assessment .list--actions__item,.openassessment .step--self-assessment .self-assessment__assessment .list--actions__item{text-align:right;margin-top:5px}.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:block;margin-bottom:10px;min-width:215px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}@media screen and (min-width: 1300px){.openassessment .step--response .response__submission__content .action--save,.openassessment .step--response .response__submission__actions .action--save,.openassessment .step--peer-assessment .peer-assessment__assessment .action--save,.openassessment .step--self-assessment .self-assessment__assessment .action--save{display:inline-block;margin-left:10px;margin-bottom:0}}.openassessment .staff-assessment__assessment .assessment__rubric__question,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question,.step--student-training .student-training__assessment .assessment__rubric__question{*zoom:1;margin-bottom:40px}.openassessment .staff-assessment__assessment .assessment__rubric__question:before,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:before,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:before,.step--student-training .student-training__assessment .assessment__rubric__question:before,.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{content:" ";display:table}.openassessment .staff-assessment__assessment .assessment__rubric__question:after,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question:after,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question:after,.step--student-training .student-training__assessment .assessment__rubric__question:after{clear:both}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title,.step--student-training .student-training__assessment .assessment__rubric__question .question__title{border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title .icon,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title .icon,.step--student-training .student-training__assessment .assessment__rubric__question .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question .question__title__copy,.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question .question__title__copy,.step--student-training .student-training__assessment .assessment__rubric__question .question__title__copy{margin-right:10px;white-space:pre-wrap;display:inline}.openassessment .staff-assessment__assessment .question__answers,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers,.openassessment .step--self-assessment .self-assessment__assessment .question__answers,.step--student-training .student-training__assessment .question__answers{margin-top:20px;margin-bottom:20px;margin-right:20px}.openassessment .staff-assessment__assessment .question__answers .answer,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer,.step--student-training .student-training__assessment .question__answers .answer,.openassessment .staff-assessment__assessment .question__answers .answer--feedback,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback,.step--student-training .student-training__assessment .question__answers .answer--feedback{*zoom:1;display:block;margin-bottom:10px;border-bottom:1px solid #f5f6f7;padding-bottom:10px}.openassessment .staff-assessment__assessment .question__answers .answer:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:before,.step--student-training .student-training__assessment .question__answers .answer:before,.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:before,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:before,.step--student-training .student-training__assessment .question__answers .answer--feedback:before,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{content:" ";display:table}.openassessment .staff-assessment__assessment .question__answers .answer:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer:after,.step--student-training .student-training__assessment .question__answers .answer:after,.openassessment .staff-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback:after,.step--student-training .student-training__assessment .question__answers .answer--feedback:after{clear:both}.openassessment .staff-assessment__assessment .question__answers .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input,.step--student-training .student-training__assessment .question__answers .wrapper--input{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value,.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{display:inline-block;vertical-align:middle}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__label{margin-bottom:0;color:#414141}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value{margin-left:10px}.openassessment .staff-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label,.step--student-training .student-training__assessment .question__answers .wrapper--input .answer__value:checked+.answer__label{color:#000}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{display:block}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:right;display:block;margin-left:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:right;display:block;margin-left:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata,.step--student-training .student-training__assessment .question__answers .wrapper--metadata{float:right;display:block;margin-left:2.35765%;width:65.88078%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .wrapper--metadata:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .wrapper--metadata:last-child,.step--student-training .student-training__assessment .question__answers .wrapper--metadata:last-child{margin-left:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip,.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{display:block}.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{margin-bottom:10px;white-space:pre-wrap;margin-left:20px;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:right;display:block;margin-left:3.57866%;width:74.10533%;float:right;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:right;display:block;margin-left:3.57866%;width:74.10533%;float:right;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__tip,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip,.step--student-training .student-training__assessment .question__answers .answer__tip{float:right;display:block;margin-left:3.57866%;width:74.10533%;float:right;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:last-child,.step--student-training .student-training__assessment .question__answers .answer__tip:last-child{margin-left:0}}.openassessment .staff-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__tip:after,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__tip:after,.step--student-training .student-training__assessment .question__answers .answer__tip:after{content:""}.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:left;text-align:left}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:left;text-align:left}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer__points,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points,.step--student-training .student-training__assessment .question__answers .answer__points{float:left;text-align:left}}.openassessment .staff-assessment__assessment .question__answers .answer__points__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer__points__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer__points__label,.step--student-training .student-training__assessment .question__answers .answer__points__label{display:inline-block;margin-right:5px;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:4.82916%;width:100%}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input{float:right;display:block;margin-left:2.35765%;width:31.76157%;margin-bottom:0}.openassessment .staff-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer .wrapper--input:last-child,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer .wrapper--input:last-child,.step--student-training .student-training__assessment .question__answers .answer .wrapper--input:last-child{margin-left:0}}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__label,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__label,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__label{margin-bottom:5px}.openassessment .staff-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--peer-assessment .peer-assessment__assessment .question__answers .answer--feedback .answer__value,.openassessment .step--self-assessment .self-assessment__assessment .question__answers .answer--feedback .answer__value,.step--student-training .student-training__assessment .question__answers .answer--feedback .answer__value{min-height:100px;margin-left:0}.has--hints{overflow:visible}.ui-hint .icon{margin-right:5px}.ui-hint:hover,.ui-hint:focus{color:#065683}.ui-hint:after{right:0;display:block;white-space:normal;width:200px;background:rgba(3,3,3,0.75)}.ui-hint.hint--top:after{right:0;margin-right:-20px}.openassessment .wrapper--ui-staff{box-shadow:inset 0 1px -2px 1px rgba(3,3,3,0.4);margin-top:40px;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;border:1px solid #891c4e;border-top:5px solid #b62568;padding:20px 20px;background:#2c2f36;display:none}.openassessment .ui-staff .ui-slidable__content{padding:10px 10px}.openassessment .ui-staff__title{color:#fff}.openassessment .ui-staff__subtitle{font-size:16px;color:#fff !important;margin-bottom:10px}.openassessment .ui-staff__subtitle span{font-weight:inherit}.openassessment .ui-staff__content{margin-top:20px;color:#fff}.openassessment .ui-staff__content__section{padding-bottom:10px}.openassessment .ui-staff__content__section .wrapper--input,.openassessment .ui-staff__content__section .wrapper--content{padding:20px 20px;background-color:#fff;color:#414141}.openassessment .staff-info__summary .label,.openassessment .staff-info__summary .value{display:inline-block;vertical-align:center;color:#fff}.openassessment .staff-info__summary .label{margin-left:10px}.openassessment .openassessment_student_info_form{margin-bottom:10px}.openassessment .openassessment_student_info_form .action--submit-username{margin-right:0}.openassessment .staff-info__status__table,.openassessment .staff-info__classifierset__table{-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;word-break:initial}.openassessment .staff-info__status__table .title,.openassessment .staff-info__classifierset__table .title{text-align:right;color:#fff;margin-bottom:10px}.openassessment .staff-info__status__table .label,.openassessment .staff-info__classifierset__table .label{color:#fff}.openassessment .staff-info__status__table .value,.openassessment .staff-info__classifierset__table .value{color:#fff}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td,.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td{border:1px solid rgba(255,255,255,0.25);padding:10px 10px}.openassessment .staff-info__status__table th,.openassessment .staff-info__status__table td[scope],.openassessment .staff-info__classifierset__table th,.openassessment .staff-info__classifierset__table td[scope]{text-align:right}.openassessment .wrapper--staff-assessment{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .staff-assessment__display__header{*zoom:1}.openassessment .staff-assessment__display__header:before,.openassessment .staff-assessment__display__header:after{content:" ";display:table}.openassessment .staff-assessment__display__header:after{clear:both}.openassessment .staff-assessment__display__header .staff-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .staff-assessment__display__response{color:#414141}.openassessment .staff-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-right:0;white-space:pre-wrap}.openassessment .staff-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .ui-staff .staff__grade__control{border-top:5px solid #d9d9d9;background:#fff;background-color:#fff}.openassessment .ui-staff .staff__grade__control .staff__grade__header{margin-bottom:0}.openassessment .ui-staff .staff__grade__control .staff__grade__title{text-align:right;float:none;padding:0 20px;display:block;width:100%}.openassessment .ui-staff .staff__grade__control .staff__grade__title .staff__grade__show-form{font-size:16px;background-color:#fff;text-transform:none;letter-spacing:normal;padding:0}.openassessment .ui-staff .staff__grade__control .staff__grade__status{display:inline-block;padding:0 20px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:left;position:relative;top:-40px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:left;position:relative;top:-40px}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status{margin-top:0;float:left;position:relative;top:-40px}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{border-radius:2px;padding:5px 10px;background:#d9d9d9;line-height:0}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}@media screen and (min-width: 1300px){.openassessment .ui-staff .staff__grade__control .staff__grade__status .staff__grade__value{display:block}}.openassessment .ui-staff .staff__grade__control .staff__grade__status .copy{color:#414141}.openassessment .ui-staff .staff__grade__control .submit_assessment--action{display:inline}.openassessment .ui-staff .staff__grade__control .staff__grade__content{padding:0}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .comments__label{color:#414141}.openassessment .staff-info__workflow-cancellation .staff-info__cancel-submission__content .cancel_submission_comments{width:100%;min-height:100px;text-align:left}.openassessment .staff-info__student__report .staff-info__final__grade__table{margin-top:10px}.wrapper--openassessment{position:relative}.openassessment{direction:rtl}.openassessment .openassessment__title{*zoom:1;display:block}.openassessment .openassessment__title:before,.openassessment .openassessment__title:after{content:" ";display:table}.openassessment .openassessment__title:after{clear:both}.openassessment .openassessment__message{*zoom:1;display:block;background:none;padding:0}.openassessment .openassessment__message:before,.openassessment .openassessment__message:after{content:" ";display:table}.openassessment .openassessment__message:after{clear:both}.openassessment .list--actions{margin-bottom:10px;text-align:center}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .list--actions{text-align:left}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .list--actions{text-align:left}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .list--actions{text-align:left}}@media screen and (min-width: 1300px){.openassessment .list--actions{text-align:left}}.openassessment .list--actions.has--error .message{margin-bottom:20px;text-align:right}.openassessment .list--actions .action--submit{margin-right:10px;margin-top:10px}.openassessment .list--actions .action--submit .copy,.openassessment .list--actions .action--submit .icon{display:inline}.openassessment .list--actions .action--submit .fa-caret-right:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.openassessment .openassessment__steps{counter-reset:item}.openassessment .openassessment__steps__step .step__title{display:block;margin-bottom:10px;width:100%;margin-bottom:0}.openassessment .openassessment__steps__step .step__title .step__counter,.openassessment .openassessment__steps__step .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-right:20px;border-right:1px solid #d9d9d9;padding-right:20px}@media screen and (max-width: 529px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-right:0;border-right:none;padding-right:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__title .wrapper--copy{margin-right:0;border-right:none;padding-right:0}}.openassessment .openassessment__steps__step .step__title .step__counter{display:none}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:none;content:counter(item, decimal);counter-increment:item;color:#414141}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__title .step__counter{display:inline-block}.openassessment .openassessment__steps__step .step__title .step__counter:before{display:inline}}.openassessment .openassessment__steps__step .step__title .step__label,.openassessment .openassessment__steps__step .step__title .step__deadline{display:block}.openassessment .openassessment__steps__step .step__title .step__deadline{color:#414141}.openassessment .openassessment__steps__step .step__status{display:inline;margin-top:5px}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:left;position:relative;top:-12px}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:left;position:relative;top:-12px}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status{margin-top:0;float:left;position:relative;top:-12px}}.openassessment .openassessment__steps__step .step__status .step__status__value{border-radius:2px;padding:4px 10px;background:#d9d9d9;position:relative}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}@media screen and (min-width: 1300px){.openassessment .openassessment__steps__step .step__status .step__status__value{display:block}}.openassessment .openassessment__steps__step .step__status .icon{display:inline-block;vertical-align:baseline;margin-left:10px}.openassessment .openassessment__steps__step .step__status .copy{color:#414141}.openassessment .openassessment__steps__step .wrapper--step__content{margin-top:10px;padding-top:10px;border-top:1px solid #d9d9d9}.openassessment .openassessment__steps__step .step__message{*zoom:1;display:block;margin-bottom:20px}.openassessment .openassessment__steps__step .step__message:before,.openassessment .openassessment__steps__step .step__message:after{content:" ";display:table}.openassessment .openassessment__steps__step .step__message:after{clear:both}.openassessment .openassessment__steps__step .step__instruction{margin-bottom:20px;color:#414141}.openassessment .openassessment__steps__step .step__content{margin-bottom:20px}.openassessment .openassessment__steps__step .step__content__section{margin-bottom:40px}.openassessment .openassessment__steps__step.is--loading .step__header{padding-bottom:0;border-bottom:none;margin-bottom:0}.openassessment .openassessment__steps__step.is--loading .step__counter:before,.openassessment .openassessment__steps__step.is--loading .step__label,.openassessment .openassessment__steps__step.is--loading .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--loading .step__status__value .wrapper--anim{-webkit-animation:pulse 3s ease-in-out infinite;-moz-animation:pulse 3s ease-in-out infinite;animation:pulse 3s ease-in-out infinite}.openassessment .openassessment__steps__step.is--loading .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--loading .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__counter:before{border-color:#ffc01f;color:#414141}.openassessment .openassessment__steps__step.is--incomplete .step__status__value{background:#ffc01f}.openassessment .openassessment__steps__step.is--incomplete .step__status__value .copy,.openassessment .openassessment__steps__step.is--incomplete .step__status__value .icon{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__counter:before,.openassessment .openassessment__steps__step.is--unavailable .step__label,.openassessment .openassessment__steps__step.is--unavailable .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--unavailable .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__counter:before,.openassessment .openassessment__steps__step.is--waiting .step__label,.openassessment .openassessment__steps__step.is--waiting .step__deadline{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value{background:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__status__value .icon{display:inline-block;color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__status__value .copy{color:#414141}.openassessment .openassessment__steps__step.is--waiting .step__title .grade__value__title{color:#d9d9d9}.openassessment .openassessment__steps__step.is--waiting .step__instruction,.openassessment .openassessment__steps__step.is--waiting .grade__value__description{color:#414141}.openassessment .openassessment__steps__step.is--complete .step__counter:before{color:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value{background:#008100}.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--complete .step__status__value .copy,.openassessment .openassessment__steps__step.is--complete .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--complete button{opacity:1}.openassessment .openassessment__steps__step.is--in-progress .step__counter:before{color:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value{background:#0075b4}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{display:inline-block}.openassessment .openassessment__steps__step.is--in-progress .step__status__value .copy,.openassessment .openassessment__steps__step.is--in-progress .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.is--in-progress button{opacity:1}.openassessment .openassessment__steps__step.has--error .step__counter:before{border-color:rgba(178,6,16,0.5);color:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value{background:#b20610}.openassessment .openassessment__steps__step.has--error .step__status__value .copy,.openassessment .openassessment__steps__step.has--error .step__status__value .icon{color:#fff}.openassessment .openassessment__steps__step.has--error .step__label{color:#b20610}.openassessment .ui-slidable__container{-webkit-transition:color .25s ease-in;-moz-transition:color .25s ease-in;transition:color .25s ease-in}.openassessment .ui-slidable__container .ui-slidable__content{width:100%}.openassessment .ui-slidable__container .icon{-webkit-transition:all .25s ease-in-out 0s;-moz-transition:all .25s ease-in-out 0s;transition:all .25s ease-in-out 0s;-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.openassessment .ui-slidable__container.is--initially--collapsed .ui-slidable__content{display:none}.openassessment .ui-slidable__container.is--showing .ui-slidable .icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%;-ms-transform-origin:50% 50%;-o-transform-origin:50% 50%;transform-origin:50% 50%}.openassessment .ui-slidable__container.is--showing .ui-slidable__container:not(.is--showing) .icon{-webkit-transition:all .25s ease-in-out 0s;-moz-transition:all .25s ease-in-out 0s;transition:all .25s ease-in-out 0s;-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.openassessment .ui-slidable__container .ui-slidable{background:none;border-radius:5px;padding:0px}.openassessment .ui-slidable__container .ui-slidable:active,.openassessment .ui-slidable__container .ui-slidable:focus:not(:disabled),.openassessment .ui-slidable__container .ui-slidable:hover:not(:disabled){background:none;border:none;box-shadow:none}.openassessment .ui-slidable__container .ui-slidable:not([disabled]){color:#0075b4}.openassessment .ui-slidable__container .ui-slidable:not([disabled]):hover,.openassessment .ui-slidable__container .ui-slidable:not([disabled]):focus{color:#065683}.openassessment .ui-slidable__container .ui-slidable .wrapper--copy{text-align:left}.openassessment .submission__answer__part__prompt{position:relative;margin:10px 0;border:1px solid #d9d9d9;border-right:10px solid #b9c1c9;padding-right:30px}.openassessment .submission__answer__part__prompt__copy{color:#414141}.openassessment .submission__answer__part__prompt__copy>*{margin-bottom:20px}.openassessment .submission__answer__part__text__value{margin:10px 0}.openassessment .submission__answer__part__text__value:not(textarea){border:1px solid #b9c1c9;background-color:#fff;padding:20px}.openassessment .submission__file__description__label{margin-bottom:5px}.openassessment .submission__answer__file__block{margin-bottom:8px}.openassessment .submission__img__preview{float:left;margin-right:10px}.openassessment .step--response .response__submission{padding:10px 10px 20px 10px;background:#f5f6f7;border-radius:2px}.openassessment .step--response .response__submission__content{margin-bottom:20px}.openassessment .step--response .response__submission__content .files__descriptions{display:none}.openassessment .step--response .response__submission__content .files__descriptions .submission__file__description{padding-bottom:10px}.openassessment .step--response .response__submission__content .submission__answer__part__text textarea{min-height:200px}.openassessment .step--response .response__submission__content .submission__file__description textarea{min-height:80px;width:70%}.openassessment .step--response .response__submission__content .tip{padding:5px 10px;background:#b9c1c9;color:#fff}.openassessment .step--peer-assessment .peer-assessment__display__header{*zoom:1}.openassessment .step--peer-assessment .peer-assessment__display__header:before,.openassessment .step--peer-assessment .peer-assessment__display__header:after{content:" ";display:table}.openassessment .step--peer-assessment .peer-assessment__display__header:after{clear:both}.openassessment .step--peer-assessment .peer-assessment__display__header .peer-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--peer-assessment .peer-assessment__display__response{color:#414141}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-right:0;white-space:pre-wrap}.openassessment .step--peer-assessment .peer-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.openassessment .step--self-assessment .self-assessment__display__header{*zoom:1}.openassessment .step--self-assessment .self-assessment__display__header:before,.openassessment .step--self-assessment .self-assessment__display__header:after{content:" ";display:table}.openassessment .step--self-assessment .self-assessment__display__header:after{clear:both}.openassessment .step--self-assessment .self-assessment__display__title{margin-bottom:10px;color:#414141}.openassessment .step--self-assessment .self-assessment__display__response{color:#414141}.openassessment .step--self-assessment .self-assessment__assessment .assessment__fields{margin-bottom:20px}.openassessment .step--self-assessment .self-assessment__assessment .question__answers{overflow:visible}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .wrapper--input{margin-top:20px}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback .question__title__copy{margin-right:0;white-space:pre-wrap}.openassessment .step--self-assessment .self-assessment__assessment .assessment__rubric__question--feedback textarea{min-height:100px}@media screen and (max-width: 529px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:7.42297%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:4.82916%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .step--grade .step__title{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .step--grade .step__title:last-child{margin-left:0}}.openassessment .step--grade .step__title .step__counter,.openassessment .step--grade .step__title .step__counter:before{display:none}.openassessment .step--grade .step__title .wrapper--copy{margin-right:0;border-right:none;padding-right:0}.openassessment .step--grade .step__title .step__label,.openassessment .step--grade .step__title .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--grade .step__title .step__label{margin-left:10px}.openassessment .step--grade .grade__value__description{margin-bottom:20px;color:#414141}.openassessment .submission__answer__display__title{margin-bottom:10px;color:#414141}.openassessment .submission__answer__display__content,.openassessment .self-assessment__display__image,.openassessment .peer-assessment__display__image,.openassessment .staff-assessment__display__image,.openassessment .submission__answer__display__image{padding:10px 10px 20px 10px;border-radius:2px;list-style:none}.openassessment .submission__answer__display__content .submission__answer__part,.openassessment .self-assessment__display__image .submission__answer__part,.openassessment .peer-assessment__display__image .submission__answer__part,.openassessment .staff-assessment__display__image .submission__answer__part,.openassessment .submission__answer__display__image .submission__answer__part{margin:30px 0px 0px}.openassessment .submission__answer__display__content .submission__answer__part:first-child,.openassessment .self-assessment__display__image .submission__answer__part:first-child,.openassessment .peer-assessment__display__image .submission__answer__part:first-child,.openassessment .staff-assessment__display__image .submission__answer__part:first-child,.openassessment .submission__answer__display__image .submission__answer__part:first-child{margin-top:0px}.openassessment .submission__peer-evaluations__title{margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions{margin-bottom:10px}.openassessment .submission__peer-evaluations__questions:last-child{margin-bottom:0;border-bottom:none;padding-bottom:0}.openassessment .submission__peer-evaluations__questions .question{margin-bottom:30px}.openassessment .submission__peer-evaluations__questions .question__title{*zoom:1;margin-bottom:20px;border-bottom:2px solid #414141;padding-bottom:5px}.openassessment .submission__peer-evaluations__questions .question__title:before,.openassessment .submission__peer-evaluations__questions .question__title:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__title:after{clear:both}.openassessment .submission__peer-evaluations__questions .question__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__peer-evaluations__questions .question__title__copy{margin-right:10px;white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .question__score{float:left;margin-right:20px}.openassessment .submission__peer-evaluations__questions .question__score__value{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__value:after{content:"/";margin-right:5px;color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential{color:#414141}.openassessment .submission__peer-evaluations__questions .question__score__potential .unit{margin-right:5px}.openassessment .submission__peer-evaluations__questions .question__answers{*zoom:1;display:block;overflow:visible}.openassessment .submission__peer-evaluations__questions .question__answers:before,.openassessment .submission__peer-evaluations__questions .question__answers:after{content:" ";display:table}.openassessment .submission__peer-evaluations__questions .question__answers:after{clear:both}.openassessment .submission__peer-evaluations__questions .answer{margin-bottom:20px}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:4.82916%;width:47.58542%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(2n+1){clear:right}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:right}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:right}}@media screen and (min-width: 1300px){.openassessment .submission__peer-evaluations__questions .answer{float:right;display:block;margin-left:2.35765%;width:31.76157%}.openassessment .submission__peer-evaluations__questions .answer:last-child{margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n){margin-left:0}.openassessment .submission__peer-evaluations__questions .answer:nth-child(3n+1){clear:right}}.openassessment .submission__peer-evaluations__questions .answer.self-assessment{border-right:5px solid #f5f6f7;padding-right:20px}.openassessment .submission__peer-evaluations__questions .answer.self-assessment .answer__source{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__source{display:block;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__source .answer__source__value-with-points{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value{display:block;margin-bottom:10px;color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value .answer__value__raw{white-space:pre-wrap}.openassessment .submission__peer-evaluations__questions .answer__value__value{color:#414141}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint{color:#d9d9d9}.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:hover,.openassessment .submission__peer-evaluations__questions .answer__value__value .ui-hint:focus{color:#065683}.openassessment .submission__peer-evaluations__questions .answer__score,.openassessment .submission__peer-evaluations__questions .answer__score__value{display:block;color:#414141}@media screen and (min-width: 530px) and (max-width: 759px){.openassessment .answer--feedback{float:right;display:block;margin-left:4.82916%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}@media screen and (min-width: 760px) and (max-width: 924px){.openassessment .answer--feedback{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}@media screen and (min-width: 925px) and (max-width: 1299px){.openassessment .answer--feedback{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}@media screen and (min-width: 1300px){.openassessment .answer--feedback{float:right;display:block;margin-left:2.35765%;width:100%}.openassessment .answer--feedback:last-child{margin-left:0}}.openassessment .answer--feedback .answer--feedback__title__copy{margin-right:10px}.openassessment .answer--feedback .answer--feedback__content{margin-top:5px}.openassessment .answer--feedback .feedback{margin-bottom:20px}.openassessment .answer--feedback .feedback__source{display:block;color:#414141}.openassessment .answer--feedback .feedback__value{display:block}.openassessment .answer--feedback .feedback__value .feedback__value__raw{white-space:pre-wrap}.openassessment .submission__feedback__title{border-bottom-color:solid #414141;color:#414141}.openassessment .submission__feedback__title .icon{display:inline-block;vertical-align:middle}.openassessment .submission__feedback__title__copy{margin-right:10px}.openassessment .submission__feeedback__content{margin-bottom:20px}.openassessment .submission__feedback__instructions{margin-bottom:20px;color:#414141}.openassessment .submission__feedback__elements{padding:20px 20px 20px 20px;background:#f5f6f7;border-radius:2px}.openassessment .submission__feedback__fields{margin-bottom:20px}.openassessment .submission__feedback__fields .field{margin-bottom:30px}.openassessment .submission__feedback__fields .field:last-child{margin-bottom:0}.openassessment .submission__feedback__fields select{display:block}.openassessment .submission__feedback__fields textarea{min-height:200px}.openassessment .submission__feedback__actions{padding-top:0}.openassessment .submission__feedback__actions .list--actions{padding:0}.openassessment .action--submit{margin-left:10px}.open-response-assessment-block table{table-layout:fixed}.open-response-assessment-block .open-response-assessment-msg{margin-bottom:20px}.open-response-assessment-block .open-response-assessment-content,.open-response-assessment-block .open-response-assessment-item{display:none}.open-response-assessment-block .open-response-assessment-summary td{border:none}.open-response-assessment-block .open-response-assessment-item-breadcrumbs{padding-bottom:20px}.open-response-assessment-block .open-response-assessment-item-breadcrumbs a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table th{background-color:#ffffff;text-align:left}.open-response-assessment-block .open-response-assessment-main-table th button{background:none;border:none;box-shadow:none;outline:none;font-size:14px}.open-response-assessment-block .open-response-assessment-main-table th a{color:#000000 !important}.open-response-assessment-block .open-response-assessment-main-table td a{cursor:pointer}.open-response-assessment-block .open-response-assessment-main-table tbody tr:nth-child(odd){background-color:#f9f9f9}.open-response-assessment-block .ora-summary-value,.open-response-assessment-block .backgrid{text-overflow:ellipsis;overflow:hidden}.open-response-assessment-block .ora-summary-title{color:#0079bc;font-weight:bold;text-transform:uppercase;font-size:0.8em}.open-response-assessment-block .ora-summary-value{font-size:1.5em;padding-top:3px}.open-response-assessment-block .backgrid .peer,.open-response-assessment-block .backgrid .self,.open-response-assessment-block .backgrid .staff,.open-response-assessment-block .backgrid .waiting,.open-response-assessment-block .backgrid .training{width:80px}.open-response-assessment-block .backgrid .total{width:130px}.open-response-assessment-block .backgrid .done{width:160px}.view--placeholder{background:transparent;margin:40px 40px}.wrapper--openassessment .wrapper--staff-area{width:100%;overflow:auto;margin-top:20px;padding-top:20px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar{position:relative;text-align:right;margin:0 0 8px;padding:10px}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button{padding:5px 10px;font-size:12px;border-radius:5px;text-transform:uppercase;color:#414141;background-color:rgba(3,3,3,0.05)}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active{color:white;background-color:#b62568}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button.is--active:hover{background-color:#921e53}.wrapper--openassessment .wrapper--staff-area .wrapper--staff-toolbar .ui-staff__button:hover{color:white;background-color:#0075b4}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff{margin-top:0}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button{margin:0;padding:0;border:0;float:right;background-color:#2c2f36;font-size:12px;-webkit-appearance:none;background-image:none;text-shadow:none;box-shadow:none;border:none;border-image:none}.wrapper--openassessment .wrapper--staff-area .wrapper--ui-staff .ui-staff_close_button:hover{color:#fff}.staff-info__student .label{color:#fff;margin-bottom:10px}.staff-info__student .label input{display:block;padding-top:0;padding-bottom:0}.staff-info__student .title{color:#fff;margin-bottom:10px}.staff-info__student .title--sub{color:#fff;margin-top:10px;margin-bottom:10px}.staff-info__student .student__answer__display__content{border:1px solid rgba(255,255,255,0.25);padding:10px 10px;margin-bottom:10px}.staff-info__student .staff-info__student__report{list-style-type:none}.staff-info__student .staff-info__student__report .title{margin-top:10px;border-top:1px solid #fff;padding:10px 20px 10px 0}.staff-info__student .staff-info__student__report .title span{font-weight:inherit}.staff-info__student .value{width:50%}.step--student-training .student-training__display__header{*zoom:1}.step--student-training .student-training__display__header:before,.step--student-training .student-training__display__header:after{content:" ";display:table}.step--student-training .student-training__display__header:after{clear:both}.step--student-training .student-training__display__title{margin-bottom:10px;color:#414141}.step--student-training .student-training__display__response{color:#414141}.step--student-training .student-training__assessment .assessment__fields{margin-bottom:20px}.step--student-training .student-training__assessment .question__answers{overflow:visible}.step--student-training .student-training__assessment .assessment__rubric__question--feedback textarea{min-height:100px}.step--student-training .message--correct{margin-top:20px}.step--student-training .message--correct.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--correct.is--hidden .step__header{border-bottom:none}.step--student-training .message--incorrect{margin-top:20px}.step--student-training .message--incorrect.is--hidden{height:0;width:0;padding:0;margin:0;display:none}.step--student-training .message--incorrect.is--hidden .step__header{border-bottom:none}#openassessment-editor{margin-bottom:0;height:100%}#openassessment-editor .openassessment_editor_content_and_tabs{width:100%;height:calc(100% - 55px)}#openassessment-editor #oa_editor_window_title{float:right}#openassessment-editor input[type="number"]{background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;width:45%}#openassessment-editor #openassessment_editor_header{background-color:#e5e5e5;width:100%;top:0;height:42px}#openassessment-editor #openassessment_editor_header .editor_tabs{width:35%;min-width:190px;max-width:360px;float:left}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab{float:left;padding:0;width:29%;margin:0px 2%;height:35px;-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px;box-shadow:none;border:0;text-align:center}#openassessment-editor #openassessment_editor_header .editor_tabs .oa_editor_tab a{padding:8px 0 0 0;width:100%;text-transform:uppercase;outline-color:transparent}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab{background:#e5e5e5 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab a{color:#424243}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab{background:#626365 none}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.ui-state-default.oa_editor_tab:hover a,#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-active.oa_editor_tab a{color:#fff}#openassessment-editor #openassessment_editor_header .editor_tabs .ui-state-default.oa_editor_tab:hover{background-color:rgba(164,165,168,0.75)}#openassessment-editor #openassessment_validation_alert{width:100%;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;background-color:#323232;border-bottom:3px solid #c0ac00;padding:10px;position:absolute;z-index:10;max-height:200px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon{position:absolute;top:35%;font-size:200%;right:3%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_icon:before{font-family:FontAwesome;content:"\f071";display:inline-block;color:#c0ac00;float:right;height:0;width:0}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header{width:85%;margin:0 10% 0 5%}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_title{width:auto;color:white}#openassessment-editor #openassessment_validation_alert .openassessment_alert_header .openassessment_alert_message{font-size:80%;color:darkgray}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close{display:inline-block;position:absolute;top:0px;left:0px;color:#e9e9e9;text-align:center;margin:5px 10px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close [class^="icon"]{width:auto;margin:0;padding:2px}#openassessment-editor #openassessment_validation_alert .openassessment_alert_close:hover{color:#80a9cc}#openassessment-editor .oa_editor_content_wrapper{height:calc(100% - 42px);width:100%;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;border:1px solid #838486;background-color:white;overflow-y:scroll;position:absolute;bottom:0;z-index:11;transition:height 1s ease-in-out 0;-webkit-transition:height 1s ease-in-out 0;-moz-transition:height 1s ease-in-out 0}#openassessment-editor #openassessment_rubric_editor{width:100%;height:100%}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper label{width:25%;text-align:right}#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper input{width:45%;min-width:100px}#openassessment-editor #openassessment_step_select_description{padding:10px 10px 0 10px;font-size:80%;border-top:1px solid #838486;margin:7.5px}#openassessment-editor .openassessment_assessment_module_settings_editor.openassessment_shrink_for_drag{height:40px}#openassessment-editor #ai_training_examples{margin:5px 10px 10px 20px;height:300px;width:calc(100% - 20px);resize:none;font:inherit;line-height:110%;font-size:80%;padding:5px 10px}#openassessment-editor .openassessment_assessment_module_settings_editor{padding:0 0 0 15px;margin:10px;border:1px solid #dbdbdc;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;position:relative;background-color:white}#openassessment-editor .openassessment_assessment_module_settings_editor .drag-handle{position:absolute;background:center rgba(164,165,168,0.25);display:block;top:0px;left:0px;z-index:10;width:15px;height:100%;border-right:1px solid #dbdbdc;cursor:move;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description_closed{padding:5px 15px 5px 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor .openassessment_description{padding:5px 15px 0 15px;font-size:75%;margin:0}#openassessment-editor .openassessment_assessment_module_settings_editor.ui-sortable-helper{opacity:50%;border-color:#009fe6}#openassessment-editor .openassessment_assessment_module_settings_editor:hover{border-color:#80a9cc}#openassessment-editor .openassessment_assessment_module_settings_editor:hover .drag-handle{background-color:#009fe6;border-color:#009fe6;-webkit-transition:none;-moz-transition:none;transition:none}#openassessment-editor .openassessment_description{font-size:75%;margin:0}#openassessment-editor .openassessment_inclusion_wrapper{background-color:rgba(164,165,168,0.25);margin:0;padding:7px 13px 7px 5px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px}#openassessment-editor .openassessment_inclusion_wrapper label{cursor:pointer;margin-right:10px}#openassessment-editor .openassessment_inclusion_wrapper:hover{background-color:rgba(164,165,168,0.5)}#openassessment-editor label{padding-left:10px}#openassessment-editor .xblock_actions{background-color:#c8c9cb;position:absolute;width:100%;bottom:0}#openassessment-editor .openassessment_tab_instructions{background-color:rgba(164,165,168,0.25);border-bottom:1px solid #838486;padding:10px}#openassessment-editor .openassessment_container_header{margin:10px 10px;padding-bottom:5px;border-bottom:1px solid;overflow:auto;color:#626365}#openassessment-editor .action.expand-collapse{float:right}#openassessment-editor .action.expand-collapse .ui-toggle-expansion{color:#626365;cursor:pointer}#openassessment-editor .action.expand-collapse .ui-toggle-expansion:hover{color:#838486;cursor:pointer}#openassessment-editor .openassessment_container_header_title_box{float:right;width:80%;display:inline-block}#openassessment-editor .openassessment_container_header_title{text-transform:uppercase;cursor:default;padding-top:2px}#openassessment-editor .openassessment_container_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove{cursor:pointer;float:left}#openassessment-editor .openassessment_container_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2:after,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#openassessment-editor .openassessment_container_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button h2,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2{text-transform:uppercase;font-size:80%;float:left;display:inline-block;padding:3px 8px 3px 13px}#openassessment-editor .openassessment_container_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove:hover{background-color:#626365;border-radius:4px;color:white}#openassessment-editor .openassessment_container_add_button{background-color:white;border:1px solid;border-radius:4px;text-align:center;color:#009fe6;padding:10px;margin:15px 10px}#openassessment-editor .openassessment_container_add_button h6:before{font-family:"FontAwesome";display:inline-block;margin-right:5px;margin-left:10px;width:auto;height:auto;content:"\f067"}#openassessment-editor .openassessment_container_add_button.openassessment_highlighted_field{color:red;border-width:2px}#openassessment-editor .openassessment_container_add_button:hover{color:white;background-color:#009fe6;cursor:pointer}#openassessment-editor .openassessment_container_remove_button.is--disabled,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove,#openassessment-editor .openassessment_container_remove_button.is--disabled:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .is--disabled.openassessment_criterion_option_remove_button:hover,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .is--disabled.openassessment_option_header_remove:hover,#openassessment-editor .openassessment_container_add_button.is--disabled,#openassessment-editor .openassessment_container_add_button.is--disabled:hover{color:#c8c9cb;background-color:transparent;cursor:auto}#openassessment-editor #oa_prompts_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description{width:100%;min-height:100px;resize:none;border:1px solid #b2b2b2;border-radius:4px;padding:10px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;overflow:auto;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}#openassessment-editor #oa_prompts_editor_wrapper .openassessment_prompt_description.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .wrapper-comp-settings{display:block}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option{text-align:center;border:1px solid;color:#009fe6;background-color:white;padding:7.5px;margin:10px 20px 10px 10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;cursor:pointer}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option h2:before{font-family:FontAwesome;content:"\f067";display:inline-block;margin:0 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option:hover{background-color:#009fe6;color:white}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry{padding-left:0;margin-left:10px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label{padding:0;margin:0;width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label input{font-size:11px;float:left;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt{padding:10px;width:70%;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt.openassessment_highlighted_field{border:2px solid red}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_header{background-color:#c8c9cb;padding:5px;margin:10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label{width:100%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper label select{float:left;margin-left:24%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_direction label{margin-right:15px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option{padding:5px 15px 5px 5px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header{background-color:rgba(164,165,168,0.25);padding:5px 10px 5px 5px;margin:5px 5px 8px 5px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;overflow:auto}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_title{cursor:default;float:right;padding:2.5px 5px 0 0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper{width:30%;border-top:none;padding:10px 0px 10px 5px;float:right;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper .wrapper-comp-setting{min-width:150px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label{width:100%;padding-right:calc((100% - 150px)/2);margin:0;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label input{min-width:50px;padding:10px;float:left;width:30%;font-size:11px}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper{float:right;width:70%;padding:10px 20px 10px 10px;border-top:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label{width:100%;vertical-align:middle;padding:0;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label input{padding:10px;font-size:11px;width:56%;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper{padding:15px 20px 0px 5px;width:100%;display:inline-block;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label{width:100%;text-align:right;margin:0}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea{padding:10px;height:70px;width:69.5%;float:left}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{height:70px;width:70%}#openassessment-editor #oa_rubric_editor_wrapper .openassessment_input_styling,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label textarea,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input,#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt,#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;background-image:-webkit-linear-gradient(#F2F2F2, #fff);background-image:-moz-linear-gradient(#F2F2F2, #fff);background-image:linear,false,#F2F2F2,#fff;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper{padding:0}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper #openassessment_rubric_feedback_header{background-color:rgba(164,165,168,0.25);padding:7.5px 15px 7.5px 7.5px;margin:10px 10px 0 10px;border-radius:3px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper label{padding-top:10px}#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_feedback_wrapper textarea{padding:10px;width:70%;float:left}#openassessment-editor hr{background-color:transparent;color:#424243;height:1px;border:0px;clear:both}.modal-lg.modal-window.confirm.openassessment_modal_window{height:80%;top:10%;min-width:600px}.openassessment_full_height.edit-xblock-modal,.openassessment_full_height.xblock-editor,.openassessment_full_height.xblock.xblock-studio_view.xblock-initialized,[class*="view-"] .modal-lg.modal-editor .openassessment_full_height.modal-content{height:100%}#openassessment-editor .openassessment_highlighted_field{border-color:red;border-width:2px}#student_training_settings_editor .openassessment_training_example{padding:5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header{margin:10px;padding:5px;border-bottom:1px solid;overflow:auto;color:#626365}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_header_title{text-transform:uppercase;width:50%;display:inline-block;float:right;padding-top:2px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove{padding:3px 7px 3px 5px;cursor:pointer;border-radius:4px;float:left}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2:after{font-family:FontAwesome;content:"\f00d";display:inline-block;color:inherit;margin:0 5px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove h2{text-transform:uppercase;font-size:80%;float:left;display:inline-block}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_header .openassessment_training_example_remove:hover{background-color:lightgray}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body{padding:0 15px 15px 15px;position:relative;overflow:scroll}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper{width:58%;display:inline-block;position:absolute;right:15px;height:100%}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_essay_wrapper textarea{min-height:90px;height:calc(100% - 35px);resize:none;box-sizing:border-box;border:1px solid #b2b2b2;border-radius:2px;padding:6px 8px 8px 8px;background-color:white;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);font-family:'Open Sans', sans-serif;font-size:11px;color:#4c4c4c;margin:5px 10px 0 15px;width:calc(100% - 25px)}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric{width:40%;display:inline-block;float:left;min-height:150px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry{margin:0;padding:10px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry .wrapper-comp-setting{min-width:100px}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label{width:100%;margin-right:0}#student_training_settings_editor .openassessment_training_example .openassessment_training_example_body .openassessment_training_example_scored_rubric .openassessment_training_example_criteria_selections .comp-setting-entry label select{width:60%;float:left}#student_training_settings_editor .openassessment_add_training_example{color:#009fe6;text-align:center;padding:5px;border:1px solid;border-radius:4px;margin:10px}#student_training_settings_editor .openassessment_add_training_example:hover{color:white;background-color:#009fe6;cursor:pointer}#student_training_settings_editor .openassessment_add_training_example:before{font-family:"FontAwesome";display:inline-block;width:auto;height:auto;content:"\f067";margin:0 10px}#openassessment-editor .wrapper-comp-settings .list-input.settings-list .field.comp-setting-entry select{padding:5px}.step--response .action--upload{text-align:center;float:left;display:inline-block;margin:10px 0;box-shadow:none}.step--response .file--upload{margin:20px 10px}.self-assessment__display__header .self-assessment__display__title,.peer-assessment__display__header .peer-assessment__display__title,.staff-assessment__display__header .staff-assessment__display__title,.submission__answer__display .submission__answer__display__title{margin:10px 0}.self-assessment__display__image,.peer-assessment__display__image,.staff-assessment__display__image,.submission__answer__display__image{max-height:400px;text-align:right;overflow:auto}.self-assessment__display__image img,.peer-assessment__display__image img,.staff-assessment__display__image img,.submission__answer__display__image img{max-height:100%;max-width:100%}.submission__answer__display__image .submission--image{max-height:250px;max-width:100%}.openassessment .step--leaderboard{font-family:"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif}.openassessment .step--leaderboard .step__title .step__counter,.openassessment .step--leaderboard .step__title .step__counter:before{display:none}.openassessment .step--leaderboard .step__title .wrapper--copy{margin-right:0;padding-right:0;border-right:0}.openassessment .step--leaderboard .step__label,.openassessment .step--leaderboard .grade__value{display:inline-block;vertical-align:middle}.openassessment .step--leaderboard .step__label{margin-left:10px}.openassessment .step--leaderboard .list.leaderboard__score__list{list-style-type:none}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item{margin:15px 0}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__list__number{display:inline-block;background:#626365;color:white;padding:5px 5px 3px 5px;font-size:16px;min-width:35px;text-align:center;-webkit-border-top-left-radius:2px;-moz-border-topleft-radius:2px;border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-topright-radius:2px;border-top-right-radius:2px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__image{max-height:150px;max-width:100%;margin-left:20px;margin-bottom:20px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__score__title{font-size:15px;color:#414141;text-transform:uppercase;display:inline-block;padding-right:15px}.openassessment .step--leaderboard .list.leaderboard__score__list li.leaderboard__score__item .leaderboard__answer{border-top:2px solid #626365;box-shadow:inset 0 0 3px 1px rgba(10,10,10,0.1);padding:10px;max-height:200px;overflow-y:scroll;font-size:14px}.wrapper--xblock .openassessment h3.staff__grade__title{font-size:inherit;margin-bottom:5px}.openassessment .self-assessment__display__header,.openassessment .peer-assessment__display__header,.openassessment .step__header,.openassessment .staff-assessment__display__header,.openassessment .submission__answer__display__header,.openassessment .student-training__display__header{margin-bottom:0 !important;border-radius:0 !important;padding-bottom:2px !important;font-size:initial !important;border-bottom:none !important}.openassessment span{line-height:normal}.openassessment h2{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__deadline{color:#414141}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title{text-align:right;float:none;margin-top:0 !important}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__label{text-transform:none;letter-spacing:normal}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter,.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .wrapper--copy{display:inline-block;vertical-align:middle}.openassessment .openassessment__steps .openassessment__steps__step .step__header .step__title .step__counter:before{content:counter(item, decimal);counter-increment:item;color:#414141}.openassessment .step__status__value{line-height:0}.openassessment .leaderboard__answer .submission__answer__display__content .submission__answer__part__text__title,.openassessment .leaderboard__answer .self-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .peer-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .staff-assessment__display__image .submission__answer__part__text__title,.openassessment .leaderboard__answer .submission__answer__display__image .submission__answer__part__text__title{text-align:right;float:none;font-size:inherit;margin:0}.openassessment .list--actions{list-style:none !important;padding-right:0 !important;text-indent:0 !important}.openassessment .list--actions li{margin-bottom:0 !important;margin-top:5px !important}.openassessment .list--actions button span,.openassessment .list--actions .button span,.openassessment .list--actions .action span{color:inherit}.openassessment span{color:inherit}.openassessment .step--peer-assessment .list--peer-assessments{list-style:none outside none !important;padding-right:0}.openassessment .self-assessment__display__title,.openassessment .peer-assessment__display__title{margin-bottom:10px !important;color:#414141 !important}.openassessment .question__answers{padding-right:0}.openassessment .question__title.ui-slidable .question__title__copy{color:inherit !important}.openassessment .step--grade .step__title{width:100% !important}.openassessment .action--submit,.openassessment .action--save,.openassessment .action--upload{text-shadow:none !important;box-shadow:none !important}.openassessment .message p{color:inherit}.openassessment .icon{font-style:normal}.openassessment .step__title .step__deadline{letter-spacing:normal;text-transform:none}.openassessment .openassessment__steps__step .step__header .step__title{margin-bottom:0}.openassessment .is--collapsed .question__answers{overflow:auto}.openassessment .wrapper--staff-assessment .student__answer__display__content p{color:inherit} diff --git a/openassessment/xblock/static/js/fixtures/templates.json b/openassessment/xblock/static/js/fixtures/templates.json index 2950b249ba..ea7fde1b81 100644 --- a/openassessment/xblock/static/js/fixtures/templates.json +++ b/openassessment/xblock/static/js/fixtures/templates.json @@ -502,7 +502,6 @@ "student_training", "peer_assessment", "self_assessment", - "example_based_assessment", "staff_assessment" ] }, @@ -668,7 +667,6 @@ "student_training", "peer_assessment", "self_assessment", - "example_based_assessment", "staff_assessment" ] }, diff --git a/openassessment/xblock/static/js/openassessment-studio.min.js b/openassessment/xblock/static/js/openassessment-studio.min.js index 51d0721026..f3e2d52ed2 100644 --- a/openassessment/xblock/static/js/openassessment-studio.min.js +++ b/openassessment/xblock/static/js/openassessment-studio.min.js @@ -1,3 +1,3 @@ -function OpenAssessmentEditor(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);new OpenAssessment.StudioView(runtime,element,server,data)}if("undefined"!=typeof OpenAssessment&&OpenAssessment||(OpenAssessment={}),void 0===window.gettext&&(window.gettext=function(text){return text}),void 0===window.ngetgext&&(window.ngettext=function(singularText,pluralText,n){return n>1?pluralText:singularText}),void 0===window.Logger&&(window.Logger={log:function(){}}),void 0===window.MathJax&&(window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}),void 0===OpenAssessment.Server||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime,this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this,url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this,url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:!0}}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(studentUsername,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:studentUsername},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},staffGradeForm:function(){var url=this.url("render_staff_grade_form");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The staff assessment form could not be loaded.")])})}).promise()},staffGradeCounts:function(){var url=this.url("render_staff_grade_counts");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The display of ungraded and checked out responses could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){if(data[0]){var studentId=data[1],attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1],errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback"),payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID,assessType){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID,assess_type:assessType})},trainingAssess:function(optionsSelected){var url=this.url("training_assess"),payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.corrections]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.msg]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.msg]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context"),payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,text_response:options.textResponse,file_upload_response:options.fileUploadResponse,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.is_released]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename,filenum){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename,filenum:filenum}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve(data.url):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},removeUploadedFiles:function(){var url=this.url("remove_all_uploaded_files");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Server error.")])})}).promise()},saveFilesDescriptions:function(descriptions){var url=this.url("save_files_descriptions");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({descriptions:descriptions}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Server error.")])})}).promise()},getDownloadUrl:function(filenum){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({filenum:filenum}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve(data.url):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission"),payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success&&defer.resolveWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()},publishEvent:function(eventName,eventData){eventData.event_name=eventName;var url=this.url("publish_event"),payload=JSON.stringify(eventData);$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType})}}}"undefined"!=typeof OpenAssessment&&OpenAssessment||(OpenAssessment={}),void 0===window.gettext&&(window.gettext=function(text){return text}),void 0===window.ngetgext&&(window.ngettext=function(singularText,pluralText,n){return n>1?pluralText:singularText}),void 0===window.Logger&&(window.Logger={log:function(){}}),void 0===window.MathJax&&(window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}),OpenAssessment.Container=function(ContainerItem,kwargs){this.containerElement=kwargs.containerElement,this.templateElement=kwargs.templateElement,this.addButtonElement=kwargs.addButtonElement,this.removeButtonClass=kwargs.removeButtonClass,this.containerItemClass=kwargs.containerItemClass,this.notifier=kwargs.notifier,this.addRemoveEnabled=void 0===kwargs.addRemoveEnabled||kwargs.addRemoveEnabled;var container=this;this.createContainerItem=function(element){return new ContainerItem(element,container.notifier)}},OpenAssessment.Container.prototype={addEventListeners:function(){var container=this;this.addRemoveEnabled?($(this.addButtonElement).click($.proxy(this.add,this)),$("."+this.removeButtonClass,this.containerElement).click(function(eventData){var item=container.createContainerItem(eventData.target);container.remove(item)})):($(this.addButtonElement).addClass("is--disabled"),$("."+this.removeButtonClass,this.containerElement).addClass("is--disabled")),$("."+this.containerItemClass,this.containerElement).each(function(index,element){container.createContainerItem(element).addEventListeners()})},add:function(){$(this.templateElement).children().first().clone().removeAttr("id").toggleClass("is--hidden",!1).toggleClass(this.containerItemClass,!0).appendTo($(this.containerElement));var container=this,containerItem=$("."+this.containerItemClass,this.containerElement).last();this.addRemoveEnabled?containerItem.find("."+this.removeButtonClass).click(function(eventData){var containerItem=container.createContainerItem(eventData.target);container.remove(containerItem)}):containerItem.find("."+this.removeButtonClass).addClass("is--disabled");var handlerItem=container.createContainerItem(containerItem);handlerItem.addEventListeners(),handlerItem.addHandler()},remove:function(item){var itemElement=$(item.element).closest("."+this.containerItemClass);this.createContainerItem(itemElement).removeHandler(),itemElement.remove()},getItemValues:function(){var values=[],container=this;return $("."+this.containerItemClass,this.containerElement).each(function(index,element){var containerItem=container.createContainerItem(element),fieldValues=containerItem.getFieldValues();values.push(fieldValues)}),values},getItem:function(index){var element=$("."+this.containerItemClass,this.containerElement).get(index);return void 0!==element?this.createContainerItem(element):null},getAllItems:function(){var container=this;return $("."+this.containerItemClass,this.containerElement).map(function(){return container.createContainerItem(this)})}},OpenAssessment.ItemUtilities={createUniqueName:function(selector,nameAttribute){for(var index=0;index<=selector.length;){if(0===selector.parent().find("*["+nameAttribute+"='"+index+"']").length)return index.toString();index++}return index.toString()},refreshOptionString:function(element){var points=$(element).attr("data-points"),label=$(element).attr("data-label"),name=$(element).val();""===label&&(label=gettext("Unnamed Option"));var singularString=label+" - "+points+" point",multipleString=label+" - "+points+" points",finalLabel="";finalLabel=""===name?gettext("Not Selected"):isNaN(points)?label:ngettext(singularString,multipleString,points),$(element).text(finalLabel)}},OpenAssessment.Prompt=function(element,notifier){this.element=element,this.notifier=notifier},OpenAssessment.Prompt.prototype={getFieldValues:function(){return{description:this.description()}},description:function(text){var sel=$(".openassessment_prompt_description",this.element);return OpenAssessment.Fields.stringField(sel,text)},addEventListeners:function(){},addHandler:function(){this.notifier.notificationFired("promptAdd",{index:this.element.index()})},removeHandler:function(){this.notifier.notificationFired("promptRemove",{index:this.element.index()})},updateHandler:function(){},validate:function(){return!0},validationErrors:function(){return[]},clearValidationErrors:function(){}},OpenAssessment.RubricOption=function(element,notifier){this.element=element,this.notifier=notifier,this.pointsField=new OpenAssessment.IntField($(".openassessment_criterion_option_points",this.element),{min:0,max:999})},OpenAssessment.RubricOption.prototype={addEventListeners:function(){$(this.element).focusout($.proxy(this.updateHandler,this))},getFieldValues:function(){var fields={label:this.label(),points:this.points(),explanation:this.explanation()},nameString=OpenAssessment.Fields.stringField($(".openassessment_criterion_option_name",this.element));return""!==nameString&&(fields.name=nameString),fields},label:function(label){var sel=$(".openassessment_criterion_option_label",this.element);return OpenAssessment.Fields.stringField(sel,label)},points:function(points){return void 0!==points&&this.pointsField.set(points),this.pointsField.get()},explanation:function(explanation){var sel=$(".openassessment_criterion_option_explanation",this.element);return OpenAssessment.Fields.stringField(sel,explanation)},addHandler:function(){var criterionElement=$(this.element).closest(".openassessment_criterion"),criterionName=$(criterionElement).data("criterion"),criterionLabel=$(".openassessment_criterion_label",criterionElement).val(),options=$(".openassessment_criterion_option",this.element.parent()),name=OpenAssessment.ItemUtilities.createUniqueName(options,"data-option");$(this.element).attr("data-criterion",criterionName).attr("data-option",name),$(".openassessment_criterion_option_name",this.element).attr("value",name);var fields=this.getFieldValues();this.notifier.notificationFired("optionAdd",{criterionName:criterionName,criterionLabel:criterionLabel,name:name,label:fields.label,points:fields.points})},removeHandler:function(){var criterionName=$(this.element).data("criterion"),optionName=$(this.element).data("option");this.notifier.notificationFired("optionRemove",{criterionName:criterionName,name:optionName})},updateHandler:function(){var fields=this.getFieldValues(),criterionName=$(this.element).data("criterion"),optionName=$(this.element).data("option"),optionLabel=fields.label,optionPoints=fields.points;this.notifier.notificationFired("optionUpdated",{criterionName:criterionName,name:optionName,label:optionLabel,points:optionPoints})},validate:function(){return this.pointsField.validate()},validationErrors:function(){return this.pointsField.validationErrors().length>0?["Option points are invalid"]:[]},clearValidationErrors:function(){this.pointsField.clearValidationErrors()}},OpenAssessment.RubricCriterion=function(element,notifier){this.element=element,this.notifier=notifier,this.labelSel=$(".openassessment_criterion_label",this.element),this.promptSel=$(".openassessment_criterion_prompt",this.element),this.optionContainer=new OpenAssessment.Container(OpenAssessment.RubricOption,{containerElement:$(".openassessment_criterion_option_list",this.element).get(0),templateElement:$("#openassessment_option_template").get(0),addButtonElement:$(".openassessment_criterion_add_option",this.element).get(0),removeButtonClass:"openassessment_criterion_option_remove_button",containerItemClass:"openassessment_criterion_option",notifier:this.notifier})},OpenAssessment.RubricCriterion.prototype={addEventListeners:function(){this.optionContainer.addEventListeners(),$(this.element).focusout($.proxy(this.updateHandler,this))},getFieldValues:function(){var fields={label:this.label(),prompt:this.prompt(),feedback:this.feedback(),options:this.optionContainer.getItemValues()},nameString=OpenAssessment.Fields.stringField($(".openassessment_criterion_name",this.element));return""!==nameString&&(fields.name=nameString),fields},label:function(label){return OpenAssessment.Fields.stringField(this.labelSel,label)},prompt:function(prompt){return OpenAssessment.Fields.stringField(this.promptSel,prompt)},feedback:function(){return $(".openassessment_criterion_feedback",this.element).val()},addOption:function(){this.optionContainer.add()},addHandler:function(){var criteria=$(".openassessment_criterion",this.element.parent()),name=OpenAssessment.ItemUtilities.createUniqueName(criteria,"data-criterion");$(this.element).attr("data-criterion",name),$(".openassessment_criterion_name",this.element).attr("value",name)},removeHandler:function(){var criterionName=$(this.element).data("criterion");this.notifier.notificationFired("criterionRemove",{criterionName:criterionName})},updateHandler:function(){var fields=this.getFieldValues(),criterionName=fields.name,criterionLabel=fields.label;this.notifier.notificationFired("criterionUpdated",{criterionName:criterionName,criterionLabel:criterionLabel})},validate:function(){var isValid=""!==this.prompt();return isValid||this.promptSel.addClass("openassessment_highlighted_field"),$.each(this.optionContainer.getAllItems(),function(){isValid=this.validate()&&isValid}),isValid},validationErrors:function(){var errors=[];return this.promptSel.hasClass("openassessment_highlighted_field")&&errors.push("Criterion prompt is invalid."),$.each(this.optionContainer.getAllItems(),function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){this.promptSel.removeClass("openassessment_highlighted_field"),$.each(this.optionContainer.getAllItems(),function(){this.clearValidationErrors()})}},OpenAssessment.TrainingExample=function(element){this.element=element,this.criteria=$(".openassessment_training_example_criterion_option",this.element),this.answer=$(".openassessment_training_example_essay_part textarea",this.element)},OpenAssessment.TrainingExample.prototype={getFieldValues:function(){var optionsSelected=this.criteria.map(function(){return{criterion:$(this).data("criterion"),option:$(this).prop("value")}}).get();return{answer:this.answer.map(function(){return $(this).prop("value")}).get(),options_selected:optionsSelected}},addHandler:function(){$(".openassessment_training_example_criterion_option",this.element).each(function(){$("option",this).each(function(){OpenAssessment.ItemUtilities.refreshOptionString($(this))})})},addEventListeners:function(){},removeHandler:function(){},updateHandler:function(){},validate:function(){var isValid=!0;return this.criteria.each(function(){var isOptionValid=""!==$(this).prop("value");isValid=isOptionValid&&isValid,isOptionValid||$(this).addClass("openassessment_highlighted_field")}),isValid},validationErrors:function(){var errors=[];return this.criteria.each(function(){$(this).hasClass("openassessment_highlighted_field")&&errors.push("Student training example is invalid.")}),errors},clearValidationErrors:function(){this.criteria.each(function(){$(this).removeClass("openassessment_highlighted_field")})}},OpenAssessment.StudioView=function(runtime,element,server,data){this.element=element,this.runtime=runtime,this.server=server,this.data=data,this.fixModalHeight(),this.initializeTabs(),this.alert=(new OpenAssessment.ValidationAlert).install();var studentTrainingListener=new OpenAssessment.StudentTrainingListener;this.promptsView=new OpenAssessment.EditPromptsView($("#oa_prompts_editor_wrapper",this.element).get(0),new OpenAssessment.Notifier([studentTrainingListener]));var staffAssessmentView=new OpenAssessment.EditStaffAssessmentView($("#oa_staff_assessment_editor",this.element).get(0)),studentTrainingView=new OpenAssessment.EditStudentTrainingView($("#oa_student_training_editor",this.element).get(0)),peerAssessmentView=new OpenAssessment.EditPeerAssessmentView($("#oa_peer_assessment_editor",this.element).get(0)),selfAssessmentView=new OpenAssessment.EditSelfAssessmentView($("#oa_self_assessment_editor",this.element).get(0)),exampleBasedAssessmentView=new OpenAssessment.EditExampleBasedAssessmentView($("#oa_ai_assessment_editor",this.element).get(0)),assessmentLookupDictionary={};assessmentLookupDictionary[staffAssessmentView.getID()]=staffAssessmentView,assessmentLookupDictionary[studentTrainingView.getID()]=studentTrainingView,assessmentLookupDictionary[peerAssessmentView.getID()]=peerAssessmentView,assessmentLookupDictionary[selfAssessmentView.getID()]=selfAssessmentView,assessmentLookupDictionary[exampleBasedAssessmentView.getID()]=exampleBasedAssessmentView,this.settingsView=new OpenAssessment.EditSettingsView($("#oa_basic_settings_editor",this.element).get(0),assessmentLookupDictionary,data),this.rubricView=new OpenAssessment.EditRubricView($("#oa_rubric_editor_wrapper",this.element).get(0),new OpenAssessment.Notifier([studentTrainingListener])),$(".openassessment_save_button",this.element).click($.proxy(this.save,this)),$(".openassessment_cancel_button",this.element).click($.proxy(this.cancel,this))},OpenAssessment.StudioView.prototype={fixModalHeight:function(){$(this.element).addClass("openassessment_full_height").parentsUntil(".modal-window").addClass("openassessment_full_height"),$(this.element).closest(".modal-window").addClass("openassessment_modal_window")},initializeTabs:function(){void 0===OpenAssessment.lastOpenEditingTab&&(OpenAssessment.lastOpenEditingTab=2),$(".openassessment_editor_content_and_tabs",this.element).tabs({active:OpenAssessment.lastOpenEditingTab})},saveTabState:function(){var tabElement=$(".openassessment_editor_content_and_tabs",this.element);OpenAssessment.lastOpenEditingTab=tabElement.tabs("option","active")},save:function(){var view=this;this.saveTabState(),this.clearValidationErrors(),this.validate()?(this.alert.hide(),this.server.checkReleased().done(function(isReleased){isReleased?view.confirmPostReleaseUpdate($.proxy(view.updateEditorContext,view)):view.updateEditorContext()}).fail(function(errMsg){view.showError(errMsg)})):this.alert.setMessage(gettext("Couldn't Save This Assignment"),gettext("Please correct the outlined fields.")).show()},confirmPostReleaseUpdate:function(onConfirm){var msg=gettext("This problem has already been released. Any changes will apply only to future assessments.");confirm(msg)&&onConfirm()},updateEditorContext:function(){this.runtime.notify("save",{state:"start"});var view=this,fileUploadType=view.settingsView.fileUploadType();this.server.updateEditorContext({prompts:view.promptsView.promptsDefinition(),feedbackPrompt:view.rubricView.feedbackPrompt(),feedback_default_text:view.rubricView.feedback_default_text(),criteria:view.rubricView.criteriaDefinition(),title:view.settingsView.displayName(),submissionStart:view.settingsView.submissionStart(),submissionDue:view.settingsView.submissionDue(),assessments:view.settingsView.assessmentsDescription(),textResponse:view.settingsView.textResponseNecessity(),fileUploadResponse:view.settingsView.fileUploadResponseNecessity(),fileUploadType:""!==fileUploadType?fileUploadType:null,fileTypeWhiteList:view.settingsView.fileTypeWhiteList(),latexEnabled:view.settingsView.latexEnabled(),leaderboardNum:view.settingsView.leaderboardNum(),editorAssessmentsOrder:view.settingsView.editorAssessmentsOrder()}).done(function(){view.runtime.notify("save",{state:"end"})}).fail(function(msg){view.showError(msg)})},cancel:function(){this.saveTabState(),this.runtime.notify("cancel",{})},showError:function(errorMsg){this.runtime.notify("error",{msg:errorMsg})},validate:function(){var settingsValid=this.settingsView.validate(),rubricValid=this.rubricView.validate(),promptsValid=this.promptsView.validate();return settingsValid&&rubricValid&&promptsValid},validationErrors:function(){return this.settingsView.validationErrors().concat(this.rubricView.validationErrors().concat(this.promptsView.validationErrors()))},clearValidationErrors:function(){this.settingsView.clearValidationErrors(),this.rubricView.clearValidationErrors(),this.promptsView.clearValidationErrors()}},OpenAssessment.EditPeerAssessmentView=function(element){this.element=element,this.name="peer-assessment",this.mustGradeField=new OpenAssessment.IntField($("#peer_assessment_must_grade",this.element),{min:0,max:99}),this.mustBeGradedByField=new OpenAssessment.IntField($("#peer_assessment_graded_by",this.element),{min:0,max:99}),new OpenAssessment.ToggleControl($("#include_peer_assessment",this.element),$("#peer_assessment_settings_editor",this.element),$("#peer_assessment_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.startDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#peer_assessment_start_date","#peer_assessment_start_time").install(),this.dueDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#peer_assessment_due_date","#peer_assessment_due_time").install()},OpenAssessment.EditPeerAssessmentView.prototype={description:function(){return{must_grade:this.mustGradeNum(),must_be_graded_by:this.mustBeGradedByNum(),start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_peer_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_peer_assessment",this.element).click()},mustGradeNum:function(num){return void 0!==num&&this.mustGradeField.set(num),this.mustGradeField.get()},mustBeGradedByNum:function(num){return void 0!==num&&this.mustBeGradedByField.set(num),this.mustBeGradedByField.get()},startDatetime:function(dateString,timeString){return this.startDatetimeControl.datetime(dateString,timeString)},dueDatetime:function(dateString,timeString){return this.dueDatetimeControl.datetime(dateString,timeString)},getID:function(){return $(this.element).attr("id")},validate:function(){var startValid=this.startDatetimeControl.validate(),dueValid=this.dueDatetimeControl.validate(),mustGradeValid=this.mustGradeField.validate(),mustBeGradedByValid=this.mustBeGradedByField.validate();return startValid&&dueValid&&mustGradeValid&&mustBeGradedByValid},validationErrors:function(){var errors=[];return this.startDatetimeControl.validationErrors().length>0&&errors.push("Peer assessment start is invalid"),this.dueDatetimeControl.validationErrors().length>0&&errors.push("Peer assessment due is invalid"),this.mustGradeField.validationErrors().length>0&&errors.push("Peer assessment must grade is invalid"),this.mustBeGradedByField.validationErrors().length>0&&errors.push("Peer assessment must be graded by is invalid"),errors},clearValidationErrors:function(){this.startDatetimeControl.clearValidationErrors(),this.dueDatetimeControl.clearValidationErrors(),this.mustGradeField.clearValidationErrors(),this.mustBeGradedByField.clearValidationErrors()}},OpenAssessment.EditSelfAssessmentView=function(element){this.element=element,this.name="self-assessment",new OpenAssessment.ToggleControl($("#include_self_assessment",this.element),$("#self_assessment_settings_editor",this.element),$("#self_assessment_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.startDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#self_assessment_start_date","#self_assessment_start_time").install(),this.dueDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#self_assessment_due_date","#self_assessment_due_time").install()},OpenAssessment.EditSelfAssessmentView.prototype={description:function(){return{start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_self_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_self_assessment",this.element).click()},startDatetime:function(dateString,timeString){return this.startDatetimeControl.datetime(dateString,timeString)},dueDatetime:function(dateString,timeString){return this.dueDatetimeControl.datetime(dateString,timeString)},getID:function(){return $(this.element).attr("id")},validate:function(){var startValid=this.startDatetimeControl.validate(),dueValid=this.dueDatetimeControl.validate();return startValid&&dueValid},validationErrors:function(){var errors=[];return this.startDatetimeControl.validationErrors().length>0&&errors.push("Self assessment start is invalid"),this.dueDatetimeControl.validationErrors().length>0&&errors.push("Self assessment due is invalid"),errors},clearValidationErrors:function(){this.startDatetimeControl.clearValidationErrors(),this.dueDatetimeControl.clearValidationErrors()}},OpenAssessment.EditStudentTrainingView=function(element){this.element=element,this.name="student-training",new OpenAssessment.ToggleControl($("#include_student_training",this.element),$("#student_training_settings_editor",this.element),$("#student_training_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.exampleContainer=new OpenAssessment.Container(OpenAssessment.TrainingExample,{containerElement:$("#openassessment_training_example_list",this.element).get(0),templateElement:$("#openassessment_training_example_template",this.element).get(0),addButtonElement:$(".openassessment_add_training_example",this.element).get(0),removeButtonClass:"openassessment_training_example_remove",containerItemClass:"openassessment_training_example"}),this.exampleContainer.addEventListeners()},OpenAssessment.EditStudentTrainingView.prototype={description:function(){return{examples:this.exampleContainer.getItemValues()}},isEnabled:function(isEnabled){var sel=$("#include_student_training",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_student_training",this.element).click()},getID:function(){return $(this.element).attr("id")},validate:function(){var isValid=!0;return $.each(this.exampleContainer.getAllItems(),function(){ -isValid=this.validate()&&isValid}),isValid},validationErrors:function(){var errors=[];return $.each(this.exampleContainer.getAllItems(),function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){$.each(this.exampleContainer.getAllItems(),function(){this.clearValidationErrors()})},addTrainingExample:function(){this.exampleContainer.add()}},OpenAssessment.EditExampleBasedAssessmentView=function(element){this.element=element,this.name="example-based-assessment",new OpenAssessment.ToggleControl($("#include_ai_assessment",this.element),$("#ai_assessment_settings_editor",this.element),$("#ai_assessment_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install()},OpenAssessment.EditExampleBasedAssessmentView.prototype={description:function(){return{examples_xml:this.exampleDefinitions()}},isEnabled:function(isEnabled){var sel=$("#include_ai_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_ai_assessment",this.element).click()},exampleDefinitions:function(xml){var sel=$("#ai_training_examples",this.element);return OpenAssessment.Fields.stringField(sel,xml)},getID:function(){return $(this.element).attr("id")},validate:function(){return!0},validationErrors:function(){return[]},clearValidationErrors:function(){}},OpenAssessment.EditStaffAssessmentView=function(element){this.element=element,this.name="staff-assessment",new OpenAssessment.ToggleControl($("#include_staff_assessment",this.element),$("#staff_assessment_description",this.element),$("#staff_assessment_description",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install()},OpenAssessment.EditStaffAssessmentView.prototype={description:function(){return{required:this.isEnabled()}},isEnabled:function(isEnabled){var sel=$("#include_staff_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_staff_assessment",this.element).click()},getID:function(){return $(this.element).attr("id")},validate:function(){return!0},validationErrors:function(){return[]},clearValidationErrors:function(){}},OpenAssessment.Fields={stringField:function(sel,value){return void 0!==value&&sel.val(value),sel.val()},booleanField:function(sel,value){return void 0!==value&&sel.prop("checked",value),sel.prop("checked")}},OpenAssessment.IntField=function(inputSel,restrictions){this.max=restrictions.max,this.min=restrictions.min,this.input=$(inputSel)},OpenAssessment.IntField.prototype={get:function(){return parseInt(this.input.val().trim(),10)},set:function(val){this.input.val(val)},validate:function(){var value=this.get(),isValid=!isNaN(value)&&value>=this.min&&value<=this.max;return-1!==this.input.val().indexOf(".")&&(isValid=!1),isValid||this.input.addClass("openassessment_highlighted_field"),isValid},clearValidationErrors:function(){this.input.removeClass("openassessment_highlighted_field")},validationErrors:function(){return this.input.hasClass("openassessment_highlighted_field")?["Int field is invalid"]:[]}},OpenAssessment.ToggleControl=function(checkboxSel,shownSel,hiddenSel,notifier){this.checkbox=checkboxSel,this.shownSection=shownSel,this.hiddenSection=hiddenSel,this.notifier=notifier},OpenAssessment.ToggleControl.prototype={install:function(){return this.checkbox.change(this,function(event){var control=event.data;this.checked?(control.notifier.notificationFired("toggleOn",{}),control.show()):(control.notifier.notificationFired("toggleOff",{}),control.hide())}),this},show:function(){this.hiddenSection.addClass("is--hidden"),this.shownSection.removeClass("is--hidden")},hide:function(){this.shownSection.addClass("is--hidden"),this.hiddenSection.removeClass("is--hidden")}},OpenAssessment.DatetimeControl=function(element,datePicker,timePicker){this.element=element,this.datePicker=datePicker,this.timePicker=timePicker},OpenAssessment.DatetimeControl.prototype={install:function(){var dateString=$(this.datePicker,this.element).val();return $(this.datePicker,this.element).datepicker({showButtonPanel:!0}).datepicker("option","dateFormat","yy-mm-dd").datepicker("setDate",dateString),$(this.timePicker,this.element).timepicker({timeFormat:"H:i",step:60}),this},datetime:function(dateString,timeString){var datePickerSel=$(this.datePicker,this.element),timePickerSel=$(this.timePicker,this.element);return void 0!==dateString&&datePickerSel.val(dateString),void 0!==timeString&&timePickerSel.val(timeString),datePickerSel.val()+"T"+timePickerSel.val()},validate:function(){var dateString=$(this.datePicker,this.element).val(),timeString=$(this.timePicker,this.element).val(),isDateValid=!1;try{isDateValid=$.datepicker.parseDate($.datepicker.ISO_8601,dateString)instanceof Date}catch(err){}isDateValid||$(this.datePicker,this.element).addClass("openassessment_highlighted_field");var matches=timeString.match(/^\d{2}:\d{2}$/g),isTimeValid=null!==matches;return isTimeValid||$(this.timePicker,this.element).addClass("openassessment_highlighted_field"),isDateValid&&isTimeValid},clearValidationErrors:function(){$(this.datePicker,this.element).removeClass("openassessment_highlighted_field"),$(this.timePicker,this.element).removeClass("openassessment_highlighted_field")},validationErrors:function(){var errors=[],dateHasError=$(this.datePicker,this.element).hasClass("openassessment_highlighted_field"),timeHasError=$(this.timePicker,this.element).hasClass("openassessment_highlighted_field");return dateHasError&&errors.push("Date is invalid"),timeHasError&&errors.push("Time is invalid"),errors}},OpenAssessment.SelectControl=function(selectSel,mapping,notifier){this.select=selectSel,this.mapping=mapping,this.notifier=notifier},OpenAssessment.SelectControl.prototype={install:function(){return this.select.change(this,function(event){var control=event.data;control.notifier.notificationFired("selectionChanged",{selected:this.value}),control.change(this.value)}),this},change:function(selected){$.isFunction(this.mapping)?this.mapping(selected):$.each(this.mapping,function(option,sel){option===selected?sel.removeClass("is--hidden"):sel.addClass("is--hidden")})}},OpenAssessment.InputControl=function(inputSel,validator){this.input=$(inputSel),this.validator=validator,this.errors=[]},OpenAssessment.InputControl.prototype={get:function(){return this.input.val()},set:function(val){this.input.val(val)},validate:function(){return this.errors=this.validator(this.get()),this.errors.length&&(this.input.addClass("openassessment_highlighted_field"),this.input.parent().nextAll(".message-status").text(this.errors.join(";")),this.input.parent().nextAll(".message-status").addClass("is-shown")),0===this.errors.length},clearValidationErrors:function(){this.input.removeClass("openassessment_highlighted_field"),this.input.parent().nextAll(".message-status").removeClass("is-shown")},validationErrors:function(){return this.errors}},OpenAssessment.StudentTrainingListener=function(){this.element=$("#oa_student_training_editor"),this.alert=new OpenAssessment.ValidationAlert},OpenAssessment.StudentTrainingListener.prototype={promptAdd:function(){var view=this.element;$("#openassessment_training_example_part_template").children().first().clone().removeAttr("id").toggleClass("is--hidden",!1).appendTo(".openassessment_training_example_essay",view)},promptRemove:function(data){var view=this.element;$(".openassessment_training_example_essay li:nth-child("+(data.index+1)+")",view).remove()},optionUpdated:function(data){this._optionSel(data.criterionName).each(function(){var criterion=this,option=$('option[value="'+data.name+'"]',criterion).attr("data-points",data.points).attr("data-label",data.label);OpenAssessment.ItemUtilities.refreshOptionString(option)})},optionAdd:function(data){var criterionAdded=!1;0===this._optionSel(data.criterionName).length&&(this.criterionAdd(data),criterionAdded=!0),this._optionSel(data.criterionName).each(function(){var criterion=this,option=$("").attr("value",data.name).attr("data-points",data.points).attr("data-label",data.label);OpenAssessment.ItemUtilities.refreshOptionString(option),$(criterion).append(option)}),criterionAdded&&this.displayAlertMsg(gettext("Criterion Added"),gettext("You have added a criterion. You will need to select an option for the criterion in the Learner Training step. To do this, click the Settings tab."))},optionRemove:function(data){var handler=this,invalidated=!1;this._optionSel(data.criterionName).each(function(){var criterionOption=this;$(criterionOption).val()===data.name.toString()&&($(criterionOption).val("").addClass("openassessment_highlighted_field").click(function(){$(criterionOption).removeClass("openassessment_highlighted_field")}),invalidated=!0),$('option[value="'+data.name+'"]',criterionOption).remove(),1===$("option",criterionOption).length&&(handler.removeAllOptions(data),invalidated=!1)}),invalidated&&this.displayAlertMsg(gettext("Option Deleted"),gettext("You have deleted an option. That option has been removed from its criterion in the sample responses in the Learner Training step. You might have to select a new option for the criterion."))},_optionSel:function(criterionName){return $('.openassessment_training_example_criterion_option[data-criterion="'+criterionName+'"]',this.element)},removeAllOptions:function(data){var changed=!1;$(".openassessment_training_example_criterion",this.element).each(function(){var criterion=this;$(criterion).data("criterion")===data.criterionName&&($(criterion).remove(),changed=!0)}),changed&&this.displayAlertMsg(gettext("Option Deleted"),gettext("You have deleted all the options for this criterion. The criterion has been removed from the sample responses in the Learner Training step."))},criterionRemove:function(data){var changed=!1,sel='.openassessment_training_example_criterion[data-criterion="'+data.criterionName+'"]';$(sel,this.element).each(function(){$(this).remove(),changed=!0}),changed&&this.displayAlertMsg(gettext("Criterion Deleted"),gettext("You have deleted a criterion. The criterion has been removed from the example responses in the Learner Training step."))},displayAlertMsg:function(title,msg){$("#include_student_training",this.element).is(":checked")&&$(".openassessment_training_example",this.element).length>1&&this.alert.setMessage(title,msg).show()},criterionUpdated:function(data){var sel='.openassessment_training_example_criterion[data-criterion="'+data.criterionName+'"]';$(sel,this.element).each(function(){$(".openassessment_training_example_criterion_name_wrapper",this).text(data.criterionLabel)})},criterionAdd:function(data){var view=this.element,criterion=$("#openassessment_training_example_criterion_template").children().first().clone().removeAttr("id").attr("data-criterion",data.criterionName).toggleClass("is--hidden",!1).appendTo(".openassessment_training_example_criteria_selections",view);criterion.find(".openassessment_training_example_criterion_option").attr("data-criterion",data.criterionName),criterion.find(".openassessment_training_example_criterion_name_wrapper").text(data.label)},examplesCriteriaLabels:function(){var examples=[];return $(".openassessment_training_example_criteria_selections",this.element).each(function(){var exampleDescription={};$(".openassessment_training_example_criterion",this).each(function(){var criterionName=$(this).data("criterion"),criterionLabel=$(".openassessment_training_example_criterion_name_wrapper",this).text().trim();exampleDescription[criterionName]=criterionLabel}),examples.push(exampleDescription)}),examples},examplesOptionsLabels:function(){var examples=[];return $(".openassessment_training_example_criteria_selections",this.element).each(function(){var exampleDescription={};$(".openassessment_training_example_criterion_option",this).each(function(){var criterionName=$(this).data("criterion");exampleDescription[criterionName]={},$("option",this).each(function(){var optionName=$(this).val(),optionLabel=$(this).text().trim();exampleDescription[criterionName][optionName]=optionLabel})}),examples.push(exampleDescription)}),examples}},OpenAssessment.AssessmentToggleListener=function(){this.alert=new OpenAssessment.ValidationAlert},OpenAssessment.AssessmentToggleListener.prototype={toggleOff:function(){this.alert.setMessage(gettext("Warning"),gettext("Changes to steps that are not selected as part of the assignment will not be saved.")).show()},toggleOn:function(){this.alert.hide()}},OpenAssessment.Notifier=function(listeners){this.listeners=listeners},OpenAssessment.Notifier.prototype={notificationFired:function(name,data){for(var i=0;i0;return isValid||this.criterionAddButton.addClass("openassessment_highlighted_field").click(function(){$(this).removeClass("openassessment_highlighted_field")}),$.each(criteria,function(){isValid=this.validate()&&isValid}),isValid},validationErrors:function(){var errors=[];return this.criterionAddButton.hasClass("openassessment_highlighted_field")&&errors.push("The rubric must contain at least one criterion"),$.each(this.getAllCriteria(),function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){this.criterionAddButton.removeClass("openassessment_highlighted_field"),$.each(this.getAllCriteria(),function(){this.clearValidationErrors()})}},OpenAssessment.EditSettingsView=function(element,assessmentViews,data){var self=this;this.settingsElement=element,this.assessmentsElement=$(element).siblings("#openassessment_assessment_module_settings_editors").get(0),this.assessmentViews=assessmentViews,this.startDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#openassessment_submission_start_date","#openassessment_submission_start_time").install(),this.dueDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#openassessment_submission_due_date","#openassessment_submission_due_time").install(),new OpenAssessment.SelectControl($("#openassessment_submission_file_upload_response",this.element),function(selectedValue){var el=$("#openassessment_submission_file_upload_type_wrapper",self.element);selectedValue?el.removeClass("is--hidden"):el.addClass("is--hidden")},new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),new OpenAssessment.SelectControl($("#openassessment_submission_upload_selector",this.element),{custom:$("#openassessment_submission_white_listed_file_types_wrapper",this.element)},new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.leaderboardIntField=new OpenAssessment.IntField($("#openassessment_leaderboard_editor",this.element),{min:0,max:100}),this.fileTypeWhiteListInputField=new OpenAssessment.InputControl($("#openassessment_submission_white_listed_file_types",this.element),function(value){var badExts=[],errors=[];if(!value)return errors.push(gettext("File types can not be empty.")),errors;var whiteList=$.map(value.replace(/\./g,"").toLowerCase().split(","),$.trim);return $.each(whiteList,function(index,ext){-1!==data.FILE_EXT_BLACK_LIST.indexOf(ext)&&badExts.push(ext)}),badExts.length&&errors.push(gettext("The following file types are not allowed: ")+badExts.join(",")),errors}),this.initializeSortableAssessments()},OpenAssessment.EditSettingsView.prototype={initializeSortableAssessments:function(){var view=this;$("#openassessment_assessment_module_settings_editors",view.element).sortable({start:function(event,ui){$(".openassessment_assessment_module_editor",view.element).hide();ui.placeholder.height("auto"),ui.helper.height("auto"),$("#openassessment_assessment_module_settings_editors",view.element).sortable("refresh").sortable("refreshPositions")},stop:function(){$(".openassessment_assessment_module_editor",view.element).show()},snap:!0,axis:"y",handle:".drag-handle",cursorAt:{top:20}}),$("#openassessment_assessment_module_settings_editors .drag-handle",view.element).disableSelection()},displayName:function(name){var sel=$("#openassessment_title_editor",this.settingsElement);return OpenAssessment.Fields.stringField(sel,name)},submissionStart:function(dateString,timeString){return this.startDatetimeControl.datetime(dateString,timeString)},submissionDue:function(dateString,timeString){return this.dueDatetimeControl.datetime(dateString,timeString)},textResponseNecessity:function(value){var sel=$("#openassessment_submission_text_response",this.settingsElement);return void 0!==value&&sel.val(value),sel.val()},fileUploadResponseNecessity:function(value,triggerChange){var sel=$("#openassessment_submission_file_upload_response",this.settingsElement);return void 0!==value&&(triggerChange=triggerChange||!1,sel.val(value),triggerChange&&$(sel).trigger("change")),sel.val()},fileUploadType:function(uploadType){var fileUploadTypeWrapper=$("#openassessment_submission_file_upload_type_wrapper",this.settingsElement);if(!$(fileUploadTypeWrapper).hasClass("is--hidden")){var sel=$("#openassessment_submission_upload_selector",this.settingsElement);return void 0!==uploadType&&sel.val(uploadType),sel.val()}return""},fileTypeWhiteList:function(exts){return void 0!==exts&&this.fileTypeWhiteListInputField.set(exts),this.fileTypeWhiteListInputField.get()},latexEnabled:function(isEnabled){var sel=$("#openassessment_submission_latex_editor",this.settingsElement);return void 0!==isEnabled&&(isEnabled?sel.val(1):sel.val(0)),"1"===sel.val()},leaderboardNum:function(num){return void 0!==num&&this.leaderboardIntField.set(num),this.leaderboardIntField.get(num)},assessmentsDescription:function(){var assessmentDescList=[],view=this;return $(".openassessment_assessment_module_settings_editor",this.assessmentsElement).each(function(){var asmntView=view.assessmentViews[$(this).attr("id")];if(asmntView.isEnabled()){var description=asmntView.description();description.name=asmntView.name,assessmentDescList.push(description)}}),assessmentDescList},editorAssessmentsOrder:function(){var editorAssessments=[],view=this;return $(".openassessment_assessment_module_settings_editor",this.assessmentsElement).each(function(){var asmntView=view.assessmentViews[$(this).attr("id")];editorAssessments.push(asmntView.name)}),editorAssessments},validate:function(){var isValid=!0;return isValid=this.startDatetimeControl.validate()&&isValid,isValid=this.dueDatetimeControl.validate()&&isValid,isValid=this.leaderboardIntField.validate()&&isValid,"custom"===this.fileUploadType()?isValid=this.fileTypeWhiteListInputField.validate()&&isValid:this.fileTypeWhiteListInputField.get()&&!this.fileTypeWhiteListInputField.validate()&&this.fileTypeWhiteListInputField.set(""),$.each(this.assessmentViews,function(){this.isEnabled()&&(isValid=this.validate()&&isValid)}),isValid},validationErrors:function(){var errors=[];return this.startDatetimeControl.validationErrors().length>0&&errors.push("Submission start is invalid"),this.dueDatetimeControl.validationErrors().length>0&&errors.push("Submission due is invalid"),this.leaderboardIntField.validationErrors().length>0&&errors.push("Leaderboard number is invalid"),this.fileTypeWhiteListInputField.validationErrors().length>0&&(errors=errors.concat(this.fileTypeWhiteListInputField.validationErrors())),$.each(this.assessmentViews,function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){this.startDatetimeControl.clearValidationErrors(),this.dueDatetimeControl.clearValidationErrors(),this.leaderboardIntField.clearValidationErrors(),this.fileTypeWhiteListInputField.clearValidationErrors(),$.each(this.assessmentViews,function(){this.clearValidationErrors()})}},OpenAssessment.ValidationAlert=function(){this.element=$("#openassessment_validation_alert"),this.editorElement=$(this.element).parent(),this.title=$(".openassessment_alert_title",this.element),this.message=$(".openassessment_alert_message",this.element),this.closeButton=$(".openassessment_alert_close",this.element),this.ALERT_YELLOW="rgb(192, 172, 0)",this.DARK_GREY="#323232"},OpenAssessment.ValidationAlert.prototype={install:function(){var alert=this;return this.closeButton.click(function(eventObject){eventObject.preventDefault(),alert.hide()}),this},hide:function(){var headerHeight=$("#openassessment_editor_header",this.editorElement).outerHeight();this.element.addClass("covered");var styles={height:"Calc(100% - "+headerHeight+"px)","border-top-right-radius":"3px","border-top-left-radius":"3px"};return $(".oa_editor_content_wrapper",this.editorElement).each(function(){$(this).css(styles)}),this},show:function(){var view=this;if(this.isVisible())$(this.element).animate({"background-color":view.ALERT_YELLOW},300,"swing",function(){$(this).animate({"background-color":view.DARK_GREY},700,"swing")});else{this.element.removeClass("covered");var alertHeight=this.element.outerHeight(),headerHeight=$("#openassessment_editor_header",this.editorElement).outerHeight(),heightString="Calc(100% - "+(alertHeight+headerHeight)+"px)",styles={height:heightString,"border-top-right-radius":"0px","border-top-left-radius":"0px"};$(".oa_editor_content_wrapper",this.editorElement).each(function(){$(this).css(styles),$(this).scrollTop($(this).scrollTop()+alertHeight)})}return this},setMessage:function(newTitle,newMessage){return this.title.text(newTitle),this.message.text(newMessage),this},isVisible:function(){return!this.element.hasClass("covered")},getTitle:function(){return this.title.text()},getMessage:function(){return this.message.text()}},function(a,b){"function"==typeof define&&define.amd?define(["underscore","backbone"],function(c,d){return a.Backgrid=b(c,d)}):"object"==typeof exports?module.exports=b(require("underscore"),require("backbone")):a.Backgrid=b(a._,a.Backbone)}(this,function(a,b){"use strict";function c(a,b,c){var d=b-(a+"").length;d=d<0?0:d;for(var e="",f=0;f20)throw new RangeError("decimals must be between 0 and 20")};k.prototype=new j,a.extend(k.prototype,{defaults:{decimals:2,decimalSeparator:".",orderSeparator:","},HUMANIZED_NUM_RE:/(\d)(?=(?:\d{3})+$)/g,fromRaw:function(b,c){if(a.isNull(b)||a.isUndefined(b))return"";b=parseFloat(b).toFixed(~~this.decimals);var d=b.split("."),e=d[0],f=d[1]?(this.decimalSeparator||".")+d[1]:"";return e.replace(this.HUMANIZED_NUM_RE,"$1"+this.orderSeparator)+f},toRaw:function(b,c){if(""===(b=b.trim()))return null;for(var d="",e=b.split(this.orderSeparator),f=0;f",{tabIndex:-1,href:a,title:this.title||b,target:this.target}).text(b)),this.delegateEvents(),this}}),v=(h.EmailCell=t.extend({className:"email-cell",formatter:o,render:function(){this.$el.empty();var a=this.model,b=this.formatter.fromRaw(a.get(this.column.get("name")),a);return this.$el.append(g("",{tabIndex:-1,href:"mailto:"+b,title:b}).text(b)),this.delegateEvents(),this}}),h.NumberCell=s.extend({className:"number-cell",decimals:k.prototype.defaults.decimals,decimalSeparator:k.prototype.defaults.decimalSeparator,orderSeparator:k.prototype.defaults.orderSeparator,formatter:k,initialize:function(a){v.__super__.initialize.apply(this,arguments);var b=this.formatter;b.decimals=this.decimals,b.decimalSeparator=this.decimalSeparator,b.orderSeparator=this.orderSeparator}})),w=(h.IntegerCell=v.extend({className:"integer-cell",decimals:0}),h.PercentCell=v.extend({className:"percent-cell",multiplier:l.prototype.defaults.multiplier,symbol:l.prototype.defaults.symbol,formatter:l,initialize:function(){w.__super__.initialize.apply(this,arguments);var a=this.formatter;a.multiplier=this.multiplier,a.symbol=this.symbol}})),x=h.DatetimeCell=s.extend({className:"datetime-cell",includeDate:m.prototype.defaults.includeDate,includeTime:m.prototype.defaults.includeTime,includeMilli:m.prototype.defaults.includeMilli,formatter:m,initialize:function(b){x.__super__.initialize.apply(this,arguments);var c=this.formatter;c.includeDate=this.includeDate,c.includeTime=this.includeTime,c.includeMilli=this.includeMilli;var d=this.includeDate?"YYYY-MM-DD":"";d+=this.includeDate&&this.includeTime?"T":"",d+=this.includeTime?"HH:mm:ss":"",d+=this.includeTime&&this.includeMilli?".SSS":"",this.editor=this.editor.extend({attributes:a.extend({},this.editor.prototype.attributes,this.editor.attributes,{placeholder:d})})}}),y=(h.DateCell=x.extend({className:"date-cell",includeTime:!1}),h.TimeCell=x.extend({className:"time-cell",includeDate:!1}),h.BooleanCellEditor=q.extend({tagName:"input",attributes:{tabIndex:-1,type:"checkbox"},events:{mousedown:function(){this.mouseDown=!0},blur:"enterOrExitEditMode",mouseup:function(){this.mouseDown=!1},change:"saveOrCancel",keydown:"saveOrCancel"},render:function(){var a=this.model,b=this.formatter.fromRaw(a.get(this.column.get("name")),a);return this.$el.prop("checked",b),this},enterOrExitEditMode:function(a){if(!this.mouseDown){var b=this.model;b.trigger("backgrid:edited",b,this.column,new i(a))}},saveOrCancel:function(a){var b=this.model,c=this.column,d=this.formatter,e=new i(a);if(e.passThru()&&"change"!=a.type)return!0;e.cancel()&&(a.stopPropagation(),b.trigger("backgrid:edited",b,c,e));var f=this.$el;if(e.save()||e.moveLeft()||e.moveRight()||e.moveUp()||e.moveDown()){a.preventDefault(),a.stopPropagation();var g=d.toRaw(f.prop("checked"),b);b.set(c.get("name"),g),b.trigger("backgrid:edited",b,c,e)}else if("change"==a.type){var g=d.toRaw(f.prop("checked"),b);b.set(c.get("name"),g),f.focus()}}})),z=(h.BooleanCell=s.extend({className:"boolean-cell",editor:y,events:{click:"enterEditMode"},render:function(){this.$el.empty();var a=this.model,b=this.column,c=h.callByNeed(b.editable(),b,a);return this.$el.append(g("",{tabIndex:-1,type:"checkbox",checked:this.formatter.fromRaw(a.get(b.get("name")),a),disabled:!c})),this.delegateEvents(),this}}),h.SelectCellEditor=q.extend({tagName:"select",events:{change:"save",blur:"close",keydown:"close"},template:a.template('',null,{variable:null,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g}),setOptionValues:function(b){this.optionValues=b,this.optionValues=a.result(this,"optionValues")},setMultiple:function(a){this.multiple=a,this.$el.prop("multiple",a)},_renderOptions:function(b,c){for(var d="",e=0;e-1});return d},render:function(){this.$el.empty();var b=a.result(this,"optionValues"),c=this.model,d=this.formatter.fromRaw(c.get(this.column.get("name")),c);if(!a.isArray(b))throw new TypeError("optionValues must be an array");for(var e=null,f=null,e=null,h=null,i=null,j=0;j-1}));else{if(!a.isObject(e))throw new TypeError("optionValues elements must be a name-value pair or an object hash of { name: 'optgroup label', value: [option name-value pairs] }");h=e.name,i=g("",{label:h}),i.append(this._renderOptions.call(this,e.values,d)),this.$el.append(i)}}return this.delegateEvents(),this},save:function(a){var b=this.model,c=this.column;b.set(c.get("name"),this.formatter.toRaw(this.$el.val(),b))},close:function(a){var b=this.model,c=this.column,d=new i(a);d.cancel()?(a.stopPropagation(),b.trigger("backgrid:edited",b,c,new i(a))):(d.save()||d.moveLeft()||d.moveRight()||d.moveUp()||d.moveDown()||"blur"==a.type)&&(a.preventDefault(),a.stopPropagation(),this.save(a),b.trigger("backgrid:edited",b,c,new i(a)))}})),A=h.SelectCell=s.extend({className:"select-cell",editor:z,multiple:!1,formatter:p,optionValues:void 0,delimiter:", ",initialize:function(a){A.__super__.initialize.apply(this,arguments),this.listenTo(this.model,"backgrid:edit",function(a,b,c,d){b.get("name")==this.column.get("name")&&(d.setOptionValues(this.optionValues),d.setMultiple(this.multiple))})},render:function(){this.$el.empty();var b=a.result(this,"optionValues"),c=this.model,d=this.formatter.fromRaw(c.get(this.column.get("name")),c),e=[];try{if(!a.isArray(b)||a.isEmpty(b))throw new TypeError;for(var f=0;f|Array.<{name: string, values: Array.}>}");throw a}return this.delegateEvents(),this}}),B=h.Column=b.Model.extend({defaults:{name:void 0,label:void 0,sortable:!0,editable:!0,renderable:!0,formatter:void 0,sortType:"cycle",sortValue:void 0,direction:null,cell:void 0,headerCell:void 0},initialize:function(){this.has("label")||this.set({label:this.get("name")},{silent:!0});var a=h.resolveNameToClass(this.get("headerCell"),"HeaderCell"),b=h.resolveNameToClass(this.get("cell"),"Cell");this.set({cell:b,headerCell:a},{silent:!0})},sortValue:function(){var b=this.get("sortValue");return a.isString(b)?this[b]:a.isFunction(b)?b:function(a,b){return a.get(b)}}});a.each(["sortable","renderable","editable"],function(b){B.prototype[b]=function(){var c=this.get(b);return a.isString(c)?this[c]:a.isFunction(c)?c:!!c}});var C=h.Columns=b.Collection.extend({model:B}),D=h.Row=b.View.extend({tagName:"tr",initialize:function(a){var c=this.columns=a.columns;c instanceof b.Collection||(c=this.columns=new C(c));for(var d=this.cells=[],e=0;e").text(b.get("label")).append(""):document.createTextNode(b.get("label")),this.$el.append(a),this.$el.addClass(b.get("name")),this.$el.addClass(b.get("direction")),this.delegateEvents(),this}}),G=(h.HeaderRow=h.Row.extend({initialize:function(){h.Row.prototype.initialize.apply(this,arguments)},makeCell:function(a,b){var c=a.get("headerCell")||b.headerCell||F;return c=new c({column:a,collection:this.collection})}}),h.Header=b.View.extend({tagName:"thead",initialize:function(a){this.columns=a.columns,this.columns instanceof b.Collection||(this.columns=new C(this.columns)),this.row=new h.HeaderRow({columns:this.columns,collection:this.collection})},render:function(){return this.$el.append(this.row.render().$el),this.delegateEvents(),this},remove:function(){return this.row.remove.apply(this.row,arguments),b.View.prototype.remove.apply(this,arguments)}})),H=h.Body=b.View.extend({tagName:"tbody",initialize:function(a){this.columns=a.columns,this.columns instanceof b.Collection||(this.columns=new C(this.columns)),this.row=a.row||this.row||D,this.rows=this.collection.map(function(a){return new this.row({columns:this.columns,model:a})},this),this.emptyText=a.emptyText,this._unshiftEmptyRowMayBe();var c=this.collection;this.listenTo(c,"add",this.insertRow),this.listenTo(c,"remove",this.removeRow),this.listenTo(c,"sort",this.refresh),this.listenTo(c,"reset",this.refresh),this.listenTo(c,"backgrid:sort",this.sort),this.listenTo(c,"backgrid:edited",this.moveToNextCell),this.listenTo(this.columns,"add remove",this.updateEmptyRow)},_unshiftEmptyRowMayBe:function(){if(0===this.rows.length&&null!=this.emptyText)return this.emptyRow=new E({emptyText:this.emptyText,columns:this.columns}),this.rows.unshift(this.emptyRow),!0},insertRow:function(a,c,d){if(this.rows[0]instanceof E&&this.rows.pop().remove(),!(c instanceof b.Collection||d))return void this.collection.add(a,d=c);var e=new this.row({columns:this.columns,model:a}),f=c.indexOf(a);this.rows.splice(f,0,e);var g=this.$el,h=g.children(),i=e.render().$el;return f>=h.length?g.append(i):h.eq(f).before(i),this},removeRow:function(b,c,d){return d?((a.isUndefined(d.render)||d.render)&&this.rows[d.index].remove(),this.rows.splice(d.index,1),this._unshiftEmptyRowMayBe()&&this.render(),this):(this.collection.remove(b,d=c),void(this._unshiftEmptyRowMayBe()&&this.render()))},updateEmptyRow:function(){null!=this.emptyRow&&this.emptyRow.render()},refresh:function(){for(var a=0;a=0&&p").text(this.caption)),this.header&&this.$el.append(this.header.render().$el),this.footer&&this.$el.append(this.footer.render().$el),this.$el.append(this.body.render().$el),this.delegateEvents(),this.trigger("backgrid:rendered",this),this},remove:function(){return this.header&&this.header.remove.apply(this.header,arguments),this.body.remove.apply(this.body,arguments),this.footer&&this.footer.remove.apply(this.footer,arguments),b.View.prototype.remove.apply(this,arguments)}}),h}); +function OpenAssessmentEditor(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);new OpenAssessment.StudioView(runtime,element,server,data)}if("undefined"!=typeof OpenAssessment&&OpenAssessment||(OpenAssessment={}),void 0===window.gettext&&(window.gettext=function(text){return text}),void 0===window.ngetgext&&(window.ngettext=function(singularText,pluralText,n){return n>1?pluralText:singularText}),void 0===window.Logger&&(window.Logger={log:function(){}}),void 0===window.MathJax&&(window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}),void 0===OpenAssessment.Server||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime,this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this,url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this,url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:!0}}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(studentUsername,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:studentUsername},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},staffGradeForm:function(){var url=this.url("render_staff_grade_form");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The staff assessment form could not be loaded.")])})}).promise()},staffGradeCounts:function(){var url=this.url("render_staff_grade_counts");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The display of ungraded and checked out responses could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){if(data[0]){var studentId=data[1],attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1],errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback"),payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID,assessType){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID,assess_type:assessType})},trainingAssess:function(optionsSelected){var url=this.url("training_assess"),payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.corrections]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.msg]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.msg]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context"),payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,text_response:options.textResponse,file_upload_response:options.fileUploadResponse,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){data.success?defer.resolveWith(this,[data.is_released]):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename,filenum){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename,filenum:filenum}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve(data.url):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},removeUploadedFiles:function(){var url=this.url("remove_all_uploaded_files");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Server error.")])})}).promise()},saveFilesDescriptions:function(descriptions){var url=this.url("save_files_descriptions");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({descriptions:descriptions}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve():defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Server error.")])})}).promise()},getDownloadUrl:function(filenum){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({filenum:filenum}),contentType:jsonContentType}).done(function(data){data.success?defer.resolve(data.url):defer.rejectWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission"),payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){data.success&&defer.resolveWith(this,[data.msg])}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()},publishEvent:function(eventName,eventData){eventData.event_name=eventName;var url=this.url("publish_event"),payload=JSON.stringify(eventData);$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType})}}}"undefined"!=typeof OpenAssessment&&OpenAssessment||(OpenAssessment={}),void 0===window.gettext&&(window.gettext=function(text){return text}),void 0===window.ngetgext&&(window.ngettext=function(singularText,pluralText,n){return n>1?pluralText:singularText}),void 0===window.Logger&&(window.Logger={log:function(){}}),void 0===window.MathJax&&(window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}),OpenAssessment.Container=function(ContainerItem,kwargs){this.containerElement=kwargs.containerElement,this.templateElement=kwargs.templateElement,this.addButtonElement=kwargs.addButtonElement,this.removeButtonClass=kwargs.removeButtonClass,this.containerItemClass=kwargs.containerItemClass,this.notifier=kwargs.notifier,this.addRemoveEnabled=void 0===kwargs.addRemoveEnabled||kwargs.addRemoveEnabled;var container=this;this.createContainerItem=function(element){return new ContainerItem(element,container.notifier)}},OpenAssessment.Container.prototype={addEventListeners:function(){var container=this;this.addRemoveEnabled?($(this.addButtonElement).click($.proxy(this.add,this)),$("."+this.removeButtonClass,this.containerElement).click(function(eventData){var item=container.createContainerItem(eventData.target);container.remove(item)})):($(this.addButtonElement).addClass("is--disabled"),$("."+this.removeButtonClass,this.containerElement).addClass("is--disabled")),$("."+this.containerItemClass,this.containerElement).each(function(index,element){container.createContainerItem(element).addEventListeners()})},add:function(){$(this.templateElement).children().first().clone().removeAttr("id").toggleClass("is--hidden",!1).toggleClass(this.containerItemClass,!0).appendTo($(this.containerElement));var container=this,containerItem=$("."+this.containerItemClass,this.containerElement).last();this.addRemoveEnabled?containerItem.find("."+this.removeButtonClass).click(function(eventData){var containerItem=container.createContainerItem(eventData.target);container.remove(containerItem)}):containerItem.find("."+this.removeButtonClass).addClass("is--disabled");var handlerItem=container.createContainerItem(containerItem);handlerItem.addEventListeners(),handlerItem.addHandler()},remove:function(item){var itemElement=$(item.element).closest("."+this.containerItemClass);this.createContainerItem(itemElement).removeHandler(),itemElement.remove()},getItemValues:function(){var values=[],container=this;return $("."+this.containerItemClass,this.containerElement).each(function(index,element){var containerItem=container.createContainerItem(element),fieldValues=containerItem.getFieldValues();values.push(fieldValues)}),values},getItem:function(index){var element=$("."+this.containerItemClass,this.containerElement).get(index);return void 0!==element?this.createContainerItem(element):null},getAllItems:function(){var container=this;return $("."+this.containerItemClass,this.containerElement).map(function(){return container.createContainerItem(this)})}},OpenAssessment.ItemUtilities={createUniqueName:function(selector,nameAttribute){for(var index=0;index<=selector.length;){if(0===selector.parent().find("*["+nameAttribute+"='"+index+"']").length)return index.toString();index++}return index.toString()},refreshOptionString:function(element){var points=$(element).attr("data-points"),label=$(element).attr("data-label"),name=$(element).val();""===label&&(label=gettext("Unnamed Option"));var singularString=label+" - "+points+" point",multipleString=label+" - "+points+" points",finalLabel="";finalLabel=""===name?gettext("Not Selected"):isNaN(points)?label:ngettext(singularString,multipleString,points),$(element).text(finalLabel)}},OpenAssessment.Prompt=function(element,notifier){this.element=element,this.notifier=notifier},OpenAssessment.Prompt.prototype={getFieldValues:function(){return{description:this.description()}},description:function(text){var sel=$(".openassessment_prompt_description",this.element);return OpenAssessment.Fields.stringField(sel,text)},addEventListeners:function(){},addHandler:function(){this.notifier.notificationFired("promptAdd",{index:this.element.index()})},removeHandler:function(){this.notifier.notificationFired("promptRemove",{index:this.element.index()})},updateHandler:function(){},validate:function(){return!0},validationErrors:function(){return[]},clearValidationErrors:function(){}},OpenAssessment.RubricOption=function(element,notifier){this.element=element,this.notifier=notifier,this.pointsField=new OpenAssessment.IntField($(".openassessment_criterion_option_points",this.element),{min:0,max:999})},OpenAssessment.RubricOption.prototype={addEventListeners:function(){$(this.element).focusout($.proxy(this.updateHandler,this))},getFieldValues:function(){var fields={label:this.label(),points:this.points(),explanation:this.explanation()},nameString=OpenAssessment.Fields.stringField($(".openassessment_criterion_option_name",this.element));return""!==nameString&&(fields.name=nameString),fields},label:function(label){var sel=$(".openassessment_criterion_option_label",this.element);return OpenAssessment.Fields.stringField(sel,label)},points:function(points){return void 0!==points&&this.pointsField.set(points),this.pointsField.get()},explanation:function(explanation){var sel=$(".openassessment_criterion_option_explanation",this.element);return OpenAssessment.Fields.stringField(sel,explanation)},addHandler:function(){var criterionElement=$(this.element).closest(".openassessment_criterion"),criterionName=$(criterionElement).data("criterion"),criterionLabel=$(".openassessment_criterion_label",criterionElement).val(),options=$(".openassessment_criterion_option",this.element.parent()),name=OpenAssessment.ItemUtilities.createUniqueName(options,"data-option");$(this.element).attr("data-criterion",criterionName).attr("data-option",name),$(".openassessment_criterion_option_name",this.element).attr("value",name);var fields=this.getFieldValues();this.notifier.notificationFired("optionAdd",{criterionName:criterionName,criterionLabel:criterionLabel,name:name,label:fields.label,points:fields.points})},removeHandler:function(){var criterionName=$(this.element).data("criterion"),optionName=$(this.element).data("option");this.notifier.notificationFired("optionRemove",{criterionName:criterionName,name:optionName})},updateHandler:function(){var fields=this.getFieldValues(),criterionName=$(this.element).data("criterion"),optionName=$(this.element).data("option"),optionLabel=fields.label,optionPoints=fields.points;this.notifier.notificationFired("optionUpdated",{criterionName:criterionName,name:optionName,label:optionLabel,points:optionPoints})},validate:function(){return this.pointsField.validate()},validationErrors:function(){return this.pointsField.validationErrors().length>0?["Option points are invalid"]:[]},clearValidationErrors:function(){this.pointsField.clearValidationErrors()}},OpenAssessment.RubricCriterion=function(element,notifier){this.element=element,this.notifier=notifier,this.labelSel=$(".openassessment_criterion_label",this.element),this.promptSel=$(".openassessment_criterion_prompt",this.element),this.optionContainer=new OpenAssessment.Container(OpenAssessment.RubricOption,{containerElement:$(".openassessment_criterion_option_list",this.element).get(0),templateElement:$("#openassessment_option_template").get(0),addButtonElement:$(".openassessment_criterion_add_option",this.element).get(0),removeButtonClass:"openassessment_criterion_option_remove_button",containerItemClass:"openassessment_criterion_option",notifier:this.notifier})},OpenAssessment.RubricCriterion.prototype={addEventListeners:function(){this.optionContainer.addEventListeners(),$(this.element).focusout($.proxy(this.updateHandler,this))},getFieldValues:function(){var fields={label:this.label(),prompt:this.prompt(),feedback:this.feedback(),options:this.optionContainer.getItemValues()},nameString=OpenAssessment.Fields.stringField($(".openassessment_criterion_name",this.element));return""!==nameString&&(fields.name=nameString),fields},label:function(label){return OpenAssessment.Fields.stringField(this.labelSel,label)},prompt:function(prompt){return OpenAssessment.Fields.stringField(this.promptSel,prompt)},feedback:function(){return $(".openassessment_criterion_feedback",this.element).val()},addOption:function(){this.optionContainer.add()},addHandler:function(){var criteria=$(".openassessment_criterion",this.element.parent()),name=OpenAssessment.ItemUtilities.createUniqueName(criteria,"data-criterion");$(this.element).attr("data-criterion",name),$(".openassessment_criterion_name",this.element).attr("value",name)},removeHandler:function(){var criterionName=$(this.element).data("criterion");this.notifier.notificationFired("criterionRemove",{criterionName:criterionName})},updateHandler:function(){var fields=this.getFieldValues(),criterionName=fields.name,criterionLabel=fields.label;this.notifier.notificationFired("criterionUpdated",{criterionName:criterionName,criterionLabel:criterionLabel})},validate:function(){var isValid=""!==this.prompt();return isValid||this.promptSel.addClass("openassessment_highlighted_field"),$.each(this.optionContainer.getAllItems(),function(){isValid=this.validate()&&isValid}),isValid},validationErrors:function(){var errors=[];return this.promptSel.hasClass("openassessment_highlighted_field")&&errors.push("Criterion prompt is invalid."),$.each(this.optionContainer.getAllItems(),function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){this.promptSel.removeClass("openassessment_highlighted_field"),$.each(this.optionContainer.getAllItems(),function(){this.clearValidationErrors()})}},OpenAssessment.TrainingExample=function(element){this.element=element,this.criteria=$(".openassessment_training_example_criterion_option",this.element),this.answer=$(".openassessment_training_example_essay_part textarea",this.element)},OpenAssessment.TrainingExample.prototype={getFieldValues:function(){var optionsSelected=this.criteria.map(function(){return{criterion:$(this).data("criterion"),option:$(this).prop("value")}}).get();return{answer:this.answer.map(function(){return $(this).prop("value")}).get(),options_selected:optionsSelected}},addHandler:function(){$(".openassessment_training_example_criterion_option",this.element).each(function(){$("option",this).each(function(){OpenAssessment.ItemUtilities.refreshOptionString($(this))})})},addEventListeners:function(){},removeHandler:function(){},updateHandler:function(){},validate:function(){var isValid=!0;return this.criteria.each(function(){var isOptionValid=""!==$(this).prop("value");isValid=isOptionValid&&isValid,isOptionValid||$(this).addClass("openassessment_highlighted_field")}),isValid},validationErrors:function(){var errors=[];return this.criteria.each(function(){$(this).hasClass("openassessment_highlighted_field")&&errors.push("Student training example is invalid.")}),errors},clearValidationErrors:function(){this.criteria.each(function(){$(this).removeClass("openassessment_highlighted_field")})}},OpenAssessment.StudioView=function(runtime,element,server,data){this.element=element,this.runtime=runtime,this.server=server,this.data=data,this.fixModalHeight(),this.initializeTabs(),this.alert=(new OpenAssessment.ValidationAlert).install();var studentTrainingListener=new OpenAssessment.StudentTrainingListener;this.promptsView=new OpenAssessment.EditPromptsView($("#oa_prompts_editor_wrapper",this.element).get(0),new OpenAssessment.Notifier([studentTrainingListener]));var staffAssessmentView=new OpenAssessment.EditStaffAssessmentView($("#oa_staff_assessment_editor",this.element).get(0)),studentTrainingView=new OpenAssessment.EditStudentTrainingView($("#oa_student_training_editor",this.element).get(0)),peerAssessmentView=new OpenAssessment.EditPeerAssessmentView($("#oa_peer_assessment_editor",this.element).get(0)),selfAssessmentView=new OpenAssessment.EditSelfAssessmentView($("#oa_self_assessment_editor",this.element).get(0)),assessmentLookupDictionary={};assessmentLookupDictionary[staffAssessmentView.getID()]=staffAssessmentView,assessmentLookupDictionary[studentTrainingView.getID()]=studentTrainingView,assessmentLookupDictionary[peerAssessmentView.getID()]=peerAssessmentView,assessmentLookupDictionary[selfAssessmentView.getID()]=selfAssessmentView,this.settingsView=new OpenAssessment.EditSettingsView($("#oa_basic_settings_editor",this.element).get(0),assessmentLookupDictionary,data),this.rubricView=new OpenAssessment.EditRubricView($("#oa_rubric_editor_wrapper",this.element).get(0),new OpenAssessment.Notifier([studentTrainingListener])),$(".openassessment_save_button",this.element).click($.proxy(this.save,this)),$(".openassessment_cancel_button",this.element).click($.proxy(this.cancel,this))},OpenAssessment.StudioView.prototype={fixModalHeight:function(){$(this.element).addClass("openassessment_full_height").parentsUntil(".modal-window").addClass("openassessment_full_height"),$(this.element).closest(".modal-window").addClass("openassessment_modal_window")},initializeTabs:function(){void 0===OpenAssessment.lastOpenEditingTab&&(OpenAssessment.lastOpenEditingTab=2),$(".openassessment_editor_content_and_tabs",this.element).tabs({active:OpenAssessment.lastOpenEditingTab})},saveTabState:function(){var tabElement=$(".openassessment_editor_content_and_tabs",this.element);OpenAssessment.lastOpenEditingTab=tabElement.tabs("option","active")},save:function(){var view=this;this.saveTabState(),this.clearValidationErrors(),this.validate()?(this.alert.hide(),this.server.checkReleased().done(function(isReleased){isReleased?view.confirmPostReleaseUpdate($.proxy(view.updateEditorContext,view)):view.updateEditorContext()}).fail(function(errMsg){view.showError(errMsg)})):this.alert.setMessage(gettext("Couldn't Save This Assignment"),gettext("Please correct the outlined fields.")).show()},confirmPostReleaseUpdate:function(onConfirm){var msg=gettext("This problem has already been released. Any changes will apply only to future assessments.");confirm(msg)&&onConfirm()},updateEditorContext:function(){this.runtime.notify("save",{state:"start"});var view=this,fileUploadType=view.settingsView.fileUploadType();this.server.updateEditorContext({prompts:view.promptsView.promptsDefinition(),feedbackPrompt:view.rubricView.feedbackPrompt(),feedback_default_text:view.rubricView.feedback_default_text(),criteria:view.rubricView.criteriaDefinition(),title:view.settingsView.displayName(),submissionStart:view.settingsView.submissionStart(),submissionDue:view.settingsView.submissionDue(),assessments:view.settingsView.assessmentsDescription(),textResponse:view.settingsView.textResponseNecessity(),fileUploadResponse:view.settingsView.fileUploadResponseNecessity(),fileUploadType:""!==fileUploadType?fileUploadType:null,fileTypeWhiteList:view.settingsView.fileTypeWhiteList(),latexEnabled:view.settingsView.latexEnabled(),leaderboardNum:view.settingsView.leaderboardNum(),editorAssessmentsOrder:view.settingsView.editorAssessmentsOrder()}).done(function(){view.runtime.notify("save",{state:"end"})}).fail(function(msg){view.showError(msg)})},cancel:function(){this.saveTabState(),this.runtime.notify("cancel",{})},showError:function(errorMsg){this.runtime.notify("error",{msg:errorMsg})},validate:function(){var settingsValid=this.settingsView.validate(),rubricValid=this.rubricView.validate(),promptsValid=this.promptsView.validate();return settingsValid&&rubricValid&&promptsValid},validationErrors:function(){return this.settingsView.validationErrors().concat(this.rubricView.validationErrors().concat(this.promptsView.validationErrors()))},clearValidationErrors:function(){this.settingsView.clearValidationErrors(),this.rubricView.clearValidationErrors(),this.promptsView.clearValidationErrors()}},OpenAssessment.EditPeerAssessmentView=function(element){this.element=element,this.name="peer-assessment",this.mustGradeField=new OpenAssessment.IntField($("#peer_assessment_must_grade",this.element),{min:0,max:99}),this.mustBeGradedByField=new OpenAssessment.IntField($("#peer_assessment_graded_by",this.element),{min:0,max:99}),new OpenAssessment.ToggleControl($("#include_peer_assessment",this.element),$("#peer_assessment_settings_editor",this.element),$("#peer_assessment_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.startDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#peer_assessment_start_date","#peer_assessment_start_time").install(),this.dueDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#peer_assessment_due_date","#peer_assessment_due_time").install()},OpenAssessment.EditPeerAssessmentView.prototype={description:function(){return{must_grade:this.mustGradeNum(),must_be_graded_by:this.mustBeGradedByNum(),start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_peer_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_peer_assessment",this.element).click()},mustGradeNum:function(num){return void 0!==num&&this.mustGradeField.set(num),this.mustGradeField.get()},mustBeGradedByNum:function(num){return void 0!==num&&this.mustBeGradedByField.set(num),this.mustBeGradedByField.get()},startDatetime:function(dateString,timeString){return this.startDatetimeControl.datetime(dateString,timeString)},dueDatetime:function(dateString,timeString){return this.dueDatetimeControl.datetime(dateString,timeString)},getID:function(){return $(this.element).attr("id")},validate:function(){var startValid=this.startDatetimeControl.validate(),dueValid=this.dueDatetimeControl.validate(),mustGradeValid=this.mustGradeField.validate(),mustBeGradedByValid=this.mustBeGradedByField.validate();return startValid&&dueValid&&mustGradeValid&&mustBeGradedByValid},validationErrors:function(){var errors=[];return this.startDatetimeControl.validationErrors().length>0&&errors.push("Peer assessment start is invalid"),this.dueDatetimeControl.validationErrors().length>0&&errors.push("Peer assessment due is invalid"),this.mustGradeField.validationErrors().length>0&&errors.push("Peer assessment must grade is invalid"),this.mustBeGradedByField.validationErrors().length>0&&errors.push("Peer assessment must be graded by is invalid"),errors},clearValidationErrors:function(){this.startDatetimeControl.clearValidationErrors(),this.dueDatetimeControl.clearValidationErrors(),this.mustGradeField.clearValidationErrors(),this.mustBeGradedByField.clearValidationErrors()}},OpenAssessment.EditSelfAssessmentView=function(element){this.element=element,this.name="self-assessment",new OpenAssessment.ToggleControl($("#include_self_assessment",this.element),$("#self_assessment_settings_editor",this.element),$("#self_assessment_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.startDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#self_assessment_start_date","#self_assessment_start_time").install(),this.dueDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#self_assessment_due_date","#self_assessment_due_time").install()},OpenAssessment.EditSelfAssessmentView.prototype={description:function(){return{start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_self_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_self_assessment",this.element).click()},startDatetime:function(dateString,timeString){return this.startDatetimeControl.datetime(dateString,timeString)},dueDatetime:function(dateString,timeString){return this.dueDatetimeControl.datetime(dateString,timeString)},getID:function(){return $(this.element).attr("id")},validate:function(){var startValid=this.startDatetimeControl.validate(),dueValid=this.dueDatetimeControl.validate();return startValid&&dueValid},validationErrors:function(){var errors=[];return this.startDatetimeControl.validationErrors().length>0&&errors.push("Self assessment start is invalid"),this.dueDatetimeControl.validationErrors().length>0&&errors.push("Self assessment due is invalid"),errors},clearValidationErrors:function(){this.startDatetimeControl.clearValidationErrors(),this.dueDatetimeControl.clearValidationErrors()}},OpenAssessment.EditStudentTrainingView=function(element){this.element=element,this.name="student-training",new OpenAssessment.ToggleControl($("#include_student_training",this.element),$("#student_training_settings_editor",this.element),$("#student_training_description_closed",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.exampleContainer=new OpenAssessment.Container(OpenAssessment.TrainingExample,{containerElement:$("#openassessment_training_example_list",this.element).get(0),templateElement:$("#openassessment_training_example_template",this.element).get(0),addButtonElement:$(".openassessment_add_training_example",this.element).get(0),removeButtonClass:"openassessment_training_example_remove",containerItemClass:"openassessment_training_example"}),this.exampleContainer.addEventListeners()},OpenAssessment.EditStudentTrainingView.prototype={description:function(){return{examples:this.exampleContainer.getItemValues()}},isEnabled:function(isEnabled){var sel=$("#include_student_training",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_student_training",this.element).click()},getID:function(){return $(this.element).attr("id")},validate:function(){var isValid=!0;return $.each(this.exampleContainer.getAllItems(),function(){isValid=this.validate()&&isValid}),isValid},validationErrors:function(){var errors=[];return $.each(this.exampleContainer.getAllItems(),function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){ +$.each(this.exampleContainer.getAllItems(),function(){this.clearValidationErrors()})},addTrainingExample:function(){this.exampleContainer.add()}},OpenAssessment.EditStaffAssessmentView=function(element){this.element=element,this.name="staff-assessment",new OpenAssessment.ToggleControl($("#include_staff_assessment",this.element),$("#staff_assessment_description",this.element),$("#staff_assessment_description",this.element),new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install()},OpenAssessment.EditStaffAssessmentView.prototype={description:function(){return{required:this.isEnabled()}},isEnabled:function(isEnabled){var sel=$("#include_staff_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},toggleEnabled:function(){$("#include_staff_assessment",this.element).click()},getID:function(){return $(this.element).attr("id")},validate:function(){return!0},validationErrors:function(){return[]},clearValidationErrors:function(){}},OpenAssessment.Fields={stringField:function(sel,value){return void 0!==value&&sel.val(value),sel.val()},booleanField:function(sel,value){return void 0!==value&&sel.prop("checked",value),sel.prop("checked")}},OpenAssessment.IntField=function(inputSel,restrictions){this.max=restrictions.max,this.min=restrictions.min,this.input=$(inputSel)},OpenAssessment.IntField.prototype={get:function(){return parseInt(this.input.val().trim(),10)},set:function(val){this.input.val(val)},validate:function(){var value=this.get(),isValid=!isNaN(value)&&value>=this.min&&value<=this.max;return-1!==this.input.val().indexOf(".")&&(isValid=!1),isValid||this.input.addClass("openassessment_highlighted_field"),isValid},clearValidationErrors:function(){this.input.removeClass("openassessment_highlighted_field")},validationErrors:function(){return this.input.hasClass("openassessment_highlighted_field")?["Int field is invalid"]:[]}},OpenAssessment.ToggleControl=function(checkboxSel,shownSel,hiddenSel,notifier){this.checkbox=checkboxSel,this.shownSection=shownSel,this.hiddenSection=hiddenSel,this.notifier=notifier},OpenAssessment.ToggleControl.prototype={install:function(){return this.checkbox.change(this,function(event){var control=event.data;this.checked?(control.notifier.notificationFired("toggleOn",{}),control.show()):(control.notifier.notificationFired("toggleOff",{}),control.hide())}),this},show:function(){this.hiddenSection.addClass("is--hidden"),this.shownSection.removeClass("is--hidden")},hide:function(){this.shownSection.addClass("is--hidden"),this.hiddenSection.removeClass("is--hidden")}},OpenAssessment.DatetimeControl=function(element,datePicker,timePicker){this.element=element,this.datePicker=datePicker,this.timePicker=timePicker},OpenAssessment.DatetimeControl.prototype={install:function(){var dateString=$(this.datePicker,this.element).val();return $(this.datePicker,this.element).datepicker({showButtonPanel:!0}).datepicker("option","dateFormat","yy-mm-dd").datepicker("setDate",dateString),$(this.timePicker,this.element).timepicker({timeFormat:"H:i",step:60}),this},datetime:function(dateString,timeString){var datePickerSel=$(this.datePicker,this.element),timePickerSel=$(this.timePicker,this.element);return void 0!==dateString&&datePickerSel.val(dateString),void 0!==timeString&&timePickerSel.val(timeString),datePickerSel.val()+"T"+timePickerSel.val()},validate:function(){var dateString=$(this.datePicker,this.element).val(),timeString=$(this.timePicker,this.element).val(),isDateValid=!1;try{isDateValid=$.datepicker.parseDate($.datepicker.ISO_8601,dateString)instanceof Date}catch(err){}isDateValid||$(this.datePicker,this.element).addClass("openassessment_highlighted_field");var matches=timeString.match(/^\d{2}:\d{2}$/g),isTimeValid=null!==matches;return isTimeValid||$(this.timePicker,this.element).addClass("openassessment_highlighted_field"),isDateValid&&isTimeValid},clearValidationErrors:function(){$(this.datePicker,this.element).removeClass("openassessment_highlighted_field"),$(this.timePicker,this.element).removeClass("openassessment_highlighted_field")},validationErrors:function(){var errors=[],dateHasError=$(this.datePicker,this.element).hasClass("openassessment_highlighted_field"),timeHasError=$(this.timePicker,this.element).hasClass("openassessment_highlighted_field");return dateHasError&&errors.push("Date is invalid"),timeHasError&&errors.push("Time is invalid"),errors}},OpenAssessment.SelectControl=function(selectSel,mapping,notifier){this.select=selectSel,this.mapping=mapping,this.notifier=notifier},OpenAssessment.SelectControl.prototype={install:function(){return this.select.change(this,function(event){var control=event.data;control.notifier.notificationFired("selectionChanged",{selected:this.value}),control.change(this.value)}),this},change:function(selected){$.isFunction(this.mapping)?this.mapping(selected):$.each(this.mapping,function(option,sel){option===selected?sel.removeClass("is--hidden"):sel.addClass("is--hidden")})}},OpenAssessment.InputControl=function(inputSel,validator){this.input=$(inputSel),this.validator=validator,this.errors=[]},OpenAssessment.InputControl.prototype={get:function(){return this.input.val()},set:function(val){this.input.val(val)},validate:function(){return this.errors=this.validator(this.get()),this.errors.length&&(this.input.addClass("openassessment_highlighted_field"),this.input.parent().nextAll(".message-status").text(this.errors.join(";")),this.input.parent().nextAll(".message-status").addClass("is-shown")),0===this.errors.length},clearValidationErrors:function(){this.input.removeClass("openassessment_highlighted_field"),this.input.parent().nextAll(".message-status").removeClass("is-shown")},validationErrors:function(){return this.errors}},OpenAssessment.StudentTrainingListener=function(){this.element=$("#oa_student_training_editor"),this.alert=new OpenAssessment.ValidationAlert},OpenAssessment.StudentTrainingListener.prototype={promptAdd:function(){var view=this.element;$("#openassessment_training_example_part_template").children().first().clone().removeAttr("id").toggleClass("is--hidden",!1).appendTo(".openassessment_training_example_essay",view)},promptRemove:function(data){var view=this.element;$(".openassessment_training_example_essay li:nth-child("+(data.index+1)+")",view).remove()},optionUpdated:function(data){this._optionSel(data.criterionName).each(function(){var criterion=this,option=$('option[value="'+data.name+'"]',criterion).attr("data-points",data.points).attr("data-label",data.label);OpenAssessment.ItemUtilities.refreshOptionString(option)})},optionAdd:function(data){var criterionAdded=!1;0===this._optionSel(data.criterionName).length&&(this.criterionAdd(data),criterionAdded=!0),this._optionSel(data.criterionName).each(function(){var criterion=this,option=$("").attr("value",data.name).attr("data-points",data.points).attr("data-label",data.label);OpenAssessment.ItemUtilities.refreshOptionString(option),$(criterion).append(option)}),criterionAdded&&this.displayAlertMsg(gettext("Criterion Added"),gettext("You have added a criterion. You will need to select an option for the criterion in the Learner Training step. To do this, click the Settings tab."))},optionRemove:function(data){var handler=this,invalidated=!1;this._optionSel(data.criterionName).each(function(){var criterionOption=this;$(criterionOption).val()===data.name.toString()&&($(criterionOption).val("").addClass("openassessment_highlighted_field").click(function(){$(criterionOption).removeClass("openassessment_highlighted_field")}),invalidated=!0),$('option[value="'+data.name+'"]',criterionOption).remove(),1===$("option",criterionOption).length&&(handler.removeAllOptions(data),invalidated=!1)}),invalidated&&this.displayAlertMsg(gettext("Option Deleted"),gettext("You have deleted an option. That option has been removed from its criterion in the sample responses in the Learner Training step. You might have to select a new option for the criterion."))},_optionSel:function(criterionName){return $('.openassessment_training_example_criterion_option[data-criterion="'+criterionName+'"]',this.element)},removeAllOptions:function(data){var changed=!1;$(".openassessment_training_example_criterion",this.element).each(function(){var criterion=this;$(criterion).data("criterion")===data.criterionName&&($(criterion).remove(),changed=!0)}),changed&&this.displayAlertMsg(gettext("Option Deleted"),gettext("You have deleted all the options for this criterion. The criterion has been removed from the sample responses in the Learner Training step."))},criterionRemove:function(data){var changed=!1,sel='.openassessment_training_example_criterion[data-criterion="'+data.criterionName+'"]';$(sel,this.element).each(function(){$(this).remove(),changed=!0}),changed&&this.displayAlertMsg(gettext("Criterion Deleted"),gettext("You have deleted a criterion. The criterion has been removed from the example responses in the Learner Training step."))},displayAlertMsg:function(title,msg){$("#include_student_training",this.element).is(":checked")&&$(".openassessment_training_example",this.element).length>1&&this.alert.setMessage(title,msg).show()},criterionUpdated:function(data){var sel='.openassessment_training_example_criterion[data-criterion="'+data.criterionName+'"]';$(sel,this.element).each(function(){$(".openassessment_training_example_criterion_name_wrapper",this).text(data.criterionLabel)})},criterionAdd:function(data){var view=this.element,criterion=$("#openassessment_training_example_criterion_template").children().first().clone().removeAttr("id").attr("data-criterion",data.criterionName).toggleClass("is--hidden",!1).appendTo(".openassessment_training_example_criteria_selections",view);criterion.find(".openassessment_training_example_criterion_option").attr("data-criterion",data.criterionName),criterion.find(".openassessment_training_example_criterion_name_wrapper").text(data.label)},examplesCriteriaLabels:function(){var examples=[];return $(".openassessment_training_example_criteria_selections",this.element).each(function(){var exampleDescription={};$(".openassessment_training_example_criterion",this).each(function(){var criterionName=$(this).data("criterion"),criterionLabel=$(".openassessment_training_example_criterion_name_wrapper",this).text().trim();exampleDescription[criterionName]=criterionLabel}),examples.push(exampleDescription)}),examples},examplesOptionsLabels:function(){var examples=[];return $(".openassessment_training_example_criteria_selections",this.element).each(function(){var exampleDescription={};$(".openassessment_training_example_criterion_option",this).each(function(){var criterionName=$(this).data("criterion");exampleDescription[criterionName]={},$("option",this).each(function(){var optionName=$(this).val(),optionLabel=$(this).text().trim();exampleDescription[criterionName][optionName]=optionLabel})}),examples.push(exampleDescription)}),examples}},OpenAssessment.AssessmentToggleListener=function(){this.alert=new OpenAssessment.ValidationAlert},OpenAssessment.AssessmentToggleListener.prototype={toggleOff:function(){this.alert.setMessage(gettext("Warning"),gettext("Changes to steps that are not selected as part of the assignment will not be saved.")).show()},toggleOn:function(){this.alert.hide()}},OpenAssessment.Notifier=function(listeners){this.listeners=listeners},OpenAssessment.Notifier.prototype={notificationFired:function(name,data){for(var i=0;i0;return isValid||this.criterionAddButton.addClass("openassessment_highlighted_field").click(function(){$(this).removeClass("openassessment_highlighted_field")}),$.each(criteria,function(){isValid=this.validate()&&isValid}),isValid},validationErrors:function(){var errors=[];return this.criterionAddButton.hasClass("openassessment_highlighted_field")&&errors.push("The rubric must contain at least one criterion"),$.each(this.getAllCriteria(),function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){this.criterionAddButton.removeClass("openassessment_highlighted_field"),$.each(this.getAllCriteria(),function(){this.clearValidationErrors()})}},OpenAssessment.EditSettingsView=function(element,assessmentViews,data){var self=this;this.settingsElement=element,this.assessmentsElement=$(element).siblings("#openassessment_assessment_module_settings_editors").get(0),this.assessmentViews=assessmentViews,this.startDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#openassessment_submission_start_date","#openassessment_submission_start_time").install(),this.dueDatetimeControl=new OpenAssessment.DatetimeControl(this.element,"#openassessment_submission_due_date","#openassessment_submission_due_time").install(),new OpenAssessment.SelectControl($("#openassessment_submission_file_upload_response",this.element),function(selectedValue){var el=$("#openassessment_submission_file_upload_type_wrapper",self.element);selectedValue?el.removeClass("is--hidden"):el.addClass("is--hidden")},new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),new OpenAssessment.SelectControl($("#openassessment_submission_upload_selector",this.element),{custom:$("#openassessment_submission_white_listed_file_types_wrapper",this.element)},new OpenAssessment.Notifier([new OpenAssessment.AssessmentToggleListener])).install(),this.leaderboardIntField=new OpenAssessment.IntField($("#openassessment_leaderboard_editor",this.element),{min:0,max:100}),this.fileTypeWhiteListInputField=new OpenAssessment.InputControl($("#openassessment_submission_white_listed_file_types",this.element),function(value){var badExts=[],errors=[];if(!value)return errors.push(gettext("File types can not be empty.")),errors;var whiteList=$.map(value.replace(/\./g,"").toLowerCase().split(","),$.trim);return $.each(whiteList,function(index,ext){-1!==data.FILE_EXT_BLACK_LIST.indexOf(ext)&&badExts.push(ext)}),badExts.length&&errors.push(gettext("The following file types are not allowed: ")+badExts.join(",")),errors}),this.initializeSortableAssessments()},OpenAssessment.EditSettingsView.prototype={initializeSortableAssessments:function(){var view=this;$("#openassessment_assessment_module_settings_editors",view.element).sortable({start:function(event,ui){$(".openassessment_assessment_module_editor",view.element).hide();ui.placeholder.height("auto"),ui.helper.height("auto"),$("#openassessment_assessment_module_settings_editors",view.element).sortable("refresh").sortable("refreshPositions")},stop:function(){$(".openassessment_assessment_module_editor",view.element).show()},snap:!0,axis:"y",handle:".drag-handle",cursorAt:{top:20}}),$("#openassessment_assessment_module_settings_editors .drag-handle",view.element).disableSelection()},displayName:function(name){var sel=$("#openassessment_title_editor",this.settingsElement);return OpenAssessment.Fields.stringField(sel,name)},submissionStart:function(dateString,timeString){return this.startDatetimeControl.datetime(dateString,timeString)},submissionDue:function(dateString,timeString){return this.dueDatetimeControl.datetime(dateString,timeString)},textResponseNecessity:function(value){var sel=$("#openassessment_submission_text_response",this.settingsElement);return void 0!==value&&sel.val(value),sel.val()},fileUploadResponseNecessity:function(value,triggerChange){var sel=$("#openassessment_submission_file_upload_response",this.settingsElement);return void 0!==value&&(triggerChange=triggerChange||!1,sel.val(value),triggerChange&&$(sel).trigger("change")),sel.val()},fileUploadType:function(uploadType){var fileUploadTypeWrapper=$("#openassessment_submission_file_upload_type_wrapper",this.settingsElement);if(!$(fileUploadTypeWrapper).hasClass("is--hidden")){var sel=$("#openassessment_submission_upload_selector",this.settingsElement);return void 0!==uploadType&&sel.val(uploadType),sel.val()}return""},fileTypeWhiteList:function(exts){return void 0!==exts&&this.fileTypeWhiteListInputField.set(exts),this.fileTypeWhiteListInputField.get()},latexEnabled:function(isEnabled){var sel=$("#openassessment_submission_latex_editor",this.settingsElement);return void 0!==isEnabled&&(isEnabled?sel.val(1):sel.val(0)),"1"===sel.val()},leaderboardNum:function(num){return void 0!==num&&this.leaderboardIntField.set(num),this.leaderboardIntField.get(num)},assessmentsDescription:function(){var assessmentDescList=[],view=this;return $(".openassessment_assessment_module_settings_editor",this.assessmentsElement).each(function(){var asmntView=view.assessmentViews[$(this).attr("id")];if(asmntView.isEnabled()){var description=asmntView.description();description.name=asmntView.name,assessmentDescList.push(description)}}),assessmentDescList},editorAssessmentsOrder:function(){var editorAssessments=[],view=this;return $(".openassessment_assessment_module_settings_editor",this.assessmentsElement).each(function(){var asmntView=view.assessmentViews[$(this).attr("id")];editorAssessments.push(asmntView.name)}),editorAssessments},validate:function(){var isValid=!0;return isValid=this.startDatetimeControl.validate()&&isValid,isValid=this.dueDatetimeControl.validate()&&isValid,isValid=this.leaderboardIntField.validate()&&isValid,"custom"===this.fileUploadType()?isValid=this.fileTypeWhiteListInputField.validate()&&isValid:this.fileTypeWhiteListInputField.get()&&!this.fileTypeWhiteListInputField.validate()&&this.fileTypeWhiteListInputField.set(""),$.each(this.assessmentViews,function(){this.isEnabled()&&(isValid=this.validate()&&isValid)}),isValid},validationErrors:function(){var errors=[];return this.startDatetimeControl.validationErrors().length>0&&errors.push("Submission start is invalid"),this.dueDatetimeControl.validationErrors().length>0&&errors.push("Submission due is invalid"),this.leaderboardIntField.validationErrors().length>0&&errors.push("Leaderboard number is invalid"),this.fileTypeWhiteListInputField.validationErrors().length>0&&(errors=errors.concat(this.fileTypeWhiteListInputField.validationErrors())),$.each(this.assessmentViews,function(){errors=errors.concat(this.validationErrors())}),errors},clearValidationErrors:function(){this.startDatetimeControl.clearValidationErrors(),this.dueDatetimeControl.clearValidationErrors(),this.leaderboardIntField.clearValidationErrors(),this.fileTypeWhiteListInputField.clearValidationErrors(),$.each(this.assessmentViews,function(){this.clearValidationErrors()})}},OpenAssessment.ValidationAlert=function(){this.element=$("#openassessment_validation_alert"),this.editorElement=$(this.element).parent(),this.title=$(".openassessment_alert_title",this.element),this.message=$(".openassessment_alert_message",this.element),this.closeButton=$(".openassessment_alert_close",this.element),this.ALERT_YELLOW="rgb(192, 172, 0)",this.DARK_GREY="#323232"},OpenAssessment.ValidationAlert.prototype={install:function(){var alert=this;return this.closeButton.click(function(eventObject){eventObject.preventDefault(),alert.hide()}),this},hide:function(){var headerHeight=$("#openassessment_editor_header",this.editorElement).outerHeight();this.element.addClass("covered");var styles={height:"Calc(100% - "+headerHeight+"px)","border-top-right-radius":"3px","border-top-left-radius":"3px"};return $(".oa_editor_content_wrapper",this.editorElement).each(function(){$(this).css(styles)}),this},show:function(){var view=this;if(this.isVisible())$(this.element).animate({"background-color":view.ALERT_YELLOW},300,"swing",function(){$(this).animate({"background-color":view.DARK_GREY},700,"swing")});else{this.element.removeClass("covered");var alertHeight=this.element.outerHeight(),headerHeight=$("#openassessment_editor_header",this.editorElement).outerHeight(),heightString="Calc(100% - "+(alertHeight+headerHeight)+"px)",styles={height:heightString,"border-top-right-radius":"0px","border-top-left-radius":"0px"};$(".oa_editor_content_wrapper",this.editorElement).each(function(){$(this).css(styles),$(this).scrollTop($(this).scrollTop()+alertHeight)})}return this},setMessage:function(newTitle,newMessage){return this.title.text(newTitle),this.message.text(newMessage),this},isVisible:function(){return!this.element.hasClass("covered")},getTitle:function(){return this.title.text()},getMessage:function(){return this.message.text()}},function(a,b){"function"==typeof define&&define.amd?define(["underscore","backbone"],function(c,d){return a.Backgrid=b(c,d)}):"object"==typeof exports?module.exports=b(require("underscore"),require("backbone")):a.Backgrid=b(a._,a.Backbone)}(this,function(a,b){"use strict";function c(a,b,c){var d=b-(a+"").length;d=d<0?0:d;for(var e="",f=0;f20)throw new RangeError("decimals must be between 0 and 20")};k.prototype=new j,a.extend(k.prototype,{defaults:{decimals:2,decimalSeparator:".",orderSeparator:","},HUMANIZED_NUM_RE:/(\d)(?=(?:\d{3})+$)/g,fromRaw:function(b,c){if(a.isNull(b)||a.isUndefined(b))return"";b=parseFloat(b).toFixed(~~this.decimals);var d=b.split("."),e=d[0],f=d[1]?(this.decimalSeparator||".")+d[1]:"";return e.replace(this.HUMANIZED_NUM_RE,"$1"+this.orderSeparator)+f},toRaw:function(b,c){if(""===(b=b.trim()))return null;for(var d="",e=b.split(this.orderSeparator),f=0;f",{tabIndex:-1,href:a,title:this.title||b,target:this.target}).text(b)),this.delegateEvents(),this}}),v=(h.EmailCell=t.extend({className:"email-cell",formatter:o,render:function(){this.$el.empty();var a=this.model,b=this.formatter.fromRaw(a.get(this.column.get("name")),a);return this.$el.append(g("",{tabIndex:-1,href:"mailto:"+b,title:b}).text(b)),this.delegateEvents(),this}}),h.NumberCell=s.extend({className:"number-cell",decimals:k.prototype.defaults.decimals,decimalSeparator:k.prototype.defaults.decimalSeparator,orderSeparator:k.prototype.defaults.orderSeparator,formatter:k,initialize:function(a){v.__super__.initialize.apply(this,arguments);var b=this.formatter;b.decimals=this.decimals,b.decimalSeparator=this.decimalSeparator,b.orderSeparator=this.orderSeparator}})),w=(h.IntegerCell=v.extend({className:"integer-cell",decimals:0}),h.PercentCell=v.extend({className:"percent-cell",multiplier:l.prototype.defaults.multiplier,symbol:l.prototype.defaults.symbol,formatter:l,initialize:function(){w.__super__.initialize.apply(this,arguments);var a=this.formatter;a.multiplier=this.multiplier,a.symbol=this.symbol}})),x=h.DatetimeCell=s.extend({className:"datetime-cell",includeDate:m.prototype.defaults.includeDate,includeTime:m.prototype.defaults.includeTime,includeMilli:m.prototype.defaults.includeMilli,formatter:m,initialize:function(b){x.__super__.initialize.apply(this,arguments);var c=this.formatter;c.includeDate=this.includeDate,c.includeTime=this.includeTime,c.includeMilli=this.includeMilli;var d=this.includeDate?"YYYY-MM-DD":"";d+=this.includeDate&&this.includeTime?"T":"",d+=this.includeTime?"HH:mm:ss":"",d+=this.includeTime&&this.includeMilli?".SSS":"",this.editor=this.editor.extend({attributes:a.extend({},this.editor.prototype.attributes,this.editor.attributes,{placeholder:d})})}}),y=(h.DateCell=x.extend({className:"date-cell",includeTime:!1}),h.TimeCell=x.extend({className:"time-cell",includeDate:!1}),h.BooleanCellEditor=q.extend({tagName:"input",attributes:{tabIndex:-1,type:"checkbox"},events:{mousedown:function(){this.mouseDown=!0},blur:"enterOrExitEditMode",mouseup:function(){this.mouseDown=!1},change:"saveOrCancel",keydown:"saveOrCancel"},render:function(){var a=this.model,b=this.formatter.fromRaw(a.get(this.column.get("name")),a);return this.$el.prop("checked",b),this},enterOrExitEditMode:function(a){if(!this.mouseDown){var b=this.model;b.trigger("backgrid:edited",b,this.column,new i(a))}},saveOrCancel:function(a){var b=this.model,c=this.column,d=this.formatter,e=new i(a);if(e.passThru()&&"change"!=a.type)return!0;e.cancel()&&(a.stopPropagation(),b.trigger("backgrid:edited",b,c,e));var f=this.$el;if(e.save()||e.moveLeft()||e.moveRight()||e.moveUp()||e.moveDown()){a.preventDefault(),a.stopPropagation();var g=d.toRaw(f.prop("checked"),b);b.set(c.get("name"),g),b.trigger("backgrid:edited",b,c,e)}else if("change"==a.type){var g=d.toRaw(f.prop("checked"),b);b.set(c.get("name"),g),f.focus()}}})),z=(h.BooleanCell=s.extend({className:"boolean-cell",editor:y,events:{click:"enterEditMode"},render:function(){this.$el.empty();var a=this.model,b=this.column,c=h.callByNeed(b.editable(),b,a);return this.$el.append(g("",{tabIndex:-1,type:"checkbox",checked:this.formatter.fromRaw(a.get(b.get("name")),a),disabled:!c})),this.delegateEvents(),this}}),h.SelectCellEditor=q.extend({tagName:"select",events:{change:"save",blur:"close",keydown:"close"},template:a.template('',null,{variable:null,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g}),setOptionValues:function(b){this.optionValues=b,this.optionValues=a.result(this,"optionValues")},setMultiple:function(a){this.multiple=a,this.$el.prop("multiple",a)},_renderOptions:function(b,c){for(var d="",e=0;e-1});return d},render:function(){this.$el.empty();var b=a.result(this,"optionValues"),c=this.model,d=this.formatter.fromRaw(c.get(this.column.get("name")),c);if(!a.isArray(b))throw new TypeError("optionValues must be an array");for(var e=null,f=null,e=null,h=null,i=null,j=0;j-1}));else{if(!a.isObject(e))throw new TypeError("optionValues elements must be a name-value pair or an object hash of { name: 'optgroup label', value: [option name-value pairs] }");h=e.name,i=g("",{label:h}),i.append(this._renderOptions.call(this,e.values,d)),this.$el.append(i)}}return this.delegateEvents(),this},save:function(a){var b=this.model,c=this.column;b.set(c.get("name"),this.formatter.toRaw(this.$el.val(),b))},close:function(a){var b=this.model,c=this.column,d=new i(a);d.cancel()?(a.stopPropagation(),b.trigger("backgrid:edited",b,c,new i(a))):(d.save()||d.moveLeft()||d.moveRight()||d.moveUp()||d.moveDown()||"blur"==a.type)&&(a.preventDefault(),a.stopPropagation(),this.save(a),b.trigger("backgrid:edited",b,c,new i(a)))}})),A=h.SelectCell=s.extend({className:"select-cell",editor:z,multiple:!1,formatter:p,optionValues:void 0,delimiter:", ",initialize:function(a){A.__super__.initialize.apply(this,arguments),this.listenTo(this.model,"backgrid:edit",function(a,b,c,d){b.get("name")==this.column.get("name")&&(d.setOptionValues(this.optionValues),d.setMultiple(this.multiple))})},render:function(){this.$el.empty();var b=a.result(this,"optionValues"),c=this.model,d=this.formatter.fromRaw(c.get(this.column.get("name")),c),e=[];try{if(!a.isArray(b)||a.isEmpty(b))throw new TypeError;for(var f=0;f|Array.<{name: string, values: Array.}>}");throw a}return this.delegateEvents(),this}}),B=h.Column=b.Model.extend({defaults:{name:void 0,label:void 0,sortable:!0,editable:!0,renderable:!0,formatter:void 0,sortType:"cycle",sortValue:void 0,direction:null,cell:void 0,headerCell:void 0},initialize:function(){this.has("label")||this.set({label:this.get("name")},{silent:!0});var a=h.resolveNameToClass(this.get("headerCell"),"HeaderCell"),b=h.resolveNameToClass(this.get("cell"),"Cell");this.set({cell:b,headerCell:a},{silent:!0})},sortValue:function(){var b=this.get("sortValue");return a.isString(b)?this[b]:a.isFunction(b)?b:function(a,b){return a.get(b)}}});a.each(["sortable","renderable","editable"],function(b){B.prototype[b]=function(){var c=this.get(b);return a.isString(c)?this[c]:a.isFunction(c)?c:!!c}});var C=h.Columns=b.Collection.extend({model:B}),D=h.Row=b.View.extend({tagName:"tr",initialize:function(a){var c=this.columns=a.columns;c instanceof b.Collection||(c=this.columns=new C(c));for(var d=this.cells=[],e=0;e").text(b.get("label")).append(""):document.createTextNode(b.get("label")),this.$el.append(a),this.$el.addClass(b.get("name")),this.$el.addClass(b.get("direction")),this.delegateEvents(),this}}),G=(h.HeaderRow=h.Row.extend({initialize:function(){h.Row.prototype.initialize.apply(this,arguments)},makeCell:function(a,b){var c=a.get("headerCell")||b.headerCell||F;return c=new c({column:a,collection:this.collection})}}),h.Header=b.View.extend({tagName:"thead",initialize:function(a){this.columns=a.columns,this.columns instanceof b.Collection||(this.columns=new C(this.columns)),this.row=new h.HeaderRow({columns:this.columns,collection:this.collection})},render:function(){return this.$el.append(this.row.render().$el),this.delegateEvents(),this},remove:function(){return this.row.remove.apply(this.row,arguments),b.View.prototype.remove.apply(this,arguments)}})),H=h.Body=b.View.extend({tagName:"tbody",initialize:function(a){this.columns=a.columns,this.columns instanceof b.Collection||(this.columns=new C(this.columns)),this.row=a.row||this.row||D,this.rows=this.collection.map(function(a){return new this.row({columns:this.columns,model:a})},this),this.emptyText=a.emptyText,this._unshiftEmptyRowMayBe();var c=this.collection;this.listenTo(c,"add",this.insertRow),this.listenTo(c,"remove",this.removeRow),this.listenTo(c,"sort",this.refresh),this.listenTo(c,"reset",this.refresh),this.listenTo(c,"backgrid:sort",this.sort),this.listenTo(c,"backgrid:edited",this.moveToNextCell),this.listenTo(this.columns,"add remove",this.updateEmptyRow)},_unshiftEmptyRowMayBe:function(){if(0===this.rows.length&&null!=this.emptyText)return this.emptyRow=new E({emptyText:this.emptyText,columns:this.columns}),this.rows.unshift(this.emptyRow),!0},insertRow:function(a,c,d){if(this.rows[0]instanceof E&&this.rows.pop().remove(),!(c instanceof b.Collection||d))return void this.collection.add(a,d=c);var e=new this.row({columns:this.columns,model:a}),f=c.indexOf(a);this.rows.splice(f,0,e);var g=this.$el,h=g.children(),i=e.render().$el;return f>=h.length?g.append(i):h.eq(f).before(i),this},removeRow:function(b,c,d){return d?((a.isUndefined(d.render)||d.render)&&this.rows[d.index].remove(),this.rows.splice(d.index,1),this._unshiftEmptyRowMayBe()&&this.render(),this):(this.collection.remove(b,d=c),void(this._unshiftEmptyRowMayBe()&&this.render()))},updateEmptyRow:function(){null!=this.emptyRow&&this.emptyRow.render()},refresh:function(){for(var a=0;a=0&&p").text(this.caption)),this.header&&this.$el.append(this.header.render().$el),this.footer&&this.$el.append(this.footer.render().$el),this.$el.append(this.body.render().$el),this.delegateEvents(),this.trigger("backgrid:rendered",this),this},remove:function(){return this.header&&this.header.remove.apply(this.header,arguments),this.body.remove.apply(this.body,arguments),this.footer&&this.footer.remove.apply(this.footer,arguments),b.View.prototype.remove.apply(this,arguments)}}),h}); diff --git a/openassessment/xblock/static/js/spec/oa_server.js b/openassessment/xblock/static/js/spec/oa_server.js index 6c32e4a713..aa0d4552a4 100644 --- a/openassessment/xblock/static/js/spec/oa_server.js +++ b/openassessment/xblock/static/js/spec/oa_server.js @@ -82,7 +82,6 @@ describe("OpenAssessment.Server", function() { "student_training", "peer_assessment", "self_assessment", - "example_based_assessment" ]; var TITLE = 'This is the title.'; diff --git a/openassessment/xblock/static/js/spec/studio/oa_edit.js b/openassessment/xblock/static/js/spec/studio/oa_edit.js index 7d2b3d5325..fb43aef4bb 100644 --- a/openassessment/xblock/static/js/spec/studio/oa_edit.js +++ b/openassessment/xblock/static/js/spec/studio/oa_edit.js @@ -117,7 +117,6 @@ describe("OpenAssessment.StudioView", function() { "student-training", "peer-assessment", "self-assessment", - "example-based-assessment", "staff-assessment" ] }; diff --git a/openassessment/xblock/static/js/spec/studio/oa_edit_assessments.js b/openassessment/xblock/static/js/spec/studio/oa_edit_assessments.js index 9d00e3a665..a2f5a71961 100644 --- a/openassessment/xblock/static/js/spec/studio/oa_edit_assessments.js +++ b/openassessment/xblock/static/js/spec/studio/oa_edit_assessments.js @@ -238,19 +238,6 @@ describe("OpenAssessment edit assessment views", function() { }); }); - describe("OpenAssessment.EditExampleBasedAssessmentView", function() { - var view = null; - - beforeEach(function() { - var element = $("#oa_ai_assessment_editor").get(0); - view = new OpenAssessment.EditExampleBasedAssessmentView(element); - }); - - it("Enables and disables", function() { testEnableAndDisable(view); }); - it("Loads a description", function() { testLoadXMLExamples(view); }); - it("shows an alert when disabled", function() { testAlertOnDisable(view); }); - }); - describe("OpenAssessment.EditStaffAssessmentView", function() { var view = null; diff --git a/openassessment/xblock/static/js/spec/studio/oa_edit_settings.js b/openassessment/xblock/static/js/spec/studio/oa_edit_settings.js index 14734340c1..62d9d53b3f 100644 --- a/openassessment/xblock/static/js/spec/studio/oa_edit_settings.js +++ b/openassessment/xblock/static/js/spec/studio/oa_edit_settings.js @@ -49,7 +49,6 @@ describe("OpenAssessment.EditSettingsView", function() { // The Peer and Self Editor ID's var PEER = "oa_peer_assessment_editor"; var SELF = "oa_self_assessment_editor"; - var AI = "oa_ai_assessment_editor"; var TRAINING = "oa_student_training_editor"; var STAFF = "oa_staff_assessment_editor"; @@ -61,7 +60,6 @@ describe("OpenAssessment.EditSettingsView", function() { assessmentViews = {}; assessmentViews[SELF] = new StubView("self-assessment", "Self assessment description"); assessmentViews[PEER] = new StubView("peer-assessment", "Peer assessment description"); - assessmentViews[AI] = new StubView("ai-assessment", "Example Based assessment description"); assessmentViews[TRAINING] = new StubView("student-training", "Student Training description"); assessmentViews[STAFF] = new StubView("staff-assessment", "Staff assessment description"); @@ -131,7 +129,6 @@ describe("OpenAssessment.EditSettingsView", function() { // Disable all assessments, and expect an empty description assessmentViews[PEER].isEnabled(false); assessmentViews[SELF].isEnabled(false); - assessmentViews[AI].isEnabled(false); assessmentViews[TRAINING].isEnabled(false); assessmentViews[STAFF].isEnabled(false); expect(view.assessmentsDescription()).toEqual([]); @@ -139,7 +136,6 @@ describe("OpenAssessment.EditSettingsView", function() { // Enable the first assessment only assessmentViews[PEER].isEnabled(false); assessmentViews[SELF].isEnabled(true); - assessmentViews[AI].isEnabled(false); assessmentViews[TRAINING].isEnabled(false); expect(view.assessmentsDescription()).toEqual([ { @@ -151,7 +147,6 @@ describe("OpenAssessment.EditSettingsView", function() { // Enable the second assessment only assessmentViews[PEER].isEnabled(true); assessmentViews[SELF].isEnabled(false); - assessmentViews[AI].isEnabled(false); assessmentViews[TRAINING].isEnabled(false); expect(view.assessmentsDescription()).toEqual([ { @@ -163,7 +158,6 @@ describe("OpenAssessment.EditSettingsView", function() { // Enable both assessments assessmentViews[PEER].isEnabled(true); assessmentViews[SELF].isEnabled(true); - assessmentViews[AI].isEnabled(false); assessmentViews[TRAINING].isEnabled(false); expect(view.assessmentsDescription()).toEqual([ { diff --git a/openassessment/xblock/static/js/src/lms/oa_base.js b/openassessment/xblock/static/js/src/lms/oa_base.js index 4f1585f56f..9ac3243209 100644 --- a/openassessment/xblock/static/js/src/lms/oa_base.js +++ b/openassessment/xblock/static/js/src/lms/oa_base.js @@ -208,7 +208,7 @@ OpenAssessment.BaseView.prototype = { // extract typed-in response and replace newline with br var previewText = parentElement.find('textarea[data-preview="' + previewName + '"]').val(); var previewContainer = parentElement.find('.preview_content[data-preview="' + previewName + '"]'); - previewContainer.html(previewText.replace(/\r\n|\r|\n/g,"
    ")); + previewContainer.html(previewText.replace(/\r\n|\r|\n/g, "
    ")); // Render in mathjax previewContainer.parent().parent().parent().show(); diff --git a/openassessment/xblock/static/js/src/studio/oa_edit.js b/openassessment/xblock/static/js/src/studio/oa_edit.js index a384e9d5e0..01bb169a65 100644 --- a/openassessment/xblock/static/js/src/studio/oa_edit.js +++ b/openassessment/xblock/static/js/src/studio/oa_edit.js @@ -50,15 +50,11 @@ OpenAssessment.StudioView = function(runtime, element, server, data) { var selfAssessmentView = new OpenAssessment.EditSelfAssessmentView( $("#oa_self_assessment_editor", this.element).get(0) ); - var exampleBasedAssessmentView = new OpenAssessment.EditExampleBasedAssessmentView( - $("#oa_ai_assessment_editor", this.element).get(0) - ); var assessmentLookupDictionary = {}; assessmentLookupDictionary[staffAssessmentView.getID()] = staffAssessmentView; assessmentLookupDictionary[studentTrainingView.getID()] = studentTrainingView; assessmentLookupDictionary[peerAssessmentView.getID()] = peerAssessmentView; assessmentLookupDictionary[selfAssessmentView.getID()] = selfAssessmentView; - assessmentLookupDictionary[exampleBasedAssessmentView.getID()] = exampleBasedAssessmentView; this.settingsView = new OpenAssessment.EditSettingsView( $("#oa_basic_settings_editor", this.element).get(0), assessmentLookupDictionary, data diff --git a/openassessment/xblock/static/js/src/studio/oa_edit_assessment.js b/openassessment/xblock/static/js/src/studio/oa_edit_assessment.js index 6ded7be00c..775e601a1a 100644 --- a/openassessment/xblock/static/js/src/studio/oa_edit_assessment.js +++ b/openassessment/xblock/static/js/src/studio/oa_edit_assessment.js @@ -525,103 +525,6 @@ OpenAssessment.EditStudentTrainingView.prototype = { } }; -/** - Interface for editing example-based assessment settings. - - Args: - element (DOM element): The DOM element representing this view. - - Returns: - OpenAssessment.EditExampleBasedAssessmentView - - **/ -OpenAssessment.EditExampleBasedAssessmentView = function(element) { - this.element = element; - this.name = "example-based-assessment"; - - new OpenAssessment.ToggleControl( - $("#include_ai_assessment", this.element), - $("#ai_assessment_settings_editor", this.element), - $("#ai_assessment_description_closed", this.element), - new OpenAssessment.Notifier([ - new OpenAssessment.AssessmentToggleListener() - ]) - ).install(); -}; - -OpenAssessment.EditExampleBasedAssessmentView.prototype = { - - /** - Return a description of the assessment. - - Returns: - object literal - - Example usage: - >>> editTrainingView.description(); - { - examples_xml: "XML DEFINITION HERE", - } - - **/ - description: function() { - return { - examples_xml: this.exampleDefinitions() - }; - }, - - /** - Get or set whether the assessment is enabled. - - Args: - isEnabled (boolean, optional): If provided, set the enabled state of the assessment. - - Returns: - boolean - ***/ - isEnabled: function(isEnabled) { - var sel = $("#include_ai_assessment", this.element); - return OpenAssessment.Fields.booleanField(sel, isEnabled); - }, - - /** - Toggle whether the assessment is enabled or disabled. - This triggers the actual click event and is mainly useful for testing. - **/ - toggleEnabled: function() { - $("#include_ai_assessment", this.element).click(); - }, - - /** - Get or set the XML defining the training examples. - - Args: - xml (string, optional): The XML of the training example definitions. - - Returns: - string - - **/ - exampleDefinitions: function(xml) { - var sel = $("#ai_training_examples", this.element); - return OpenAssessment.Fields.stringField(sel, xml); - }, - - /** - Gets the ID of the assessment - - Returns: - string (CSS ID of the Element object) - **/ - getID: function() { - return $(this.element).attr('id'); - }, - - validate: function() { return true; }, - validationErrors: function() { return []; }, - clearValidationErrors: function() {} -}; - /** * Interface for editing staff assessment settings. * diff --git a/openassessment/xblock/static/sass/oa/views/_oa-base.scss b/openassessment/xblock/static/sass/oa/views/_oa-base.scss index b71270d361..cf202f0371 100644 --- a/openassessment/xblock/static/sass/oa/views/_oa-base.scss +++ b/openassessment/xblock/static/sass/oa/views/_oa-base.scss @@ -1086,7 +1086,7 @@ } .action--save { - @extend %action-button + @extend %action-button !optional } .feedback { diff --git a/openassessment/xblock/static/xml/example_based_example.xml b/openassessment/xblock/static/xml/example_based_example.xml deleted file mode 100644 index ff54f1876a..0000000000 --- a/openassessment/xblock/static/xml/example_based_example.xml +++ /dev/null @@ -1,72 +0,0 @@ - - Example Based Example - - - - Born in northern New South Wales, Dowling entered the Royal Australian Naval College in 1915. After graduating in 1919 he went to sea aboard various Royal Navy and RAN vessels, and later specialised in gunnery. In 1937, he was given command of the sloop HMAS Swan. Following the outbreak of World War II, he saw action in the Mediterranean theatre as executive officer of the Royal Navy cruiser HMS Naiad, and survived her sinking by a German U-boat in March 1942. Returning to Australia, he served as Director of Plans and later Deputy Chief of Naval Staff before taking command of the light cruiser HMAS Hobart in November 1944. His achievements in the South West Pacific earned him the Distinguished Service Order. - - Dowling took command of the RAN's first aircraft carrier, HMAS Sydney, in 1948. He became Chief of Naval Personnel in 1950, and Flag Officer Commanding HM Australian Fleet in 1953. Soon after taking up the position of CNS in February 1955, he was promoted to vice admiral and appointed a Companion of the Order of the Bath. As CNS he had to deal with shortages of money, manpower and equipment, and with the increasing role of the United States in Australia's defence planning, at the expense of traditional ties with Britain. Knighted in 1957, Dowling was Chairman of COSC from March 1959 until May 1961, when he retired from the military. In 1963 he was appointed a Knight Commander of the Royal Victorian Order and became Australian Secretary to HM Queen Elizabeth II, serving until his death in 1969. - - - - - Roy Russell Dowling was born on 28 May 1901 in Condong, a township on the Tweed River in northern New South Wales. His parents were sugar cane inspector Russell Dowling and his wife Lily. The youth entered the Royal Australian Naval College (RANC) at Jervis Bay, Federal Capital Territory, in 1915. An underachiever academically, he excelled at sports, and became chief cadet captain before graduating in 1918 with the King's Medal, awarded for "gentlemanly bearing, character, good influence among his fellows and officer-like qualities".[1][2] The following year he was posted to Britain as a midshipman, undergoing training with the Royal Navy and seeing service on HMS Ramillies and HMS Venturous.[3] By January 1923 he was back in Australia, serving aboard the cruiser HMAS Adelaide. He was promoted to lieutenant in March.[4] In April 1924, Adelaide joined the Royal Navy's Special Service Squadron on its worldwide cruise, taking in New Zealand, Canada, the United States, Panama, and the West Indies, before docking in September at Portsmouth, England. There Dowling left the ship for his next appointment, training as a gunnery officer and serving in that capacity at HMS Excellent. - - - - - After his return to Australia in December 1926, Dowling spent eighteen months on HMAS Platypus and HMAS Anzac, where he continued to specialise in gunnery. In July 1928, he took on an instructional role at the gunnery school in Flinders Naval Depot on Western Port Bay, Victoria. He married Jessie Blanch in Melbourne on 8 May 1930; the couple had two sons and three daughters.[1][6] Jessie accompanied him on his next posting to Britain commencing in January 1931. - - - - He was promoted to lieutenant commander on 15 March, and was appointed gunnery officer on the light cruiser HMS Colombo in May. Dowling returned to Australia in January 1933, and was appointed squadron gunnery officer aboard the heavy cruiser HMAS Canberra that April.[1][4] The ship operated mainly within Australian waters over the next two years.[7] In July 1935, Dowling took charge of the gunnery school at Flinders Naval Depot. He was promoted to commander on 31 December 1936.[1][4] The following month, he assumed command of the newly commissioned Grimsby-class sloop HMAS Swan, carrying out duties in the South West Pacific.[8] Completing his tenure on Swan in January 1939, he was briefly assigned to the Navy Office, Melbourne, before returning to Britain in March for duty at HMS Pembroke, where he awaited posting aboard the yet-to-be-commissioned anti-aircraft cruiser, HMS Naiad. - - - - - - - Censorship in the Libraries - - 'All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us.' --Katherine Paterson, Author - - Write a persuasive essay to a newspaper reflecting your views on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading. - - Read for conciseness, clarity of thought, and form. - - - Ideas - Determine if there is a unifying theme or main idea. - - - - - Content - Assess the content of the submission - - - - - (Optional) What aspects of this response stood out to you? What did it do well? How could it improve? - - - I noticed that this response... - - - diff --git a/openassessment/xblock/student_training_mixin.py b/openassessment/xblock/student_training_mixin.py index 47a597f8f3..61f894973f 100644 --- a/openassessment/xblock/student_training_mixin.py +++ b/openassessment/xblock/student_training_mixin.py @@ -2,16 +2,17 @@ Student training step in the OpenAssessment XBlock. """ import logging + from webob import Response from xblock.core import XBlock + from openassessment.assessment.api import student_training -from openassessment.workflow import api as workflow_api from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.xblock.data_conversion import convert_training_examples_list_to_dict, create_submission_dict + from .resolve_dates import DISTANT_FUTURE from .user_data import get_user_preferences - logger = logging.getLogger(__name__) diff --git a/openassessment/xblock/studio_mixin.py b/openassessment/xblock/studio_mixin.py index a5c6befffb..4ac3358478 100644 --- a/openassessment/xblock/studio_mixin.py +++ b/openassessment/xblock/studio_mixin.py @@ -3,27 +3,24 @@ """ import copy import logging -import pkg_resources from uuid import uuid4 -from xml import UpdateFromXmlError -from django.conf import settings -from django.template import Context -from django.template.loader import get_template -from django.utils.translation import ugettext as _, ugettext_lazy +import pkg_resources from voluptuous import MultipleInvalid from xblock.core import XBlock from xblock.fields import List, Scope from xblock.fragment import Fragment +from django.conf import settings +from django.template.loader import get_template +from django.utils.translation import ugettext_lazy + +from openassessment.xblock.data_conversion import (create_rubric_dict, make_django_template_key, + update_assessments_format) from openassessment.xblock.defaults import DEFAULT_EDITOR_ASSESSMENTS_ORDER, DEFAULT_RUBRIC_FEEDBACK_TEXT -from openassessment.xblock.validation import validator -from openassessment.xblock.data_conversion import ( - create_rubric_dict, make_django_template_key, update_assessments_format -) -from openassessment.xblock.schema import EDITOR_UPDATE_SCHEMA from openassessment.xblock.resolve_dates import resolve_dates -from openassessment.xblock.xml import serialize_examples_to_xml_str, parse_examples_from_xml_str +from openassessment.xblock.schema import EDITOR_UPDATE_SCHEMA +from openassessment.xblock.validation import validator logger = logging.getLogger(__name__) @@ -73,7 +70,7 @@ def studio_view(self, context=None): """ rendered_template = get_template( 'openassessmentblock/edit/oa_edit.html' - ).render(Context(self.editor_context())) + ).render(self.editor_context()) fragment = Fragment(rendered_template) if settings.DEBUG: self.add_javascript_files(fragment, "static/js/src/oa_shared.js") @@ -181,15 +178,12 @@ def update_editor_context(self, data, suffix=''): logger.exception('Editor context is invalid') return {'success': False, 'msg': self._('Error updating XBlock configuration')} - # Check that the editor assessment order contains all the assessments. We are more flexible on example-based. - given_without_example_based = set(data['editor_assessments_order']) - {'example-based-assessment'} - if set(DEFAULT_EDITOR_ASSESSMENTS_ORDER) != given_without_example_based: + # Check that the editor assessment order contains all the assessments. + current_order = set(data['editor_assessments_order']) + if set(DEFAULT_EDITOR_ASSESSMENTS_ORDER) != current_order: # Backwards compatibility: "staff-assessment" may not be present. # If that is the only problem with this data, just add it manually and continue. - if set(DEFAULT_EDITOR_ASSESSMENTS_ORDER) == ( - # Check the given set, minus example-based, plus staff - given_without_example_based | {'staff-assessment'} - ): + if set(DEFAULT_EDITOR_ASSESSMENTS_ORDER) == current_order | {'staff-assessment'}: data['editor_assessments_order'].append('staff-assessment') logger.info('Backwards compatibility: editor_assessments_order now contains staff-assessment') else: @@ -218,27 +212,6 @@ def update_editor_context(self, data, suffix=''): if 'name' not in option: option['name'] = uuid4().hex - # If example based assessment is enabled, we replace it's xml definition with the dictionary - # definition we expect for validation and storing. - for assessment in data['assessments']: - if assessment['name'] == 'example-based-assessment': - try: - assessment['examples'] = parse_examples_from_xml_str(assessment['examples_xml']) - except UpdateFromXmlError: - return {'success': False, 'msg': self._( - u'Validation error: There was an error in the XML definition of the ' - u'examples provided by the user. Please correct the XML definition before saving.') - } - except KeyError: - return {'success': False, 'msg': self._( - u'Validation error: No examples were provided for example based assessment.' - )} - # This is where we default to EASE for problems which are edited in the GUI - assessment['algorithm_id'] = 'ease' - if assessment['name'] == 'student-training': - for example in assessment['examples']: - example['answer'] = {'parts': [{'text': text} for text in example['answer']]} - xblock_validator = validator(self, self._) success, msg = xblock_validator( create_rubric_dict(data['prompts'], data['criteria']), @@ -356,13 +329,6 @@ def _assessments_editor_context(self, assessment_dates): else: assessments['training'] = {'examples': [student_training_template], 'template': student_training_template} - example_based_assessment = self.get_assessment_module('example-based-assessment') - - if example_based_assessment: - assessments['example_based_assessment'] = { - 'examples': serialize_examples_to_xml_str(example_based_assessment) - } - return assessments def _editor_assessments_order_context(self): @@ -379,19 +345,13 @@ def _editor_assessments_order_context(self): # since the user last saved their ordering. effective_order = copy.deepcopy(DEFAULT_EDITOR_ASSESSMENTS_ORDER) - # If the problem already contains example-based assessment - # then allow the editor to display example-based assessments, - # which is not included in the default - enabled_assessments = [asmnt['name'] for asmnt in self.valid_assessments] - if 'example-based-assessment' in enabled_assessments: - effective_order.insert(0, 'example-based-assessment') - # Account for changes the user has made to the default order user_order = copy.deepcopy(self.editor_assessments_order) effective_order = self._subset_in_relative_order(effective_order, user_order) # Account for inconsistencies between the user's order and the problems # that are currently enabled in the problem (These cannot be changed) + enabled_assessments = [asmnt['name'] for asmnt in self.valid_assessments] enabled_ordered_assessments = [ assessment for assessment in enabled_assessments if assessment in user_order ] diff --git a/openassessment/xblock/submission_mixin.py b/openassessment/xblock/submission_mixin.py index f4e4d51dbf..938b5b3bbc 100644 --- a/openassessment/xblock/submission_mixin.py +++ b/openassessment/xblock/submission_mixin.py @@ -3,17 +3,16 @@ from xblock.core import XBlock -from submissions import api +from data_conversion import create_submission_dict, prepare_submission_for_serialization from openassessment.fileupload import api as file_upload_api from openassessment.fileupload.exceptions import FileUploadError from openassessment.workflow.errors import AssessmentWorkflowError +from submissions import api +from validation import validate_submission from .resolve_dates import DISTANT_FUTURE from .user_data import get_user_preferences -from data_conversion import create_submission_dict, prepare_submission_for_serialization -from validation import validate_submission - logger = logging.getLogger(__name__) diff --git a/openassessment/xblock/test/base.py b/openassessment/xblock/test/base.py index bd3dd9f3e2..78e5e91e1a 100644 --- a/openassessment/xblock/test/base.py +++ b/openassessment/xblock/test/base.py @@ -3,20 +3,19 @@ Base class for handler-level testing of the XBlock. """ import copy -import mock -import os.path -import json from functools import wraps +import json +import os.path -from submissions import api as submissions_api +import mock +import webob +from workbench.runtime import WorkbenchRuntime -from openassessment.workflow import api as workflow_api from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api from openassessment.test_utils import CacheResetTest, TransactionCacheResetTest - -from workbench.runtime import WorkbenchRuntime -import webob +from openassessment.workflow import api as workflow_api +from submissions import api as submissions_api # Sample peer assessments PEER_ASSESSMENTS = [ diff --git a/openassessment/xblock/test/data/example_based_assessment.xml b/openassessment/xblock/test/data/example_based_assessment.xml deleted file mode 100644 index ab5e23a020..0000000000 --- a/openassessment/xblock/test/data/example_based_assessment.xml +++ /dev/null @@ -1,71 +0,0 @@ - - Open Assessment Test - - - Given the state of the world today, what do you think should be done to combat poverty? - - - Given the state of the world today, what do you think should be done to combat pollution? - - - - - Ideas - How good are the ideas? - - - - - - Content - How good is the content? - - - - - - - - - Example Answer One - - - - Example Answer Two - - - - Example Answer Three - - - - Example Answer Four - - - - - - - diff --git a/openassessment/xblock/test/data/example_based_only.xml b/openassessment/xblock/test/data/example_based_only.xml deleted file mode 100644 index 4b860a8351..0000000000 --- a/openassessment/xblock/test/data/example_based_only.xml +++ /dev/null @@ -1,69 +0,0 @@ - - Open Assessment Test - - - Given the state of the world today, what do you think should be done to combat poverty? - - - Given the state of the world today, what do you think should be done to combat pollution? - - - - - Ideas - How good are the ideas? - - - - - - Content - How good is the content? - - - - - - - - - Example Answer One - - - - Example Answer Two - - - - Example Answer Three - - - - Example Answer Four - - - - - diff --git a/openassessment/xblock/test/data/feedback_only_criterion_ai.xml b/openassessment/xblock/test/data/feedback_only_criterion_ai.xml deleted file mode 100644 index 3f14e95d89..0000000000 --- a/openassessment/xblock/test/data/feedback_only_criterion_ai.xml +++ /dev/null @@ -1,41 +0,0 @@ - - Feedback only criterion - - - Given the state of the world today, what do you think should be done to combat poverty? - - - Given the state of the world today, what do you think should be done to combat pollution? - - - - - vocabulary - How good is the vocabulary? - - - - - 𝖋𝖊𝖊𝖉𝖇𝖆𝖈𝖐 𝖔𝖓𝖑𝖞 - This criterion accepts only written feedback, so it has no options - - - - - - This is my answer. - - - - - diff --git a/openassessment/xblock/test/data/grade_scenario_ai_only.xml b/openassessment/xblock/test/data/grade_scenario_ai_only.xml deleted file mode 100644 index fa46b64856..0000000000 --- a/openassessment/xblock/test/data/grade_scenario_ai_only.xml +++ /dev/null @@ -1,69 +0,0 @@ - - Open Assessment Test - - - Given the state of the world today, what do you think should be done to combat poverty? - - - Given the state of the world today, what do you think should be done to combat pollution? - - - - - 𝓒𝓸𝓷𝓬𝓲𝓼𝓮 - How concise is it? - - - - - - Form - How well-formed is it? - - - - - - - - - Example Answer One - - - - Example Answer Two - - - - Example Answer Three - - - - Example Answer Four - - - - - diff --git a/openassessment/xblock/test/data/grade_waiting_scenario.xml b/openassessment/xblock/test/data/grade_waiting_scenario.xml index b307f57499..813730e25e 100644 --- a/openassessment/xblock/test/data/grade_waiting_scenario.xml +++ b/openassessment/xblock/test/data/grade_waiting_scenario.xml @@ -43,28 +43,6 @@ - - - Example Answer One - - - - Example Answer Two - - - - Example Answer Three - - - - Example Answer Four - - - diff --git a/openassessment/xblock/test/data/invalid_assessment_examples.json b/openassessment/xblock/test/data/invalid_assessment_examples.json index 9246505845..a1e081409a 100644 --- a/openassessment/xblock/test/data/invalid_assessment_examples.json +++ b/openassessment/xblock/test/data/invalid_assessment_examples.json @@ -71,59 +71,5 @@ "must_be_graded_by": 3 } ] - }, - - "example_based_no_training_examples": { - "rubric": { - "criteria": [ - { - "order_num": 0, - "name": "vocabulary", - "prompt": "how good is the vocabulary?", - "options": [ - { - "order_num": 0, - "points": 0, - "name": "poor", - "explanation": "poor job!" - }, - { - "order_num": 1, - "points": 1, - "name": "good", - "explanation": "good job!" - } - ] - }, - { - "order_num": 1, - "name": "grammar", - "prompt": "how good is the grammar?", - "options": [ - { - "order_num": 0, - "points": 0, - "name": "poor", - "explanation": "poor job!" - }, - { - "order_num": 1, - "points": 1, - "name": "good", - "explanation": "good job!" - } - ] - } - ] - }, - "assessments": [ - { - "name": "example-based-assessment", - "start": null, - "due": null, - "algorithm_id": "ease", - "examples": [] - } - ] } } diff --git a/openassessment/xblock/test/data/invalid_assessments.json b/openassessment/xblock/test/data/invalid_assessments.json index 6c9569ecb1..3481e1c697 100644 --- a/openassessment/xblock/test/data/invalid_assessments.json +++ b/openassessment/xblock/test/data/invalid_assessments.json @@ -211,38 +211,5 @@ ], "current_assessments": null, "is_released": false - }, - - "example_based_algorithm_id_is_not_ease": { - "assessments": [ - { - "name": "example-based-assessment", - "start": null, - "due": null, - "algorithm_id": "NOT_EASE", - "examples": [ - { - "answer": "тєѕт αηѕωєя", - "options_selected": [ - { - "criterion": "Test criterion", - "option": "No" - } - ] - }, - { - "answer": "тєѕт αηѕωєя TWO", - "options_selected": [ - { - "criterion": "Test criterion", - "option": "Yes" - } - ] - } - ] - } - ], - "current_assessments": null, - "is_released": false } } diff --git a/openassessment/xblock/test/data/invalid_rubrics.json b/openassessment/xblock/test/data/invalid_rubrics.json index a12a153083..a4707b1344 100644 --- a/openassessment/xblock/test/data/invalid_rubrics.json +++ b/openassessment/xblock/test/data/invalid_rubrics.json @@ -523,34 +523,6 @@ "is_released": true }, - "example_based_duplicate_option_points": { - "is_example_based": true, - "rubric": { - "prompts": [{"description": "Test Prompt 1."}, {"description": "Test Prompt 2."}], - "criteria": [ - { - "order_num": 0, - "name": "Test criterion", - "prompt": "Test criterion prompt", - "options": [ - { - "order_num": 0, - "points": 2, - "name": "No", - "explanation": "No explanation" - }, - { - "order_num": 1, - "points": 2, - "name": "Yes", - "explanation": "Yes explanation" - } - ] - } - ] - } - }, - "zero_options_feedback_optional": { "rubric": { "prompts": [{"description": "Test Prompt 1."}, {"description": "Test Prompt 2."}], diff --git a/openassessment/xblock/test/data/invalid_update_xblock.json b/openassessment/xblock/test/data/invalid_update_xblock.json index 6a7d0d6647..e2c8081b02 100644 --- a/openassessment/xblock/test/data/invalid_update_xblock.json +++ b/openassessment/xblock/test/data/invalid_update_xblock.json @@ -1225,7 +1225,7 @@ ], "editor_assessments_order": [ "student-training", "peer-assessment", - "self-assessment", "example-based-assessment", "staff-assessment", + "self-assessment", "staff-assessment", "NOT A VALID ASSESSMENT" ] }, @@ -1278,7 +1278,7 @@ "due": null } ], - "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "example-based-assessment", "staff-assessment"], + "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "staff-assessment"], "submission_due": "2014-02-27T09:46", "submission_start": "2014-02-10T09:46" }, @@ -1331,7 +1331,7 @@ "due": null } ], - "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "example-based-assessment", "staff-assessment"], + "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "staff-assessment"], "submission_due": "2014-02-27T09:46", "submission_start": "2014-02-10T09:46" }, @@ -1406,7 +1406,7 @@ "due": null } ], - "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "example-based-assessment", "staff-assessment"], + "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "staff-assessment"], "submission_due": "2014-02-27T09:46", "submission_start": "2014-02-10T09:46" }, @@ -1461,7 +1461,7 @@ "due": null } ], - "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "example-based-assessment", "staff-assessment"], + "editor_assessments_order": ["student-training", "peer-assessment", "self-assessment", "staff-assessment"], "submission_due": "2014-02-27T09:46", "submission_start": "2014-02-10T09:46" }, diff --git a/openassessment/xblock/test/data/serialize.json b/openassessment/xblock/test/data/serialize.json index 957f824eb9..bedbf7b978 100644 --- a/openassessment/xblock/test/data/serialize.json +++ b/openassessment/xblock/test/data/serialize.json @@ -1311,30 +1311,6 @@ } ], "assessments": [ - { - "name": "example-based-assessment", - "algorithm_id": "sample-algorithm-id", - "examples": [ - { - "answer": "тєѕт αηѕωєя", - "options_selected": [ - { - "criterion": "Test criterion", - "option": "No" - } - ] - }, - { - "answer": "тєѕт αηѕωєя TWO", - "options_selected": [ - { - "criterion": "Test criterion", - "option": "Yes" - } - ] - } - ] - }, { "name": "peer-assessment", "start": "2014-02-27T09:46:28", @@ -1356,20 +1332,6 @@ "", "Foo", "", - "", - "", - "", - "тєѕт αηѕωєя", - "", - "", - "", - "", "", "", "", diff --git a/openassessment/xblock/test/data/staff_dates_scenario.xml b/openassessment/xblock/test/data/staff_dates_scenario.xml index 31121e4764..29de12be23 100644 --- a/openassessment/xblock/test/data/staff_dates_scenario.xml +++ b/openassessment/xblock/test/data/staff_dates_scenario.xml @@ -84,14 +84,6 @@ - - - Example Answer One - - ", - "", - "", - "тєѕт αηѕωєя TWO", - "", - "", - "", - "тєѕт αηѕωєя TWO", - "", - "", - "", - "тєѕт αηѕωєя TWO", - "", - "", - "", - "тєѕт αηѕωєя TWO", - "' + \ - '' + \ - '' - ASSESSMENT_CSS_IDS = { - "example-based-assessment": "oa_ai_assessment_editor", "peer-assessment": "oa_peer_assessment_editor", "self-assessment": "oa_self_assessment_editor", "student-training": "oa_student_training_editor", @@ -127,11 +121,6 @@ def test_render_studio_with_training(self, xblock): frag = self.runtime.render(xblock, 'studio_view') self.assertTrue(frag.body_html().find('openassessment-edit')) - @scenario('data/example_based_only.xml') - def test_render_studio_with_ai(self, xblock): - frag = self.runtime.render(xblock, 'studio_view') - self.assertTrue('ai_assessment_settings_editor' in frag.body_html()) - @file_data('data/update_xblock.json') @scenario('data/basic_scenario.xml') def test_update_editor_context(self, xblock, data): @@ -161,27 +150,6 @@ def test_update_editor_context_saves_assessment_order(self, xblock): self.assertTrue(resp['success'], msg=resp.get('msg')) self.assertEqual(xblock.editor_assessments_order, data['editor_assessments_order']) - @scenario('data/basic_scenario.xml') - def test_update_editor_context_saves_assessment_order_with_ai(self, xblock): - # Update the XBlock with a different editor assessment order - data = copy.deepcopy(self.UPDATE_EDITOR_DATA) - data['assessments'] = [{ - 'name': 'example-based-assessment', - 'examples_xml': self.EXAMPLE_BASED_ASSESSMENT_EXAMPLES - }] - data['editor_assessments_order'] = [ - "example-based-assessment", - "student-training", - "peer-assessment", - "self-assessment", - "staff-assessment", - ] - xblock.runtime.modulestore = MagicMock() - xblock.runtime.modulestore.has_published_version.return_value = False - resp = self.request(xblock, 'update_editor_context', json.dumps(data), response_format='json') - self.assertTrue(resp['success'], msg=resp.get('msg')) - self.assertEqual(xblock.editor_assessments_order, data['editor_assessments_order']) - @scenario('data/basic_scenario.xml') def test_update_editor_context_saves_leaderboard(self, xblock): data = copy.deepcopy(self.UPDATE_EDITOR_DATA) diff --git a/openassessment/xblock/test/test_submission.py b/openassessment/xblock/test/test_submission.py index f011a7f1ed..3f37cde693 100644 --- a/openassessment/xblock/test/test_submission.py +++ b/openassessment/xblock/test/test_submission.py @@ -3,22 +3,22 @@ Test submission to the OpenAssessment XBlock. """ -import json import datetime as dt -import boto -from boto.s3.key import Key -from django.test.utils import override_settings -from mock import patch, Mock +import json + +import boto3 +from mock import Mock, patch from moto import mock_s3 import pytz -from submissions import api as sub_api -from submissions.api import SubmissionRequestError, SubmissionInternalError -from openassessment.fileupload import api +from django.test.utils import override_settings +from openassessment.fileupload import api from openassessment.workflow import api as workflow_api -from openassessment.xblock.openassessmentblock import OpenAssessmentBlock from openassessment.xblock.data_conversion import create_submission_dict, prepare_submission_for_serialization +from openassessment.xblock.openassessmentblock import OpenAssessmentBlock +from submissions import api as sub_api +from submissions.api import SubmissionInternalError, SubmissionRequestError from .base import XBlockHandlerTestCase, scenario @@ -157,11 +157,14 @@ def test_upload_url(self, xblock): @scenario('data/file_upload_scenario.xml') def test_download_url(self, xblock): """ Test generate correct download URL with existing file. should create a file and get the download URL """ - conn = boto.connect_s3() - bucket = conn.create_bucket('mybucket') - key = Key(bucket) - key.key = "submissions_attachments/test_student/test_course/" + xblock.scope_ids.usage_id - key.set_contents_from_string("How d'ya do?") + s3 = boto3.resource('s3') + s3.create_bucket(Bucket='mybucket') + s3.Object( + 'mybucket', + 'submissions_attachments/test_student/test_course/' + xblock.scope_ids.usage_id + ).put( + Body="How d'ya do?" + ) download_url = api.get_download_url("test_student/test_course/" + xblock.scope_ids.usage_id) xblock.xmodule_runtime = Mock( @@ -172,6 +175,8 @@ def test_download_url(self, xblock): resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json') self.assertTrue(resp['success']) + print download_url + print resp['url'] self.assertEqual(download_url, resp['url']) @mock_s3 @@ -182,11 +187,11 @@ def test_download_url(self, xblock): ) @scenario('data/file_upload_scenario.xml') def test_download_url_non_existing_file(self, xblock): - """ Test generate a download URL for non-existing file, should return empty string """ + """ For non-existing file, a valid url will be returned, but it will 404 when followed. """ resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json') self.assertTrue(resp['success']) - self.assertEqual(u'', resp['url']) + self.assertIn(u'https://mybucket.s3.amazonaws.com/submissions_attachments', resp['url']) @mock_s3 @override_settings( @@ -197,11 +202,11 @@ def test_download_url_non_existing_file(self, xblock): @scenario('data/file_upload_scenario.xml') def test_remove_all_uploaded_files(self, xblock): """ Test remove all user files """ - conn = boto.connect_s3() - bucket = conn.create_bucket('mybucket') - key = Key(bucket) - key.key = "submissions_attachments/test_student/test_course/" + xblock.scope_ids.usage_id - key.set_contents_from_string("How d'ya do?") + s3 = boto3.resource('s3') + s3.create_bucket(Bucket='mybucket') + s3.Object('mybucket', 'submissions_attachments/test_student/test_course/' + xblock.scope_ids.usage_id).put( + Body="How d'ya do?" + ) xblock.xmodule_runtime = Mock( course_id='test_course', @@ -219,7 +224,7 @@ def test_remove_all_uploaded_files(self, xblock): resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json') self.assertTrue(resp['success']) - self.assertEqual(u'', resp['url']) + self.assertIn(u'https://mybucket.s3.amazonaws.com/submissions_attachments', resp['url']) @mock_s3 @override_settings( diff --git a/openassessment/xblock/test/test_validation.py b/openassessment/xblock/test/test_validation.py index f5aa3f1e22..6f7edebb6c 100644 --- a/openassessment/xblock/test/test_validation.py +++ b/openassessment/xblock/test/test_validation.py @@ -5,15 +5,16 @@ import copy from datetime import datetime as dt + +import ddt import mock import pytz -import ddt + from django.test import TestCase + from openassessment.xblock.openassessmentblock import OpenAssessmentBlock -from openassessment.xblock.validation import ( - validator, validate_assessments, validate_rubric, - validate_dates, validate_assessment_examples, validate_submission -) +from openassessment.xblock.validation import (validate_assessment_examples, validate_assessments, validate_dates, + validate_rubric, validate_submission, validator) STUB_I18N = lambda x: x @@ -89,12 +90,10 @@ class RubricValidationTest(TestCase): def test_valid_rubric(self, data): current_rubric = data.get('current_rubric') is_released = data.get('is_released', False) - is_example_based = data.get('is_example_based', False) success, msg = validate_rubric( data['rubric'], current_rubric, is_released, - is_example_based, STUB_I18N ) self.assertTrue(success) @@ -104,9 +103,8 @@ def test_valid_rubric(self, data): def test_invalid_rubric(self, data): current_rubric = data.get('current_rubric') is_released = data.get('is_released', False) - is_example_based = data.get('is_example_based', False) success, msg = validate_rubric( - data['rubric'], current_rubric, is_released, is_example_based, STUB_I18N + data['rubric'], current_rubric, is_released, STUB_I18N ) self.assertFalse(success) self.assertGreater(len(msg), 0) @@ -264,13 +262,6 @@ class ValidationIntegrationTest(TestCase): ] ASSESSMENTS = [ - { - "name": "example-based-assessment", - "start": None, - "due": None, - "examples": EXAMPLES, - "algorithm_id": "ease" - }, { "name": "student-training", "start": None, @@ -327,26 +318,6 @@ def test_student_training_examples_invalid_option(self): self.assertFalse(is_valid) self.assertEqual(msg, u'Example 1 has an invalid option for "vocabulary": "Invalid option!"') - def test_example_based_assessment_duplicate_point_values(self): - # Mutate the rubric so that two options have the same point value - # for a particular criterion. - # This should cause a validation error with example-based assessment. - mutated_rubric = copy.deepcopy(self.RUBRIC) - mutated_rubric['criteria'][0]['options'] = copy.deepcopy(self.CRITERION_OPTIONS) - for option in mutated_rubric['criteria'][0]['options']: - option['points'] = 1 - - # Expect a validation error - is_valid, msg = self.validator(mutated_rubric, self.ASSESSMENTS) - self.assertFalse(is_valid) - self.assertEqual(msg, u'Example-based assessments cannot have duplicate point values.') - - # But it should be okay if we don't have example-based assessment - no_example_based = copy.deepcopy(self.ASSESSMENTS)[1:] - is_valid, msg = self.validator(mutated_rubric, no_example_based) - self.assertTrue(is_valid) - self.assertEqual(msg, u'') - def test_leaderboard_num_validation(self): self._assert_leaderboard_num_valid(-1, False) self._assert_leaderboard_num_valid(0, True) diff --git a/openassessment/xblock/test/test_xml.py b/openassessment/xblock/test/test_xml.py index 85b91d048f..a7a24198b6 100644 --- a/openassessment/xblock/test/test_xml.py +++ b/openassessment/xblock/test/test_xml.py @@ -3,22 +3,21 @@ Tests for serializing to/from XML. """ import copy -import datetime as dt -import mock + +import dateutil.parser +import ddt import lxml.etree as etree +import mock import pytz -import dateutil.parser + from django.test import TestCase -import ddt -from openassessment.xblock.data_conversion import create_prompts_list, update_assessments_format +from openassessment.xblock.data_conversion import create_prompts_list from openassessment.xblock.openassessmentblock import OpenAssessmentBlock -from openassessment.xblock.xml import ( - serialize_content, parse_from_xml_str, _parse_prompts_xml, parse_rubric_xml, - parse_examples_xml, parse_assessments_xml, - serialize_rubric_to_xml_str, serialize_examples_to_xml_str, - serialize_assessments_to_xml_str, UpdateFromXmlError -) +from openassessment.xblock.xml import (UpdateFromXmlError, _parse_prompts_xml, parse_assessments_xml, + parse_examples_xml, parse_from_xml_str, parse_rubric_xml, + serialize_assessments_to_xml_str, serialize_content, + serialize_examples_to_xml_str, serialize_rubric_to_xml_str) def _parse_date(value): diff --git a/openassessment/xblock/validation.py b/openassessment/xblock/validation.py index 22f7c834e7..9acbe52d50 100644 --- a/openassessment/xblock/validation.py +++ b/openassessment/xblock/validation.py @@ -2,11 +2,12 @@ Validate changes to an XBlock before it is updated. """ from collections import Counter -from submissions.api import MAX_TOP_SUBMISSIONS -from openassessment.assessment.serializers import rubric_from_dict, InvalidRubric + from openassessment.assessment.api.student_training import validate_training_examples -from openassessment.xblock.resolve_dates import resolve_dates, DateValidationError, InvalidDateFormat +from openassessment.assessment.serializers import InvalidRubric, rubric_from_dict from openassessment.xblock.data_conversion import convert_training_examples_list_to_dict +from openassessment.xblock.resolve_dates import DateValidationError, InvalidDateFormat, resolve_dates +from submissions.api import MAX_TOP_SUBMISSIONS def _match_by_order(items, others): @@ -48,11 +49,10 @@ def _duplicates(items): def _is_valid_assessment_sequence(assessments): """ Check whether the sequence of assessments is valid. The rules enforced are: - -must have one of staff-, peer-, self-, or example-based-assessment listed + -must have one of staff-, peer-, or self- listed -in addition to those, only student-training is a valid entry -no duplicate entries -if staff-assessment is present, it must come last - -if example-based-assessment is present, it must come first -if student-training is present, it must be followed at some point by peer-assessment Args: @@ -63,7 +63,7 @@ def _is_valid_assessment_sequence(assessments): """ sequence = [asmnt.get('name') for asmnt in assessments] - required = ['example-based-assessment', 'staff-assessment', 'peer-assessment', 'self-assessment'] + required = ['staff-assessment', 'peer-assessment', 'self-assessment'] optional = ['student-training'] # at least one of required? @@ -82,10 +82,6 @@ def _is_valid_assessment_sequence(assessments): if 'staff-assessment' in sequence and 'staff-assessment' != sequence[-1]: return False - # if using example-based, it must be first - if 'example-based-assessment' in sequence and 'example-based-assessment' != sequence[0]: - return False - # if using training, must be followed by peer at some point if 'student-training' in sequence: train_index = sequence.index('student-training') @@ -154,12 +150,6 @@ def validate_assessments(assessments, current_assessments, is_released, _): return False, _('Each example response for learner training must be unique.') answers.append(example.get('answer')) - # Example-based assessment MUST specify 'ease' or 'fake' as the algorithm ID, - # at least for now. Later, we may make this more flexible. - if assessment_dict.get('name') == 'example-based-assessment': - if assessment_dict.get('algorithm_id') not in ['ease', 'fake']: - return False, _('The "algorithm_id" value must be set to "ease" or "fake"') - # Staff grading must be required if it is the only step if assessment_dict.get('name') == 'staff-assessment' and len(assessments) == 1: required = assessment_dict.get('required') @@ -178,7 +168,7 @@ def validate_assessments(assessments, current_assessments, is_released, _): return True, u'' -def validate_rubric(rubric_dict, current_rubric, is_released, is_example_based, _): +def validate_rubric(rubric_dict, current_rubric, is_released, _): """ Check that the rubric is semantically valid. @@ -186,7 +176,6 @@ def validate_rubric(rubric_dict, current_rubric, is_released, is_example_based, rubric_dict (dict): Serialized Rubric model representing the updated state of the rubric. current_rubric (dict): Serialized Rubric model representing the current state of the rubric. is_released (bool): True if and only if the problem has been released. - is_example_based (bool): True if and only if this is an example-based assessment. _ (function): The service function used to get the appropriate i18n text Returns: @@ -214,15 +203,6 @@ def validate_rubric(rubric_dict, current_rubric, is_released, is_example_based, msg = _(u'Criteria with no options must require written feedback.') return False, msg - # Example-based assessments impose the additional restriction - # that the point values for options must be unique within - # a particular rubric criterion. - if is_example_based: - duplicates = _duplicates([option['points'] for option in criterion['options']]) - if len(duplicates) > 0: - msg = _(u"Example-based assessments cannot have duplicate point values.") - return False, msg - # After a problem is released, authors are allowed to change text, # but nothing that would change the point value of a rubric. if is_released: @@ -300,14 +280,14 @@ def validate_assessment_examples(rubric_dict, assessments, _): """ for asmnt in assessments: - if asmnt['name'] == 'student-training' or asmnt['name'] == 'example-based-assessment': + if asmnt['name'] == 'student-training': examples = convert_training_examples_list_to_dict(asmnt['examples']) # Must have at least one training example if len(examples) == 0: return False, _( - u"Learner training and example-based assessments must have at least one training example." + u"Learner training must have at least one training example." ) # Delegate to the student training API to validate the @@ -347,12 +327,11 @@ def _inner(rubric_dict, assessments, leaderboard_show=0, submission_start=None, return False, msg # Rubric - is_example_based = 'example-based-assessment' in [asmnt.get('name') for asmnt in assessments] current_rubric = { 'prompts': oa_block.prompts, 'criteria': oa_block.rubric_criteria } - success, msg = validate_rubric(rubric_dict, current_rubric, is_released, is_example_based, _) + success, msg = validate_rubric(rubric_dict, current_rubric, is_released, _) if not success: return False, msg diff --git a/openassessment/xblock/workflow_mixin.py b/openassessment/xblock/workflow_mixin.py index 6c7f623124..c22bcbff7f 100644 --- a/openassessment/xblock/workflow_mixin.py +++ b/openassessment/xblock/workflow_mixin.py @@ -6,7 +6,6 @@ from openassessment.workflow import api as workflow_api from openassessment.workflow.models import AssessmentWorkflowCancellation -from openassessment.xblock.data_conversion import create_rubric_dict class WorkflowMixin(object): @@ -17,7 +16,6 @@ class WorkflowMixin(object): # Dictionary mapping assessment names (e.g. peer-assessment) # to the corresponding workflow step names. ASSESSMENT_STEP_NAMES = { - "example-based-assessment": "ai", "self-assessment": "self", "peer-assessment": "peer", "student-training": "training", @@ -54,14 +52,7 @@ def create_workflow(self, submission_uuid): """ steps = self._create_step_list() - ai_module = self.get_assessment_module('example-based-assessment') - on_init_params = { - 'ai': { - 'rubric': create_rubric_dict(self.prompts, self.rubric_criteria_with_labels), - 'algorithm_id': ai_module["algorithm_id"] if ai_module else None - } - } - workflow_api.create_workflow(submission_uuid, steps, on_init_params=on_init_params) + workflow_api.create_workflow(submission_uuid, steps, on_init_params={}) def workflow_requirements(self): """ diff --git a/openassessment/xblock/xml.py b/openassessment/xblock/xml.py index 5f318aef10..45a74b9a5e 100644 --- a/openassessment/xblock/xml.py +++ b/openassessment/xblock/xml.py @@ -2,12 +2,13 @@ Serialize and deserialize OpenAssessment XBlock content to/from XML. """ from uuid import uuid4 as uuid -import lxml.etree as etree -import pytz + import dateutil.parser import defusedxml.ElementTree as safe_etree -from data_conversion import update_assessments_format -from defaults import DEFAULT_RUBRIC_FEEDBACK_TEXT +import lxml.etree as etree +import pytz + +from openassessment.xblock.data_conversion import update_assessments_format class UpdateFromXmlError(Exception): @@ -531,10 +532,6 @@ def parse_assessments_xml(assessments_root): # Assessment start if 'start' in assessment.attrib: - # Example-based assessment is NOT allowed to have a start date - if assessment_dict['name'] == 'example-based-assessment': - raise UpdateFromXmlError('Example-based assessment cannot have a start date') - # Other assessment types CAN have a start date parsed_start = parse_date(assessment.get('start'), name="{} start date".format(assessment_dict['name'])) @@ -546,10 +543,6 @@ def parse_assessments_xml(assessments_root): # Assessment due if 'due' in assessment.attrib: - # Example-based assessment is NOT allowed to have a due date - if assessment_dict['name'] == 'example-based-assessment': - raise UpdateFromXmlError('Example-based assessment cannot have a due date') - # Other assessment types CAN have a due date parsed_due = parse_date(assessment.get('due'), name="{} due date".format(assessment_dict['name'])) @@ -586,14 +579,10 @@ def parse_assessments_xml(assessments_root): # Student training and AI Grading should always have examples set, even if it's an empty list. # (Validation rules, applied later, are responsible for # ensuring that users specify at least one example). - # All assessments except for Student Training and AI (example-based-assessment) types ignore examples. + # All assessments except for Student Training ignore examples. if assessment_dict['name'] == 'student-training': assessment_dict['examples'] = parse_examples_xml(examples) - if assessment_dict['name'] == 'example-based-assessment': - assessment_dict['examples'] = parse_examples_xml(examples) - assessment_dict['algorithm_id'] = unicode(assessment.get('algorithm_id', 'ease')) - # Update the list of assessments assessments_list.append(assessment_dict) @@ -661,9 +650,6 @@ def serialize_assessments(assessments_root, oa_block): if assessment_dict.get('due') is not None: assessment.set('due', unicode(assessment_dict['due'])) - if assessment_dict.get('algorithm_id') is not None: - assessment.set('algorithm_id', unicode(assessment_dict['algorithm_id'])) - if assessment_dict.get('required') is not None: assessment.set('required', unicode(assessment_dict['required'])) diff --git a/pylintrc b/pylintrc index ac4981ea5a..dbdf6a9c93 100644 --- a/pylintrc +++ b/pylintrc @@ -55,7 +55,7 @@ [MASTER] ignore = persistent = yes -load-plugins = edx_lint.pylint,pylint_django,pylint_celery +load-plugins = edx_lint.pylint,pylint_django [MESSAGES CONTROL] disable = diff --git a/requirements/base.txt b/requirements/base.txt index 0241c01ca7..8d1348ebb9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,31 +1,22 @@ -# Include the requirements we're caching as "wheel" archives -# to speed up the test builds. --r wheels.txt - # edX Internal Requirements -git+https://github.com/edx/XBlock.git@xblock-0.4.12#egg=XBlock==0.4.12 +edx-submissions>=2.0.6,<3.0.0 +git+https://github.com/edx/django-rest-framework.git@1ceda7c086fddffd1c440cc86856441bbf0bd9cb#egg=djangorestframework==3.6.3 +git+https://github.com/edx/XBlock.git@xblock-1.0.1#egg=XBlock==1.0.1 +git+https://github.com/edx/xblock-sdk.git@v0.1.4#egg=xblock-sdk==0.1.4 # Third Party Requirements -boto>=2.32.1,<3.0.0 -python-swiftclient==3.1.0 -celery==3.1.18 -defusedxml==0.4.1 -django<1.9a0 # Resolves known bug on gemnasium. See TNL-6266 -django-extensions==1.5.9 -django-model-utils==2.3.1 -git+https://github.com/edx/django-rest-framework.git@1ceda7c086fddffd1c440cc86856441bbf0bd9cb#egg=djangorestframework==3.6.3 -dogapi==1.2.1 -edx-submissions==2.0.6 -jsonfield==1.0.3 -lazy==1.1 -libsass==0.10.0 -loremipsum==1.0.5 -path.py==8.2.1 -python-dateutil==2.1 -python-memcached==1.48 +boto3>=1.4.4,<2.0.0 +python-swiftclient>=3.1.0,<4.0.0 +defusedxml>=0.4.1,<1.0.0 +django-model-utils>=2.3.1,<3.0.0 +django-nose>=1.4.1,<2.0.0 +dogapi>=1.2.1,<2.0.0 +jsonfield>=1.0.3,<2.0.0 +lazy>=1.1,<2.0 +libsass>=0.10.0,<1.0.0 +loremipsum>=1.0.5,<2.0.0 +lxml>=3.4.4,<4.0.0 +path.py>=8.2.1,<9.0.0 +python-dateutil>=2.1,<3.0 pytz -voluptuous==0.8.5 - -# AI grading -git+https://github.com/edx/ease.git@release-2015-07-14#egg=ease==0.1.3 -git+https://github.com/edx/nltk.git@2.0.6#egg=nltk==2.0.6 +voluptuous>=0.10.5,<1.0.0 diff --git a/requirements/dev.txt b/requirements/dev.txt deleted file mode 100644 index 64ef3d6dbc..0000000000 --- a/requirements/dev.txt +++ /dev/null @@ -1,13 +0,0 @@ --r test.txt - -# Debug tools -bpython==0.13 -django-debug-panel==0.8.1 -django-debug-toolbar==1.3.2 -django-pdb==0.3.2 -sqlparse==0.1.10 - -# runserver_plus -Werkzeug==0.9.6 - -git+https://github.com/edx/xblock-sdk.git@0.1.1#egg=xblock-sdk==0.1.1 \ No newline at end of file diff --git a/requirements/django.txt b/requirements/django.txt new file mode 100644 index 0000000000..a3bed1acb7 --- /dev/null +++ b/requirements/django.txt @@ -0,0 +1 @@ +django>=1.8,<2.0 diff --git a/requirements/perf.txt b/requirements/perf.txt deleted file mode 100644 index d31b162581..0000000000 --- a/requirements/perf.txt +++ /dev/null @@ -1,3 +0,0 @@ -locustio==0.7.0 -loremipsum==1.0.5 -pyzmq==14.0.1 diff --git a/requirements/quality.txt b/requirements/quality.txt new file mode 100644 index 0000000000..70e1ffb002 --- /dev/null +++ b/requirements/quality.txt @@ -0,0 +1,4 @@ +pep8==1.7.0 + +git+https://github.com/edx/edx-lint.git@0.5.4#egg=edx_lint==0.5.4 +git+https://github.com/edx/i18n-tools.git@v0.3.9#egg=i18n_tools==0.3.9 diff --git a/requirements/test-acceptance.txt b/requirements/test-acceptance.txt index 78de51a235..a4ecc50aef 100644 --- a/requirements/test-acceptance.txt +++ b/requirements/test-acceptance.txt @@ -1,5 +1,5 @@ bok-choy==0.5.3 ddt==1.0.0 nose==1.3.7 -pyinstrument==0.13.1 -selenium==2.53.1 +pyinstrument==0.13.2 +selenium==2.53.1 # when upgrading to 3.x, will need to ensure geckodriver is installed diff --git a/requirements/test.txt b/requirements/test.txt index 35192823ca..4c7d87acb6 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,14 +1,12 @@ --r base.txt --r test-acceptance.txt +-r quality.txt -coverage==4.0.2 -django-nose==1.4.1 -freezegun==0.1.11 -mock==1.0.1 -moto==0.3.1 -pep8==1.7.0 +coverage==4.4.1 +ddt==1.0.0 factory_boy==2.8.1 +freezegun==0.3.9 +mock==2.0.0 +moto==1.0.1 +nose==1.3.7 +tox==2.7.0 -git+https://github.com/edx/django-pyfs.git@1.0.3#egg=django-pyfs==1.0.3 -git+https://github.com/edx/i18n-tools.git@56f048af9b6868613c14aeae760548834c495011#egg=i18n_tools -git+https://github.com/edx/edx-lint.git@v0.4.1#egg=edx_lint==0.4.1 +git+https://github.com/edx/django-pyfs.git@1.0.6#egg=django-pyfs==1.0.6 diff --git a/logs/.gitkeep b/requirements/tox.txt similarity index 100% rename from logs/.gitkeep rename to requirements/tox.txt diff --git a/requirements/wheels.txt b/requirements/wheels.txt deleted file mode 100644 index a296282ebb..0000000000 --- a/requirements/wheels.txt +++ /dev/null @@ -1,8 +0,0 @@ -# These packages require compilation, so they take a long time to install. -# To speed up the test builds, we create binary "wheel" archives. -# http://pip.readthedocs.org/en/latest/reference/pip_wheel.html - -lxml==3.4.4 -numpy==1.6.2 -scikit-learn==0.12.1 -scipy==0.14.0 diff --git a/scripts/data/ai-test-data.json b/scripts/data/ai-test-data.json deleted file mode 100644 index f62c843297..0000000000 --- a/scripts/data/ai-test-data.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "text": "Food is any substance[1] consumed to provide nutritional support for the body. It is usually of plant or animal origin, and contains essential nutrients, such as carbohydrates, fats, proteins, vitamins, or minerals. The substance is ingested by an organism and assimilated by the organism's cells in an effort to produce energy, maintain life, or stimulate growth. Historically, people secured food through two methods: hunting and gathering, and agriculture. Today, most of the food energy consumed by the world population is supplied by the food industry. Food safety and food security are monitored by agencies like the International Association for Food Protection, World Resources Institute, World Food Programme, Food and Agriculture Organization, and International Food Information Council. They address issues such as sustainability, biological diversity, climate change, nutritional economics, population growth, water supply, and access to food.", - "score": 0 - }, - { - "text": "Most food has its origin in plants. Some food is obtained directly from plants; but even animals that are used as food sources are raised by feeding them food derived from plants. Cereal grain is a staple food that provides more food energy worldwide than any other type of crop. Maize, wheat, and rice – in all of their varieties – account for 87% of all grain production worldwide.[2] Most of the grain that is produced worldwide is fed to livestock. Some foods not from animal or plant sources include various edible fungi, especially mushrooms. Fungi and ambient bacteria are used in the preparation of fermented and pickled foods like leavened bread, alcoholic drinks, cheese, pickles, kombucha, and yogurt. Another example is blue-green algae such as Spirulina.[3] Inorganic substances such as salt, baking soda and cream of tartar are used to preserve or chemically alter an ingredient.", - "score": 1 - }, - { - "text": "Many plants or plant parts are eaten as food. There are around 2,000 plant species which are cultivated for food, and many have several distinct cultivars.[4] Seeds of plants are a good source of food for animals, including humans, because they contain the nutrients necessary for the plant's initial growth, including many healthful fats, such as Omega fats. In fact, the majority of food consumed by human beings are seed-based foods. Edible seeds include cereals (maize, wheat, rice, et cetera), legumes (beans, peas, lentils, et cetera), and nuts. Oilseeds are often pressed to produce rich oils - sunflower, flaxseed, rapeseed (including canola oil), sesame, et cetera.[5] Seeds are typically high in unsaturated fats and, in moderation, are considered a health food, although not all seeds are edible. Large seeds, such as those from a lemon, pose a choking hazard, while seeds from cherries and apples contain cyanide which could be poisonous only if consumed in large volumes.[6] Fruits are the ripened ovaries of plants, including the seeds within. Many plants and animals have coevolved such that the fruits of the former are an attractive food source to the latter, because animals that eat the fruits may excrete the seeds some distance away. Fruits, therefore, make up a significant part of the diets of most cultures. Some botanical fruits, such as tomatoes, pumpkins, and eggplants, are eaten as vegetables.[7] (For more information, see list of fruits.) Vegetables are a second type of plant matter that is commonly eaten as food. These include root vegetables (potatoes and carrots), bulbs (onion family), leaf vegetables (spinach and lettuce), stem vegetables (bamboo shoots and asparagus), and inflorescence vegetables (globe artichokes and broccoli and other vegetables such as cabbage or cauliflower).[8]", - "score": 0 - }, - { - "text": "Animals are used as food either directly or indirectly by the products they produce. Meat is an example of a direct product taken from an animal, which comes from muscle systems or from organs. Various raw meats Food products produced by animals include milk produced by mammary glands, which in many cultures is drunk or processed into dairy products (cheese, butter, etc.). In addition, birds and other animals lay eggs, which are often eaten, and bees produce honey, a reduced nectar from flowers, which is a popular sweetener in many cultures. Some cultures consume blood, sometimes in the form of blood sausage, as a thickener for sauces, or in a cured, salted form for times of food scarcity, and others use blood in stews such as jugged hare.[9] Some cultures and people do not consume meat or animal food products for cultural, dietary, health, ethical, or ideological reasons. Vegetarians choose to forgo food from animal sources to varying degrees. Vegans do not consume any foods that are or contain ingredients from an animal source.", - "score": 2 - }, - { - "text": "Most food has always been obtained through agriculture. With increasing concern over both the methods and products of modern industrial agriculture, there has been a growing trend toward sustainable agricultural practices. This approach, partly fueled by consumer demand, encourages biodiversity, local self-reliance and organic farming methods.[10] Major influences on food production include international organizations (e.g. the World Trade Organization and Common Agricultural Policy), national government policy (or law), and war.[11] In popular culture, the mass production of food, specifically meats such as chicken and beef, has come under fire from various documentaries, most recently Food, Inc, documenting the mass slaughter and poor treatment of animals, often for easier revenues from large corporations. Along with a current trend towards environmentalism, people in Western culture have had an increasing trend towards the use of herbal supplements, foods for a specific group of person (such as dieters, women, or athletes), functional foods (fortified foods, such as omega-3 eggs), and a more ethnically diverse diet.[12] Several organisations have begun calling for a new kind of agriculture in which agroecosystems provide food but also support vital ecosystem services so that soil fertility and biodiversity are maintained rather than compromised. According to the International Water Management Institute and UNEP, well-managed agroecosystems not only provide food, fiber and animal products, they also provide services such as flood mitigation, groundwater recharge, erosion control and habitats for plants, birds fish and other animals.[13]", - "score": 3 - }, - { - "text": "Generally regarded as the most pleasant taste, sweetness is almost always caused by a type of simple sugar such as glucose or fructose, or disaccharides such as sucrose, a molecule combining glucose and fructose.[16] Complex carbohydrates are long chains and thus do not have the sweet taste. Artificial sweeteners such as sucralose are used to mimic the sugar molecule, creating the sensation of sweet, without the calories. Other types of sugar include raw sugar, which is known for its amber color, as it is unprocessed. As sugar is vital for energy and survival, the taste of sugar is pleasant. The stevia plant contains a compound known as steviol which, when extracted, has 300 times the sweetness of sugar while having minimal impact on blood sugar.[17] Sour Sourness is caused by the taste of acids, such as vinegar in alcoholic beverages. Sour foods include citrus, specifically lemons, limes, and to a lesser degree oranges. Sour is evolutionarily significant as it is a sign for a food that may have gone rancid due to bacteria.[18] Many foods, however, are slightly acidic, and help stimulate the taste buds and enhance flavor.", - "score": 1 - }, - { - "text": "Saltiness is the taste of alkali metal ions such as sodium and potassium. It is found in almost every food in low to moderate proportions to enhance flavor, although to eat pure salt is regarded as highly unpleasant. There are many different types of salt, with each having a different degree of saltiness, including sea salt, fleur de sel, kosher salt, mined salt, and grey salt. Other than enhancing flavor, its significance is that the body needs and maintains a delicate electrolyte balance, which is the kidney's function. Salt may be iodized, meaning iodine has been added to it, a necessary nutrient that promotes thyroid function. Some canned foods, notably soups or packaged broths, tend to be high in salt as a means of preserving the food longer. Historically speaking, salt has been used as a meat preservative as salt promotes water excretion, thus working as a preservative. Similarly, dried foods also promote food safety.[19] Bitter Bitterness is a sensation often considered unpleasant characterized by having a sharp, pungent taste. Dark, unsweetened chocolate, caffeine, lemon rind, and some types of fruit are known to be bitter. Umami Also named as Savoury. Umami, the Japanese word for delicious, is the least known in Western popular culture but has a long tradition in Asian cuisine. Umami is the taste of glutamates, especially monosodium glutamate (MSG).[16] It is characterized as savory, meaty, and rich in flavor. Salmon and mushrooms are foods high in umami. Meat and other animal byproducts are described as having this taste.[citation needed]", - "score": 2 - } -] diff --git a/scripts/download-nltk-data.sh b/scripts/download-nltk-data.sh deleted file mode 100755 index ce973724c3..0000000000 --- a/scripts/download-nltk-data.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -PYTHON=`which python` -$PYTHON -m nltk.downloader stopwords maxent_treebank_pos_tagger wordnet --quiet diff --git a/scripts/install-python.sh b/scripts/install-python.sh deleted file mode 100755 index 80797c4c60..0000000000 --- a/scripts/install-python.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -cd `dirname $BASH_SOURCE` && cd .. - -echo "Installing Python requirements..." -pip install -q -r requirements/base.txt --exists-action w - -echo "Installing the OpenAssessment XBlock..." -cat <`_ to start the test. diff --git a/test/performance/__init__.py b/test/performance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/performance/course.tar.gz b/test/performance/course.tar.gz deleted file mode 100644 index be9b203737..0000000000 Binary files a/test/performance/course.tar.gz and /dev/null differ diff --git a/test/performance/locustfile.py b/test/performance/locustfile.py deleted file mode 100644 index 6cfc33ce34..0000000000 --- a/test/performance/locustfile.py +++ /dev/null @@ -1,248 +0,0 @@ -""" -Performance tests for the OpenAssessment XBlock. -""" - -import os -import json -import random -from collections import namedtuple -import gevent -import loremipsum -from locust import HttpLocust, TaskSet, task - - - -class OpenAssessmentPage(object): - """ - Encapsulate interactions with the OpenAssessment XBlock's pages. - """ - - # These assume that the course fixture has been installed - ProblemFixture = namedtuple('ProblemFixture', [ - 'course_id', 'base_url', 'base_handler_url', - 'rubric_options', 'render_step_handlers' - ]) - - PROBLEMS = { - 'peer_then_self': ProblemFixture( - course_id="ora2/1/1", - base_url= "courses/ora2/1/1/courseware/efa85eb090164a208d772a344df7181d/69f15a02c5af4e95b9c5525771b8f4ee/", - base_handler_url="courses/ora2/1/1/xblock/i4x:;_;_ora2;_1;_openassessment;_0e2bbf6cc89e45d98b028fa4e2d46314/handler/", - rubric_options={ - 'Ideas': ['Poor', 'Fair', 'Good'], - 'Content': ['Poor', 'Fair', 'Good', 'Excellent'] - }, - render_step_handlers=[ - 'render_submission', 'render_peer_assessment', - 'render_self_assessment', 'render_grade', - ] - ), - - 'example_based': ProblemFixture( - course_id="ora2/1/1", - base_url="courses/ora2/1/1/courseware/efa85eb090164a208d772a344df7181d/fb039ef8a34641509190918ada79122a/", - base_handler_url="courses/ora2/1/1/xblock/i4x:;_;_ora2;_1;_openassessment;_8df3fa4de26747e0ad99b4157e45f5e5/handler/", - rubric_options={ - 'Ideas': ['Bad', 'Good'], - 'Content': ['Bad', 'Good'] - }, - render_step_handlers=['render_submission', 'render_grade'] - ) - } - - def __init__(self, hostname, client, problem_name): - """ - Initialize the page to use specified HTTP client. - - Args: - hostname (unicode): The hostname (used for the referer HTTP header) - client (HttpSession): The HTTP client to use. - problem_name (unicode): Name of the problem (one of the keys in `OpenAssessmentPage.PROBLEMS`) - - """ - self.hostname = hostname - self.client = client - self.problem_fixture = self.PROBLEMS[problem_name] - self.logged_in = False - - # Configure basic auth - if 'BASIC_AUTH_USER' in os.environ and 'BASIC_AUTH_PASSWORD' in os.environ: - self.client.auth = (os.environ['BASIC_AUTH_USER'], os.environ['BASIC_AUTH_PASSWORD']) - - def log_in(self): - """ - Log in as a unique user with access to the XBlock(s) under test. - """ - resp = self.client.get( - "auto_auth", - params={'course_id': self.problem_fixture.course_id}, - verify=False, - timeout=120 - ) - self.logged_in = (resp.status_code == 200) - return self - - def load_steps(self): - """ - Load all steps in the OpenAssessment flow. - """ - # Load the container page - self.client.get(self.problem_fixture.base_url, verify=False) - - # Load each of the steps in parallel - get_unverified = lambda url: self.client.get(url, verify=False) - gevent.joinall([ - gevent.spawn(get_unverified, url) for url in [ - self.handler_url(handler) - for handler in self.problem_fixture.render_step_handlers - ] - ], timeout=0.5) - - return self - - def submit_response(self): - """ - Submit a response. - """ - payload = json.dumps({ - 'submission': u' '.join(loremipsum.get_paragraphs(random.randint(1, 10))), - }) - self.client.post(self.handler_url('submit'), data=payload, headers=self._post_headers, verify=False) - - def peer_assess(self, continue_grading=False): - """ - Assess a peer. - - Kwargs: - continue_grading (bool): If true, simulate "continued grading" - in which a student asks to assess peers in addition to the required number. - - """ - params = { - 'options_selected': self._select_random_options(), - 'overall_feedback': loremipsum.get_paragraphs(random.randint(1, 3)), - 'criterion_feedback': {} - } - - if continue_grading: - params['continue_grading'] = True - - payload = json.dumps(params) - self.client.post(self.handler_url('peer_assess'), data=payload, headers=self._post_headers, verify=False) - - def self_assess(self): - """ - Complete a self-assessment. - """ - payload = json.dumps({ - 'options_selected': self._select_random_options() - }) - self.client.post(self.handler_url('self_assess'), data=payload, headers=self._post_headers, verify=False) - - def handler_url(self, handler_name): - """ - Return the full URL for an XBlock handler. - - Args: - handler_name (str): The name of the XBlock handler method. - - Returns: - str - """ - return "{base}{handler}".format(base=self.problem_fixture.base_handler_url, handler=handler_name) - - def _select_random_options(self): - """ - Select random options for each criterion in the rubric. - """ - return { - criterion: random.choice(options) - for criterion, options in self.problem_fixture.rubric_options.iteritems() - } - - @property - def _post_headers(self): - """ - Headers for a POST request, including the CSRF token. - """ - return { - 'Content-type': 'application/json', - 'Accept': 'application/json', - 'X-CSRFToken': self.client.cookies.get('csrftoken', ''), - 'Referer': self.hostname - } - - -class OpenAssessmentTasks(TaskSet): - """ - Virtual user interactions with the OpenAssessment XBlock. - """ - - def __init__(self, *args, **kwargs): # pylint: disable=W0613 - """ - Initialize the task set. - """ - super(OpenAssessmentTasks, self).__init__(*args, **kwargs) - self.hostname = self.locust.host - self.page = None - - @task - def peer_and_self(self): - """ - Test the peer-->self workflow. - """ - if self.page is None: - self.page = OpenAssessmentPage(self.hostname, self.client, 'peer_then_self') # pylint: disable=E1101 - self.page.log_in() - - if not self.page.logged_in: - self.page.log_in() - else: - self._submit_response() - - # Randomly peer/self assess or log in as a new user. - # This should be sufficient to get students through - # the entire flow (satisfying the requirements for peer assessment). - action = random.randint(0, 100) - if action <= 80: - continue_grading = random.randint(0, 10) < 4 - self.page.peer_assess(continue_grading=continue_grading) - self.page.self_assess() - else: - self.page.log_in() - - @task - def example_based(self): - """ - Test example-based assessment only. - """ - if self.page is None: - self.page = OpenAssessmentPage(self.hostname, self.client, 'example_based') # pylint: disable=E1101 - self.page.log_in() - - if not self.page.logged_in: - self.page.log_in() - else: - self._submit_response() - if random.randint(0, 100) < 50: - self.page.log_in() - - def _submit_response(self): - """ - Simulate the user loading the page, submitting a response, - then reloading the steps (usually triggered by AJAX). - If the user has already submitted, the handler will return - an error message in the JSON, but the HTTP status will still be 200. - """ - self.page.load_steps() - self.page.submit_response() - self.page.load_steps() - - -class OpenAssessmentLocust(HttpLocust): - """ - Performance test definition for the OpenAssessment XBlock. - """ - task_set = OpenAssessmentTasks - min_wait = 10000 - max_wait = 15000 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000000..382ab04f38 --- /dev/null +++ b/tox.ini @@ -0,0 +1,40 @@ +[tox] +envlist = py27-django{18,111} + +[testenv] +whitelist_externals = make +passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH +deps = + coveralls + -rrequirements/base.txt + -rrequirements/test.txt + django18: django>=1.8,<1.9 + django111: django>=1.11,<2.0 +commands = + make test-python + +[testenv:js] +whitelist_externals = make +passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH +deps = + -rrequirements/base.txt +commands = + make install-js + make javascript + make sass + make verify-generated-files + make test-js + +[testenv:quality] +whitelist_externals = + make + npm +passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH +deps = + -rrequirements/base.txt + -rrequirements/quality.txt +commands = + npm install jshint --no-save + npm install jscs --no-save + make quality + python manage.py makemessages -l eo diff --git a/urls.py b/urls.py index 96353ed1ef..caaedb41fc 100644 --- a/urls.py +++ b/urls.py @@ -1,19 +1,19 @@ +import workbench.urls + from django.conf import settings -from django.conf.urls import include, patterns, url -from django.views.i18n import javascript_catalog +from django.conf.urls import include, url from django.contrib import admin +from django.views.i18n import javascript_catalog import openassessment.assessment.urls import openassessment.fileupload.urls -import workbench.urls # Packages to include in the JavaScript i18n strings JS_INFO_DICT = { 'packages': ('openassessment.xblock',), } -urlpatterns = patterns( - '', +urlpatterns = [ # Django built-in url(r'^admin/', include(admin.site.urls)), @@ -24,11 +24,11 @@ url(r'^peer/evaluations/', include(openassessment.assessment.urls)), # JavaScript i18n - (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', JS_INFO_DICT), + url(r'^jsi18n/$', javascript_catalog, JS_INFO_DICT), # File upload to local filesystem url(r'^openassessment/storage', include(openassessment.fileupload.urls)), -) +] # We need to do explicit setup of the Django debug toolbar because autodiscovery # causes problems when you mix debug toolbar >= 1.0 + django < 1.7, and the @@ -36,6 +36,4 @@ # http://django-debug-toolbar.readthedocs.org/en/1.0/installation.html#explicit-setup if settings.DEBUG: import debug_toolbar - urlpatterns += patterns('', - url(r'^__debug__/', include(debug_toolbar.urls)), - ) + urlpatterns.append(url(r'^__debug__/', include(debug_toolbar.urls)))