diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index e649bf89f6..3e603a108d 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -145,6 +145,8 @@ def has_staff_access_to_course(user, course): ''' Returns True if the given user has staff access to the course. This means that user is in the staff_* group, or is an overall admin. + TODO (vshnayder): this needs to be changed to allow per-course_id permissions, not per-course + (e.g. staff in 2012 is different from 2013, but maybe some people always have access) course is the course field of the location being accessed. ''' @@ -160,7 +162,14 @@ def has_staff_access_to_course(user, course): return True return False -def has_access_to_course(user,course): +def has_staff_access_to_course_id(user, course_id): + """Helper method that takes a course_id instead of a course name""" + loc = CourseDescriptor.id_to_location(course_id) + return has_staff_access_to_course(user, loc.course) + + +def has_access_to_course(user, course): + '''course is the .course element of a location''' if course.metadata.get('ispublic'): return True return has_staff_access_to_course(user,course) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 59fa5d69e1..02e6d00a58 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -28,7 +28,7 @@ from util.cache import cache, cache_if_anonymous from student.models import UserTestGroup, CourseEnrollment from courseware import grades from courseware.courses import (check_course, get_courses_by_university, - has_staff_access_to_course) + has_staff_access_to_course_id) log = logging.getLogger("mitx.courseware") @@ -36,6 +36,9 @@ log = logging.getLogger("mitx.courseware") template_imports = {'urllib': urllib} def user_groups(user): + """ + TODO (vshnayder): This is not used. When we have a new plan for groups, adjust appropriately. + """ if not user.is_authenticated(): return [] @@ -65,65 +68,6 @@ def courses(request): universities = get_courses_by_university(request.user) return render_to_response("courses.html", {'universities': universities}) -@cache_control(no_cache=True, no_store=True, must_revalidate=True) -def gradebook(request, course_id): - if 'course_admin' not in user_groups(request.user): - raise Http404 - course = check_course(course_id) - - enrolled_students = User.objects.filter(courseenrollment__course_id=course_id).order_by('username') - - # TODO (vshnayder): implement pagination. - enrolled_students = enrolled_students[:1000] # HACK! - - student_info = [{'username': student.username, - 'id': student.id, - 'email': student.email, - 'grade_summary': grades.grade(student, request, course), - 'realname': UserProfile.objects.get(user=student).name - } - for student in enrolled_students] - - return render_to_response('gradebook.html', {'students': student_info, - 'course': course, 'course_id': course_id}) - - -@login_required -@cache_control(no_cache=True, no_store=True, must_revalidate=True) -def profile(request, course_id, student_id=None): - ''' User profile. Show username, location, etc, as well as grades . - We need to allow the user to change some of these settings .''' - course = check_course(course_id) - - if student_id is None: - student = request.user - else: - if 'course_admin' not in user_groups(request.user): - raise Http404 - student = User.objects.get(id=int(student_id)) - - user_info = UserProfile.objects.get(user=student) - - student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(request.user, course) - course_module = get_module(request.user, request, course.location, student_module_cache) - - courseware_summary = grades.progress_summary(student, course_module, course.grader, student_module_cache) - grade_summary = grades.grade(request.user, request, course, student_module_cache) - - context = {'name': user_info.name, - 'username': student.username, - 'location': user_info.location, - 'language': user_info.language, - 'email': student.email, - 'course': course, - 'csrf': csrf(request)['csrf_token'], - 'courseware_summary' : courseware_summary, - 'grade_summary' : grade_summary - } - context.update() - - return render_to_response('profile.html', context) - def render_accordion(request, course, chapter, section): ''' Draws navigation bar. Takes current position in accordion as @@ -298,3 +242,104 @@ def university_profile(request, org_id): template_file = "university_profile/{0}.html".format(org_id).lower() return render_to_response(template_file, context) + + +@login_required +@cache_control(no_cache=True, no_store=True, must_revalidate=True) +def profile(request, course_id, student_id=None): + """ User profile. Show username, location, etc, as well as grades . + We need to allow the user to change some of these settings. + + Course staff are allowed to see the profiles of students in their class. + """ + course = check_course(course_id) + + if student_id is None or student_id == request.user.id: + # always allowed to see your own profile + student = request.user + else: + # Requesting access to a different student's profile + if not has_staff_access_to_course_id(request.user, course_id): + raise Http404 + student = User.objects.get(id=int(student_id)) + + user_info = UserProfile.objects.get(user=student) + + student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(request.user, course) + course_module = get_module(request.user, request, course.location, student_module_cache) + + courseware_summary = grades.progress_summary(student, course_module, course.grader, student_module_cache) + grade_summary = grades.grade(request.user, request, course, student_module_cache) + + context = {'name': user_info.name, + 'username': student.username, + 'location': user_info.location, + 'language': user_info.language, + 'email': student.email, + 'course': course, + 'csrf': csrf(request)['csrf_token'], + 'courseware_summary' : courseware_summary, + 'grade_summary' : grade_summary + } + context.update() + + return render_to_response('profile.html', context) + + + +# ======== Instructor views ============================================================================= + +@cache_control(no_cache=True, no_store=True, must_revalidate=True) +def gradebook(request, course_id): + """ + Show the gradebook for this course: + - only displayed to course staff + - shows students who are enrolled. + """ + if not has_staff_access_to_course_id(request.user, course_id): + raise Http404 + + course = check_course(course_id) + + enrolled_students = User.objects.filter(courseenrollment__course_id=course_id).order_by('username') + + # TODO (vshnayder): implement pagination. + enrolled_students = enrolled_students[:1000] # HACK! + + student_info = [{'username': student.username, + 'id': student.id, + 'email': student.email, + 'grade_summary': grades.grade(student, request, course), + 'realname': UserProfile.objects.get(user=student).name + } + for student in enrolled_students] + + return render_to_response('gradebook.html', {'students': student_info, + 'course': course, 'course_id': course_id}) + + +@cache_control(no_cache=True, no_store=True, must_revalidate=True) +def grade_summary(request, course_id): + """Display the grade summary for a course.""" + if not has_staff_access_to_course_id(request.user, course_id): + raise Http404 + + course = check_course(course_id) + + # For now, just a static page + context = {'course': course } + return render_to_response('grade_summary.html', context) + + +@cache_control(no_cache=True, no_store=True, must_revalidate=True) +def instructor_dashboard(request, course_id): + """Display the instructor dashboard for a course.""" + if not has_staff_access_to_course_id(request.user, course_id): + raise Http404 + + course = check_course(course_id) + + # For now, just a static page + context = {'course': course } + return render_to_response('instructor_dashboard.html', context) + diff --git a/lms/templates/course_navigation.html b/lms/templates/course_navigation.html index 84b0c04ca0..62e6586a0d 100644 --- a/lms/templates/course_navigation.html +++ b/lms/templates/course_navigation.html @@ -7,6 +7,7 @@ def url_class(url): return "" %> <%! from django.core.urlresolvers import reverse %> +<%! from courseware.courses import has_staff_access_to_course_id %> diff --git a/lms/templates/grade_summary.html b/lms/templates/grade_summary.html new file mode 100644 index 0000000000..3fdcd910ae --- /dev/null +++ b/lms/templates/grade_summary.html @@ -0,0 +1,16 @@ +<%inherit file="main.html" /> +<%! from django.core.urlresolvers import reverse %> +<%namespace name='static' file='static_content.html'/> + +<%include file="course_navigation.html" args="active_page=''" /> + +
+
+
+

Grade summary

+ +

Not implemented yet

+ +
+
+
diff --git a/lms/templates/instructor_dashboard.html b/lms/templates/instructor_dashboard.html new file mode 100644 index 0000000000..67f45ee64e --- /dev/null +++ b/lms/templates/instructor_dashboard.html @@ -0,0 +1,24 @@ +<%inherit file="main.html" /> +<%! from django.core.urlresolvers import reverse %> +<%namespace name='static' file='static_content.html'/> + +<%block name="headextra"> + <%static:css group='course'/> + + +<%include file="course_navigation.html" args="active_page=''" /> + +
+
+
+

Instructor Dashboard

+ +

+ Gradebook + +

+ Grade summary + +

+
+
diff --git a/lms/urls.py b/lms/urls.py index 8f4e251ade..4f1895e2c4 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -140,8 +140,13 @@ if settings.COURSEWARE_ENABLED: 'courseware.views.profile', name="student_profile"), # For the instructor + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/instructor$', + 'courseware.views.instructor_dashboard', name="instructor_dashboard"), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/gradebook$', - 'courseware.views.gradebook'), + 'courseware.views.gradebook', name='gradebook'), + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/grade_summary$', + 'courseware.views.grade_summary', name='grade_summary'), + ) # Multicourse wiki