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

Mobile API v4 and other related fixes #33

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a599e80
refactor: rename descriptor -> block within lms/djangoapps/mobile_api
pkulkark Jan 4, 2023
1a392c5
feat: [AXIM-6] Add DefaultPagination for UserCourseEnrollmentsList v3
KyryloKireiev Sep 12, 2023
8cbaeb3
docs: add docstring for the paginator property override
GlugovGrGlib Nov 20, 2023
5143b0b
fix: remove trailing whitespace failing quality check
GlugovGrGlib Nov 20, 2023
bd7fb96
feat: list courses details by keys
yusuf-musleh Jun 26, 2023
dec80d3
fix: Add platform name to mobile config cache key (#32504)
moeez96 Jun 19, 2023
44ee2d9
refactor: Add logging to mobile_api app (#33064)
moeez96 Aug 23, 2023
2308390
test: Update more tests that had short passwords.
feanil Sep 29, 2023
b36d16f
test: Update to an even longer password.
feanil Oct 11, 2023
13fb8d0
test: Update to reuse variables in more places.
feanil Oct 16, 2023
f82d340
feat: [AXIM-26] Extended BlocksInCourseView API
KyryloKireiev Sep 15, 2023
f604789
fix: [FC-0031] Add parameters description, refactor list method
KyryloKireiev Oct 30, 2023
bca3236
refactor: [FC-0031] Use serializer instead of custom function
KyryloKireiev Dec 15, 2023
3ef443c
docs: [FC-0031] Update docstring
GlugovGrGlib Dec 19, 2023
b32683e
feat: Add course price in mobile enrollment api (#34255)
jawad-khan Feb 19, 2024
abdfd80
refactor: Increase size for mobile config singelton value (#34288)
moeez96 Feb 27, 2024
3e2ba6b
feat(mobile_api): Add course access object to mobile course info API …
GlugovGrGlib Apr 25, 2024
01bbdab
feat: Added upgrade deadline in blocks api (#34750)
jawad-khan May 9, 2024
72c5221
feat: [FC-0047] Extend mobile API with course progress and primary co…
KyryloKireiev Jul 10, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def setUp(self):
self.factory = RequestFactory()
self.global_admin = AdminFactory()
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.course_create_rerun_url = reverse('course_handler')
self.course_start = datetime.datetime.utcnow()
self.course_end = self.course_start + datetime.timedelta(days=30)
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/tests/test_course_listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ def setUp(self):
super().setUp()
# create and log in a staff user.
# create and log in a non-staff user
self.user = UserFactory()
self.user = UserFactory(password=self.TEST_PASSWORD)
self.factory = RequestFactory()
self.request = self.factory.get('/course')
self.request.user = self.user
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

def _create_course_with_access_groups(self, course_location, user=None):
"""
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/tests/test_course_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1866,10 +1866,10 @@ def _get_course_details_response(self, global_staff):
"""
Return the course details page as either global or non-global staff
"""
user = UserFactory(is_staff=global_staff)
user = UserFactory(is_staff=global_staff, password=self.TEST_PASSWORD)
CourseInstructorRole(self.course.id).add_users(user)

self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)

return self.client.get_html(self.course_details_url)

Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/tests/test_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def setUp(self):

self.uname = 'testuser'
self.email = 'test+courses@edx.org'
self.password = 'foo'
self.password = self.TEST_PASSWORD

# Create the use so we can log them in.
self.user = UserFactory.create(username=self.uname, email=self.email, password=self.password)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def setUp(self):
# create and log in a staff user.
self.user = UserFactory(is_staff=True)
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# create a course via the view handler to create course
self.course_key = self.store.make_course_key('Org_1', 'Course_1', 'Run_1')
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def setUp(self):
super().setUp()

self.email = 'a@b.com'
self.pw = 'xyz'
self.pw = 'password1234'
self.username = 'testuser'
self.client = AjaxEnabledTestClient()
# clear the cache so ratelimiting won't affect these tests
Expand Down
10 changes: 5 additions & 5 deletions cms/djangoapps/contentstore/views/tests/test_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def test_cannot_create_certificate_if_user_has_no_write_permissions(self):
Tests user without write permissions on course should not able to create certificate
"""
user = UserFactory()
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.ajax_post(
self._url(),
data=CERTIFICATE_JSON
Expand Down Expand Up @@ -635,7 +635,7 @@ def test_delete_certificate_without_write_permissions(self, signatory_path):
"""
self._add_course_certificates(count=2, signatory_count=1, asset_path_format=signatory_path)
user = UserFactory()
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.delete(
self._url(cid=1),
content_type="application/json",
Expand All @@ -653,7 +653,7 @@ def test_delete_certificate_without_global_staff_permissions(self, signatory_pat
user = UserFactory()
for role in [CourseInstructorRole, CourseStaffRole]:
role(self.course.id).add_users(user)
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.delete(
self._url(cid=1),
content_type="application/json",
Expand Down Expand Up @@ -681,7 +681,7 @@ def test_update_active_certificate_without_global_staff_permissions(self, signat
user = UserFactory()
for role in [CourseInstructorRole, CourseStaffRole]:
role(self.course.id).add_users(user)
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.put(
self._url(cid=1),
data=json.dumps(cert_data),
Expand Down Expand Up @@ -799,7 +799,7 @@ def test_certificate_activation_without_write_permissions(self, activate, signat
test_url = reverse_course_url('certificate_activation_handler', self.course.id)
self._add_course_certificates(count=1, signatory_count=2, asset_path_format=signatory_path)
user = UserFactory()
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.post(
test_url,
data=json.dumps({"is_active": activate}),
Expand Down
5 changes: 3 additions & 2 deletions cms/djangoapps/contentstore/views/tests/test_organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ class TestOrganizationListing(TestCase):
"""Verify Organization listing behavior."""
def setUp(self):
super().setUp()
self.staff = UserFactory(is_staff=True)
self.client.login(username=self.staff.username, password='test')
self.password = "password1234"
self.staff = UserFactory(is_staff=True, password=self.password)
self.client.login(username=self.staff.username, password=self.password)
self.org_names_listing_url = reverse('organizations')
self.org_short_names = ["alphaX", "betaX", "orgX"]
for index, short_name in enumerate(self.org_short_names):
Expand Down
16 changes: 8 additions & 8 deletions cms/djangoapps/maintenance/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class TestMaintenanceIndex(ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.user = AdminFactory()
login_success = self.client.login(username=self.user.username, password='test')
login_success = self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.assertTrue(login_success)
self.view_url = reverse('maintenance:maintenance_index')

Expand All @@ -56,7 +56,7 @@ class MaintenanceViewTestCase(ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.user = AdminFactory()
login_success = self.client.login(username=self.user.username, password='test')
login_success = self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.assertTrue(login_success)

def verify_error_message(self, data, error_message):
Expand Down Expand Up @@ -110,8 +110,8 @@ def test_non_global_staff_access(self, url):
"""
Test that all maintenance app views are not accessible to non-global-staff user.
"""
user = UserFactory(username='test', email='test@example.com', password='test')
login_success = self.client.login(username=user.username, password='test')
user = UserFactory(username='test', email='test@example.com', password=self.TEST_PASSWORD)
login_success = self.client.login(username=user.username, password=self.TEST_PASSWORD)
self.assertTrue(login_success)

response = self.client.get(url)
Expand Down Expand Up @@ -245,13 +245,13 @@ def setUp(self):
self.admin = AdminFactory.create(
email='staff@edx.org',
username='admin',
password='pass'
password=self.TEST_PASSWORD
)
self.client.login(username=self.admin.username, password='pass')
self.client.login(username=self.admin.username, password=self.TEST_PASSWORD)
self.non_staff_user = UserFactory.create(
email='test@edx.org',
username='test',
password='pass'
password=self.TEST_PASSWORD
)

def test_index(self):
Expand Down Expand Up @@ -301,7 +301,7 @@ def _test_403(self, viewname, kwargs=None):
self.assertEqual(response.status_code, 403)

def test_authorization(self):
self.client.login(username=self.non_staff_user, password='pass')
self.client.login(username=self.non_staff_user, password=self.TEST_PASSWORD)
announcement = Announcement.objects.create(content="Test Delete")
announcement.save()

Expand Down
2 changes: 1 addition & 1 deletion common/djangoapps/course_modes/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_expiration_timezone(self):
'_expiration_datetime_1': expiration.time(),
}

self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)

# Create a new course mode from django admin page
response = self.client.post(reverse('admin:course_modes_coursemode_add'), data=data)
Expand Down
62 changes: 62 additions & 0 deletions common/djangoapps/student/models/course_enrollment.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,73 @@ class UnenrollmentNotAllowed(CourseEnrollmentException):
pass


class CourseEnrollmentQuerySet(models.QuerySet):
"""
Custom queryset for CourseEnrollment with Table-level filter methods.
"""

def active(self):
"""
Returns a queryset of CourseEnrollment objects for courses that are currently active.
"""
return self.filter(is_active=True)

def without_certificates(self, username):
"""
Returns a queryset of CourseEnrollment objects for courses that do not have a certificate.
"""
return self.exclude(course_id__in=self.get_user_course_ids_with_certificates(username))

def with_certificates(self, username):
"""
Returns a queryset of CourseEnrollment objects for courses that have a certificate.
"""
return self.filter(course_id__in=self.get_user_course_ids_with_certificates(username))

def in_progress(self, username, time_zone=UTC):
"""
Returns a queryset of CourseEnrollment objects for courses that are currently in progress.
"""
now = datetime.now(time_zone)
return self.active().without_certificates(username).filter(
Q(course__start__lte=now, course__end__gte=now)
| Q(course__start__isnull=True, course__end__isnull=True)
| Q(course__start__isnull=True, course__end__gte=now)
| Q(course__start__lte=now, course__end__isnull=True),
)

def completed(self, username):
"""
Returns a queryset of CourseEnrollment objects for courses that have been completed.
"""
return self.active().with_certificates(username)

def expired(self, username, time_zone=UTC):
"""
Returns a queryset of CourseEnrollment objects for courses that have expired.
"""
now = datetime.now(time_zone)
return self.active().without_certificates(username).filter(course__end__lt=now)

def get_user_course_ids_with_certificates(self, username):
"""
Gets user's course ids with certificates.
"""
from lms.djangoapps.certificates.models import GeneratedCertificate # pylint: disable=import-outside-toplevel
course_ids_with_certificates = GeneratedCertificate.objects.filter(
user__username=username
).values_list('course_id', flat=True)
return course_ids_with_certificates


class CourseEnrollmentManager(models.Manager):
"""
Custom manager for CourseEnrollment with Table-level filter methods.
"""

def get_queryset(self):
return CourseEnrollmentQuerySet(self.model, using=self._db)

def is_small_course(self, course_id):
"""
Returns false if the number of enrollments are one greater than 'max_enrollments' else true
Expand Down
4 changes: 2 additions & 2 deletions common/djangoapps/student/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory

TEST_PASSWORD = 'test'
TEST_PASSWORD = 'Password1234'


class GroupFactory(DjangoModelFactory): # lint-amnesty, pylint: disable=missing-class-docstring
Expand Down Expand Up @@ -81,7 +81,7 @@ class Meta:
model = User
django_get_or_create = ('email', 'username')

_DEFAULT_PASSWORD = 'test'
_DEFAULT_PASSWORD = 'Password1234'

username = factory.Sequence('robot{}'.format)
email = factory.Sequence('robot+test+{}@edx.org'.format)
Expand Down
19 changes: 10 additions & 9 deletions common/djangoapps/student/tests/test_admin_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_save_valid_data(self):
'email': self.user.email
}

self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# # adding new role from django admin page
response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data)
Expand All @@ -78,7 +78,7 @@ def test_save_without_org_and_course_data(self):
'course_id': str(self.course.id)
}

self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# # adding new role from django admin page
response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data)
Expand All @@ -96,7 +96,7 @@ def test_save_with_course_only(self):

}

self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# # adding new role from django admin page
response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data)
Expand All @@ -115,7 +115,7 @@ def test_save_with_org_only(self):

}

