Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: course staff mgmt command #310

Merged
merged 1 commit into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 25 additions & 28 deletions edx_exams/apps/core/management/commands/bulk_add_course_staff.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import time

from django.core.management.base import BaseCommand
from django.db import transaction
from django.db import IntegrityError, transaction

from edx_exams.apps.core.models import CourseStaffRole, User

Expand Down Expand Up @@ -63,34 +63,31 @@
Add the given set of course staff provided in csv
"""
reader = list(csv.DictReader(csv_file))
users = {}

# bulk create users
for i in range(0, len(reader), batch_size):
User.objects.bulk_create(
(User(
username=row.get('username'),
email=row.get('email'),
) for row in reader[i:i + batch_size]),
ignore_conflicts=True,
)
CourseStaffRole.objects.bulk_create(
(CourseStaffRole(
user=User.objects.get(username=row.get('username')),
course_id=row.get('course_id'),
role=row.get('role'),
) for row in reader[i:i + batch_size]),
ignore_conflicts=True,
)
users_list = []
for row in reader[i:i + batch_size]:
username = row.get('username')
email = row.get('email')
try:
users_list.append(User.objects.get_or_create(username=row.get('username'), email=row.get('email')))
except IntegrityError:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a pass at handling IntegrityError here. This handles the case where there's a duplicate username with a different email. This does not handle when there is a new username with a duplicate email address. I don't know if this is an issue, but this is also something that may need to be handled in the Model/db itself so might be more cumbersome than needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a case we're expecting can we log something or output information about the rows that failed to create when this is run so the failure isn't silent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I can do that.

logger.warning(
f'User with username={username} and email={email} was not created due to an existing duplicate '
f'user with username.'
)
continue

Check failure on line 80 in edx_exams/apps/core/management/commands/bulk_add_course_staff.py

View workflow job for this annotation

GitHub Actions / tests (ubuntu-20.04, 3.8, django42)

Missing coverage

Missing coverage on lines 75-80
users_dict = {(u.username, u) for (u, c) in users_list}
users.update(users_dict)
time.sleep(batch_delay)

# bulk create course staff
# for i in range(0, len(reader), batch_size):
# CourseStaffRole.objects.bulk_create(
# (CourseStaffRole(
# user=User.objects.get(username=row.get('username')),
# course_id=row.get('course_id'),
# role=row.get('role'),
# ) for row in reader[i:i + batch_size]),
# ignore_conflicts=True,
# )
# time.sleep(batch_delay)
CourseStaffRole.objects.bulk_create(
(CourseStaffRole(
user=users.get(row.get('username')),
course_id=row.get('course_id'),
role=row.get('role'),
) for row in reader),
ignore_conflicts=True,
batch_size=batch_size,
)
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_add_course_staff_with_not_default_batch_size(self):
'sam,sam@pond.com,staff,course-v1:edx+test+f20\n']
with NamedTemporaryFile() as csv:
csv = self._write_test_csv(csv, lines)
with self.assertNumQueries(8):
with self.assertNumQueries(12):
call_command(self.command, f'--csv_path={csv.name}', '--batch_size=1')

def test_add_course_staff_with_batch_size_larger_than_list(self):
Expand All @@ -99,7 +99,7 @@ def test_add_course_staff_with_batch_size_larger_than_list(self):
'sam,sam@pond.com,staff,course-v1:edx+test+f20\n']
with NamedTemporaryFile() as csv:
csv = self._write_test_csv(csv, lines)
with self.assertNumQueries(6):
with self.assertNumQueries(11):
call_command(self.command, f'--csv_path={csv.name}', '--batch_size=3')

def test_add_course_staff_with_batch_size_smaller_than_list(self):
Expand All @@ -109,7 +109,7 @@ def test_add_course_staff_with_batch_size_smaller_than_list(self):
'tam,tam@pond.com,staff,course-v1:edx+test+f20\n']
with NamedTemporaryFile() as csv:
csv = self._write_test_csv(csv, lines)
with self.assertNumQueries(9):
with self.assertNumQueries(16):
call_command(self.command, f'--csv_path={csv.name}', '--batch_size=2')

def test_add_course_staff_with_not_default_batch_delay(self):
Expand All @@ -125,16 +125,16 @@ def test_add_course_staff_with_not_default_batch_delay(self):

def test_num_queries_correct(self):
"""
Assert the number of queries to be 4 + 1 * number of lines:
Assert the number of queries to be 2 + 1 * number of lines:
2 for savepoint/release savepoint
1 to bulk create users, 1 to bulk create course role
1 for each user (to get user)
1 to bulk create course role
4 for each user (to get user, and savepoints)
"""
num_lines = 20
lines = [f'pam{i},pam{i}@pond.com,staff,course-v1:edx+test+f20\n' for i in range(num_lines)]
with NamedTemporaryFile() as csv:
csv = self._write_test_csv(csv, lines)
with self.assertNumQueries(4 + num_lines):
with self.assertNumQueries(3 + 4 * num_lines):
call_command(self.command, f'--csv_path={csv.name}')

def test_dupe_user_csv(self):
Expand Down
Loading