diff --git a/common/djangoapps/student/management/commands/export_staff_users.py b/common/djangoapps/student/management/commands/export_staff_users.py new file mode 100644 index 0000000000000000000000000000000000000000..964f1dc7122f4faa8af6a36f8cbeec9f2390d457 --- /dev/null +++ b/common/djangoapps/student/management/commands/export_staff_users.py @@ -0,0 +1,134 @@ +from __future__ import absolute_import, print_function + +import csv +import logging +from datetime import datetime, timedelta +from django.core.management.base import BaseCommand +from django.conf import settings +from django.core.mail.message import EmailMultiAlternatives +from django.template.loader import get_template +from pytz import utc +from os import remove + +from openedx.core.djangoapps.content.course_overviews.models import CourseOverview +from student.models import CourseAccessRole + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + Example usage: + $ ./manage.py lms export_staff_users -d 7 --settings=devstack_docker + $ ./manage.py lms export_staff_users --days 7 --settings=devstack_docker + $ ./manage.py lms export_staff_users --days 7 --dry true --settings=devstack_docker + """ + + help = """ + This command will export a csv of all users who have logged in within the given days and + have staff access role in active courses (Courses with end date in the future). + """ + + def add_arguments(self, parser): + parser.add_argument( + '-d', + '--days', + type=int, + default=7, + help='Indicate the login time period in days starting from today' + ) + parser.add_argument( + '-r', + '--dry', + type=str, + help='Indicate that the email should not be sent to author-support' + ) + + subject = 'Staff users CSV' + to_addresses = ['author-support@edx.org'] + from_address = settings.DEFAULT_FROM_EMAIL + txt_template_path = 'email/export_staff_users.txt' + html_template_path = 'email/export_staff_users.html' + csv_filename = 'staff_users.csv' + + def write_csv(self, query_set, filename): + """ + Writes the queryset into a csv file with the given filename + + Arguments: + query_set: query_set to be converted + filename: filename for the csv + """ + writer = csv.DictWriter( + filename, + fieldnames=['id', 'user__username', 'user__email', 'role'] + ) + writer.writeheader() + for data_item in query_set: + writer.writerow(data_item) + + def handle(self, *args, **kwargs): + days = kwargs['days'] + dry = kwargs.get('dry') + if dry: + self.to_addresses = ['sustaining-mavericks@edx.org'] + current_date = datetime.now(tz=utc) + starting_date = current_date - timedelta(days=days) + active_courses = CourseOverview.objects.filter(end__gte=current_date).values_list('id', flat=True) + course_access_roles = CourseAccessRole.objects.filter( + role__in=['staff', 'instructor'], + user__last_login__range=(starting_date, current_date), + course_id__in=active_courses, + user__is_staff=False + ).values('id', 'user__username', 'user__email', 'role') + if not course_access_roles: + return + with open(self.csv_filename, 'a+') as csv_file: + self.write_csv( + query_set=course_access_roles, + filename=csv_file + ) + context = {'time_period': days} + try: + self.send_email(context) + logger.info( + 'Sent staff users email for the period {} to {}. Staff users count:{}'.format( + starting_date, + current_date, + course_access_roles.count() + ) + ) + except Exception: + logger.exception( + 'Failed to send staff users email for the period {}-{}'.format(starting_date, current_date) + ) + + def send_email(self, context): + """ + Sends an email to admin containing a csv of all users who have logged in within the given days and + have staff access role in active courses (Courses with end date in the future). + + Arguments: + context: context for the email template + """ + plain_content = self.render_template(self.txt_template_path, context) + html_content = self.render_template(self.html_template_path, context) + + with open(self.csv_filename, 'r') as csv_file: + email_message = EmailMultiAlternatives(self.subject, plain_content, self.from_address, to=self.to_addresses) + email_message.attach_alternative(html_content, 'text/html') + email_message.attach(self.csv_filename, csv_file.read(), 'text/csv') + email_message.send() + + remove(self.csv_filename) + + def render_template(self, path, context): + """ + Takes a template path and context and returns a rendered template + + Arguments: + path: path of the file + context: context for the template + """ + txt_template = get_template(path) + return txt_template.render(context) diff --git a/common/djangoapps/student/management/tests/test_export_staff_users.py b/common/djangoapps/student/management/tests/test_export_staff_users.py new file mode 100644 index 0000000000000000000000000000000000000000..9afc3f667c79c36251be769c8c855b2b39f90356 --- /dev/null +++ b/common/djangoapps/student/management/tests/test_export_staff_users.py @@ -0,0 +1,35 @@ +""" +Unit tests for export_staff_users management command. +""" +from datetime import timedelta + +from django.core import mail +from django.core.management import call_command +from django.test import TestCase +from django.utils.timezone import now + +from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory +from student.tests.factories import CourseAccessRoleFactory, UserFactory + + +class TestExportStaffUsers(TestCase): + """ + Tests the `export_staff_users` command. + """ + + @staticmethod + def create_users_data(): + staff_user = UserFactory(last_login=now() - timedelta(days=5)) + instructor_user = UserFactory(last_login=now() - timedelta(days=5)) + course = CourseOverviewFactory(end=now() + timedelta(days=30)) + archived_course = CourseOverviewFactory(end=now() - timedelta(days=30)) + course_ids = [course.id, archived_course.id] + for course_id in course_ids: + CourseAccessRoleFactory.create(course_id=course_id, user=staff_user, role="staff") + CourseAccessRoleFactory.create(course_id=course_id, user=instructor_user, role="instructor") + + def test_export_staff_users(self): + self.create_users_data() + self.assertEqual(len(mail.outbox), 0) + call_command('export_staff_users', days=7) + self.assertEqual(len(mail.outbox), 1) diff --git a/common/djangoapps/student/templates/email/email_base.html b/common/djangoapps/student/templates/email/email_base.html new file mode 100644 index 0000000000000000000000000000000000000000..0cf1739f84c0d304e0785cca4bf954f2ce2cb27c --- /dev/null +++ b/common/djangoapps/student/templates/email/email_base.html @@ -0,0 +1,21 @@ +{% load i18n %} +{% get_current_language as LANGUAGE_CODE %} +<!DOCTYPE html> +<html lang="{{ LANGUAGE_CODE }}"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="initial-scale=1.0"> <!-- So that mobile webkit will display zoomed in --> + <meta name="format-detection" content="telephone=no"> <!-- disable auto telephone linking in iOS --> +</head> +<body style="font-family:Arial,'Helvetica Neue',Helvetica,sans-serif;font-size:14px;line-height:150%;margin:auto"> + +<table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" style="padding: 5px;"> + <tr> + <td align="" valign="top"> + {% block body %} + {% endblock body %} + </td> + </tr> +</table> +</body> +</html> diff --git a/common/djangoapps/student/templates/email/export_staff_users.html b/common/djangoapps/student/templates/email/export_staff_users.html new file mode 100644 index 0000000000000000000000000000000000000000..c54f5a52b7f34a8d2ec3e6c56e29f21b1a6a8392 --- /dev/null +++ b/common/djangoapps/student/templates/email/export_staff_users.html @@ -0,0 +1,15 @@ +{% extends "email/email_base.html" %} +{% block body %} +<!-- Message Body --> + <p> + Dear Admin, + <p> + <p> + Please find the attached CSV containing a list of all staff users + who have logged in within the last {{ time_period }} days + </p> + + <p>Thanks,</p> + <p>The edX Team</p> +<!-- End Message Body --> +{% endblock body %} diff --git a/common/djangoapps/student/templates/email/export_staff_users.txt b/common/djangoapps/student/templates/email/export_staff_users.txt new file mode 100644 index 0000000000000000000000000000000000000000..cda5918cebfa3f7e6bf8ad115442c83fe3d80140 --- /dev/null +++ b/common/djangoapps/student/templates/email/export_staff_users.txt @@ -0,0 +1,7 @@ +Dear Admin, + + Please find the attached CSV containing a list of all staff users who have logged in within the last {{ time_period }} days + + +Thanks, +The edX Team