jazkarta / edx-platform

the edX learning management system (LMS) and course authoring tool, Studio
http://code.edx.org/
GNU Affero General Public License v3.0
1 stars 0 forks source link

Fix problems in viewing/downloading gradebook #98

Closed cewing closed 9 years ago

cewing commented 9 years ago

from an email to @cpennington and @dormsbee:

After updating all the URLs on the coach dashboard to use the new CCXLocator-based urls, the gradebook no longer works. Each time we attempt to access a student’s grades using the “student_grades” method from “instructor.offline_gradecalc” we are seeing the traceback I include below.

At first I thought that this was a problem with the change in how we are getting a course from the CCXLocator-based url, but then I couldn’t see any difference in the course between what we were doing before and what we are doing now. So I went to a course and tried to view the gradebook (using the student gradebook link on the “student admin” section of the “instructor” tab of the course), and got exactly the same traceback.

This peaked my curiosity so I tried again with another course that uses the deprecated form of the course id. Here the traceback did not occur.

So I’m wondering if there’s a bug in how student grades are calculated with new-style course IDs, or if I’m doing something wrong.

Here’s how I got where I am:

  1. I set up devstack and checked out my ccx-custom-ids branch from the Jazkarta repository of edx-platform (https://github.com/jazkarta/edx-platform/tree/ccx-custom-ids)
  2. I started up the lms with paver devstack lms to get it to checkout my ccx-keys package (https://github.com/jazkarta/ccx-keys)
  3. I stopped the lms and started up the studio
  4. I created a course with a new-style identifier by making a new run of the demo course that comes with devstack
  5. I completed setting up the course by setting dates, adding some staff, and publishing everything
  6. I stopped studio and restarted lms
  7. I logged in to LMS as staff@example.com and viewed the new course I created. I enrolled “verified@example.com” so that there would be a student in the course.
  8. I went to the instructor tab and to the “student admin’ sub-tab.
  9. I clicked on “student gradebook” (the link at the end of the top section) and got the traceback at the end of this email:

If I check the gradebook for the original demo course (which is using the old-style deprecated org/course/run id format) the same procedure does not result in errors and I see the gradebook just fine.

cewing commented 9 years ago

The traceback in question:

Environment:

Request Method: GET
Request URL: http://localhost:8000/courses/course-v1:edX+DemoX+example_run/instructor/api/gradebook

Django Version: 1.4.20
Python Version: 2.7.3
Installed Applications:
('django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.humanize',
 'django.contrib.messages',
 'django.contrib.sessions',
 'django.contrib.sites',
 'djcelery',
 'south',
 'config_models',
 'service_status',
 'edxmako',
 'pipeline',
 'staticfiles',
 'static_replace',
 'circuit',
 'courseware',
 'student',
 'static_template_view',
 'staticbook',
 'track',
 'eventtracking.django',
 'util',
 'certificates',
 'dashboard',
 'instructor',
 'instructor_task',
 'open_ended_grading',
 'psychometrics',
 'licenses',
 'openedx.core.djangoapps.course_groups',
 'bulk_email',
 'branding',
 'external_auth',
 'django_openid_auth',
 'provider',
 'provider.oauth2',
 'oauth2_provider',
 'oauth_exchange',
 'wiki',
 'django_notify',
 'course_wiki',
 'mptt',
 'sekizai',
 'wiki.plugins.links',
 'wiki.plugins.notifications',
 'course_wiki.plugins.markdownedx',
 'foldit',
 'django.contrib.admin',
 'django_nose',
 'debug',
 'django_comment_client',
 'django_comment_common',
 'discussion_api',
 'notes',
 'edxnotes',
 'splash',
 'datadog',
 'rest_framework',
 'openedx.core.djangoapps.user_api',
 'shoppingcart',
 'notification_prefs',
 'notifier_api',
 'course_modes',
 'enrollment',
 'verify_student',
 'dark_lang',
 'microsite_configuration',
 'reverification',
 'embargo',
 'monitoring',
 'course_action_state',
 'edx_jsme',
 'django_countries',
 'mobile_api',
 'survey',
 'lms.djangoapps.lms_xblock',
 'openedx.core.djangoapps.content.course_structures',
 'course_structure_api',
 'mailing',
 'corsheaders',
 'cors_csrf',
 'commerce',
 'django_openid_auth',
 'edx_sga',
 'submissions',
 'openassessment',
 'openassessment.assessment',
 'openassessment.fileupload',
 'openassessment.workflow',
 'openassessment.xblock',
 'edxval',
 'milestones',
 'debug_toolbar_mongo',
 'ccx')
Installed Middleware:
('request_cache.middleware.RequestCache',
 'microsite_configuration.middleware.MicrositeMiddleware',
 'django_comment_client.middleware.AjaxExceptionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
 'student.middleware.UserStandingMiddleware',
 'contentserver.middleware.StaticContentServer',
 'crum.CurrentRequestUserMiddleware',
 'openedx.core.djangoapps.user_api.middleware.UserTagsEventContextMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'track.middleware.TrackMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'cors_csrf.middleware.CorsCSRFMiddleware',
 'cors_csrf.middleware.CsrfCrossDomainCookieMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'splash.middleware.SplashMiddleware',
 'dark_lang.middleware.DarkLangMiddleware',
 'geoinfo.middleware.CountryMiddleware',
 'embargo.middleware.EmbargoMiddleware',
 'lang_pref.middleware.LanguagePreferenceMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.transaction.TransactionMiddleware',
 'django_comment_client.utils.ViewNameMiddleware',
 'codejail.django_integration.ConfigureCodeJailMiddleware',
 'ratelimitbackend.middleware.RateLimitMiddleware',
 'edxmako.middleware.MakoMiddleware',
 'session_inactivity_timeout.middleware.SessionInactivityTimeout',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'courseware.middleware.RedirectUnenrolledMiddleware',
 'course_wiki.middleware.WikiAccessMiddleware',
 'microsite_configuration.middleware.MicrositeSessionCookieDomainMiddleware',
 'ccx.overrides.CcxMiddleware')

Traceback:
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  109.                         response = callback(request, *callback_args, **callback_kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/decorators/cache.py" in _cache_controlled
  75.             response = viewfunc(request, *args, **kw)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py" in wrapped
  234.                 return func(*args, **kwargs)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py" in spoc_gradebook
  2390.         for student in enrolled_students
File "/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/offline_gradecalc.py" in student_grades
  85.         return grades.grade(student, request, course, keep_raw_scores=keep_raw_scores)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/db/transaction.py" in inner
  224.                 return func(*args, **kwargs)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/grades.py" in grade
  131.         return _grade(student, request, course, keep_raw_scores)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/grades.py" in _grade
  215.                         course.id, student, module_descriptor, create_module, scores_cache=submissions_scores
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/grades.py" in get_score
  450.         problem = module_creator(problem_descriptor)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/grades.py" in create_module
  210.                     return get_module_for_descriptor(student, request, descriptor, field_data_cache, course.id)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/module_render.py" in get_module_for_descriptor
  277.         request_token=request_token(request),
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/module_render.py" in get_module_for_descriptor_internal
  704.         if not has_access(user, 'load', descriptor, course_id):
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/access.py" in has_access
  97.         return _has_access_descriptor(user, action, obj, course_key)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/access.py" in _has_access_descriptor
  457.     return _dispatch(checkers, action, user, descriptor)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/access.py" in _dispatch
  538.         result = table[action]()
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/access.py" in can_load
  418.         if descriptor.visible_to_staff_only and not _has_staff_access_to_descriptor(user, descriptor, course_key):
File "/edx/app/edxapp/venvs/edxapp/src/xblock/xblock/fields.py" in __get__
  473.             if field_data.has(xblock, self.name):
File "/edx/app/edxapp/venvs/edxapp/src/xblock/xblock/field_data.py" in has
  174.         return self._field_data(block, name).has(block, name)
File "/edx/app/edxapp/venvs/edxapp/src/xblock/xblock/field_data.py" in has
  198.         return self._source.has(block, name)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/field_overrides.py" in has
  120.                     if self.get_override(ancestor, name) is not NOTSET:
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/field_overrides.py" in get_override
  94.                 value = provider.get(block, name, NOTSET)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/student_field_overrides.py" in get
  18.         return get_override_for_user(self.user, block, name, default)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/student_field_overrides.py" in get_override_for_user
  31.         overrides = _get_overrides_for_user(user, block)
File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/student_field_overrides.py" in _get_overrides_for_user
  42.         course_id=block.runtime.course_id,
File "/edx/app/edxapp/edx-platform/common/lib/xmodule/xmodule/x_module.py" in __getattr__
  1750.             return getattr(self._descriptor_system, name)

Exception Type: AttributeError at /courses/course-v1:edX+DemoX+example_run/instructor/api/gradebook
Exception Value: 'CachingDescriptorSystem' object has no attribute ‘course_id'
pdpinch commented 9 years ago

There was an conversation over e-mail that I will paste in here.

cewing commented 9 years ago

From the email conversation:

This looked to me like a discrepancy between the DescriptorSystems produced by old-mongo and split-mongo (and possibly between ModuleSystem and DescriptorSystem). So, I poked around, and lo-and-behold: ModuleSystem sets a course_id attribute: https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/x_module.py#L1619 The CachingDescriptorSystem for OldMongo sets a course_id attribute: https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/modulestore/mongo/base.py#L223 The CachingDescriptorSystem for SplitMongo doesn't: https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py#L42-L79 This is a problem now because student_field_overrides is reading that attribute, but the grading code tries really hard to only give you XBlocks that haven't been bound to a student (and thus only have a DescriptorSystem, not a ModuleSystem).

Student-specific overrides seems incompatible to me with having an un-bound XBlock, but I'm not sure whether to force the XBlock to be bound, or whether there's another option.

pdpinch commented 9 years ago

Thanks Cris. I would also note this follow-up from Cale:

Yeah, your code just tripped over an API inconsistency.

Probably the easiest thing (immediately) would just be to add course_id to the SplitMongo CachingDescriptorSystem (and possibly to the base DescriptorSystem object).

On May 27, 2015, at 10:38 PM, Cris Ewing notifications@github.com wrote:

From the email conversation:

This looked to me like a discrepancy between the DescriptorSystems produced by old-mongo and split-mongo (and possibly between ModuleSystem and DescriptorSystem). So, I poked around, and lo-and-behold: ModuleSystem sets a course_id attribute: https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/x_module.py#L1619 The CachingDescriptorSystem for OldMongo sets a course_id attribute: https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/modulestore/mongo/base.py#L223 The CachingDescriptorSystem for SplitMongo doesn't: https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py#L42-L79 This is a problem now because student_field_overrides is reading that attribute, but the grading code tries really hard to only give you XBlocks that haven't been bound to a student (and thus only have a DescriptorSystem, not a ModuleSystem).

Student-specific overrides seems incompatible to me with having an un-bound XBlock, but I'm not sure whether to force the XBlock to be bound, or whether there's another option.

— Reply to this email directly or view it on GitHub.

pdpinch commented 9 years ago

I just finished discussing this with @ormsbee

He felt that we should go ahead with the suggestion from @cpennington for now:

Probably the easiest thing (immediately) would just be to add course_id to the SplitMongo CachingDescriptorSystem (and possibly to the base DescriptorSystem object).

But, once the CCX usage keys are in place, we should think about leveraging the more standard grading path in the instructor dashboard, i.e. Instructor > Data Download > Generate Grade report.

I think we avoided that earlier because we didn't want to think about recreating the whole infrastructure for Data Download, but now that we have a unique key for each CCX that wouldn't be necessary.

I'll open a new issue for that investigation (which starts with some research on MIT's part)

cewing commented 9 years ago

This is fixed, but the tests for the gradebook are still failing. We have another issue open (#96) to cover fixing tests, so I will close this.