Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
0b4e03e8
Commit
0b4e03e8
authored
Jun 10, 2013
by
dcadams
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #66 from edx/feature-dcadams-usermanagement
Feature user-management autoenroll
parents
1c348a94
7d961bae
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
453 additions
and
86 deletions
+453
-86
common/djangoapps/student/migrations/0025_auto__add_field_courseenrollmentallowed_auto_enroll.py
+174
-0
common/djangoapps/student/models.py
+1
-0
common/djangoapps/student/views.py
+11
-7
lms/djangoapps/instructor/tests/test_enrollment.py
+178
-0
lms/djangoapps/instructor/views.py
+83
-72
lms/templates/courseware/instructor_dashboard.html
+6
-5
lms/urls.py
+0
-2
No files found.
common/djangoapps/student/migrations/0025_auto__add_field_courseenrollmentallowed_auto_enroll.py
0 → 100644
View file @
0b4e03e8
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding field 'CourseEnrollmentAllowed.auto_enroll'
db
.
add_column
(
'student_courseenrollmentallowed'
,
'auto_enroll'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
def
backwards
(
self
,
orm
):
# Deleting field 'CourseEnrollmentAllowed.auto_enroll'
db
.
delete_column
(
'student_courseenrollmentallowed'
,
'auto_enroll'
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'student.courseenrollment'
:
{
'Meta'
:
{
'unique_together'
:
"(('user', 'course_id'),)"
,
'object_name'
:
'CourseEnrollment'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'null'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'student.courseenrollmentallowed'
:
{
'Meta'
:
{
'unique_together'
:
"(('email', 'course_id'),)"
,
'object_name'
:
'CourseEnrollmentAllowed'
},
'auto_enroll'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'null'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'student.pendingemailchange'
:
{
'Meta'
:
{
'object_name'
:
'PendingEmailChange'
},
'activation_key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'new_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['auth.User']"
,
'unique'
:
'True'
})
},
'student.pendingnamechange'
:
{
'Meta'
:
{
'object_name'
:
'PendingNameChange'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'new_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'rationale'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'1024'
,
'blank'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['auth.User']"
,
'unique'
:
'True'
})
},
'student.registration'
:
{
'Meta'
:
{
'object_name'
:
'Registration'
,
'db_table'
:
"'auth_registration'"
},
'activation_key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'unique'
:
'True'
})
},
'student.testcenterregistration'
:
{
'Meta'
:
{
'object_name'
:
'TestCenterRegistration'
},
'accommodation_code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'accommodation_request'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'1024'
,
'blank'
:
'True'
}),
'authorization_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'client_authorization_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'20'
,
'db_index'
:
'True'
}),
'confirmed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'eligibility_appointment_date_first'
:
(
'django.db.models.fields.DateField'
,
[],
{
'db_index'
:
'True'
}),
'eligibility_appointment_date_last'
:
(
'django.db.models.fields.DateField'
,
[],
{
'db_index'
:
'True'
}),
'exam_series_code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'15'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'processed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'testcenter_user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'default'
:
'None'
,
'to'
:
"orm['student.TestCenterUser']"
}),
'updated_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'upload_error_message'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'512'
,
'blank'
:
'True'
}),
'upload_status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'20'
,
'blank'
:
'True'
}),
'uploaded_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'user_updated_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'db_index'
:
'True'
})
},
'student.testcenteruser'
:
{
'Meta'
:
{
'object_name'
:
'TestCenterUser'
},
'address_1'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'40'
}),
'address_2'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'40'
,
'blank'
:
'True'
}),
'address_3'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'40'
,
'blank'
:
'True'
}),
'candidate_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'city'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'client_candidate_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'company_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'50'
,
'blank'
:
'True'
}),
'confirmed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'country'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'3'
,
'db_index'
:
'True'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'extension'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'8'
,
'blank'
:
'True'
}),
'fax'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'35'
,
'blank'
:
'True'
}),
'fax_country_code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'3'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'middle_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'phone'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'35'
}),
'phone_country_code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'3'
,
'db_index'
:
'True'
}),
'postal_code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'16'
,
'blank'
:
'True'
}),
'processed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'salutation'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'blank'
:
'True'
}),
'state'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'20'
,
'blank'
:
'True'
}),
'suffix'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'updated_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'upload_error_message'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'512'
,
'blank'
:
'True'
}),
'upload_status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'20'
,
'blank'
:
'True'
}),
'uploaded_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'db_index'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'default'
:
'None'
,
'to'
:
"orm['auth.User']"
,
'unique'
:
'True'
}),
'user_updated_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'db_index'
:
'True'
})
},
'student.userprofile'
:
{
'Meta'
:
{
'object_name'
:
'UserProfile'
,
'db_table'
:
"'auth_userprofile'"
},
'allow_certificate'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'courseware'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'course.xml'"
,
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'gender'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'6'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'goals'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'level_of_education'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'6'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'location'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'mailing_address'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'meta'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'db_index'
:
'True'
,
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'related_name'
:
"'profile'"
,
'unique'
:
'True'
,
'to'
:
"orm['auth.User']"
}),
'year_of_birth'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
})
},
'student.usertestgroup'
:
{
'Meta'
:
{
'object_name'
:
'UserTestGroup'
},
'description'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'users'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.User']"
,
'db_index'
:
'True'
,
'symmetrical'
:
'False'
})
}
}
complete_apps
=
[
'student'
]
\ No newline at end of file
common/djangoapps/student/models.py
View file @
0b4e03e8
...
...
@@ -662,6 +662,7 @@ class CourseEnrollmentAllowed(models.Model):
"""
email
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
course_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
auto_enroll
=
models
.
BooleanField
(
default
=
0
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
db_index
=
True
)
...
...
common/djangoapps/student/views.py
View file @
0b4e03e8
...
...
@@ -32,7 +32,7 @@ from student.models import (Registration, UserProfile, TestCenterUser, TestCente
TestCenterRegistration
,
TestCenterRegistrationForm
,
PendingNameChange
,
PendingEmailChange
,
CourseEnrollment
,
unique_id_for_user
,
get_testcenter_registration
)
get_testcenter_registration
,
CourseEnrollmentAllowed
)
from
certificates.models
import
CertificateStatuses
,
certificate_status_for_student
...
...
@@ -264,7 +264,6 @@ def dashboard(request):
if
not
user
.
is_active
:
message
=
render_to_string
(
'registration/activate_account_notice.html'
,
{
'email'
:
user
.
email
})
# Global staff can see what courses errored on their dashboard
staff_access
=
False
errored_courses
=
{}
...
...
@@ -454,7 +453,6 @@ def login_user(request, error=""):
expires_time
=
time
.
time
()
+
max_age
expires
=
cookie_date
(
expires_time
)
response
.
set_cookie
(
settings
.
EDXMKTG_COOKIE_NAME
,
'true'
,
max_age
=
max_age
,
expires
=
expires
,
domain
=
settings
.
SESSION_COOKIE_DOMAIN
,
...
...
@@ -698,7 +696,6 @@ def create_account(request, post_override=None):
expires_time
=
time
.
time
()
+
max_age
expires
=
cookie_date
(
expires_time
)
response
.
set_cookie
(
settings
.
EDXMKTG_COOKIE_NAME
,
'true'
,
max_age
=
max_age
,
expires
=
expires
,
domain
=
settings
.
SESSION_COOKIE_DOMAIN
,
...
...
@@ -708,7 +705,6 @@ def create_account(request, post_override=None):
return
response
def
exam_registration_info
(
user
,
course
):
""" Returns a Registration object if the user is currently registered for a current
exam of the course. Returns None if the user is not registered, or if there is no
...
...
@@ -849,7 +845,6 @@ def create_exam_registration(request, post_override=None):
response_data
[
'non_field_errors'
]
=
form
.
non_field_errors
()
return
HttpResponse
(
json
.
dumps
(
response_data
),
mimetype
=
"application/json"
)
# only do the following if there is accommodation text to send,
# and a destination to which to send it.
# TODO: still need to create the accommodation email templates
...
...
@@ -872,7 +867,6 @@ def create_exam_registration(request, post_override=None):
# response_data['non_field_errors'] = [ 'Could not send accommodation e-mail.', ]
# return HttpResponse(json.dumps(response_data), mimetype="application/json")
js
=
{
'success'
:
True
}
return
HttpResponse
(
json
.
dumps
(
js
),
mimetype
=
"application/json"
)
...
...
@@ -916,6 +910,16 @@ def activate_account(request, key):
if
not
r
[
0
]
.
user
.
is_active
:
r
[
0
]
.
activate
()
already_active
=
False
#Enroll student in any pending courses he/she may have if auto_enroll flag is set
student
=
User
.
objects
.
filter
(
id
=
r
[
0
]
.
user_id
)
if
student
:
ceas
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
student
[
0
]
.
email
)
for
cea
in
ceas
:
if
cea
.
auto_enroll
:
course_id
=
cea
.
course_id
enrollment
,
created
=
CourseEnrollment
.
objects
.
get_or_create
(
user_id
=
student
[
0
]
.
id
,
course_id
=
course_id
)
resp
=
render_to_response
(
"registration/activation_complete.html"
,
{
'user_logged_in'
:
user_logged_in
,
'already_active'
:
already_active
})
return
resp
if
len
(
r
)
==
0
:
...
...
lms/djangoapps/instructor/tests/test_enrollment.py
0 → 100644
View file @
0b4e03e8
'''
Unit tests for enrollment methods in views.py
'''
from
django.test.utils
import
override_settings
from
django.contrib.auth.models
import
Group
,
User
from
django.core.urlresolvers
import
reverse
from
courseware.access
import
_course_staff_group_name
from
courseware.tests.tests
import
LoginEnrollmentTestCase
,
TEST_DATA_XML_MODULESTORE
,
get_user
from
xmodule.modulestore.django
import
modulestore
import
xmodule.modulestore.django
from
student.models
import
CourseEnrollment
,
CourseEnrollmentAllowed
from
instructor.views
import
get_and_clean_student_list
@override_settings
(
MODULESTORE
=
TEST_DATA_XML_MODULESTORE
)
class
TestInstructorEnrollsStudent
(
LoginEnrollmentTestCase
):
'''
Check Enrollment/Unenrollment with/without auto-enrollment on activation
'''
def
setUp
(
self
):
self
.
full
=
modulestore
()
.
get_course
(
"edX/full/6.002_Spring_2012"
)
self
.
toy
=
modulestore
()
.
get_course
(
"edX/toy/2012_Fall"
)
#Create instructor and student accounts
self
.
instructor
=
'instructor1@test.com'
self
.
student1
=
'student1@test.com'
self
.
student2
=
'student2@test.com'
self
.
password
=
'foo'
self
.
create_account
(
'it1'
,
self
.
instructor
,
self
.
password
)
self
.
create_account
(
'st1'
,
self
.
student1
,
self
.
password
)
self
.
create_account
(
'st2'
,
self
.
student2
,
self
.
password
)
self
.
activate_user
(
self
.
instructor
)
self
.
activate_user
(
self
.
student1
)
self
.
activate_user
(
self
.
student2
)
def
make_instructor
(
course
):
group_name
=
_course_staff_group_name
(
course
.
location
)
g
=
Group
.
objects
.
create
(
name
=
group_name
)
g
.
user_set
.
add
(
get_user
(
self
.
instructor
))
make_instructor
(
self
.
toy
)
#Enroll Students
self
.
logout
()
self
.
login
(
self
.
student1
,
self
.
password
)
self
.
enroll
(
self
.
toy
)
self
.
logout
()
self
.
login
(
self
.
student2
,
self
.
password
)
self
.
enroll
(
self
.
toy
)
#Enroll Instructor
self
.
logout
()
self
.
login
(
self
.
instructor
,
self
.
password
)
self
.
enroll
(
self
.
toy
)
def
test_unenrollment
(
self
):
'''
Do un-enrollment test
'''
course
=
self
.
toy
url
=
reverse
(
'instructor_dashboard'
,
kwargs
=
{
'course_id'
:
course
.
id
})
response
=
self
.
client
.
post
(
url
,
{
'action'
:
'Unenroll multiple students'
,
'multiple_students'
:
'student1@test.com, student2@test.com'
})
#Check the page output
self
.
assertContains
(
response
,
'<td>student1@test.com</td>'
)
self
.
assertContains
(
response
,
'<td>student2@test.com</td>'
)
self
.
assertContains
(
response
,
'<td>un-enrolled</td>'
)
#Check the enrollment table
user
=
User
.
objects
.
get
(
email
=
'student1@test.com'
)
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
,
user
=
user
)
self
.
assertEqual
(
0
,
len
(
ce
))
user
=
User
.
objects
.
get
(
email
=
'student2@test.com'
)
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
,
user
=
user
)
self
.
assertEqual
(
0
,
len
(
ce
))
def
test_enrollment_new_student_autoenroll_on
(
self
):
'''
Do auto-enroll on test
'''
#Run the Enroll students command
course
=
self
.
toy
url
=
reverse
(
'instructor_dashboard'
,
kwargs
=
{
'course_id'
:
course
.
id
})
response
=
self
.
client
.
post
(
url
,
{
'action'
:
'Enroll multiple students'
,
'multiple_students'
:
'test1_1@student.com, test1_2@student.com'
,
'auto_enroll'
:
'on'
})
#Check the page output
self
.
assertContains
(
response
,
'<td>test1_1@student.com</td>'
)
self
.
assertContains
(
response
,
'<td>test1_2@student.com</td>'
)
self
.
assertContains
(
response
,
'<td>user does not exist, enrollment allowed, pending with auto enrollment on</td>'
)
#Check the enrollmentallowed db entries
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
'test1_1@student.com'
,
course_id
=
course
.
id
)
self
.
assertEqual
(
1
,
cea
[
0
]
.
auto_enroll
)
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
'test1_2@student.com'
,
course_id
=
course
.
id
)
self
.
assertEqual
(
1
,
cea
[
0
]
.
auto_enroll
)
#Check there is no enrollment db entry other than for the setup instructor and students
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
)
self
.
assertEqual
(
3
,
len
(
ce
))
#Create and activate student accounts with same email
self
.
student1
=
'test1_1@student.com'
self
.
password
=
'bar'
self
.
create_account
(
's1_1'
,
self
.
student1
,
self
.
password
)
self
.
activate_user
(
self
.
student1
)
self
.
student2
=
'test1_2@student.com'
self
.
create_account
(
's1_2'
,
self
.
student2
,
self
.
password
)
self
.
activate_user
(
self
.
student2
)
#Check students are enrolled
user
=
User
.
objects
.
get
(
email
=
'test1_1@student.com'
)
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
,
user
=
user
)
self
.
assertEqual
(
1
,
len
(
ce
))
user
=
User
.
objects
.
get
(
email
=
'test1_2@student.com'
)
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
,
user
=
user
)
self
.
assertEqual
(
1
,
len
(
ce
))
def
test_enrollmemt_new_student_autoenroll_off
(
self
):
'''
Do auto-enroll off test
'''
#Run the Enroll students command
course
=
self
.
toy
url
=
reverse
(
'instructor_dashboard'
,
kwargs
=
{
'course_id'
:
course
.
id
})
response
=
self
.
client
.
post
(
url
,
{
'action'
:
'Enroll multiple students'
,
'multiple_students'
:
'test2_1@student.com, test2_2@student.com'
})
#Check the page output
self
.
assertContains
(
response
,
'<td>test2_1@student.com</td>'
)
self
.
assertContains
(
response
,
'<td>test2_2@student.com</td>'
)
self
.
assertContains
(
response
,
'<td>user does not exist, enrollment allowed, pending with auto enrollment off</td>'
)
#Check the enrollmentallowed db entries
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
'test2_1@student.com'
,
course_id
=
course
.
id
)
self
.
assertEqual
(
0
,
cea
[
0
]
.
auto_enroll
)
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
'test2_2@student.com'
,
course_id
=
course
.
id
)
self
.
assertEqual
(
0
,
cea
[
0
]
.
auto_enroll
)
#Check there is no enrollment db entry other than for the setup instructor and students
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
)
self
.
assertEqual
(
3
,
len
(
ce
))
#Create and activate student accounts with same email
self
.
student
=
'test2_1@student.com'
self
.
password
=
'bar'
self
.
create_account
(
's2_1'
,
self
.
student
,
self
.
password
)
self
.
activate_user
(
self
.
student
)
self
.
student
=
'test2_2@student.com'
self
.
create_account
(
's2_2'
,
self
.
student
,
self
.
password
)
self
.
activate_user
(
self
.
student
)
#Check students are not enrolled
user
=
User
.
objects
.
get
(
email
=
'test2_1@student.com'
)
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
,
user
=
user
)
self
.
assertEqual
(
0
,
len
(
ce
))
user
=
User
.
objects
.
get
(
email
=
'test2_2@student.com'
)
ce
=
CourseEnrollment
.
objects
.
filter
(
course_id
=
course
.
id
,
user
=
user
)
self
.
assertEqual
(
0
,
len
(
ce
))
def
test_get_and_clean_student_list
(
self
):
'''
Clean user input test
'''
string
=
"abc@test.com, def@test.com ghi@test.com
\n
\n
jkl@test.com "
cleaned_string
,
cleaned_string_lc
=
get_and_clean_student_list
(
string
)
self
.
assertEqual
(
cleaned_string
,
[
'abc@test.com'
,
'def@test.com'
,
'ghi@test.com'
,
'jkl@test.com'
])
lms/djangoapps/instructor/views.py
View file @
0b4e03e8
...
...
@@ -269,7 +269,6 @@ def instructor_dashboard(request, course_id):
except
:
msg
+=
"<font color='red'>Couldn't reset module state. </font>"
elif
"Get link to student's progress page"
in
action
:
unique_student_identifier
=
request
.
POST
.
get
(
'unique_student_identifier'
,
''
)
try
:
...
...
@@ -312,6 +311,7 @@ def instructor_dashboard(request, course_id):
msg2
,
rg_stud_data
=
_do_remote_gradebook
(
request
.
user
,
course
,
'get-membership'
)
datatable
=
{
'header'
:
[
'Student email'
,
'Match?'
]}
rg_students
=
[
x
[
'email'
]
for
x
in
rg_stud_data
[
'retdata'
]]
def
domatch
(
x
):
return
'yes'
if
x
.
email
in
rg_students
else
'No'
datatable
[
'data'
]
=
[[
x
.
email
,
domatch
(
x
)]
for
x
in
stud_data
[
'students'
]]
...
...
@@ -347,7 +347,6 @@ def instructor_dashboard(request, course_id):
msg2
,
_
=
_do_remote_gradebook
(
request
.
user
,
course
,
'post-grades'
,
files
=
files
)
msg
+=
msg2
#----------------------------------------
# Admin
...
...
@@ -413,6 +412,7 @@ def instructor_dashboard(request, course_id):
profkeys
=
[
'name'
,
'language'
,
'location'
,
'year_of_birth'
,
'gender'
,
'level_of_education'
,
'mailing_address'
,
'goals'
]
datatable
=
{
'header'
:
[
'username'
,
'email'
]
+
profkeys
}
def
getdat
(
u
):
p
=
u
.
profile
return
[
u
.
username
,
u
.
email
]
+
[
getattr
(
p
,
x
,
''
)
for
x
in
profkeys
]
...
...
@@ -421,9 +421,8 @@ def instructor_dashboard(request, course_id):
datatable
[
'title'
]
=
'Student profile data for course
%
s'
%
course_id
return
return_csv
(
'profiledata_
%
s.csv'
%
course_id
,
datatable
)
elif
'Download CSV of all responses to problem'
in
action
:
problem_to_dump
=
request
.
POST
.
get
(
'problem_to_dump'
,
''
)
problem_to_dump
=
request
.
POST
.
get
(
'problem_to_dump'
,
''
)
if
problem_to_dump
[
-
4
:]
==
".xml"
:
problem_to_dump
=
problem_to_dump
[:
-
4
]
...
...
@@ -441,7 +440,7 @@ def instructor_dashboard(request, course_id):
if
smdat
:
datatable
=
{
'header'
:
[
'username'
,
'state'
]}
datatable
[
'data'
]
=
[
[
x
.
student
.
username
,
x
.
state
]
for
x
in
smdat
]
datatable
[
'data'
]
=
[
[
x
.
student
.
username
,
x
.
state
]
for
x
in
smdat
]
datatable
[
'title'
]
=
'Student state for problem
%
s'
%
problem_to_dump
return
return_csv
(
'student_state_from_
%
s.csv'
%
problem_to_dump
,
datatable
)
...
...
@@ -478,7 +477,6 @@ def instructor_dashboard(request, course_id):
msg
+=
_list_course_forum_members
(
course_id
,
rolename
,
datatable
)
track
.
views
.
server_track
(
request
,
'list-{0}'
.
format
(
rolename
),
{},
page
=
'idashboard'
)
elif
action
==
'Remove forum admin'
:
uname
=
request
.
POST
[
'forumadmin'
]
msg
+=
_update_forum_role_membership
(
uname
,
course
,
FORUM_ROLE_ADMINISTRATOR
,
FORUM_ROLE_REMOVE
)
...
...
@@ -536,35 +534,17 @@ def instructor_dashboard(request, course_id):
datatable
[
'data'
]
=
[[
x
.
email
]
for
x
in
ceaset
]
datatable
[
'title'
]
=
action
elif
action
==
'Enroll
student
'
:
elif
action
==
'Enroll
multiple students
'
:
student
=
request
.
POST
.
get
(
'enstudent'
,
''
)
ret
=
_do_enroll_students
(
course
,
course_id
,
student
)
students
=
request
.
POST
.
get
(
'multiple_students'
,
''
)
auto_enroll
=
bool
(
request
.
POST
.
get
(
'auto_enroll'
))
ret
=
_do_enroll_students
(
course
,
course_id
,
students
,
auto_enroll
=
auto_enroll
)
datatable
=
ret
[
'datatable'
]
elif
action
==
'Un-enroll student'
:
student
=
request
.
POST
.
get
(
'enstudent'
,
''
)
datatable
=
{}
isok
=
False
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
course_id
=
course_id
,
email
=
student
)
if
cea
:
cea
.
delete
()
msg
+=
"Un-enrolled student with email '
%
s'"
%
student
isok
=
True
try
:
nce
=
CourseEnrollment
.
objects
.
get
(
user
=
User
.
objects
.
get
(
email
=
student
),
course_id
=
course_id
)
nce
.
delete
()
msg
+=
"Un-enrolled student with email '
%
s'"
%
student
except
Exception
as
err
:
if
not
isok
:
msg
+=
"Error! Failed to un-enroll student with email '
%
s'
\n
"
%
student
msg
+=
str
(
err
)
+
'
\n
'
elif
action
==
'Enroll multiple students'
:
elif
action
==
'Unenroll multiple students'
:
students
=
request
.
POST
.
get
(
'
enroll_multiple
'
,
''
)
ret
=
_do_
enroll_students
(
course
,
course_id
,
students
)
students
=
request
.
POST
.
get
(
'
multiple_students
'
,
''
)
ret
=
_do_
unenroll_students
(
course_id
,
students
)
datatable
=
ret
[
'datatable'
]
elif
action
==
'List sections available in remote gradebook'
:
...
...
@@ -586,7 +566,6 @@ def instructor_dashboard(request, course_id):
ret
=
_do_enroll_students
(
course
,
course_id
,
students
,
overload
=
overload
)
datatable
=
ret
[
'datatable'
]
#----------------------------------------
# psychometrics
...
...
@@ -982,17 +961,11 @@ def grade_summary(request, course_id):
#-----------------------------------------------------------------------------
# enrollment
def
_do_enroll_students
(
course
,
course_id
,
students
,
overload
=
False
):
def
_do_enroll_students
(
course
,
course_id
,
students
,
overload
=
False
,
auto_enroll
=
False
):
"""Do the actual work of enrolling multiple students, presented as a string
of emails separated by commas or returns"""
new_students
=
split_by_comma_and_whitespace
(
students
)
new_students
=
[
str
(
s
.
strip
())
for
s
in
new_students
]
new_students_lc
=
[
x
.
lower
()
for
x
in
new_students
]
if
''
in
new_students
:
new_students
.
remove
(
''
)
new_students
,
new_students_lc
=
get_and_clean_student_list
(
students
)
status
=
dict
([
x
,
'unprocessed'
]
for
x
in
new_students
)
if
overload
:
# delete all but staff
...
...
@@ -1012,27 +985,35 @@ def _do_enroll_students(course, course_id, students, overload=False):
try
:
user
=
User
.
objects
.
get
(
email
=
student
)
except
User
.
DoesNotExist
:
# user not signed up yet, put in pending enrollment allowed table
if
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
student
,
course_id
=
course_id
):
status
[
student
]
=
'user does not exist, enrollment already allowed, pending'
#User not signed up yet, put in pending enrollment allowed table
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
student
,
course_id
=
course_id
)
#If enrollmentallowed already exists, update auto_enroll flag to however it was set in UI
#Will be 0 or 1 records as there is a unique key on email + course_id
if
cea
:
cea
[
0
]
.
auto_enroll
=
auto_enroll
cea
[
0
]
.
save
()
status
[
student
]
=
'user does not exist, enrollment already allowed, pending with auto enrollment '
\
+
(
'on'
if
auto_enroll
else
'off'
)
continue
cea
=
CourseEnrollmentAllowed
(
email
=
student
,
course_id
=
course_id
)
cea
=
CourseEnrollmentAllowed
(
email
=
student
,
course_id
=
course_id
,
auto_enroll
=
auto_enroll
)
cea
.
save
()
status
[
student
]
=
'user does not exist, enrollment allowed, pending
'
status
[
student
]
=
'user does not exist, enrollment allowed, pending
with auto enrollment '
+
(
'on'
if
auto_enroll
else
'off'
)
continue
if
CourseEnrollment
.
objects
.
filter
(
user
=
user
,
course_id
=
course_id
):
status
[
student
]
=
'already enrolled'
continue
try
:
n
ce
=
CourseEnrollment
(
user
=
user
,
course_id
=
course_id
)
n
ce
.
save
()
ce
=
CourseEnrollment
(
user
=
user
,
course_id
=
course_id
)
ce
.
save
()
status
[
student
]
=
'added'
except
:
status
[
student
]
=
'rejected'
datatable
=
{
'header'
:
[
'StudentEmail'
,
'action'
]}
datatable
[
'data'
]
=
[[
x
,
status
[
x
]]
for
x
in
s
tatus
]
datatable
[
'data'
]
=
[[
x
,
status
[
x
]]
for
x
in
s
orted
(
status
)
]
datatable
[
'title'
]
=
'Enrollment of students'
def
sf
(
stat
):
...
...
@@ -1044,39 +1025,69 @@ def _do_enroll_students(course, course_id, students, overload=False):
return
data
@ensure_csrf_cookie
@cache_control
(
no_cache
=
True
,
no_store
=
True
,
must_revalidate
=
True
)
def
enroll_students
(
request
,
course_id
):
"""Allows a staff member to enroll students in a course.
#Unenrollment
def
_do_unenroll_students
(
course_id
,
students
):
"""Do the actual work of un-enrolling multiple students, presented as a string
of emails separated by commas or returns"""
This is a short-term hack for Berkeley courses launching fall
2012. In the long term, we would like functionality like this, but
we would like both the instructor and the student to agree. Right
now, this allows any instructor to add students to their course,
which we do not want.
old_students
,
old_students_lc
=
get_and_clean_student_list
(
students
)
status
=
dict
([
x
,
'unprocessed'
]
for
x
in
old_students
)
It is poorly written and poorly tested, but it's designed to be
stripped out.
"""
for
student
in
old_students
:
course
=
get_course_with_access
(
request
.
user
,
course_id
,
'staff'
)
existing_students
=
[
ce
.
user
.
email
for
ce
in
CourseEnrollment
.
objects
.
filter
(
course_id
=
course_id
)]
isok
=
False
cea
=
CourseEnrollmentAllowed
.
objects
.
filter
(
course_id
=
course_id
,
email
=
student
)
#Will be 0 or 1 records as there is a unique key on email + course_id
if
cea
:
cea
[
0
]
.
delete
()
status
[
student
]
=
"un-enrolled"
isok
=
True
new_students
=
request
.
POST
.
get
(
'new_students'
)
ret
=
_do_enroll_students
(
course
,
course_id
,
new_students
)
added_students
=
ret
[
'added'
]
rejected_students
=
ret
[
'rejected'
]
try
:
user
=
User
.
objects
.
get
(
email
=
student
)
except
User
.
DoesNotExist
:
continue
ce
=
CourseEnrollment
.
objects
.
filter
(
user
=
user
,
course_id
=
course_id
)
#Will be 0 or 1 records as there is a unique key on user + course_id
if
ce
:
try
:
ce
[
0
]
.
delete
()
status
[
student
]
=
"un-enrolled"
except
Exception
as
err
:
if
not
isok
:
status
[
student
]
=
"Error! Failed to un-enroll"
datatable
=
{
'header'
:
[
'StudentEmail'
,
'action'
]}
datatable
[
'data'
]
=
[[
x
,
status
[
x
]]
for
x
in
sorted
(
status
)]
datatable
[
'title'
]
=
'Un-enrollment of students'
data
=
dict
(
datatable
=
datatable
)
return
data
return
render_to_response
(
"enroll_students.html"
,
{
'course'
:
course_id
,
'existing_students'
:
existing_students
,
'added_students'
:
added_students
,
'rejected_students'
:
rejected_students
,
'debug'
:
new_students
})
def
get_and_clean_student_list
(
students
):
"""
Separate out individual student email from the comma, or space separated string.
In:
students: string coming from the input text area
Return:
students: list of cleaned student emails
students_lc: list of lower case cleaned student emails
"""
students
=
split_by_comma_and_whitespace
(
students
)
students
=
[
str
(
s
.
strip
())
for
s
in
students
]
students
=
[
s
for
s
in
students
if
s
!=
''
]
students_lc
=
[
x
.
lower
()
for
x
in
students
]
return
students
,
students_lc
#-----------------------------------------------------------------------------
# answer distribution
def
get_answers_distribution
(
request
,
course_id
):
"""
Get the distribution of answers for all graded problems in the course.
...
...
@@ -1168,5 +1179,5 @@ def dump_grading_context(course):
msg
+=
"
%
s (format=
%
s, Assignment=
%
s
%
s)
\n
"
%
(
s
.
display_name
,
format
,
aname
,
notes
)
msg
+=
"all descriptors:
\n
"
msg
+=
"length=
%
d
\n
"
%
len
(
gc
[
'all_descriptors'
])
msg
=
'<pre>
%
s</pre>'
%
msg
.
replace
(
'<'
,
'<'
)
msg
=
'<pre>
%
s</pre>'
%
msg
.
replace
(
'<'
,
'<'
)
return
msg
lms/templates/courseware/instructor_dashboard.html
View file @
0b4e03e8
...
...
@@ -296,9 +296,6 @@ function goto( mode)
<p>
<input
type=
"submit"
name=
"action"
value=
"List enrolled students"
>
<input
type=
"submit"
name=
"action"
value=
"List students who may enroll but may not have yet signed up"
>
<p>
Student Email:
<input
type=
"text"
name=
"enstudent"
>
<input
type=
"submit"
name=
"action"
value=
"Un-enroll student"
>
<input
type=
"submit"
name=
"action"
value=
"Enroll student"
>
<hr
width=
"40%"
style=
"align:left"
>
%if settings.MITX_FEATURES.get('REMOTE_GRADEBOOK_URL','') and instructor_access:
...
...
@@ -320,9 +317,13 @@ function goto( mode)
%endif
<p>
Add students: enter emails, separated by new lines or commas;
</p>
<textarea
rows=
"6"
cols=
"70"
name=
"enroll_multiple"
></textarea>
<p>
Enroll or un-enroll one or many students: enter emails, separated by new lines or commas;
</p>
<textarea
rows=
"6"
cols=
"70"
name=
"multiple_students"
></textarea>
<p>
<input
type=
"checkbox"
name=
"auto_enroll"
>
Auto-enroll students when they activate
<input
type=
"submit"
name=
"action"
value=
"Enroll multiple students"
>
<p>
<input
type=
"submit"
name=
"action"
value=
"Unenroll multiple students"
>
%endif
...
...
lms/urls.py
View file @
0b4e03e8
...
...
@@ -267,8 +267,6 @@ if settings.COURSEWARE_ENABLED:
'instructor.views.gradebook'
,
name
=
'gradebook'
),
url
(
r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/grade_summary$'
,
'instructor.views.grade_summary'
,
name
=
'grade_summary'
),
url
(
r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/enroll_students$'
,
'instructor.views.enroll_students'
,
name
=
'enroll_students'
),
url
(
r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading$'
,
'open_ended_grading.views.staff_grading'
,
name
=
'staff_grading'
),
url
(
r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading/get_next$'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment