Merge pull request #18695 from edx/mroytman/course-validation-default-grading-policy

mroytman/course validation default grading policy
This commit is contained in:
Michael Roytman
2018-08-02 12:53:20 -04:00
committed by GitHub
7 changed files with 100 additions and 46 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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):
"""

View File

@@ -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
View File

@@ -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",

View File

@@ -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",