self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# # adding new role from django admin page
response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data)
Expand All @@ -136,7 +136,7 @@ def test_save_with_invalid_course(self):
'email': email
}

self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# Adding new role with invalid data
response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data)
Expand All @@ -163,7 +163,7 @@ def test_save_valid_course_invalid_org(self):
'email': self.user.email
}

self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# # adding new role from django admin page
response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data)
Expand Down Expand Up @@ -230,7 +230,7 @@ def setUp(self):
user=self.user,
course_id=self.course.id, # pylint: disable=no-member
)
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

@ddt.data(*ADMIN_URLS)
@ddt.unpack
Expand Down Expand Up @@ -324,13 +324,14 @@ class LoginFailuresAdminTest(TestCase):
def setUpClass(cls):
"""Setup class"""
super().setUpClass()
cls.user = UserFactory.create(username='§', is_staff=True, is_superuser=True)
cls.TEST_PASSWORD = 'Password1234'
cls.user = UserFactory.create(username='§', password=cls.TEST_PASSWORD, is_staff=True, is_superuser=True)
cls.user.save()

def setUp(self):
"""Setup."""
super().setUp()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.user2 = UserFactory.create(username='Zażółć gęślą jaźń')
self.user_lockout_until = datetime.datetime.now(UTC)
LoginFailures.objects.create(user=self.user, failure_count=10, lockout_until=self.user_lockout_until)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def setUp(self):
# Create student account
student = UserFactory.create()
CourseEnrollmentFactory.create(user=student, course_id=self.course.id)
self.client.login(username=student.username, password="test")
self.client.login(username=student.username, password=self.TEST_PASSWORD)

self.url = reverse('dashboard')
# URL for email settings modal
Expand Down
6 changes: 3 additions & 3 deletions common/djangoapps/student/tests/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def _create_account(self):
params = {
'username': 'test_user',
'email': 'test_user@example.com',
'password': 'edx',
'password': 'Password1234',
'name': 'Test User',
'honor_code': True,
'terms_of_service': True
Expand Down Expand Up @@ -319,7 +319,7 @@ def setUp(self, tracker='common.djangoapps.student.views.management.tracker'):
self.new_email = 'new.email@edx.org'
self.req_factory = RequestFactory()
self.request = self.req_factory.post('unused_url', data={
'password': 'test',
'password': 'Password1234',
'new_email': self.new_email
})
self.request.user = self.user
Expand Down Expand Up @@ -628,7 +628,7 @@ def setUp(self, tracker='common.djangoapps.student.views.management.tracker'):
self.new_secondary_email = 'new.secondary.email@edx.org'
self.req_factory = RequestFactory()
self.request = self.req_factory.post('unused_url', data={
'password': 'test',
'password': 'Password1234',
'new_email': self.new_secondary_email
})
self.request.user = self.user
Expand Down
Loading