Merge pull request #22252 from edx/asad/prod-969-export-csv
Export staff users csv
This commit is contained in:
@@ -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)
|
||||
@@ -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)
|
||||
21
common/djangoapps/student/templates/email/email_base.html
Normal file
21
common/djangoapps/student/templates/email/email_base.html
Normal file
@@ -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>
|
||||
@@ -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 %}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user