From 30c8e1571e7ce8482d8dc09d2b32b7c63621f3ee Mon Sep 17 00:00:00 2001
From: asadazam93 <asadazam93@gmail.com>
Date: Wed, 6 Nov 2019 18:00:13 +0500
Subject: [PATCH] Export staff users csv

---
 .../management/commands/export_staff_users.py | 134 ++++++++++++++++++
 .../tests/test_export_staff_users.py          |  35 +++++
 .../student/templates/email/email_base.html   |  21 +++
 .../templates/email/export_staff_users.html   |  15 ++
 .../templates/email/export_staff_users.txt    |   7 +
 5 files changed, 212 insertions(+)
 create mode 100644 common/djangoapps/student/management/commands/export_staff_users.py
 create mode 100644 common/djangoapps/student/management/tests/test_export_staff_users.py
 create mode 100644 common/djangoapps/student/templates/email/email_base.html
 create mode 100644 common/djangoapps/student/templates/email/export_staff_users.html
 create mode 100644 common/djangoapps/student/templates/email/export_staff_users.txt

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 00000000000..964f1dc7122
--- /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 00000000000..9afc3f667c7
--- /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 00000000000..0cf1739f84c
--- /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 00000000000..c54f5a52b7f
--- /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 00000000000..cda5918cebf
--- /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
-- 
GitLab