views.py 10.6 KB
Newer Older
1
import json
David Ormsbee committed
2
import logging
Piotr Mitros committed
3
import urllib
4
import itertools
Piotr Mitros committed
5 6

from django.conf import settings
David Ormsbee committed
7
from django.core.context_processors import csrf
8
from django.core.urlresolvers import reverse
David Ormsbee committed
9
from django.contrib.auth.models import User
10
from django.contrib.auth.decorators import login_required
11
from django.http import Http404, HttpResponse
David Ormsbee committed
12
from django.shortcuts import redirect
13
from mitxmako.shortcuts import render_to_response, render_to_string
14
#from django.views.decorators.csrf import ensure_csrf_cookie
15
from django_future.csrf import ensure_csrf_cookie
16
from django.views.decorators.cache import cache_control
17

Calen Pennington committed
18
from module_render import toc_for_course, get_module, get_section
19
from models import StudentModuleCache
20
from student.models import UserProfile
21
from xmodule.modulestore import Location
22
from xmodule.modulestore.search import path_to_location
23
from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError, NoPathToItem
24 25
from xmodule.modulestore.django import modulestore
from xmodule.course_module import CourseDescriptor
Piotr Mitros committed
26

27
from util.cache import cache, cache_if_anonymous
28
from student.models import UserTestGroup, CourseEnrollment
29
from courseware import grades
30
from courseware.courses import check_course, get_courses_by_university
31

32

33 34
log = logging.getLogger("mitx.courseware")

35
template_imports = {'urllib': urllib}
Piotr Mitros committed
36

37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
def user_groups(user):
    if not user.is_authenticated():
        return []

    # TODO: Rewrite in Django
    key = 'user_group_names_{user.id}'.format(user=user)
    cache_expiration = 60 * 60  # one hour

    # Kill caching on dev machines -- we switch groups a lot
    group_names = cache.get(key)

    if group_names is None:
        group_names = [u.name for u in UserTestGroup.objects.filter(users=user)]
        cache.set(key, group_names, cache_expiration)

    return group_names


Piotr Mitros committed
56

57
@ensure_csrf_cookie
58
@cache_if_anonymous
59
def courses(request):
60 61 62 63
    '''
    Render "find courses" page.  The course selection work is done in courseware.courses.
    '''
    universities = get_courses_by_university(request.user)
64 65
    return render_to_response("courses.html", {'universities': universities})

66
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
67
def gradebook(request, course_id):
68
    if 'course_admin' not in user_groups(request.user):
Piotr Mitros committed
69
        raise Http404
70
    course = check_course(course_id)
71

Piotr Mitros committed
72
    student_objects = User.objects.all()[:100]
73
    student_info = []
74

75
    for student in student_objects:
76 77
        student_module_cache = StudentModuleCache(student, course)
        course, _, _, _ = get_module(request.user, request, course.location, student_module_cache)
78 79 80 81 82
        student_info.append({
            'username': student.username,
            'id': student.id,
            'email': student.email,
            'grade_info': grades.grade_sheet(student, course, student_module_cache),
83
            'realname': UserProfile.objects.get(user=student).name
84
        })
Piotr Mitros committed
85

Matthew Mongeau committed
86
    return render_to_response('gradebook.html', {'students': student_info, 'course': course})
Piotr Mitros committed
87

88

89
@login_required
Piotr Mitros committed
90
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
91
def profile(request, course_id, student_id=None):
Piotr Mitros committed
92 93
    ''' User profile. Show username, location, etc, as well as grades .
        We need to allow the user to change some of these settings .'''
94
    course = check_course(course_id)
95

96
    if student_id is None:
97
        student = request.user
98
    else:
99
        if 'course_admin' not in user_groups(request.user):
100
            raise Http404
101
        student = User.objects.get(id=int(student_id))
102

103
    user_info = UserProfile.objects.get(user=student)
Piotr Mitros committed
104

105
    student_module_cache = StudentModuleCache(request.user, course)
106
    course_module, _, _, _ = get_module(request.user, request, course.location, student_module_cache)
Victor Shnayder committed
107

108 109 110 111 112
    context = {'name': user_info.name,
               'username': student.username,
               'location': user_info.location,
               'language': user_info.language,
               'email': student.email,
Matthew Mongeau committed
113
               'course': course,
114 115
               'csrf': csrf(request)['csrf_token']
               }
116
    context.update(grades.grade_sheet(student, course_module, course.grader, student_module_cache))
117

Piotr Mitros committed
118 119
    return render_to_response('profile.html', context)

120 121

def render_accordion(request, course, chapter, section):
Piotr Mitros committed
122
    ''' Draws navigation bar. Takes current position in accordion as
123 124 125 126
        parameter.

        If chapter and section are '' or None, renders a default accordion.

127
        Returns the html string'''
128

129
    # grab the table of contents
130
    toc = toc_for_course(request.user, request, course, chapter, section)
131 132

    active_chapter = 1
Piotr Mitros committed
133 134
    for i in range(len(toc)):
        if toc[i]['active']:
135 136
            active_chapter = i

137 138
    context = dict([('active_chapter', active_chapter),
                    ('toc', toc),
139 140
                    ('course_name', course.title),
                    ('course_id', course.id),
141
                    ('csrf', csrf(request)['csrf_token'])] + template_imports.items())
