Merge pull request #18695 from edx/mroytman/course-validation-default-grading-policy
mroytman/course validation default grading policy
This commit is contained in:
@@ -13,7 +13,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
class CourseQualityViewTest(SharedModuleStoreTestCase, APITestCase):
|
||||
"""
|
||||
Test importing courses via a RESTful API (POST method only)
|
||||
Test course quality view via a RESTful API
|
||||
"""
|
||||
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
class CourseValidationViewTest(SharedModuleStoreTestCase, APITestCase):
|
||||
"""
|
||||
Test importing courses via a RESTful API (POST method only)
|
||||
Test course validation view via a RESTful API
|
||||
"""
|
||||
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
|
||||
|
||||
@@ -96,6 +96,7 @@ class CourseValidationViewTest(SharedModuleStoreTestCase, APITestCase):
|
||||
'has_certificate': False,
|
||||
},
|
||||
'grades': {
|
||||
'has_grading_policy': False,
|
||||
'sum_of_weights': 1.0,
|
||||
},
|
||||
'is_self_paced': True,
|
||||
|
||||
@@ -9,6 +9,7 @@ from pytz import UTC
|
||||
from contentstore.course_info_model import get_course_updates
|
||||
from contentstore.views.certificates import CertificateManager
|
||||
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
|
||||
from xmodule.course_metadata_utils import DEFAULT_GRADING_POLICY
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from .utils import get_bool_param, course_author_access_required
|
||||
@@ -195,8 +196,10 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView):
|
||||
)
|
||||
|
||||
def _grades_validation(self, course):
|
||||
has_grading_policy = self._has_grading_policy(course)
|
||||
sum_of_weights = course.grader.sum_of_weights
|
||||
return dict(
|
||||
has_grading_policy=has_grading_policy,
|
||||
sum_of_weights=sum_of_weights,
|
||||
)
|
||||
|
||||
@@ -276,3 +279,48 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView):
|
||||
|
||||
def _has_start_date(self, course):
|
||||
return not course.start_date_is_still_default
|
||||
|
||||
def _has_grading_policy(self, course):
|
||||
grading_policy_formatted = {}
|
||||
default_grading_policy_formatted = {}
|
||||
|
||||
for grader, assignment_type, weight in course.grader.subgraders:
|
||||
grading_policy_formatted[assignment_type] = {
|
||||
'type': assignment_type,
|
||||
'short_label': grader.short_label,
|
||||
'min_count': grader.min_count,
|
||||
'drop_count': grader.drop_count,
|
||||
'weight': weight,
|
||||
}
|
||||
|
||||
# the default grading policy Lab assignment type does not have a short-label,
|
||||
# but courses with the default grading policy do return a short-label for Lab
|
||||
# assignments, so we ignore the Lab short-label
|
||||
if 'Lab' in grading_policy_formatted:
|
||||
grading_policy_formatted['Lab'].pop('short_label')
|
||||
|
||||
for assignment in DEFAULT_GRADING_POLICY['GRADER']:
|
||||
default_assignment_grading_policy_formatted = {
|
||||
'type': assignment['type'],
|
||||
'min_count': assignment['min_count'],
|
||||
'drop_count': assignment['drop_count'],
|
||||
'weight': assignment['weight'],
|
||||
}
|
||||
|
||||
# the default grading policy Lab assignment type does not have a short-label, so only
|
||||
# add short_label to dictionary when the assignment has one
|
||||
if 'short_label' in assignment:
|
||||
default_assignment_grading_policy_formatted['short_label'] = assignment['short_label']
|
||||
|
||||
default_grading_policy_formatted[assignment['type']] = default_assignment_grading_policy_formatted
|
||||
|
||||
# check for equality
|
||||
if len(grading_policy_formatted) != len(default_grading_policy_formatted):
|
||||
return True
|
||||
else:
|
||||
for assignment_type in grading_policy_formatted:
|
||||
if (assignment_type not in default_grading_policy_formatted or
|
||||
grading_policy_formatted[assignment_type] != default_grading_policy_formatted[assignment_type]):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -14,6 +14,44 @@ from pytz import utc
|
||||
|
||||
DEFAULT_START_DATE = datetime(2030, 1, 1, tzinfo=utc)
|
||||
|
||||
"""
|
||||
Default grading policy for a course run.
|
||||
"""
|
||||
DEFAULT_GRADING_POLICY = {
|
||||
"GRADER": [
|
||||
{
|
||||
"type": "Homework",
|
||||
"short_label": "HW",
|
||||
"min_count": 12,
|
||||
"drop_count": 2,
|
||||
"weight": 0.15,
|
||||
},
|
||||
{
|
||||
"type": "Lab",
|
||||
"min_count": 12,
|
||||
"drop_count": 2,
|
||||
"weight": 0.15,
|
||||
},
|
||||
{
|
||||
"type": "Midterm Exam",
|
||||
"short_label": "Midterm",
|
||||
"min_count": 1,
|
||||
"drop_count": 0,
|
||||
"weight": 0.3,
|
||||
},
|
||||
{
|
||||
"type": "Final Exam",
|
||||
"short_label": "Final",
|
||||
"min_count": 1,
|
||||
"drop_count": 0,
|
||||
"weight": 0.4,
|
||||
}
|
||||
],
|
||||
"GRADE_CUTOFFS": {
|
||||
"Pass": 0.5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def clean_course_key(course_key, padding_char):
|
||||
"""
|
||||
|
||||
@@ -20,7 +20,7 @@ from six import text_type
|
||||
from xblock.fields import Scope, List, String, Dict, Boolean, Integer, Float
|
||||
|
||||
from xmodule import course_metadata_utils
|
||||
from xmodule.course_metadata_utils import DEFAULT_START_DATE
|
||||
from xmodule.course_metadata_utils import DEFAULT_START_DATE, DEFAULT_GRADING_POLICY
|
||||
from xmodule.graders import grader_from_conf
|
||||
from xmodule.seq_module import SequenceDescriptor, SequenceModule
|
||||
from xmodule.tabs import CourseTabList, InvalidTabsException
|
||||
@@ -229,40 +229,7 @@ class CourseFields(object):
|
||||
)
|
||||
grading_policy = Dict(
|
||||
help=_("Grading policy definition for this class"),
|
||||
default={
|
||||
"GRADER": [
|
||||
{
|
||||
"type": "Homework",
|
||||
"min_count": 12,
|
||||
"drop_count": 2,
|
||||
"short_label": "HW",
|
||||
"weight": 0.15,
|
||||
},
|
||||
{
|
||||
"type": "Lab",
|
||||
"min_count": 12,
|
||||
"drop_count": 2,
|
||||
"weight": 0.15,
|
||||
},
|
||||
{
|
||||
"type": "Midterm Exam",
|
||||
"short_label": "Midterm",
|
||||
"min_count": 1,
|
||||
"drop_count": 0,
|
||||
"weight": 0.3,
|
||||
},
|
||||
{
|
||||
"type": "Final Exam",
|
||||
"short_label": "Final",
|
||||
"min_count": 1,
|
||||
"drop_count": 0,
|
||||
"weight": 0.4,
|
||||
}
|
||||
],
|
||||
"GRADE_CUTOFFS": {
|
||||
"Pass": 0.5,
|
||||
},
|
||||
},
|
||||
default=DEFAULT_GRADING_POLICY,
|
||||
scope=Scope.content
|
||||
)
|
||||
show_calculator = Boolean(
|
||||
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -106,9 +106,9 @@
|
||||
}
|
||||
},
|
||||
"@edx/studio-frontend": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@edx/studio-frontend/-/studio-frontend-1.16.1.tgz",
|
||||
"integrity": "sha512-JP+C2PAvnDNurOPFMAQ5R1yKNK2YXLJIvtccsXMDQP9/lUgE1h9ZItD0dYhQM7P4tpeLqRVQ0O/lqh9A43rFDw==",
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@edx/studio-frontend/-/studio-frontend-1.16.3.tgz",
|
||||
"integrity": "sha512-KcfH8KbjZ8FGj6X9OMPf8HHmzaXTDD3c1L1rnSvLgHLLm624CeY1khbPFVN1a/HBQ8tNqjPDtgERvzyfnnAGDQ==",
|
||||
"requires": {
|
||||
"@edx/edx-bootstrap": "1.0.0",
|
||||
"@edx/paragon": "3.1.2",
|
||||
@@ -121,7 +121,7 @@
|
||||
"js-cookie": "2.2.0",
|
||||
"popper.js": "1.12.9",
|
||||
"prop-types": "15.6.0",
|
||||
"react": "16.4.1",
|
||||
"react": "16.4.2",
|
||||
"react-dom": "16.1.0",
|
||||
"react-dropzone": "4.2.13",
|
||||
"react-intl": "2.4.0",
|
||||
@@ -158,7 +158,7 @@
|
||||
"font-awesome": "4.7.0",
|
||||
"mailto-link": "1.0.0",
|
||||
"prop-types": "15.6.0",
|
||||
"react": "16.4.1",
|
||||
"react": "16.4.2",
|
||||
"react-dom": "16.1.0",
|
||||
"react-element-proptypes": "1.0.0",
|
||||
"react-proptype-conditional-require": "1.0.4"
|
||||
@@ -175,9 +175,9 @@
|
||||
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
|
||||
},
|
||||
"react": {
|
||||
"version": "16.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.4.1.tgz",
|
||||
"integrity": "sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==",
|
||||
"version": "16.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
|
||||
"integrity": "sha512-dMv7YrbxO4y2aqnvA7f/ik9ibeLSHQJTI6TrYAenPSaQ6OXfb+Oti+oJiy8WBxgRzlKatYqtCjphTgDSCEiWFg==",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"@edx/cookie-policy-banner": "1.1.10",
|
||||
"@edx/edx-bootstrap": "0.4.3",
|
||||
"@edx/paragon": "2.6.4",
|
||||
"@edx/studio-frontend": "1.16.1",
|
||||
"@edx/studio-frontend": "1.16.3",
|
||||
"babel-core": "6.26.0",
|
||||
"babel-loader": "6.4.1",
|
||||
"babel-plugin-transform-class-properties": "6.24.1",
|
||||
|
||||
Reference in New Issue
Block a user