feat: Add management command to unsubscribe user email (#31705)

This commit is contained in:
Shahbaz Shabbir
2023-03-01 18:23:56 +05:00
committed by GitHub
parent b3b3176d03
commit ff0805a189
2 changed files with 138 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
"""Management command to unsubscribe user's email in bulk on Braze."""
import csv
import logging
from django.core.management.base import BaseCommand, CommandError
from lms.djangoapps.utils import get_braze_client
logger = logging.getLogger(__name__)
CHUNK_SIZE = 50
class Command(BaseCommand):
"""
Management command to unsubscribe user's email in bulk on Braze.
"""
help = """
Unsubscribe for all given user's email on braze.
Example:
Unsubscribe user's email for multiple users on braze.
$ ... unsubscribe_user_email -p <csv_file_path>
"""
def add_arguments(self, parser):
parser.add_argument(
'-p', '--csv_path',
metavar='csv_path',
dest='csv_path',
required=False,
help='Path to CSV file.')
def _chunked_iterable(self, iterable):
"""
Yield successive CHUNK_SIZE sized chunks from iterable.
"""
for i in range(0, len(iterable), CHUNK_SIZE):
yield iterable[i:i + CHUNK_SIZE]
def _chunk_list(self, emails_list):
"""
Chunk a list into sub-lists of length CHUNK_SIZE.
"""
return list(self._chunked_iterable(emails_list))
def handle(self, *args, **options):
emails = []
csv_file_path = options['csv_path']
try:
with open(csv_file_path, 'r') as csv_file:
reader = list(csv.DictReader(csv_file))
emails = [row.get('email') for row in reader]
except FileNotFoundError as exc:
raise CommandError(f"Error: File not found due to exception - {exc}") # lint-amnesty, pylint: disable=raise-missing-from
except csv.Error as exc:
logger.exception(f"CSV error: {exc}")
else:
logger.info("CSV file read successfully.")
chunks = self._chunk_list(emails)
try:
braze_client = get_braze_client()
if braze_client:
for i, chunk in enumerate(chunks):
braze_client.unsubscribe_user_email(
email=chunk,
)
logger.info(f"Successfully unsubscribed for chunk-{i + 1} consist of {len(chunk)} emails")
except Exception as exc: # pylint: disable=broad-except
logger.exception(f"Unable to update email status on Braze due to : {exc}")

View File

@@ -0,0 +1,64 @@
"""Tests for unsubscribe user's email management command"""
from tempfile import NamedTemporaryFile
from unittest.mock import patch
import pytest
import six
from django.core.management import call_command
from django.core.management.base import CommandError
from django.test import TestCase
COMMAND = 'unsubscribe_user_email'
class UnsubscribeUserEmailTests(TestCase):
"""
Tests unsubscribe_user_email command
"""
def setUp(self):
"""
Set up tests
"""
super().setUp()
self.lines = [
f"test_user{i}@test.com" for i in range(100)
]
self.invalid_csv_path = '/test/test.csv'
@staticmethod
def _write_test_csv(csv, lines):
"""Write a test csv file with the lines provided"""
csv.write(b"email\n")
for line in lines:
csv.write(six.b(line))
csv.seek(0)
return csv
@patch("common.djangoapps.student.management.commands.unsubscribe_user_email.get_braze_client")
def test_unsubscribe_user_email(self, mock_get_braze_client):
""" Test CSV file to unsubscribe user's email"""
with NamedTemporaryFile() as csv:
csv = self._write_test_csv(csv, self.lines)
call_command(
COMMAND,
'--csv_path',
csv.name
)
mock_get_braze_client.assert_called_once()
def test_command_error_for_csv_path(self):
""" Test command error raised if csv_path is not valid"""
with pytest.raises(CommandError):
call_command(
COMMAND,
'--csv_path',
self.invalid_csv_path
)