pep8 fixes
This commit is contained in:
@@ -4,7 +4,7 @@ Models for Student Information
|
||||
Replication Notes
|
||||
|
||||
In our live deployment, we intend to run in a scenario where there is a pool of
|
||||
Portal servers that hold the canoncial user information and that user
|
||||
Portal servers that hold the canoncial user information and that user
|
||||
information is replicated to slave Course server pools. Each Course has a set of
|
||||
servers that serves only its content and has users that are relevant only to it.
|
||||
|
||||
@@ -61,6 +61,7 @@ from xmodule.modulestore.django import modulestore
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserProfile(models.Model):
|
||||
"""This is where we store all the user demographic fields. We have a
|
||||
separate table for this rather than extending the built-in Django auth_user.
|
||||
@@ -175,6 +176,7 @@ class PendingEmailChange(models.Model):
|
||||
new_email = models.CharField(blank=True, max_length=255, db_index=True)
|
||||
activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True)
|
||||
|
||||
|
||||
class CourseEnrollment(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
course_id = models.CharField(max_length=255, db_index=True)
|
||||
@@ -185,7 +187,8 @@ class CourseEnrollment(models.Model):
|
||||
unique_together = (('user', 'course_id'), )
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s: %s (%s)" % (self.user,self.course_id,self.created)
|
||||
return "%s: %s (%s)" % (self.user, self.course_id, self.created)
|
||||
|
||||
|
||||
@receiver(post_save, sender=CourseEnrollment)
|
||||
def assign_default_role(sender, instance, **kwargs):
|
||||
@@ -276,6 +279,7 @@ def add_user_to_default_group(user, group):
|
||||
utg.users.add(User.objects.get(username=user))
|
||||
utg.save()
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def update_user_information(sender, instance, created, **kwargs):
|
||||
try:
|
||||
@@ -286,6 +290,7 @@ def update_user_information(sender, instance, created, **kwargs):
|
||||
log.error(unicode(e))
|
||||
log.error("update user info to discussion failed for user with id: " + str(instance.id))
|
||||
|
||||
|
||||
########################## REPLICATION SIGNALS #################################
|
||||
# @receiver(post_save, sender=User)
|
||||
def replicate_user_save(sender, **kwargs):
|
||||
@@ -295,6 +300,7 @@ def replicate_user_save(sender, **kwargs):
|
||||
for course_db_name in db_names_to_replicate_to(user_obj.id):
|
||||
replicate_user(user_obj, course_db_name)
|
||||
|
||||
|
||||
# @receiver(post_save, sender=CourseEnrollment)
|
||||
def replicate_enrollment_save(sender, **kwargs):
|
||||
"""This is called when a Student enrolls in a course. It has to do the
|
||||
@@ -320,12 +326,14 @@ def replicate_enrollment_save(sender, **kwargs):
|
||||
log.debug("Replicating user profile because of new enrollment")
|
||||
user_profile = UserProfile.objects.get(user_id=enrollment_obj.user_id)
|
||||
replicate_model(UserProfile.save, user_profile, enrollment_obj.user_id)
|
||||
|
||||
|
||||
|
||||
# @receiver(post_delete, sender=CourseEnrollment)
|
||||
def replicate_enrollment_delete(sender, **kwargs):
|
||||
enrollment_obj = kwargs['instance']
|
||||
return replicate_model(CourseEnrollment.delete, enrollment_obj, enrollment_obj.user_id)
|
||||
|
||||
|
||||
|
||||
# @receiver(post_save, sender=UserProfile)
|
||||
def replicate_userprofile_save(sender, **kwargs):
|
||||
"""We just updated the UserProfile (say an update to the name), so push that
|
||||
@@ -333,12 +341,13 @@ def replicate_userprofile_save(sender, **kwargs):
|
||||
user_profile_obj = kwargs['instance']
|
||||
return replicate_model(UserProfile.save, user_profile_obj, user_profile_obj.user_id)
|
||||
|
||||
|
||||
|
||||
######### Replication functions #########
|
||||
USER_FIELDS_TO_COPY = ["id", "username", "first_name", "last_name", "email",
|
||||
"password", "is_staff", "is_active", "is_superuser",
|
||||
"last_login", "date_joined"]
|
||||
|
||||
|
||||
def replicate_user(portal_user, course_db_name):
|
||||
"""Replicate a User to the correct Course DB. This is more complicated than
|
||||
it should be because Askbot extends the auth_user table and adds its own
|
||||
@@ -362,9 +371,10 @@ def replicate_user(portal_user, course_db_name):
|
||||
course_user.save(using=course_db_name)
|
||||
unmark(course_user)
|
||||
|
||||
|
||||
def replicate_model(model_method, instance, user_id):
|
||||
"""
|
||||
model_method is the model action that we want replicated. For instance,
|
||||
model_method is the model action that we want replicated. For instance,
|
||||
UserProfile.save
|
||||
"""
|
||||
if not should_replicate(instance):
|
||||
@@ -379,8 +389,10 @@ def replicate_model(model_method, instance, user_id):
|
||||
model_method(instance, using=db_name)
|
||||
unmark(instance)
|
||||
|
||||
|
||||
######### Replication Helpers #########
|
||||
|
||||
|
||||
def is_valid_course_id(course_id):
|
||||
"""Right now, the only database that's not a course database is 'default'.
|
||||
I had nicer checking in here originally -- it would scan the courses that
|
||||
@@ -390,26 +402,30 @@ def is_valid_course_id(course_id):
|
||||
"""
|
||||
return course_id != 'default'
|
||||
|
||||
|
||||
def is_portal():
|
||||
"""Are we in the portal pool? Only Portal servers are allowed to replicate
|
||||
their changes. For now, only Portal servers see multiple DBs, so we use
|
||||
that to decide."""
|
||||
return len(settings.DATABASES) > 1
|
||||
|
||||
|
||||
def db_names_to_replicate_to(user_id):
|
||||
"""Return a list of DB names that this user_id is enrolled in."""
|
||||
return [c.course_id
|
||||
for c in CourseEnrollment.objects.filter(user_id=user_id)
|
||||
if is_valid_course_id(c.course_id)]
|
||||
|
||||
|
||||
def marked_handled(instance):
|
||||
"""Have we marked this instance as being handled to avoid infinite loops
|
||||
caused by saving models in post_save hooks for the same models?"""
|
||||
return hasattr(instance, '_do_not_copy_to_course_db') and instance._do_not_copy_to_course_db
|
||||
|
||||
|
||||
def mark_handled(instance):
|
||||
"""You have to mark your instance with this function or else we'll go into
|
||||
an infinite loop since we're putting listeners on Model saves/deletes and
|
||||
an infinite loop since we're putting listeners on Model saves/deletes and
|
||||
the act of replication requires us to call the same model method.
|
||||
|
||||
We create a _replicated attribute to differentiate the first save of this
|
||||
@@ -418,16 +434,18 @@ def mark_handled(instance):
|
||||
"""
|
||||
instance._do_not_copy_to_course_db = True
|
||||
|
||||
|
||||
def unmark(instance):
|
||||
"""If we don't unmark a model after we do replication, then consecutive
|
||||
"""If we don't unmark a model after we do replication, then consecutive
|
||||
save() calls won't be properly replicated."""
|
||||
instance._do_not_copy_to_course_db = False
|
||||
|
||||
|
||||
def should_replicate(instance):
|
||||
"""Should this instance be replicated? We need to be a Portal server and
|
||||
the instance has to not have been marked_handled."""
|
||||
if marked_handled(instance):
|
||||
# Basically, avoid an infinite loop. You should
|
||||
# Basically, avoid an infinite loop. You should
|
||||
log.debug("{0} should not be replicated because it's been marked"
|
||||
.format(instance))
|
||||
return False
|
||||
|
||||
@@ -38,54 +38,55 @@ log = logging.getLogger("mitx.courseware")
|
||||
|
||||
template_imports = {'urllib': urllib}
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def instructor_dashboard(request, course_id):
|
||||
"""Display the instructor dashboard for a course."""
|
||||
course = get_course_with_access(request.user, course_id, 'staff')
|
||||
|
||||
instructor_access = has_access(request.user, course, 'instructor') # an instructor can manage staff lists
|
||||
instructor_access = has_access(request.user, course, 'instructor') # an instructor can manage staff lists
|
||||
|
||||
msg = ''
|
||||
# msg += ('POST=%s' % dict(request.POST)).replace('<','<')
|
||||
|
||||
def escape(s):
|
||||
"""escape HTML special characters in string"""
|
||||
return str(s).replace('<','<').replace('>','>')
|
||||
return str(s).replace('<', '<').replace('>', '>')
|
||||
|
||||
# assemble some course statistics for output to instructor
|
||||
datatable = {'header': ['Statistic','Value'],
|
||||
datatable = {'header': ['Statistic', 'Value'],
|
||||
'title': 'Course Statistics At A Glance',
|
||||
}
|
||||
data = [ ['# Enrolled' ,CourseEnrollment.objects.filter(course_id=course_id).count()] ]
|
||||
data = [['# Enrolled', CourseEnrollment.objects.filter(course_id=course_id).count()]]
|
||||
data += compute_course_stats(course).items()
|
||||
if request.user.is_staff:
|
||||
data.append(['metadata', escape(str(course.metadata))])
|
||||
datatable['data'] = data
|
||||
|
||||
def return_csv(fn,datatable):
|
||||
def return_csv(fn, datatable):
|
||||
response = HttpResponse(mimetype='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=%s' % fn
|
||||
writer = csv.writer(response,dialect='excel',quotechar='"', quoting=csv.QUOTE_ALL)
|
||||
writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL)
|
||||
writer.writerow(datatable['header'])
|
||||
for datarow in datatable['data']:
|
||||
writer.writerow(datarow)
|
||||
return response
|
||||
|
||||
def get_staff_group(course):
|
||||
staffgrp = get_access_group_name(course,'staff')
|
||||
staffgrp = get_access_group_name(course, 'staff')
|
||||
try:
|
||||
group = Group.objects.get(name=staffgrp)
|
||||
except Group.DoesNotExist:
|
||||
group = Group(name=staffgrp) # create the group
|
||||
group = Group(name=staffgrp) # create the group
|
||||
group.save()
|
||||
return group
|
||||
|
||||
# process actions from form POST
|
||||
action = request.POST.get('action','')
|
||||
action = request.POST.get('action', '')
|
||||
|
||||
if 'Reload' in action:
|
||||
log.debug('reloading %s (%s)' % (course_id,course))
|
||||
log.debug('reloading %s (%s)' % (course_id, course))
|
||||
try:
|
||||
data_dir = course.metadata['data_dir']
|
||||
modulestore().try_load_course(data_dir)
|
||||
@@ -93,7 +94,7 @@ def instructor_dashboard(request, course_id):
|
||||
except Exception as err:
|
||||
msg += '<br/><p>Error: %s</p>' % escape(err)
|
||||
|
||||
elif action=='Dump list of enrolled students':
|
||||
elif action == 'Dump list of enrolled students':
|
||||
log.debug(action)
|
||||
datatable = get_student_grade_summary_data(request, course, course_id, get_grades=False)
|
||||
datatable['title'] = 'List of students enrolled in %s' % course_id
|
||||
@@ -122,11 +123,11 @@ def instructor_dashboard(request, course_id):
|
||||
msg += 'Staff group = %s' % group.name
|
||||
log.debug('staffgrp=%s' % group.name)
|
||||
uset = group.user_set.all()
|
||||
datatable = {'header': ['Username','Full name']}
|
||||
datatable['data'] = [[ x.username, x.profile.name ] for x in uset]
|
||||
datatable = {'header': ['Username', 'Full name']}
|
||||
datatable['data'] = [[x.username, x.profile.name] for x in uset]
|
||||
datatable['title'] = 'List of Staff in course %s' % course_id
|
||||
|
||||
elif action=='Add course staff':
|
||||
elif action == 'Add course staff':
|
||||
uname = request.POST['staffuser']
|
||||
try:
|
||||
user = User.objects.get(username=uname)
|
||||
@@ -135,11 +136,11 @@ def instructor_dashboard(request, course_id):
|
||||
user = None
|
||||
if user is not None:
|
||||
group = get_staff_group(course)
|
||||
msg += '<font color="green">Added %s to staff group = %s</font>' % (user,group.name)
|
||||
msg += '<font color="green">Added %s to staff group = %s</font>' % (user, group.name)
|
||||
log.debug('staffgrp=%s' % group.name)
|
||||
user.groups.add(group)
|
||||
|
||||
elif action=='Remove course staff':
|
||||
elif action == 'Remove course staff':
|
||||
uname = request.POST['staffuser']
|
||||
try:
|
||||
user = User.objects.get(username=uname)
|
||||
@@ -148,21 +149,22 @@ def instructor_dashboard(request, course_id):
|
||||
user = None
|
||||
if user is not None:
|
||||
group = get_staff_group(course)
|
||||
msg += '<font color="green">Removed %s from staff group = %s</font>' % (user,group.name)
|
||||
msg += '<font color="green">Removed %s from staff group = %s</font>' % (user, group.name)
|
||||
log.debug('staffgrp=%s' % group.name)
|
||||
user.groups.remove(group)
|
||||
|
||||
# For now, mostly a static page
|
||||
context = {'course': course,
|
||||
'staff_access': True,
|
||||
'admin_access' : request.user.is_staff,
|
||||
'instructor_access' : instructor_access,
|
||||
'datatable' : datatable,
|
||||
'msg' : msg,
|
||||
'admin_access': request.user.is_staff,
|
||||
'instructor_access': instructor_access,
|
||||
'datatable': datatable,
|
||||
'msg': msg,
|
||||
}
|
||||
|
||||
return render_to_response('courseware/instructor_dashboard.html', context)
|
||||
|
||||
|
||||
def get_student_grade_summary_data(request, course, course_id, get_grades=True, get_raw_scores=False):
|
||||
'''
|
||||
Return data arrays with student identity and grades for specified course.
|
||||
@@ -171,7 +173,7 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True,
|
||||
course_id = course ID
|
||||
|
||||
Note: both are passed in, only because instructor_dashboard already has them already.
|
||||
|
||||
|
||||
returns datatable = dict(header=header, data=data)
|
||||
where
|
||||
|
||||
@@ -183,9 +185,10 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True,
|
||||
'''
|
||||
enrolled_students = User.objects.filter(courseenrollment__course_id=course_id).order_by('username')
|
||||
|
||||
header = ['ID', 'Username','Full Name','edX email','External email']
|
||||
header = ['ID', 'Username', 'Full Name', 'edX email', 'External email']
|
||||
if get_grades:
|
||||
gradeset = grades.grade(enrolled_students[0], request, course, keep_raw_scores=get_raw_scores) # just to construct the header
|
||||
# just to construct the header
|
||||
gradeset = grades.grade(enrolled_students[0], request, course, keep_raw_scores=get_raw_scores)
|
||||
# log.debug('student %s gradeset %s' % (enrolled_students[0], gradeset))
|
||||
if get_raw_scores:
|
||||
header += [score.section for score in gradeset['raw_scores']]
|
||||
@@ -196,7 +199,7 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True,
|
||||
data = []
|
||||
|
||||
for student in enrolled_students:
|
||||
datarow = [ student.id, student.username, student.profile.name, student.email ]
|
||||
datarow = [ student.id, student.username, student.profile.name, student.email ]
|
||||
try:
|
||||
datarow.append(student.externalauthmap.external_email)
|
||||
except: # ExternalAuthMap.DoesNotExist
|
||||
@@ -214,6 +217,7 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True,
|
||||
datatable['data'] = data
|
||||
return datatable
|
||||
|
||||
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def gradebook(request, course_id):
|
||||
"""
|
||||
@@ -240,7 +244,7 @@ def gradebook(request, course_id):
|
||||
'course': course,
|
||||
'course_id': course_id,
|
||||
# Checked above
|
||||
'staff_access': True,})
|
||||
'staff_access': True, })
|
||||
|
||||
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@@ -250,9 +254,10 @@ def grade_summary(request, course_id):
|
||||
|
||||
# For now, just a static page
|
||||
context = {'course': course,
|
||||
'staff_access': True,}
|
||||
'staff_access': True, }
|
||||
return render_to_response('courseware/grade_summary.html', context)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def enroll_students(request, course_id):
|
||||
@@ -269,7 +274,7 @@ def enroll_students(request, course_id):
|
||||
'''
|
||||
|
||||
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)]
|
||||
existing_students = [ce.user.email for ce in CourseEnrollment.objects.filter(course_id=course_id)]
|
||||
|
||||
if 'new_students' in request.POST:
|
||||
new_students = request.POST['new_students'].split('\n')
|
||||
@@ -282,20 +287,21 @@ def enroll_students(request, course_id):
|
||||
|
||||
for student in new_students:
|
||||
try:
|
||||
nce = CourseEnrollment(user=User.objects.get(email = student), course_id = course_id)
|
||||
nce = CourseEnrollment(user=User.objects.get(email=student), course_id=course_id)
|
||||
nce.save()
|
||||
added_students.append(student)
|
||||
except:
|
||||
rejected_students.append(student)
|
||||
|
||||
return render_to_response("enroll_students.html", {'course':course_id,
|
||||
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})
|
||||
'debug': new_students})
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def compute_course_stats(course):
|
||||
'''
|
||||
Compute course statistics, including number of problems, videos, html.
|
||||
@@ -308,23 +314,15 @@ def compute_course_stats(course):
|
||||
|
||||
counts = defaultdict(int)
|
||||
|
||||
print "hello world"
|
||||
|
||||
def walk(module):
|
||||
children = module.get_children()
|
||||
if not children:
|
||||
category = module.__class__.__name__ # HtmlDescriptor, CapaDescriptor, ...
|
||||
category = module.__class__.__name__ # HtmlDescriptor, CapaDescriptor, ...
|
||||
counts[category] += 1
|
||||
return
|
||||
for c in children:
|
||||
# print c.__class__.__name__
|
||||
walk(c)
|
||||
|
||||
walk(course)
|
||||
|
||||
print "course %s counts=%s" % (course.display_name,counts)
|
||||
|
||||
stats = dict(counts) # number of each kind of module
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
Reference in New Issue
Block a user