Skip to content

Commit

Permalink
Merge branch 'main' into varshamenon4/build-add-local-csrf-trusted
Browse files Browse the repository at this point in the history
  • Loading branch information
varshamenon4 authored Sep 3, 2024
2 parents 948b358 + 908be84 commit 4c69471
Show file tree
Hide file tree
Showing 17 changed files with 314 additions and 408 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-20.04]
python-version: ['3.8']
python-version: ['3.11']
toxenv: [django42, quality, pii_check]

steps:
Expand All @@ -38,7 +38,7 @@ jobs:
run: tox

- name: Run coverage
if: matrix.python-version == '3.8' && matrix.toxenv == 'django42'
if: matrix.python-version == '3.11' && matrix.toxenv == 'django42'
uses: py-cov-action/python-coverage-comment-action@v3
with:
GITHUB_TOKEN: ${{ github.token }}
Expand Down
7 changes: 5 additions & 2 deletions edx_exams/apps/core/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ def update_attempt_status(attempt_id, to_status):
if not allowed_to_start:
raise ExamIllegalStatusTransition(error_msg)

attempt_obj.start_time = datetime.now(pytz.UTC)
attempt_obj.allowed_time_limit_mins = _calculate_allowed_mins(attempt_obj.user, attempt_obj.exam)
# Once start time, and end time by extension, has been set further transitions to started
# must not update this value
if not attempt_obj.start_time:
attempt_obj.start_time = datetime.now(pytz.UTC)
attempt_obj.allowed_time_limit_mins = _calculate_allowed_mins(attempt_obj.user, attempt_obj.exam)

course_key = CourseKey.from_string(attempt_obj.exam.course_id)
usage_key = UsageKey.from_string(attempt_obj.exam.content_id)
Expand Down
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 @@ def add_course_staff_from_csv(self, csv_file, batch_size, batch_delay):
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:
logger.warning(
f'User with username={username} and email={email} was not created due to an existing duplicate '
f'user with username.'
)
continue
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,[email protected],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,[email protected],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,[email protected],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
15 changes: 15 additions & 0 deletions edx_exams/apps/core/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,21 @@ def test_cannot_start_if_other_attempts_active(self):
update_attempt_status(self.exam_attempt.id, ExamAttemptStatus.started)
self.assertIn('another exam attempt is currently active!', str(exc.exception))

def test_ready_to_submit_to_started(self):
"""
Test transition to started after initial start time has been set. This should not
alter the existing start time.
"""
with freeze_time(timezone.now()):
self.exam_attempt.status = ExamAttemptStatus.ready_to_submit
self.exam_attempt.start_time = timezone.now()-timedelta(minutes=10)
self.exam_attempt.allowed_time_limit_mins = 30
self.exam_attempt.save()

update_attempt_status(self.exam_attempt.id, ExamAttemptStatus.started)
self.exam_attempt.refresh_from_db()
self.assertEqual(self.exam_attempt.start_time, timezone.now()-timedelta(minutes=10))


