Commit d887c0fe by Sarina Canelake

Remove 'open_ended_grading' djangoapp & URLs (ORA1)

parent cd9fe577
...@@ -1016,7 +1016,6 @@ ADVANCED_COMPONENT_TYPES = [ ...@@ -1016,7 +1016,6 @@ ADVANCED_COMPONENT_TYPES = [
'rate', # Allows up-down voting of course content. See https://github.com/pmitros/RateXBlock 'rate', # Allows up-down voting of course content. See https://github.com/pmitros/RateXBlock
'split_test', 'split_test',
'combinedopenended',
'peergrading', 'peergrading',
'notes', 'notes',
'schoolyourself_review', 'schoolyourself_review',
......
import datetime
import json
import logging
from django.conf import settings
from xmodule.open_ended_grading_classes import peer_grading_service
from xmodule.open_ended_grading_classes.controller_query_service import ControllerQueryService
from courseware.access import has_access
from edxmako.shortcuts import render_to_string
from student.models import unique_id_for_user
from util.cache import cache
from .staff_grading_service import StaffGradingService
log = logging.getLogger(__name__)
NOTIFICATION_CACHE_TIME = 300
KEY_PREFIX = "open_ended_"
NOTIFICATION_TYPES = (
('student_needs_to_peer_grade', 'peer_grading', 'Peer Grading'),
('staff_needs_to_grade', 'staff_grading', 'Staff Grading'),
('new_student_grading_to_view', 'open_ended_problems', 'Problems you have submitted'),
('flagged_submissions_exist', 'open_ended_flagged_problems', 'Flagged Submissions')
)
def staff_grading_notifications(course, user):
staff_gs = StaffGradingService(settings.OPEN_ENDED_GRADING_INTERFACE)
pending_grading = False
img_path = ""
course_id = course.id
student_id = unique_id_for_user(user)
notification_type = "staff"
success, notification_dict = get_value_from_cache(student_id, course_id, notification_type)
if success:
return notification_dict
try:
notifications = json.loads(staff_gs.get_notifications(course_id))
if notifications['success']:
if notifications['staff_needs_to_grade']:
pending_grading = True
except:
#Non catastrophic error, so no real action
notifications = {}
#This is a dev_facing_error
log.info(
"Problem with getting notifications from staff grading service for course {0} user {1}.".format(course_id,
student_id))
if pending_grading:
img_path = "/static/images/grading_notification.png"
notification_dict = {'pending_grading': pending_grading, 'img_path': img_path, 'response': notifications}
set_value_in_cache(student_id, course_id, notification_type, notification_dict)
return notification_dict
def peer_grading_notifications(course, user):
peer_gs = peer_grading_service.PeerGradingService(settings.OPEN_ENDED_GRADING_INTERFACE, render_to_string)
pending_grading = False
img_path = ""
course_id = course.id
student_id = unique_id_for_user(user)
notification_type = "peer"
success, notification_dict = get_value_from_cache(student_id, course_id, notification_type)
if success:
return notification_dict
try:
notifications = json.loads(peer_gs.get_notifications(course_id, student_id))
if notifications['success']:
if notifications['student_needs_to_peer_grade']:
pending_grading = True
except:
#Non catastrophic error, so no real action
notifications = {}
#This is a dev_facing_error
log.info(
"Problem with getting notifications from peer grading service for course {0} user {1}.".format(course_id,
student_id))
if pending_grading:
img_path = "/static/images/grading_notification.png"
notification_dict = {'pending_grading': pending_grading, 'img_path': img_path, 'response': notifications}
set_value_in_cache(student_id, course_id, notification_type, notification_dict)
return notification_dict
def combined_notifications(course, user):
"""
Show notifications to a given user for a given course. Get notifications from the cache if possible,
or from the grading controller server if not.
@param course: The course object for which we are getting notifications
@param user: The user object for which we are getting notifications
@return: A dictionary with boolean pending_grading (true if there is pending grading), img_path (for notification
image), and response (actual response from grading controller server).
"""
#Set up return values so that we can return them for error cases
pending_grading = False
img_path = ""
notifications = {}
notification_dict = {'pending_grading': pending_grading, 'img_path': img_path, 'response': notifications}
#We don't want to show anonymous users anything.
if not user.is_authenticated():
return notification_dict
#Initialize controller query service using our mock system
controller_qs = ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE, render_to_string)
student_id = unique_id_for_user(user)
user_is_staff = bool(has_access(user, 'staff', course))
course_id = course.id
notification_type = "combined"
#See if we have a stored value in the cache
success, notification_dict = get_value_from_cache(student_id, course_id, notification_type)
if success:
return notification_dict
#Get the time of the last login of the user
last_login = user.last_login
last_time_viewed = last_login - datetime.timedelta(seconds=(NOTIFICATION_CACHE_TIME + 60))
try:
#Get the notifications from the grading controller
notifications = controller_qs.check_combined_notifications(
course.id,
student_id,
user_is_staff,
last_time_viewed,
)
if notifications.get('success'):
if (notifications.get('staff_needs_to_grade') or
notifications.get('student_needs_to_peer_grade')):
pending_grading = True
except:
#Non catastrophic error, so no real action
#This is a dev_facing_error
log.exception(
u"Problem with getting notifications from controller query service for course {0} user {1}.".format(
course_id, student_id))
if pending_grading:
img_path = "/static/images/grading_notification.png"
notification_dict = {'pending_grading': pending_grading, 'img_path': img_path, 'response': notifications}
#Store the notifications in the cache
set_value_in_cache(student_id, course_id, notification_type, notification_dict)
return notification_dict
def get_value_from_cache(student_id, course_id, notification_type):
key_name = create_key_name(student_id, course_id, notification_type)
success, value = _get_value_from_cache(key_name)
return success, value
def set_value_in_cache(student_id, course_id, notification_type, value):
key_name = create_key_name(student_id, course_id, notification_type)
_set_value_in_cache(key_name, value)
def create_key_name(student_id, course_id, notification_type):
key_name = u"{prefix}{type}_{course}_{student}".format(
prefix=KEY_PREFIX,
type=notification_type,
course=course_id,
student=student_id,
)
return key_name
def _get_value_from_cache(key_name):
value = cache.get(key_name)
success = False
if value is None:
return success, value
try:
value = json.loads(value)
success = True
except:
pass
return success, value
def _set_value_in_cache(key_name, value):
cache.set(key_name, json.dumps(value), NOTIFICATION_CACHE_TIME)
"""
LMS part of instructor grading:
- views + ajax handling
- calls the instructor grading service
"""
import logging
log = logging.getLogger(__name__)
class StaffGrading(object):
"""
Wrap up functionality for staff grading of submissions--interface exposes get_html, ajax views.
"""
def __init__(self, course):
self.course = course
def get_html(self):
return "<b>Instructor grading!</b>"
# context = {}
# return render_to_string('courseware/instructor_grading_view.html', context)
import logging
from urllib import urlencode
from xmodule.modulestore import search
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem
from xmodule.open_ended_grading_classes.controller_query_service import ControllerQueryService
from xmodule.open_ended_grading_classes.grading_service_module import GradingServiceError
from django.utils.translation import ugettext as _
from django.conf import settings
from edxmako.shortcuts import render_to_string
log = logging.getLogger(__name__)
GRADER_DISPLAY_NAMES = {
'ML': _("AI Assessment"),
'PE': _("Peer Assessment"),
'NA': _("Not yet available"),
'BC': _("Automatic Checker"),
'IN': _("Instructor Assessment"),
}
STUDENT_ERROR_MESSAGE = _("Error occurred while contacting the grading service. Please notify course staff.")
STAFF_ERROR_MESSAGE = _("Error occurred while contacting the grading service. Please notify your edX point of contact.")
def generate_problem_url(problem_url_parts, base_course_url):
"""
From a list of problem url parts generated by search.path_to_location and a base course url, generates a url to a problem
@param problem_url_parts: Output of search.path_to_location
@param base_course_url: Base url of a given course
@return: A path to the problem
"""
activate_block_id = problem_url_parts[-1]
problem_url_parts = problem_url_parts[0:-1]
problem_url = base_course_url + "/"
for i, part in enumerate(problem_url_parts):
if part is not None:
# This is the course_key. We need to turn it into its deprecated
# form.
if i == 0:
part = part.to_deprecated_string()
# This is placed between the course id and the rest of the url.
if i == 1:
problem_url += "courseware/"
problem_url += part + "/"
problem_url += '?{}'.format(urlencode({'activate_block_id': unicode(activate_block_id)}))
return problem_url
def does_location_exist(usage_key):
"""
Checks to see if a valid module exists at a given location (ie has not been deleted)
course_id - string course id
location - string location
"""
try:
search.path_to_location(modulestore(), usage_key)
return True
except ItemNotFoundError:
# If the problem cannot be found at the location received from the grading controller server,
# it has been deleted by the course author.
return False
except NoPathToItem:
# If the problem can be found, but there is no path to it, then we assume it is a draft.
# Log a warning in any case.
log.warn("Got an unexpected NoPathToItem error in staff grading with location %s. "
"This is ok if it is a draft; ensure that the location is valid.", usage_key)
return False
def create_controller_query_service():
"""
Return an instance of a service that can query edX ORA.
"""
return ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE, render_to_string)
class StudentProblemList(object):
"""
Get a list of problems that the student has attempted from ORA.
Add in metadata as needed.
"""
def __init__(self, course_id, user_id):
"""
@param course_id: The id of a course object. Get using course.id.
@param user_id: The anonymous id of the user, from the unique_id_for_user function.
"""
self.course_id = course_id
self.user_id = user_id
# We want to append this string to all of our error messages.
self.course_error_ending = _("for course {0} and student {1}.").format(self.course_id, user_id)
# This is our generic error message.
self.error_text = STUDENT_ERROR_MESSAGE
self.success = False
# Create a service to query edX ORA.
self.controller_qs = create_controller_query_service()
def fetch_from_grading_service(self):
"""
Fetch a list of problems that the student has answered from ORA.
Handle various error conditions.
@return: A boolean success indicator.
"""
# In the case of multiple calls, ensure that success is false initially.
self.success = False
try:
#Get list of all open ended problems that the grading server knows about
problem_list_dict = self.controller_qs.get_grading_status_list(self.course_id, self.user_id)
except GradingServiceError:
log.error("Problem contacting open ended grading service " + self.course_error_ending)
return self.success
except ValueError:
log.error("Problem with results from external grading service for open ended" + self.course_error_ending)
return self.success
success = problem_list_dict['success']
if 'error' in problem_list_dict:
self.error_text = problem_list_dict['error']
return success
if 'problem_list' not in problem_list_dict:
log.error("Did not receive a problem list in ORA response" + self.course_error_ending)
return success
self.problem_list = problem_list_dict['problem_list']
self.success = True
return self.success
def add_problem_data(self, base_course_url):
"""
Add metadata to problems.
@param base_course_url: the base url for any course. Can get with reverse('course')
@return: A list of valid problems in the course and their appended data.
"""
# Our list of valid problems.
valid_problems = []
if not self.success or not isinstance(self.problem_list, list):
log.error("Called add_problem_data without a valid problem list" + self.course_error_ending)
return valid_problems
# Iterate through all of our problems and add data.
for problem in self.problem_list:
try:
# Try to load the problem.
usage_key = self.course_id.make_usage_key_from_deprecated_string(problem['location'])
problem_url_parts = search.path_to_location(modulestore(), usage_key)
except (ItemNotFoundError, NoPathToItem):
# If the problem cannot be found at the location received from the grading controller server,
# it has been deleted by the course author. We should not display it.
error_message = "Could not find module for course {0} at location {1}".format(self.course_id,
problem['location'])
log.error(error_message)
continue
# Get the problem url in the courseware.
problem_url = generate_problem_url(problem_url_parts, base_course_url)
# Map the grader name from ORA to a human readable version.
grader_type_display_name = GRADER_DISPLAY_NAMES.get(problem['grader_type'], "edX Assessment")
problem['actual_url'] = problem_url
problem['grader_type_display_name'] = grader_type_display_name
valid_problems.append(problem)
return valid_problems
...@@ -1029,26 +1029,6 @@ PAID_COURSE_REGISTRATION_CURRENCY = ['usd', '$'] ...@@ -1029,26 +1029,6 @@ PAID_COURSE_REGISTRATION_CURRENCY = ['usd', '$']
# Members of this group are allowed to generate payment reports # Members of this group are allowed to generate payment reports
PAYMENT_REPORT_GENERATOR_GROUP = 'shoppingcart_report_access' PAYMENT_REPORT_GENERATOR_GROUP = 'shoppingcart_report_access'
################################# open ended grading config #####################
#By setting up the default settings with an incorrect user name and password,
# will get an error when attempting to connect
OPEN_ENDED_GRADING_INTERFACE = {
'url': 'http://example.com/peer_grading',
'username': 'incorrect_user',
'password': 'incorrect_pass',
'staff_grading': 'staff_grading',
'peer_grading': 'peer_grading',
'grading_controller': 'grading_controller'
}
# Used for testing, debugging peer grading
MOCK_PEER_GRADING = False
# Used for testing, debugging staff grading
MOCK_STAFF_GRADING = False
################################# EdxNotes config ######################### ################################# EdxNotes config #########################
# Configure the LMS to use our stub EdxNotes implementation # Configure the LMS to use our stub EdxNotes implementation
...@@ -1828,7 +1808,6 @@ INSTALLED_APPS = ( ...@@ -1828,7 +1808,6 @@ INSTALLED_APPS = (
'dashboard', 'dashboard',
'instructor', 'instructor',
'instructor_task', 'instructor_task',
'open_ended_grading',
'openedx.core.djangoapps.course_groups', 'openedx.core.djangoapps.course_groups',
'bulk_email', 'bulk_email',
'branding', 'branding',
......
...@@ -549,61 +549,6 @@ urlpatterns += ( ...@@ -549,61 +549,6 @@ urlpatterns += (
), ),
# see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls # see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls
# Open Ended grading views
url(
r'^courses/{}/staff_grading$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.views.staff_grading',
name='staff_grading',
),
url(
r'^courses/{}/staff_grading/get_next$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.staff_grading_service.get_next',
name='staff_grading_get_next',
),
url(
r'^courses/{}/staff_grading/save_grade$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.staff_grading_service.save_grade',
name='staff_grading_save_grade',
),
url(
r'^courses/{}/staff_grading/get_problem_list$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.staff_grading_service.get_problem_list',
name='staff_grading_get_problem_list',
),
# Open Ended problem list
url(
r'^courses/{}/open_ended_problems$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.views.student_problem_list',
name='open_ended_problems',
),
# Open Ended flagged problem list
url(
r'^courses/{}/open_ended_flagged_problems$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.views.flagged_problem_list',
name='open_ended_flagged_problems',
),
url(
r'^courses/{}/open_ended_flagged_problems/take_action_on_flags$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.views.take_action_on_flags',
name='open_ended_flagged_problems_take_action',
),
# Cohorts management # Cohorts management
url( url(
r'^courses/{}/cohorts/settings$'.format( r'^courses/{}/cohorts/settings$'.format(
...@@ -655,23 +600,6 @@ urlpatterns += ( ...@@ -655,23 +600,6 @@ urlpatterns += (
name='cohort_discussion_topics', name='cohort_discussion_topics',
), ),
# Open Ended Notifications
url(
r'^courses/{}/open_ended_notifications$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.views.combined_notifications',
name='open_ended_notifications',
),
url(
r'^courses/{}/peer_grading$'.format(
settings.COURSE_ID_PATTERN,
),
'open_ended_grading.views.peer_grading',
name='peer_grading',
),
url( url(
r'^courses/{}/notes$'.format( r'^courses/{}/notes$'.format(
settings.COURSE_ID_PATTERN, settings.COURSE_ID_PATTERN,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment