Unverified Commit dad73637 by Brian Mesick Committed by GitHub

Merge pull request #16495 from edx/bmedx/django111_startup_fixes

Changes necessary for Django 1.11 tests to start
parents d9970ef9 d3bbb86f
...@@ -7,7 +7,6 @@ from django.apps import AppConfig ...@@ -7,7 +7,6 @@ from django.apps import AppConfig
import cms.lib.xblock.runtime import cms.lib.xblock.runtime
import xmodule.x_module import xmodule.x_module
from openedx.core.lib.xblock_utils import xblock_local_resource_url
class XBlockConfig(AppConfig): class XBlockConfig(AppConfig):
...@@ -18,6 +17,8 @@ class XBlockConfig(AppConfig): ...@@ -18,6 +17,8 @@ class XBlockConfig(AppConfig):
verbose_name = u'XBlock Configuration' verbose_name = u'XBlock Configuration'
def ready(self): def ready(self):
from openedx.core.lib.xblock_utils import xblock_local_resource_url
# In order to allow descriptors to use a handler url, we need to # In order to allow descriptors to use a handler url, we need to
# monkey-patch the x_module library. # monkey-patch the x_module library.
# TODO: Remove this code when Runtimes are no longer created by modulestores # TODO: Remove this code when Runtimes are no longer created by modulestores
......
...@@ -19,6 +19,9 @@ def run(): ...@@ -19,6 +19,9 @@ def run():
NOTE: DO **NOT** add additional code to this method or this file! The Platform Team NOTE: DO **NOT** add additional code to this method or this file! The Platform Team
is moving all startup code to more standard locations using Django best practices. is moving all startup code to more standard locations using Django best practices.
""" """
django_db_models_options.patch() # TODO: Remove Django 1.11 upgrade shim
# SHIM: We should be able to get rid of this monkey patch post-upgrade
if django.VERSION[0] == 1 and django.VERSION[1] < 10:
django_db_models_options.patch()
django.setup() django.setup()
...@@ -5,7 +5,6 @@ from django.contrib.admin import autodiscover as django_autodiscover ...@@ -5,7 +5,6 @@ from django.contrib.admin import autodiscover as django_autodiscover
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import contentstore.views import contentstore.views
import django_cas.views
import openedx.core.djangoapps.common_views.xblock import openedx.core.djangoapps.common_views.xblock
import openedx.core.djangoapps.debug.views import openedx.core.djangoapps.debug.views
import openedx.core.djangoapps.external_auth.views import openedx.core.djangoapps.external_auth.views
...@@ -180,6 +179,8 @@ if settings.FEATURES.get('ENABLE_SERVICE_STATUS'): ...@@ -180,6 +179,8 @@ if settings.FEATURES.get('ENABLE_SERVICE_STATUS'):
urlpatterns.append(url(r'^status/', include('openedx.core.djangoapps.service_status.urls'))) urlpatterns.append(url(r'^status/', include('openedx.core.djangoapps.service_status.urls')))
if settings.FEATURES.get('AUTH_USE_CAS'): if settings.FEATURES.get('AUTH_USE_CAS'):
import django_cas.views
urlpatterns += [ urlpatterns += [
url(r'^cas-auth/login/$', openedx.core.djangoapps.external_auth.views.cas_login, name="cas-login"), url(r'^cas-auth/login/$', openedx.core.djangoapps.external_auth.views.cas_login, name="cas-login"),
url(r'^cas-auth/logout/$', django_cas.views.logout, {'next_page': '/'}, name="cas-logout"), url(r'^cas-auth/logout/$', django_cas.views.logout, {'next_page': '/'}, name="cas-logout"),
......
...@@ -116,9 +116,10 @@ class SAMLProviderDataAdmin(admin.ModelAdmin): ...@@ -116,9 +116,10 @@ class SAMLProviderDataAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
if obj: # editing an existing object if obj: # editing an existing object
return self.model._meta.get_all_field_names() # pylint: disable=protected-access return [field.name for field in self.model._meta.get_fields()] # pylint: disable=protected-access
return self.readonly_fields return self.readonly_fields
admin.site.register(SAMLProviderData, SAMLProviderDataAdmin) admin.site.register(SAMLProviderData, SAMLProviderDataAdmin)
......
...@@ -10,11 +10,11 @@ Example usage: ...@@ -10,11 +10,11 @@ Example usage:
""" """
import logging import logging
from optparse import make_option
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from six import text_type
from certificates.models import CertificateStatuses, GeneratedCertificate from certificates.models import CertificateStatuses, GeneratedCertificate
...@@ -24,33 +24,41 @@ LOGGER = logging.getLogger(__name__) ...@@ -24,33 +24,41 @@ LOGGER = logging.getLogger(__name__)
class Command(BaseCommand): class Command(BaseCommand):
"""Create a fake certificate for a user in a course. """ """Create a fake certificate for a user in a course. """
USAGE = u'Usage: create_fake_cert <USERNAME> <COURSE_KEY> --mode <MODE> --status <STATUS> --grade <GRADE>' def add_arguments(self, parser):
parser.add_argument(
'username',
metavar='USERNAME',
help='Username of the user to create the fake cert for'
)
parser.add_argument(
'course_key',
metavar='COURSE_KEY',
help='Course key of the course to grant the cert for'
)
option_list = BaseCommand.option_list + ( parser.add_argument(
make_option(
'-m', '--mode', '-m', '--mode',
metavar='CERT_MODE', metavar='CERT_MODE',
dest='cert_mode', dest='cert_mode',
default='honor', default='honor',
help='The course mode of the certificate (e.g. "honor", "verified", or "professional")' help='The course mode of the certificate (e.g. "honor", "verified", or "professional")'
), )
make_option( parser.add_argument(
'-s', '--status', '-s', '--status',
metavar='CERT_STATUS', metavar='CERT_STATUS',
dest='status', dest='status',
default=CertificateStatuses.downloadable, default=CertificateStatuses.downloadable,
help='The status of the certificate' help='The status of the certificate'
), )
make_option( parser.add_argument(
'-g', '--grade', '-g', '--grade',
metavar='CERT_GRADE', metavar='CERT_GRADE',
dest='grade', dest='grade',
default='', default='',
help='The grade for the course, as a decimal (e.g. "0.89" for 89%)' help='The grade for the course, as a decimal (e.g. "0.89" for 89 percent)'
), )
)
def handle(self, *args, **options): def handle(self, *args, **options):
"""Create a fake certificate for a user. """Create a fake certificate for a user.
...@@ -68,11 +76,8 @@ class Command(BaseCommand): ...@@ -68,11 +76,8 @@ class Command(BaseCommand):
CommandError CommandError
""" """
if len(args) < 2: user = User.objects.get(username=options['username'])
raise CommandError(self.USAGE) course_key = CourseKey.from_string(options['course_key'])
user = User.objects.get(username=args[0])
course_key = CourseKey.from_string(args[1])
cert_mode = options.get('cert_mode', 'honor') cert_mode = options.get('cert_mode', 'honor')
status = options.get('status', CertificateStatuses.downloadable) status = options.get('status', CertificateStatuses.downloadable)
grade = options.get('grade', '') grade = options.get('grade', '')
...@@ -97,7 +102,7 @@ class Command(BaseCommand): ...@@ -97,7 +102,7 @@ class Command(BaseCommand):
u"Created certificate for user %s in course %s " u"Created certificate for user %s in course %s "
u"with mode %s, status %s, " u"with mode %s, status %s, "
u"and grade %s", u"and grade %s",
user.id, unicode(course_key), user.id, text_type(course_key),
cert_mode, status, grade cert_mode, status, grade
) )
...@@ -106,6 +111,6 @@ class Command(BaseCommand): ...@@ -106,6 +111,6 @@ class Command(BaseCommand):
u"Updated certificate for user %s in course %s " u"Updated certificate for user %s in course %s "
u"with mode %s, status %s, " u"with mode %s, status %s, "
u"and grade %s", u"and grade %s",
user.id, unicode(course_key), user.id, text_type(course_key),
cert_mode, status, grade cert_mode, status, grade
) )
"""Django management command to force certificate regeneration for one user""" """Django management command to force certificate regeneration for one user"""
import copy import copy
import logging import logging
from optparse import make_option
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from six import text_type
from badges.events.course_complete import get_completion_badge from badges.events.course_complete import get_completion_badge
from badges.utils import badges_enabled from badges.utils import badges_enabled
...@@ -24,39 +23,36 @@ class Command(BaseCommand): ...@@ -24,39 +23,36 @@ class Command(BaseCommand):
help = """Put a request on the queue to recreate the certificate for a particular user in a particular course.""" help = """Put a request on the queue to recreate the certificate for a particular user in a particular course."""
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option('-n', '--noop', parser.add_argument('-n', '--noop',
action='store_true', action='store_true',
dest='noop', dest='noop',
default=False, help="Don't grade or add certificate requests to the queue")
help="Don't grade or add certificate requests to the queue"), parser.add_argument('--insecure',
make_option('--insecure', action='store_true',
action='store_true', dest='insecure',
dest='insecure', help="Don't use https for the callback url to the LMS, useful in http test environments")
default=False, parser.add_argument('-c', '--course',
help="Don't use https for the callback url to the LMS, useful in http test environments"), metavar='COURSE_ID',
make_option('-c', '--course', dest='course',
metavar='COURSE_ID', required=True,
dest='course', help='The course id (e.g., mit/6-002x/circuits-and-electronics) for which the student '
default=False, 'named in <username> should be graded')
help='The course id (e.g., mit/6-002x/circuits-and-electronics) for which the student named in' parser.add_argument('-u', '--user',
'<username> should be graded'), metavar='USERNAME',
make_option('-u', '--user', dest='username',
metavar='USERNAME', required=True,
dest='username', help='The username or email address for whom grading and certification should be requested')
default=False, parser.add_argument('-G', '--grade',
help='The username or email address for whom grading and certification should be requested'), metavar='GRADE',
make_option('-G', '--grade', dest='grade_value',
metavar='GRADE', default=None,
dest='grade_value', help='The grade string, such as "Distinction", which is passed to the certificate agent')
default=None, parser.add_argument('-T', '--template',
help='The grade string, such as "Distinction", which should be passed to the certificate agent'), metavar='TEMPLATE',
make_option('-T', '--template', dest='template_file',
metavar='TEMPLATE', default=None,
dest='template_file', help='The template file used to render this certificate, like "QMSE01-distinction.pdf"')
default=None,
help='The template file used to render this certificate, like "QMSE01-distinction.pdf"'),
)
def handle(self, *args, **options): def handle(self, *args, **options):
...@@ -69,21 +65,14 @@ class Command(BaseCommand): ...@@ -69,21 +65,14 @@ class Command(BaseCommand):
u"Starting to create tasks to regenerate certificates " u"Starting to create tasks to regenerate certificates "
u"with arguments %s and options %s" u"with arguments %s and options %s"
), ),
unicode(args), text_type(args),
unicode(cleaned_options) text_type(cleaned_options)
) )
if options['course']: # try to parse out the course from the serialized form
# try to parse out the course from the serialized form course_id = CourseKey.from_string(options['course'])
course_id = CourseKey.from_string(options['course'])
else:
raise CommandError("You must specify a course")
user = options['username'] user = options['username']
if not (course_id and user):
raise CommandError('both course id and student username are required')
student = None
if '@' in user: if '@' in user:
student = User.objects.get(email=user, courseenrollment__course_id=course_id) student = User.objects.get(email=user, courseenrollment__course_id=course_id)
else: else:
...@@ -124,7 +113,7 @@ class Command(BaseCommand): ...@@ -124,7 +113,7 @@ class Command(BaseCommand):
u"The new certificate status is '%s'." u"The new certificate status is '%s'."
), ),
student.id, student.id,
unicode(course_id), text_type(course_id),
ret ret
) )
...@@ -136,7 +125,7 @@ class Command(BaseCommand): ...@@ -136,7 +125,7 @@ class Command(BaseCommand):
u"because the noop flag is set." u"because the noop flag is set."
), ),
student.id, student.id,
unicode(course_id) text_type(course_id)
) )
LOGGER.info( LOGGER.info(
...@@ -145,5 +134,5 @@ class Command(BaseCommand): ...@@ -145,5 +134,5 @@ class Command(BaseCommand):
u"user %s and course '%s'." u"user %s and course '%s'."
), ),
student.id, student.id,
unicode(course_id) text_type(course_id)
) )
...@@ -17,11 +17,11 @@ Example usage: ...@@ -17,11 +17,11 @@ Example usage:
""" """
import logging import logging
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from six import text_type
from certificates import api as certs_api from certificates import api as certs_api
from certificates.models import CertificateStatuses, GeneratedCertificate from certificates.models import CertificateStatuses, GeneratedCertificate
...@@ -33,16 +33,15 @@ LOGGER = logging.getLogger(__name__) ...@@ -33,16 +33,15 @@ LOGGER = logging.getLogger(__name__)
class Command(BaseCommand): class Command(BaseCommand):
"""Resubmit certificates with error status. """ """Resubmit certificates with error status. """
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option( parser.add_argument(
'-c', '--course', '-c', '--course',
metavar='COURSE_KEY', metavar='COURSE_KEY',
dest='course_key_list', dest='course_key_list',
action='append', action='append',
default=[], default=[],
help='Only re-submit certificates for these courses.' help='Only re-submit certificates for these courses.'
), )
)
def handle(self, *args, **options): def handle(self, *args, **options):
"""Resubmit certificates with status 'error'. """Resubmit certificates with status 'error'.
...@@ -58,7 +57,7 @@ class Command(BaseCommand): ...@@ -58,7 +57,7 @@ class Command(BaseCommand):
""" """
only_course_keys = [] only_course_keys = []
for course_key_str in options.get('course_key_list', []): for course_key_str in options['course_key_list']:
try: try:
only_course_keys.append(CourseKey.from_string(course_key_str)) only_course_keys.append(CourseKey.from_string(course_key_str))
except InvalidKeyError: except InvalidKeyError:
...@@ -73,7 +72,7 @@ class Command(BaseCommand): ...@@ -73,7 +72,7 @@ class Command(BaseCommand):
( (
u'Starting to re-submit certificates with status "error" ' u'Starting to re-submit certificates with status "error" '
u'in these courses: %s' u'in these courses: %s'
), ", ".join([unicode(key) for key in only_course_keys]) ), ", ".join([text_type(key) for key in only_course_keys])
) )
else: else:
LOGGER.info(u'Starting to re-submit certificates with status "error".') LOGGER.info(u'Starting to re-submit certificates with status "error".')
......
...@@ -2,14 +2,15 @@ ...@@ -2,14 +2,15 @@
Management command to find all students that need certificates for Management command to find all students that need certificates for
courses that have finished, and put their cert requests on the queue. courses that have finished, and put their cert requests on the queue.
""" """
from __future__ import print_function
import datetime import datetime
import logging import logging
from optparse import make_option
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from pytz import UTC from pytz import UTC
from six import text_type
from certificates.api import generate_user_certificates from certificates.api import generate_user_certificates
from certificates.models import CertificateStatuses, certificate_status_for_student from certificates.models import CertificateStatuses, certificate_status_for_student
...@@ -34,42 +35,43 @@ class Command(BaseCommand): ...@@ -34,42 +35,43 @@ class Command(BaseCommand):
queue to be generated. queue to be generated.
""" """
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option('-n', '--noop', parser.add_argument(
action='store_true', '-n', '--noop',
dest='noop', action='store_true',
default=False, dest='noop',
help="Don't add certificate requests to the queue"), help="Don't add certificate requests to the queue"
make_option('--insecure', )
action='store_true', parser.add_argument(
dest='insecure', '--insecure',
default=False, action='store_true',
help="Don't use https for the callback url to the LMS, useful in http test environments"), dest='insecure',
make_option('-c', '--course', help="Don't use https for the callback url to the LMS, useful in http test environments"
metavar='COURSE_ID', )
dest='course', parser.add_argument(
default=False, '-c', '--course',
help='Grade and generate certificates ' metavar='COURSE_ID',
'for a specific course'), dest='course',
make_option('-f', '--force-gen', required=True,
metavar='STATUS', help='Grade and generate certificates for a specific course'
dest='force', )
default=False, parser.add_argument(
help='Will generate new certificates for only those users ' '-f', '--force-gen',
'whose entry in the certificate table matches STATUS. ' metavar='STATUS',
'STATUS can be generating, unavailable, deleted, error ' dest='force',
'or notpassing.'), default=False,
) help='Will generate new certificates for only those users whose entry in the certificate table matches '
'STATUS. STATUS can be generating, unavailable, deleted, error or notpassing.'
)
def handle(self, *args, **options): def handle(self, *args, **options):
LOGGER.info( LOGGER.info(
( (
u"Starting to create tasks for ungenerated certificates " u"Starting to create tasks for ungenerated certificates "
u"with arguments %s and options %s" u"with arguments %s and options %s"
), ),
unicode(args), text_type(args),
unicode(options) text_type(options)
) )
# Will only generate a certificate if the current # Will only generate a certificate if the current
...@@ -82,14 +84,10 @@ class Command(BaseCommand): ...@@ -82,14 +84,10 @@ class Command(BaseCommand):
valid_statuses = [CertificateStatuses.unavailable] valid_statuses = [CertificateStatuses.unavailable]
# Print update after this many students # Print update after this many students
status_interval = 500
STATUS_INTERVAL = 500 course = CourseKey.from_string(options['course'])
ended_courses = [course]
if options['course']:
course = CourseKey.from_string(options['course'])
ended_courses = [course]
else:
raise CommandError("You must specify a course")
for course_key in ended_courses: for course_key in ended_courses:
# prefetch all chapters/sequentials by saying depth=2 # prefetch all chapters/sequentials by saying depth=2
...@@ -105,16 +103,15 @@ class Command(BaseCommand): ...@@ -105,16 +103,15 @@ class Command(BaseCommand):
for student in enrolled_students: for student in enrolled_students:
count += 1 count += 1
if count % STATUS_INTERVAL == 0: if count % status_interval == 0:
# Print a status update with an approximation of # Print a status update with an approximation of
# how much time is left based on how long the last # how much time is left based on how long the last
# interval took # interval took
diff = datetime.datetime.now(UTC) - start diff = datetime.datetime.now(UTC) - start
timeleft = diff * (total - count) / STATUS_INTERVAL timeleft = diff * (total - count) / status_interval
hours, remainder = divmod(timeleft.seconds, 3600) hours, remainder = divmod(timeleft.seconds, 3600)
minutes, _seconds = divmod(remainder, 60) minutes, _seconds = divmod(remainder, 60)
print "{0}/{1} completed ~{2:02}:{3:02}m remaining".format( print("{0}/{1} completed ~{2:02}:{3:02}m remaining".format(count, total, hours, minutes))
count, total, hours, minutes)
start = datetime.datetime.now(UTC) start = datetime.datetime.now(UTC)
cert_status = certificate_status_for_student(student, course_key)['status'] cert_status = certificate_status_for_student(student, course_key)['status']
...@@ -125,7 +122,7 @@ class Command(BaseCommand): ...@@ -125,7 +122,7 @@ class Command(BaseCommand):
), ),
student.id, student.id,
cert_status, cert_status,
unicode(course_key) text_type(course_key)
) )
if cert_status in valid_statuses: if cert_status in valid_statuses:
...@@ -147,7 +144,7 @@ class Command(BaseCommand): ...@@ -147,7 +144,7 @@ class Command(BaseCommand):
u"The new certificate status is '%s'." u"The new certificate status is '%s'."
), ),
student.id, student.id,
unicode(course_key), text_type(course_key),
ret ret
) )
...@@ -159,7 +156,7 @@ class Command(BaseCommand): ...@@ -159,7 +156,7 @@ class Command(BaseCommand):
u"because the noop flag is set." u"because the noop flag is set."
), ),
student.id, student.id,
unicode(course_key) text_type(course_key)
) )
else: else:
...@@ -170,7 +167,7 @@ class Command(BaseCommand): ...@@ -170,7 +167,7 @@ class Command(BaseCommand):
), ),
student.id, student.id,
cert_status, cert_status,
unicode(valid_statuses) text_type(valid_statuses)
) )
LOGGER.info( LOGGER.info(
...@@ -178,5 +175,5 @@ class Command(BaseCommand): ...@@ -178,5 +175,5 @@ class Command(BaseCommand):
u"Completed ungenerated certificates command " u"Completed ungenerated certificates command "
u"for course '%s'" u"for course '%s'"
), ),
unicode(course_key) text_type(course_key)
) )
"""Tests for the resubmit_error_certificates management command. """ """Tests for the resubmit_error_certificates management command. """
import ddt import ddt
from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.test.utils import override_settings from django.test.utils import override_settings
from mock import patch from mock import patch
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from opaque_keys.edx.locator import CourseLocator from opaque_keys.edx.locator import CourseLocator
from six import text_type
from badges.events.course_complete import get_completion_badge from badges.events.course_complete import get_completion_badge
from badges.models import BadgeAssertion from badges.models import BadgeAssertion
from badges.tests.factories import BadgeAssertionFactory, CourseCompleteImageConfigurationFactory from badges.tests.factories import BadgeAssertionFactory, CourseCompleteImageConfigurationFactory
from certificates.management.commands import regenerate_user, resubmit_error_certificates, ungenerated_certs
from certificates.models import CertificateStatuses, GeneratedCertificate from certificates.models import CertificateStatuses, GeneratedCertificate
from course_modes.models import CourseMode from course_modes.models import CourseMode
from lms.djangoapps.grades.tests.utils import mock_passing_grade from lms.djangoapps.grades.tests.utils import mock_passing_grade
...@@ -23,7 +24,7 @@ class CertificateManagementTest(ModuleStoreTestCase): ...@@ -23,7 +24,7 @@ class CertificateManagementTest(ModuleStoreTestCase):
Base test class for Certificate Management command tests. Base test class for Certificate Management command tests.
""" """
# Override with the command module you wish to test. # Override with the command module you wish to test.
command = resubmit_error_certificates command = 'resubmit_error_certificates'
def setUp(self): def setUp(self):
super(CertificateManagementTest, self).setUp() super(CertificateManagementTest, self).setUp()
...@@ -53,11 +54,6 @@ class CertificateManagementTest(ModuleStoreTestCase): ...@@ -53,11 +54,6 @@ class CertificateManagementTest(ModuleStoreTestCase):
status=status status=status
) )
def _run_command(self, *args, **kwargs):
"""Run the management command to generate a fake cert. """
command = self.command.Command()
return command.handle(*args, **kwargs)
def _assert_cert_status(self, course_key, user, expected_status): def _assert_cert_status(self, course_key, user, expected_status):
"""Check the status of a certificate. """ """Check the status of a certificate. """
cert = GeneratedCertificate.eligible_certificates.get(user=user, course_id=course_key) cert = GeneratedCertificate.eligible_certificates.get(user=user, course_id=course_key)
...@@ -77,7 +73,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest): ...@@ -77,7 +73,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest):
# Re-submit all certificates with status 'error' # Re-submit all certificates with status 'error'
with check_mongo_calls(1): with check_mongo_calls(1):
self._run_command() call_command(self.command)
# Expect that the certificate was re-submitted # Expect that the certificate was re-submitted
self._assert_cert_status(self.courses[0].id, self.user, CertificateStatuses.notpassing) self._assert_cert_status(self.courses[0].id, self.user, CertificateStatuses.notpassing)
...@@ -89,9 +85,9 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest): ...@@ -89,9 +85,9 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest):
self._create_cert(self.courses[idx].id, self.user, CertificateStatuses.error) self._create_cert(self.courses[idx].id, self.user, CertificateStatuses.error)
# Re-submit certificates for two of the courses # Re-submit certificates for two of the courses
self._run_command(course_key_list=[ call_command(self.command, course_key_list=[
unicode(self.courses[0].id), text_type(self.courses[0].id),
unicode(self.courses[1].id) text_type(self.courses[1].id)
]) ])
# Expect that the first two courses have been re-submitted, # Expect that the first two courses have been re-submitted,
...@@ -115,7 +111,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest): ...@@ -115,7 +111,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest):
self._create_cert(self.courses[1].id, self.user, other_status) self._create_cert(self.courses[1].id, self.user, other_status)
# Re-submit certificates for all courses # Re-submit certificates for all courses
self._run_command() call_command(self.command)
# Only the certificate with status "error" should have been re-submitted # Only the certificate with status "error" should have been re-submitted
self._assert_cert_status(self.courses[0].id, self.user, CertificateStatuses.notpassing) self._assert_cert_status(self.courses[0].id, self.user, CertificateStatuses.notpassing)
...@@ -123,7 +119,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest): ...@@ -123,7 +119,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest):
def test_resubmit_error_certificate_none_found(self): def test_resubmit_error_certificate_none_found(self):
self._create_cert(self.courses[0].id, self.user, CertificateStatuses.downloadable) self._create_cert(self.courses[0].id, self.user, CertificateStatuses.downloadable)
self._run_command() call_command(self.command)
self._assert_cert_status(self.courses[0].id, self.user, CertificateStatuses.downloadable) self._assert_cert_status(self.courses[0].id, self.user, CertificateStatuses.downloadable)
def test_course_caching(self): def test_course_caching(self):
...@@ -135,17 +131,17 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest): ...@@ -135,17 +131,17 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest):
# Verify that we make only one Mongo query # Verify that we make only one Mongo query
# because the course is cached. # because the course is cached.
with check_mongo_calls(1): with check_mongo_calls(1):
self._run_command() call_command(self.command)
def test_invalid_course_key(self): def test_invalid_course_key(self):
invalid_key = u"invalid/" invalid_key = u"invalid/"
with self.assertRaisesRegexp(CommandError, invalid_key): with self.assertRaisesRegexp(CommandError, invalid_key):
self._run_command(course_key_list=[invalid_key]) call_command(self.command, course_key_list=[invalid_key])
def test_course_does_not_exist(self): def test_course_does_not_exist(self):
phantom_course = CourseLocator(org='phantom', course='phantom', run='phantom') phantom_course = CourseLocator(org='phantom', course='phantom', run='phantom')
self._create_cert(phantom_course, self.user, 'error') self._create_cert(phantom_course, self.user, 'error')
self._run_command() call_command(self.command)
# Expect that the certificate was NOT resubmitted # Expect that the certificate was NOT resubmitted
# since the course doesn't actually exist. # since the course doesn't actually exist.
...@@ -158,7 +154,7 @@ class RegenerateCertificatesTest(CertificateManagementTest): ...@@ -158,7 +154,7 @@ class RegenerateCertificatesTest(CertificateManagementTest):
""" """
Tests for regenerating certificates. Tests for regenerating certificates.
""" """
command = regenerate_user command = 'regenerate_user'
def setUp(self): def setUp(self):
""" """
...@@ -185,10 +181,10 @@ class RegenerateCertificatesTest(CertificateManagementTest): ...@@ -185,10 +181,10 @@ class RegenerateCertificatesTest(CertificateManagementTest):
self.assertTrue(BadgeAssertion.objects.filter(user=self.user, badge_class=badge_class)) self.assertTrue(BadgeAssertion.objects.filter(user=self.user, badge_class=badge_class))
self.course.issue_badges = issue_badges self.course.issue_badges = issue_badges
self.store.update_item(self.course, None) self.store.update_item(self.course, None)
self._run_command(
username=self.user.email, course=unicode(key), noop=False, insecure=False, template_file=None, args = '-u {} -c {}'.format(self.user.email, text_type(key))
grade_value=None call_command(self.command, *args.split(' '))
)
xqueue.return_value.regen_cert.assert_called_with( xqueue.return_value.regen_cert.assert_called_with(
self.user, self.user,
key, key,
...@@ -211,10 +207,10 @@ class RegenerateCertificatesTest(CertificateManagementTest): ...@@ -211,10 +207,10 @@ class RegenerateCertificatesTest(CertificateManagementTest):
""" """
key = self.course.location.course_key key = self.course.location.course_key
self._create_cert(key, self.user, CertificateStatuses.downloadable) self._create_cert(key, self.user, CertificateStatuses.downloadable)
self._run_command(
username=self.user.email, course=unicode(key), noop=False, insecure=True, template_file=None, args = '-u {} -c {} --insecure'.format(self.user.email, text_type(key))
grade_value=None call_command(self.command, *args.split(' '))
)
certificate = GeneratedCertificate.eligible_certificates.get( certificate = GeneratedCertificate.eligible_certificates.get(
user=self.user, user=self.user,
course_id=key course_id=key
...@@ -228,7 +224,7 @@ class UngenerateCertificatesTest(CertificateManagementTest): ...@@ -228,7 +224,7 @@ class UngenerateCertificatesTest(CertificateManagementTest):
""" """
Tests for generating certificates. Tests for generating certificates.
""" """
command = ungenerated_certs command = 'ungenerated_certs'
def setUp(self): def setUp(self):
""" """
...@@ -248,10 +244,11 @@ class UngenerateCertificatesTest(CertificateManagementTest): ...@@ -248,10 +244,11 @@ class UngenerateCertificatesTest(CertificateManagementTest):
mock_send_to_queue.return_value = (0, "Successfully queued") mock_send_to_queue.return_value = (0, "Successfully queued")
key = self.course.location.course_key key = self.course.location.course_key
self._create_cert(key, self.user, CertificateStatuses.unavailable) self._create_cert(key, self.user, CertificateStatuses.unavailable)
with mock_passing_grade(): with mock_passing_grade():
self._run_command( args = '-c {} --insecure'.format(text_type(key))
course=unicode(key), noop=False, insecure=True, force=False call_command(self.command, *args.split(' '))
)
self.assertTrue(mock_send_to_queue.called) self.assertTrue(mock_send_to_queue.called)
certificate = GeneratedCertificate.eligible_certificates.get( certificate = GeneratedCertificate.eligible_certificates.get(
user=self.user, user=self.user,
......
"""Tests for the create_fake_certs management command. """ """Tests for the create_fake_certs management command. """
from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.test import TestCase from django.test import TestCase
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from opaque_keys.edx.locator import CourseLocator from opaque_keys.edx.locator import CourseLocator
from six import text_type
from certificates.management.commands import create_fake_cert
from certificates.models import GeneratedCertificate from certificates.models import GeneratedCertificate
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
...@@ -24,7 +26,7 @@ class CreateFakeCertTest(TestCase): ...@@ -24,7 +26,7 @@ class CreateFakeCertTest(TestCase):
# No existing cert, so create it # No existing cert, so create it
self._run_command( self._run_command(
self.USERNAME, self.USERNAME,
unicode(self.COURSE_KEY), text_type(self.COURSE_KEY),
cert_mode='verified', cert_mode='verified',
grade='0.89' grade='0.89'
) )
...@@ -38,17 +40,16 @@ class CreateFakeCertTest(TestCase): ...@@ -38,17 +40,16 @@ class CreateFakeCertTest(TestCase):
# Cert already exists; modify it # Cert already exists; modify it
self._run_command( self._run_command(
self.USERNAME, self.USERNAME,
unicode(self.COURSE_KEY), text_type(self.COURSE_KEY),
cert_mode='honor' cert_mode='honor'
) )
cert = GeneratedCertificate.eligible_certificates.get(user=self.user, course_id=self.COURSE_KEY) cert = GeneratedCertificate.eligible_certificates.get(user=self.user, course_id=self.COURSE_KEY)
self.assertEqual(cert.mode, 'honor') self.assertEqual(cert.mode, 'honor')
def test_too_few_args(self): def test_too_few_args(self):
with self.assertRaisesRegexp(CommandError, 'Usage'): with self.assertRaisesRegexp(CommandError, 'Error: too few arguments'):
self._run_command(self.USERNAME) self._run_command(self.USERNAME)
def _run_command(self, *args, **kwargs): def _run_command(self, *args, **kwargs):
"""Run the management command to generate a fake cert. """ """Run the management command to generate a fake cert. """
command = create_fake_cert.Command() return call_command('create_fake_cert', *args, **kwargs)
return command.handle(*args, **kwargs)
...@@ -8,7 +8,7 @@ import ddt ...@@ -8,7 +8,7 @@ import ddt
import mock import mock
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, reverse_lazy
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from edx_rest_api_client import exceptions from edx_rest_api_client import exceptions
...@@ -285,7 +285,7 @@ class BasketOrderViewTests(UserMixin, TestCase): ...@@ -285,7 +285,7 @@ class BasketOrderViewTests(UserMixin, TestCase):
""" Tests for the basket order view. """ """ Tests for the basket order view. """
view_name = 'commerce_api:v0:baskets:retrieve_order' view_name = 'commerce_api:v0:baskets:retrieve_order'
MOCK_ORDER = {'number': 1} MOCK_ORDER = {'number': 1}
path = reverse(view_name, kwargs={'basket_id': 1}) path = reverse_lazy(view_name, kwargs={'basket_id': 1})
def setUp(self): def setUp(self):
super(BasketOrderViewTests, self).setUp() super(BasketOrderViewTests, self).setUp()
......
...@@ -7,7 +7,7 @@ import ddt ...@@ -7,7 +7,7 @@ import ddt
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, reverse_lazy
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from edx_rest_api_client import exceptions from edx_rest_api_client import exceptions
...@@ -83,7 +83,7 @@ class CourseApiViewTestMixin(object): ...@@ -83,7 +83,7 @@ class CourseApiViewTestMixin(object):
class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase): class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
""" Tests for CourseListView. """ """ Tests for CourseListView. """
path = reverse('commerce_api:v1:courses:list') path = reverse_lazy('commerce_api:v1:courses:list')
def test_authentication_required(self): def test_authentication_required(self):
""" Verify only authenticated users can access the view. """ """ Verify only authenticated users can access the view. """
...@@ -400,7 +400,7 @@ class OrderViewTests(UserMixin, TestCase): ...@@ -400,7 +400,7 @@ class OrderViewTests(UserMixin, TestCase):
view_name = 'commerce_api:v1:orders:detail' view_name = 'commerce_api:v1:orders:detail'
ORDER_NUMBER = 'EDX-100001' ORDER_NUMBER = 'EDX-100001'
MOCK_ORDER = {'number': ORDER_NUMBER} MOCK_ORDER = {'number': ORDER_NUMBER}
path = reverse(view_name, kwargs={'number': ORDER_NUMBER}) path = reverse_lazy(view_name, kwargs={'number': ORDER_NUMBER})
def setUp(self): def setUp(self):
super(OrderViewTests, self).setUp() super(OrderViewTests, self).setUp()
......
...@@ -257,14 +257,9 @@ class XBlockFieldBase(models.Model): ...@@ -257,14 +257,9 @@ class XBlockFieldBase(models.Model):
modified = models.DateTimeField(auto_now=True, db_index=True) modified = models.DateTimeField(auto_now=True, db_index=True)
def __unicode__(self): def __unicode__(self):
return u'{}<{!r}'.format( # pylint: disable=protected-access
self.__class__.__name__, keys = [field.name for field in self._meta.get_fields() if field.name not in ('created', 'modified')]
{ return u'{}<{!r}'.format(self.__class__.__name__, {key: getattr(self, key) for key in keys})
key: getattr(self, key)
for key in self._meta.get_all_field_names()
if key not in ('created', 'modified')
}
)
class XModuleUserStateSummaryField(XBlockFieldBase): class XModuleUserStateSummaryField(XBlockFieldBase):
......
...@@ -7,7 +7,7 @@ import re ...@@ -7,7 +7,7 @@ import re
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, reverse_lazy
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client
from django.utils import translation from django.utils import translation
...@@ -23,9 +23,9 @@ class BaseI18nTestCase(TestCase): ...@@ -23,9 +23,9 @@ class BaseI18nTestCase(TestCase):
Base utilities for i18n test classes to derive from Base utilities for i18n test classes to derive from
""" """
preview_language_url = '/update_lang/' preview_language_url = '/update_lang/'
url = reverse('dashboard')
site_lang = settings.LANGUAGE_CODE site_lang = settings.LANGUAGE_CODE
pwd = 'test_password' pwd = 'test_password'
url = reverse_lazy('dashboard')
def setUp(self): def setUp(self):
super(BaseI18nTestCase, self).setUp() super(BaseI18nTestCase, self).setUp()
...@@ -171,7 +171,9 @@ class I18nLangPrefTests(BaseI18nTestCase): ...@@ -171,7 +171,9 @@ class I18nLangPrefTests(BaseI18nTestCase):
self.user_login() self.user_login()
def set_lang_preference(self, language): def set_lang_preference(self, language):
"""Sets the user's language preference, allowing the LangPref middleware to operate to set the preference cookie.""" """
Sets the user's language preference, allowing the LangPref middleware to operate to set the preference cookie.
"""
response = self.client.patch( response = self.client.patch(
reverse('preferences_api', args=[self.user.username]), reverse('preferences_api', args=[self.user.username]),
json.dumps({LANGUAGE_KEY: language}), json.dumps({LANGUAGE_KEY: language}),
......
...@@ -13,7 +13,7 @@ from uuid import uuid4 ...@@ -13,7 +13,7 @@ from uuid import uuid4
import ddt import ddt
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, reverse_lazy
from django.http import Http404, HttpResponseBadRequest from django.http import Http404, HttpResponseBadRequest
from django.test import TestCase from django.test import TestCase
from django.test.client import Client, RequestFactory from django.test.client import Client, RequestFactory
...@@ -983,7 +983,7 @@ class ViewsTestCase(ModuleStoreTestCase): ...@@ -983,7 +983,7 @@ class ViewsTestCase(ModuleStoreTestCase):
class TestProgramMarketingView(SharedModuleStoreTestCase): class TestProgramMarketingView(SharedModuleStoreTestCase):
"""Unit tests for the program marketing page.""" """Unit tests for the program marketing page."""
program_uuid = str(uuid4()) program_uuid = str(uuid4())
url = reverse('program_marketing_view', kwargs={'program_uuid': program_uuid}) url = reverse_lazy('program_marketing_view', kwargs={'program_uuid': program_uuid})
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
......
...@@ -10,7 +10,7 @@ from uuid import uuid4 ...@@ -10,7 +10,7 @@ from uuid import uuid4
import mock import mock
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, reverse_lazy
from django.test import override_settings from django.test import override_settings
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory from openedx.core.djangoapps.catalog.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory
...@@ -31,7 +31,7 @@ class TestProgramListing(ProgramsApiConfigMixin, SharedModuleStoreTestCase): ...@@ -31,7 +31,7 @@ class TestProgramListing(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
"""Unit tests for the program listing page.""" """Unit tests for the program listing page."""
maxDiff = None maxDiff = None
password = 'test' password = 'test'
url = reverse('program_listing_view') url = reverse_lazy('program_listing_view')
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
...@@ -176,7 +176,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared ...@@ -176,7 +176,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared
"""Unit tests for the program details page.""" """Unit tests for the program details page."""
program_uuid = str(uuid4()) program_uuid = str(uuid4())
password = 'test' password = 'test'
url = reverse('program_details_view', kwargs={'program_uuid': program_uuid}) url = reverse_lazy('program_details_view', kwargs={'program_uuid': program_uuid})
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
......
...@@ -6,7 +6,6 @@ from __future__ import absolute_import ...@@ -6,7 +6,6 @@ from __future__ import absolute_import
from django.apps import AppConfig from django.apps import AppConfig
import xmodule.x_module import xmodule.x_module
from .runtime import handler_url, local_resource_url
class LMSXBlockConfig(AppConfig): class LMSXBlockConfig(AppConfig):
...@@ -17,6 +16,8 @@ class LMSXBlockConfig(AppConfig): ...@@ -17,6 +16,8 @@ class LMSXBlockConfig(AppConfig):
verbose_name = u'LMS XBlock' verbose_name = u'LMS XBlock'
def ready(self): def ready(self):
from .runtime import handler_url, local_resource_url
# In order to allow modules to use a handler url, we need to # In order to allow modules to use a handler url, we need to
# monkey-patch the x_module library. # monkey-patch the x_module library.
# TODO: Remove this code when Runtimes are no longer created by modulestores # TODO: Remove this code when Runtimes are no longer created by modulestores
......
...@@ -8,7 +8,6 @@ from django.conf import settings ...@@ -8,7 +8,6 @@ from django.conf import settings
from openedx.core.djangoapps.monkey_patch import django_db_models_options from openedx.core.djangoapps.monkey_patch import django_db_models_options
# Force settings to run so that the python path is modified # Force settings to run so that the python path is modified
settings.INSTALLED_APPS # pylint: disable=pointless-statement settings.INSTALLED_APPS # pylint: disable=pointless-statement
...@@ -19,6 +18,9 @@ def run(): ...@@ -19,6 +18,9 @@ def run():
NOTE: DO **NOT** add additional code to this method or this file! The Platform Team NOTE: DO **NOT** add additional code to this method or this file! The Platform Team
is moving all startup code to more standard locations using Django best practices. is moving all startup code to more standard locations using Django best practices.
""" """
django_db_models_options.patch() # TODO: Remove Django 1.11 upgrade shim
# SHIM: We should be able to get rid of this monkey patch post-upgrade
if django.VERSION[0] == 1 and django.VERSION[1] < 10:
django_db_models_options.patch()
django.setup() django.setup()
...@@ -18,7 +18,6 @@ from courseware.views import views as courseware_views ...@@ -18,7 +18,6 @@ from courseware.views import views as courseware_views
from courseware.views.index import CoursewareIndex from courseware.views.index import CoursewareIndex
from courseware.views.views import CourseTabView, EnrollStaffView, StaticCourseTabView from courseware.views.views import CourseTabView, EnrollStaffView, StaticCourseTabView
from debug import views as debug_views from debug import views as debug_views
from django_cas import views as django_cas_views
from django_comment_common.models import ForumsConfig from django_comment_common.models import ForumsConfig
from django_openid_auth import views as django_openid_auth_views from django_openid_auth import views as django_openid_auth_views
from lms.djangoapps.discussion import views as discussion_views from lms.djangoapps.discussion import views as discussion_views
...@@ -40,6 +39,7 @@ from openedx.core.djangoapps.programs.models import ProgramsApiConfig ...@@ -40,6 +39,7 @@ from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.verified_track_content import views as verified_track_content_views from openedx.core.djangoapps.verified_track_content import views as verified_track_content_views
from openedx.core.djangoapps.common_views.xblock import xblock_resource
from openedx.features.enterprise_support.api import enterprise_enabled from openedx.features.enterprise_support.api import enterprise_enabled
from ratelimitbackend import admin from ratelimitbackend import admin
from static_template_view import views as static_template_view_views from static_template_view import views as static_template_view_views
...@@ -290,7 +290,7 @@ urlpatterns += [ ...@@ -290,7 +290,7 @@ urlpatterns += [
# xblock Resource URL # xblock Resource URL
url( url(
r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$', r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$',
'openedx.core.djangoapps.common_views.xblock.xblock_resource', xblock_resource,
name='xblock_resource_url', name='xblock_resource_url',
), ),
...@@ -825,6 +825,8 @@ if settings.FEATURES.get('AUTH_USE_SHIB'): ...@@ -825,6 +825,8 @@ if settings.FEATURES.get('AUTH_USE_SHIB'):
] ]
if settings.FEATURES.get('AUTH_USE_CAS'): if settings.FEATURES.get('AUTH_USE_CAS'):
from django_cas import views as django_cas_views
urlpatterns += [ urlpatterns += [
url(r'^cas-auth/login/$', external_auth_views.cas_login, name='cas-login'), url(r'^cas-auth/login/$', external_auth_views.cas_login, name='cas-login'),
url(r'^cas-auth/logout/$', django_cas_views.logout, {'next_page': '/'}, name='cas-logout'), url(r'^cas-auth/logout/$', django_cas_views.logout, {'next_page': '/'}, name='cas-logout'),
......
...@@ -13,7 +13,6 @@ from opaque_keys.edx.keys import CourseKey ...@@ -13,7 +13,6 @@ from opaque_keys.edx.keys import CourseKey
from py2neo import Graph, Node, Relationship, authenticate, NodeSelector from py2neo import Graph, Node, Relationship, authenticate, NodeSelector
from py2neo.compat import integer, string, unicode as neo4j_unicode from py2neo.compat import integer, string, unicode as neo4j_unicode
from request_cache.middleware import RequestCache from request_cache.middleware import RequestCache
from xmodule.modulestore.store_utilities import DETACHED_XBLOCK_TYPES
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -37,6 +36,8 @@ def serialize_item(item): ...@@ -37,6 +36,8 @@ def serialize_item(item):
block_type: the name of the XBlock's type (i.e. 'course' block_type: the name of the XBlock's type (i.e. 'course'
or 'problem') or 'problem')
""" """
from xmodule.modulestore.store_utilities import DETACHED_XBLOCK_TYPES
# convert all fields to a dict and filter out parent and children field # convert all fields to a dict and filter out parent and children field
fields = dict( fields = dict(
(field, field_value.read_from(item)) (field, field_value.read_from(item))
......
...@@ -36,13 +36,14 @@ class NoneToEmptyQuerySet(models.query.QuerySet): ...@@ -36,13 +36,14 @@ class NoneToEmptyQuerySet(models.query.QuerySet):
""" """
def _filter_or_exclude(self, *args, **kwargs): def _filter_or_exclude(self, *args, **kwargs):
# pylint: disable=protected-access # pylint: disable=protected-access
for name in self.model._meta.get_all_field_names(): for field_object in self.model._meta.get_fields():
field_object, _model, direct, _m2m = self.model._meta.get_field_by_name(name) direct = not field_object.auto_created or field_object.concrete
if direct and hasattr(field_object, 'Empty'): if direct and hasattr(field_object, 'Empty'):
for suffix in ('', '_exact'): for suffix in ('', '_exact'):
key = '{}{}'.format(name, suffix) key = '{}{}'.format(field_object.name, suffix)
if key in kwargs and kwargs[key] is None: if key in kwargs and kwargs[key] is None:
kwargs[key] = field_object.Empty kwargs[key] = field_object.Empty
return super(NoneToEmptyQuerySet, self)._filter_or_exclude(*args, **kwargs) return super(NoneToEmptyQuerySet, self)._filter_or_exclude(*args, **kwargs)
......
...@@ -6,13 +6,14 @@ from django.conf import settings ...@@ -6,13 +6,14 @@ from django.conf import settings
from django.conf.urls import url from django.conf.urls import url
from views.learner_achievements import LearnerAchievementsFragmentView from views.learner_achievements import LearnerAchievementsFragmentView
from openedx.features.learner_profile.views.learner_profile import learner_profile
urlpatterns = [ urlpatterns = [
url( url(
r'^{username_pattern}$'.format( r'^{username_pattern}$'.format(
username_pattern=settings.USERNAME_PATTERN, username_pattern=settings.USERNAME_PATTERN,
), ),
'openedx.features.learner_profile.views.learner_profile.learner_profile', learner_profile,
name='learner_profile', name='learner_profile',
), ),
url( url(
......
...@@ -11,7 +11,7 @@ bleach==1.4 ...@@ -11,7 +11,7 @@ bleach==1.4
html5lib==0.999 html5lib==0.999
boto==2.39.0 boto==2.39.0
celery==3.1.18 celery==3.1.18
cryptography==1.5.3 cryptography==1.9
cssselect==0.9.1 cssselect==0.9.1
dealer==2.0.4 dealer==2.0.4
defusedxml==0.4.1 defusedxml==0.4.1
......
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