class TestGetAttemptById(ExamsAPITestCase):
"""
Expand Down
50 changes: 23 additions & 27 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# This file is autogenerated by pip-compile with Python 3.8
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# make upgrade
Expand All @@ -12,26 +12,22 @@ asgiref==3.8.1
# django-cors-headers
async-timeout==4.0.3
# via redis
attrs==23.2.0
attrs==24.2.0
# via
# -r requirements/base.in
# lti-consumer-xblock
# openedx-events
backports-zoneinfo==0.2.1
# via
# django
# djangorestframework
bleach==6.1.0
# via lti-consumer-xblock
boto3==1.34.150
boto3==1.35.9
# via fs-s3fs
botocore==1.34.150
botocore==1.35.9
# via
# boto3
# s3transfer
certifi==2024.7.4
certifi==2024.8.30
# via requests
cffi==1.16.0
cffi==1.17.0
# via
# cryptography
# pynacl
Expand All @@ -57,7 +53,7 @@ defusedxml==0.8.0rc2
# via
# python3-openid
# social-auth-core
django==4.2.14
django==4.2.15
# via
# -c requirements/common_constraints.txt
# -r requirements/base.in
Expand Down Expand Up @@ -101,7 +97,7 @@ django-crum==0.7.9
# edx-toggles
django-extensions==3.2.3
# via -r requirements/base.in
django-filter==24.2
django-filter==24.3
# via lti-consumer-xblock
django-model-utils==4.5.1
# via -r requirements/base.in
Expand Down Expand Up @@ -142,7 +138,7 @@ edx-ccx-keys==1.3.0
# openedx-events
edx-django-release-util==1.4.0
# via -r requirements/base.in
edx-django-utils==5.14.2
edx-django-utils==5.15.0
# via
# -r requirements/base.in
# django-config-models
Expand All @@ -154,7 +150,7 @@ edx-django-utils==5.14.2
# openedx-events
edx-drf-extensions==10.3.0
# via -r requirements/base.in
edx-event-bus-kafka==5.7.0
edx-event-bus-kafka==5.8.1
# via -r requirements/base.in
edx-event-bus-redis==0.5.0
# via -r requirements/base.in
Expand Down Expand Up @@ -184,7 +180,7 @@ fs-s3fs==1.1.1
# via openedx-django-pyfs
future==1.0.0
# via pyjwkest
idna==3.7
idna==3.8
# via requests
inflection==0.5.1
# via drf-yasg
Expand All @@ -204,7 +200,7 @@ lazy==1.6
# via lti-consumer-xblock
lti-consumer-xblock==9.11.3
# via -r requirements/base.in
lxml==5.2.2
lxml==5.3.0
# via
# lti-consumer-xblock
# xblock
Expand All @@ -219,7 +215,7 @@ markupsafe==2.1.5
# xblock
mysqlclient==2.2.4
# via -r requirements/base.in
newrelic==9.12.0
newrelic==9.13.0
# via edx-django-utils
oauthlib==3.2.2
# via
Expand All @@ -228,9 +224,9 @@ oauthlib==3.2.2
# social-auth-core
openapi-codec==1.3.2
# via django-rest-swagger
openedx-django-pyfs==3.6.0
openedx-django-pyfs==3.7.0
# via lti-consumer-xblock
openedx-events==9.11.0
openedx-events==9.12.0
# via
# -r requirements/base.in
# edx-event-bus-kafka
Expand All @@ -239,7 +235,7 @@ openedx-filters==1.9.0
# via lti-consumer-xblock
packaging==24.1
# via drf-yasg
pbr==6.0.0
pbr==6.1.0
# via stevedore
psutil==6.0.0
# via edx-django-utils
Expand All @@ -253,7 +249,7 @@ pyjwkest==1.4.2
# via
# edx-token-utils
# lti-consumer-xblock
pyjwt[crypto]==2.8.0
pyjwt[crypto]==2.9.0
# via
# drf-jwt
# edx-auth-backends
Expand All @@ -279,7 +275,7 @@ pytz==2024.1
# -r requirements/base.in
# drf-yasg
# xblock
pyyaml==6.0.1
pyyaml==6.0.2
# via
# code-annotations
# drf-yasg
Expand All @@ -302,7 +298,7 @@ s3transfer==0.10.2
# via boto3
semantic-version==2.10.0
# via edx-drf-extensions
simplejson==3.19.2
simplejson==3.19.3
# via
# django-rest-swagger
# xblock
Expand All @@ -326,7 +322,7 @@ social-auth-core==4.5.4
# social-auth-app-django
sqlparse==0.5.1
# via django
stevedore==5.2.0
stevedore==5.3.0
# via
# code-annotations
# edx-django-utils
Expand All @@ -341,7 +337,7 @@ uritemplate==4.1.1
# via
# coreapi
# drf-yasg
urllib3==1.26.19
urllib3==1.26.20
# via
# botocore
# requests
Expand All @@ -351,9 +347,9 @@ web-fragments==2.2.0
# via xblock
webencodings==0.5.1
# via bleach
webob==1.8.7
webob==1.8.8
# via xblock
xblock==5.0.0
xblock==5.1.0
# via lti-consumer-xblock

# The following packages are considered to be unsafe in a requirements file:
Expand Down
8 changes: 4 additions & 4 deletions requirements/ci.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
#
# This file is autogenerated by pip-compile with Python 3.8
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# make upgrade
#
cachetools==5.4.0
cachetools==5.5.0
# via tox
chardet==5.2.0
# via tox
colorama==0.4.6
# via tox
coverage==7.6.0
coverage==7.6.1
# via -r requirements/ci.in
distlib==0.3.8
# via virtualenv
Expand All @@ -34,7 +34,7 @@ tomli==2.0.1
# via
# pyproject-api
# tox
tox==4.16.0
tox==4.18.0
# via -r requirements/ci.in
virtualenv==20.26.3
# via tox
9 changes: 0 additions & 9 deletions requirements/common_constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,6 @@ elasticsearch<7.14.0
# django-simple-history>3.0.0 adds indexing and causes a lot of migrations to be affected


# opentelemetry requires version 6.x at the moment:
# https://github.com/open-telemetry/opentelemetry-python/issues/3570
# Normally this could be added as a constraint in edx-django-utils, where we're
# adding the opentelemetry dependency. However, when we compile pip-tools.txt,
# that uses version 7.x, and then there's no undoing that when compiling base.txt.
# So we need to pin it globally, for now.
# Ticket for unpinning: https://github.com/openedx/edx-lint/issues/407
importlib-metadata<7

# Cause: https://github.com/openedx/event-tracking/pull/290
# event-tracking 2.4.1 upgrades to pymongo 4.4.0 which is not supported on edx-platform.
# We will pin event-tracking to do not break existing installations
Expand Down
Loading

0 comments on commit 4c69471

Please sign in to comment.