Features coming down the pipe will want to be able to: * Refer to enrollments before they are actually activated (approval step). * See what courses a user used to be enrolled in for when they re-enroll in the same course, or a different run of that course. * Have different "modes" of enrolling in a course, representing things like honor certificate enrollment, auditing (no certs), etc. This change adds an is_active flag and mode (with default being "honor"). The commit is only as large as it is because many parts of the codebase were manipulating enrollments by adding and removing CourseEnrollment objects directly. It was necessary to create classmethods on CourseEnrollment to encapsulate this functionality and then port everything over to using them. The migration to add columns has been tested on a prod replica, and seems to be fine for running on a live system with single digit millions of rows of enrollments.
84 lines
3.1 KiB
Python
84 lines
3.1 KiB
Python
from django.http import Http404
|
|
from mitxmako.shortcuts import render_to_response
|
|
from django.db import connection
|
|
|
|
from student.models import CourseEnrollment
|
|
from django.contrib.auth.models import User
|
|
|
|
|
|
def dictfetchall(cursor):
|
|
'''Returns a list of all rows from a cursor as a column: result dict.
|
|
Borrowed from Django documentation'''
|
|
desc = cursor.description
|
|
table = []
|
|
table.append([col[0] for col in desc])
|
|
|
|
# ensure response from db is a list, not a tuple (which is returned
|
|
# by MySQL backed django instances)
|
|
rows_from_cursor=cursor.fetchall()
|
|
table = table + [list(row) for row in rows_from_cursor]
|
|
return table
|
|
|
|
def SQL_query_to_list(cursor, query_string):
|
|
cursor.execute(query_string)
|
|
raw_result=dictfetchall(cursor)
|
|
return raw_result
|
|
|
|
def dashboard(request):
|
|
"""
|
|
Slightly less hackish hack to show staff enrollment numbers and other
|
|
simple queries.
|
|
|
|
All queries here should be indexed and simple. Mostly, this means don't
|
|
touch courseware_studentmodule, as tempting as it may be.
|
|
|
|
"""
|
|
if not request.user.is_staff:
|
|
raise Http404
|
|
|
|
# results are passed to the template. The template knows how to render
|
|
# two types of results: scalars and tables. Scalars should be represented
|
|
# as "Visible Title": Value and tables should be lists of lists where each
|
|
# inner list represents a single row of the table
|
|
results = {"scalars":{},"tables":{}}
|
|
|
|
# count how many users we have
|
|
results["scalars"]["Unique Usernames"]=User.objects.filter().count()
|
|
results["scalars"]["Activated Usernames"]=User.objects.filter(is_active=1).count()
|
|
|
|
# count how many enrollments we have
|
|
results["scalars"]["Total Enrollments Across All Courses"] = CourseEnrollment.objects.filter(is_active=1).count()
|
|
|
|
# establish a direct connection to the database (for executing raw SQL)
|
|
cursor = connection.cursor()
|
|
|
|
# define the queries that will generate our user-facing tables
|
|
# table queries need not take the form of raw SQL, but do in this case since
|
|
# the MySQL backend for django isn't very friendly with group by or distinct
|
|
table_queries = {}
|
|
table_queries["course enrollments"] = """
|
|
select
|
|
course_id as Course,
|
|
count(user_id) as Students
|
|
from student_courseenrollment
|
|
where is_active=1
|
|
group by course_id
|
|
order by students desc;"""
|
|
table_queries["number of students in each number of classes"] = """
|
|
select registrations as 'Registered for __ Classes' ,
|
|
count(registrations) as Users
|
|
from (select count(user_id) as registrations
|
|
from student_courseenrollment
|
|
where is_active=1
|
|
group by user_id) as registrations_per_user
|
|
group by registrations;"""
|
|
|
|
# add the result for each of the table_queries to the results object
|
|
for query in table_queries.keys():
|
|
cursor.execute(table_queries[query])
|
|
results["tables"][query] = SQL_query_to_list(cursor, table_queries[query])
|
|
|
|
context={"results":results}
|
|
|
|
return render_to_response("admin_dashboard.html",context)
|