142 143
    return render_to_string('accordion.html', context)

Piotr Mitros committed
144

145
@login_required
146 147
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
148
def index(request, course_id, chapter=None, section=None,
149
          position=None):
150 151 152 153 154 155 156 157
    ''' Displays courseware accordion, and any associated content.
    If course, chapter, and section aren't all specified, just returns
    the accordion.  If they are specified, returns an error if they don't
    point to a valid module.

    Arguments:

     - request    : HTTP request
158 159 160
     - course_id  : course id (str: ORG/course/URL_NAME)
     - chapter    : chapter url_name (str)
     - section    : section url_name (str)
161 162 163 164 165 166
     - position   : position in module, eg of <sequential> module (str)

    Returns:

     - HTTPresponse
    '''
167
    course = check_course(course_id)
168 169 170
    registered = registered_for_course(course, request.user)
    if not registered:
        log.debug('User %s tried to view course %s but is not enrolled' % (request.user,course.location.url()))
171
        return redirect(reverse('about_course', args=[course.id]))
172

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    try:
        context = {
            'csrf': csrf(request)['csrf_token'],
            'accordion': render_accordion(request, course, chapter, section),
            'COURSE_TITLE': course.title,
            'course': course,
            'init': '',
            'content': ''
            }

        look_for_module = chapter is not None and section is not None
        if look_for_module:
            section_descriptor = get_section(course, chapter, section)
            if section_descriptor is not None:
                student_module_cache = StudentModuleCache(request.user,
                                                          section_descriptor)
                module, _, _, _ = get_module(request.user, request,
                                             section_descriptor.location,
                                             student_module_cache)
                context['content'] = module.get_html()
            else:
                log.warning("Couldn't find a section descriptor for course_id '{0}',"
                            "chapter '{1}', section '{2}'".format(
                                course_id, chapter, section))
Victor Shnayder committed
197 198 199 200
        else:
            if request.user.is_staff:
                # Add a list of all the errors...
                context['course_errors'] = modulestore().get_item_errors(course.location)
201 202 203 204 205 206

        result = render_to_response('courseware.html', context)
    except:
        # In production, don't want to let a 500 out for any reason
        if settings.DEBUG:
            raise
207
        else:
208 209 210 211 212 213 214 215 216 217 218 219 220
            log.exception("Error in index view: user={user}, course={course},"
                          " chapter={chapter} section={section}"
                          "position={position}".format(
                              user=request.user,
                              course=course,
                              chapter=chapter,
                              section=section,
                              position=position
                              ))
            try:
                result = render_to_response('courseware-error.html', {})
            except:
                result = HttpResponse("There was an unrecoverable error")
221

222
    return result
223

Victor Shnayder committed
224

225
@ensure_csrf_cookie
226
def jump_to(request, location):
227
    '''
228
    Show the page that contains a specific location.
229

230
    If the location is invalid, return a 404.
231

232
    If the location is valid, but not present in a course, ?
233

234 235 236 237 238 239 240 241 242 243 244
    If the location is valid, but in a course the current user isn't registered for, ?
        TODO -- let the index view deal with it?
    '''
    # Complain if the location isn't valid
    try:
        location = Location(location)
    except InvalidLocationError:
        raise Http404("Invalid location")

    # Complain if there's not data for this location
    try:
245
        (course_id, chapter, section, position) = path_to_location(modulestore(), location)
246 247
    except ItemNotFoundError:
        raise Http404("No data at this location: {0}".format(location))
248 249 250
    except NoPathToItem:
        raise Http404("This location is not in any class: {0}".format(location))

Victor Shnayder committed
251
    # Rely on index to do all error handling
252
    return index(request, course_id, chapter, section, position)
253 254

@ensure_csrf_cookie
255
def course_info(request, course_id):
256 257 258 259 260
    '''
    Display the course's info.html, or 404 if there is no such course.

    Assumes the course_id is in a valid format.
    '''
261
    course = check_course(course_id)
262

263
    return render_to_response('info.html', {'course': course})
264

265

266 267 268 269 270 271 272 273 274
def registered_for_course(course, user):
    '''Return CourseEnrollment if user is registered for course, else False'''
    if user is None:
        return False
    if user.is_authenticated():
        return CourseEnrollment.objects.filter(user=user, course_id=course.id).exists()
    else:
        return False

275
@ensure_csrf_cookie
276
@cache_if_anonymous
277 278
def course_about(request, course_id):
    course = check_course(course_id, course_must_be_open=False)
279 280
    registered = registered_for_course(course, request.user)
    return render_to_response('portal/course_about.html', {'course': course, 'registered': registered})
281 282


283 284
@ensure_csrf_cookie
@cache_if_anonymous
285
def university_profile(request, org_id):
286
    all_courses = sorted(modulestore().get_courses(), key=lambda course: course.number)
287 288 289 290 291
    valid_org_ids = set(c.org for c in all_courses)
    if org_id not in valid_org_ids:
        raise Http404("University Profile not found for {0}".format(org_id))

    # Only grab courses for this org...
292
    courses = get_courses_by_university(request.user)[org_id]
293 294 295 296
    context = dict(courses=courses, org_id=org_id)
    template_file = "university_profile/{0}.html".format(org_id).lower()

    return render_to_response(template_file, context)