fix: support files with a unicode signature in the Instructor Dashboard API

Without this, files with BOM (byte order mark; generated e.g., by Microsoft
Excel) cannot be read properly.
This commit is contained in:
Agrendalath
2023-04-21 16:26:40 +02:00
committed by Piotr Surowiec
parent b9be2b1e56
commit 2af5ddbf8a
2 changed files with 11 additions and 27 deletions

View File

@@ -590,6 +590,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
@patch.dict(settings.FEATURES, {'ALLOW_AUTOMATED_SIGNUPS': True}) @patch.dict(settings.FEATURES, {'ALLOW_AUTOMATED_SIGNUPS': True})
@ddt.ddt
class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTestCase): class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
Test Bulk account creation and enrollment from csv file Test Bulk account creation and enrollment from csv file
@@ -641,32 +642,15 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas
) )
@patch('lms.djangoapps.instructor.views.api.log.info') @patch('lms.djangoapps.instructor.views.api.log.info')
def test_account_creation_and_enrollment_with_csv(self, info_log): @ddt.data(
b"test_student@example.com,test_student_1,tester1,USA", # Typical use case.
b"\ntest_student@example.com,test_student_1,tester1,USA\n\n", # Blank lines.
b"\xef\xbb\xbftest_student@example.com,test_student_1,tester1,USA", # Unicode signature (BOM).
)
def test_account_creation_and_enrollment_with_csv(self, csv_content, info_log):
""" """
Happy path test to create a single new user Happy path test to create a single new user
""" """
csv_content = b"test_student@example.com,test_student_1,tester1,USA"
uploaded_file = SimpleUploadedFile("temp.csv", csv_content)
response = self.client.post(self.url, {'students_list': uploaded_file, 'email-students': True})
assert response.status_code == 200
data = json.loads(response.content.decode('utf-8'))
assert len(data['row_errors']) == 0
assert len(data['warnings']) == 0
assert len(data['general_errors']) == 0
manual_enrollments = ManualEnrollmentAudit.objects.all()
assert manual_enrollments.count() == 1
assert manual_enrollments[0].state_transition == UNENROLLED_TO_ENROLLED
# test the log for email that's send to new created user.
info_log.assert_called_with('email sent to new created user at %s', 'test_student@example.com')
@patch('lms.djangoapps.instructor.views.api.log.info')
def test_account_creation_and_enrollment_with_csv_with_blank_lines(self, info_log):
"""
Happy path test to create a single new user
"""
csv_content = b"\ntest_student@example.com,test_student_1,tester1,USA\n\n"
uploaded_file = SimpleUploadedFile("temp.csv", csv_content) uploaded_file = SimpleUploadedFile("temp.csv", csv_content)
response = self.client.post(self.url, {'students_list': uploaded_file, 'email-students': True}) response = self.client.post(self.url, {'students_list': uploaded_file, 'email-students': True})
assert response.status_code == 200 assert response.status_code == 200
@@ -4385,7 +4369,7 @@ class TestBulkCohorting(SharedModuleStoreTestCase):
""" """
# this temporary file will be removed in `self.tearDown()` # this temporary file will be removed in `self.tearDown()`
__, file_name = tempfile.mkstemp(suffix=suffix, dir=self.tempdir) __, file_name = tempfile.mkstemp(suffix=suffix, dir=self.tempdir)
with open(file_name, 'w') as file_pointer: with open(file_name, 'w', encoding='utf-8-sig') as file_pointer:
file_pointer.write(csv_data) file_pointer.write(csv_data)
with open(file_name) as file_pointer: with open(file_name) as file_pointer:
url = reverse('add_users_to_cohorts', kwargs={'course_id': str(self.course.id)}) url = reverse('add_users_to_cohorts', kwargs={'course_id': str(self.course.id)})

View File

@@ -339,7 +339,7 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man
try: try:
upload_file = request.FILES.get('students_list') upload_file = request.FILES.get('students_list')
if upload_file.name.endswith('.csv'): if upload_file.name.endswith('.csv'):
students = list(csv.reader(upload_file.read().decode('utf-8').splitlines())) students = list(csv.reader(upload_file.read().decode('utf-8-sig').splitlines()))
course = get_course_by_id(course_id) course = get_course_by_id(course_id)
else: else:
general_errors.append({ general_errors.append({
@@ -1519,7 +1519,7 @@ def _cohorts_csv_validator(file_storage, file_to_validate):
Verifies that the expected columns are present in the CSV used to add users to cohorts. Verifies that the expected columns are present in the CSV used to add users to cohorts.
""" """
with file_storage.open(file_to_validate) as f: with file_storage.open(file_to_validate) as f:
reader = csv.reader(f.read().decode('utf-8').splitlines()) reader = csv.reader(f.read().decode('utf-8-sig').splitlines())
try: try:
fieldnames = next(reader) fieldnames = next(reader)
@@ -3332,7 +3332,7 @@ def generate_bulk_certificate_exceptions(request, course_id):
try: try:
upload_file = request.FILES.get('students_list') upload_file = request.FILES.get('students_list')
if upload_file.name.endswith('.csv'): if upload_file.name.endswith('.csv'):
students = list(csv.reader(upload_file.read().decode('utf-8').splitlines())) students = list(csv.reader(upload_file.read().decode('utf-8-sig').splitlines()))
else: else:
general_errors.append(_('Make sure that the file you upload is in CSV format with no ' general_errors.append(_('Make sure that the file you upload is in CSV format with no '
'extraneous characters or rows.')) 'extraneous characters or rows.'))