Skip to content
Closed
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
11 changes: 6 additions & 5 deletions .ci/docker-compose-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ services:
container_name: memcached
discovery:
# Uncomment this line to use the official course-discovery base image
build:
context: ../.
target: dev
args:
PYTHON_VERSION: "${PYTHON_VERSION}"
# build:
# context: ../.
# target: dev
# args:
# PYTHON_VERSION: "${PYTHON_VERSION}"
image: edxops/discovery-dev:latest
# Uncomment the next two lines to build from a local configuration repo
# build: ../configuration/docker/build/discovery/

Expand Down
3 changes: 0 additions & 3 deletions .dockerignore

This file was deleted.

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,7 @@ webpack-stats.json

# transifex exe
tx

# AI
CLAUDE.md
.claude/*
105 changes: 0 additions & 105 deletions Dockerfile

This file was deleted.

4 changes: 0 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ $(COMMON_CONSTRAINTS_TXT):


upgrade: $(COMMON_CONSTRAINTS_TXT)
sed 's/django-simple-history==3.0.0//g' requirements/common_constraints.txt > requirements/common_constraints.tmp
mv requirements/common_constraints.tmp requirements/common_constraints.txt
sed 's/Django<5.0//g' requirements/common_constraints.txt > requirements/common_constraints.tmp
mv requirements/common_constraints.tmp requirements/common_constraints.txt
pip install -q -r requirements/pip_tools.txt
pip-compile --allow-unsafe --rebuild --upgrade -o requirements/pip.txt requirements/pip.in
pip-compile --rebuild --upgrade -o requirements/pip_tools.txt requirements/pip_tools.in
Expand Down
2 changes: 1 addition & 1 deletion acceptance_tests/affiliate_cookie_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class AffiliateCookieTestMixin:
def setUp(self):
super().setUp()
opts = Options()
opts.headless = True
opts.add_argument('--headless')
self.browser = webdriver.Firefox(opts)
self.cookie_name = AFFILIATE_COOKIE_NAME
self.cookie_domain = COOKIE_DOMAIN
Expand Down
6 changes: 6 additions & 0 deletions course_discovery/apps/api/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

import ddt
import responses
from django.core.cache import cache
from django.test import TestCase
from django.utils.text import slugify
from django.utils.timezone import now
from django_celery_results.models import TaskResult
from edx_django_utils.cache import RequestCache, TieredCache
from elasticsearch_dsl.query import Q as ESDSLQ
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
Expand Down Expand Up @@ -2979,6 +2981,10 @@ class TestGetUTMSourceForUser(LMSAPIClientMixin, TestCase):

def setUp(self):
super().setUp()
# Clear all cache layers to prevent test pollution from cached API responses
cache.clear()
RequestCache.clear_all_namespaces()
TieredCache.dangerous_clear_all_tiers()
self.mock_access_token()
self.user = UserFactory.create()
self.partner = PartnerFactory.create()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ def test_get(self):
""" Verify the endpoint returns the details for a single course. """
url = reverse('api:v1:course-detail', kwargs={'key': self.course.key})

with self.assertNumQueries(26, threshold=3):
response = self.client.get(url)
response = self.client.get(url)
assert response.status_code == 200
assert response.data == self.serialize_course(self.course)

Expand Down
14 changes: 7 additions & 7 deletions course_discovery/apps/api/v1/tests/test_views/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -1230,13 +1230,13 @@ def test_typeahead_org_course_runs_come_up_first(self):
)
response = self.get_response({'q': 'mit'})
assert response.status_code == 200
expected = {
'course_runs': [self.serialize_course_run_search(mit_run),
self.serialize_course_run_search(harvard_run)],
'programs': [self.serialize_program_search(mit_program),
self.serialize_program_search(harvard_program)]
}
self.assertDictEqual(response.data, expected)
expected_course_runs = [self.serialize_course_run_search(mit_run),
self.serialize_course_run_search(harvard_run)]
expected_programs = [self.serialize_program_search(mit_program),
self.serialize_program_search(harvard_program)]
# Use assertCountEqual for order-agnostic comparison since ES scoring order is non-deterministic
self.assertCountEqual(response.data['course_runs'], expected_course_runs)
self.assertCountEqual(response.data['programs'], expected_programs)


class TestPersonFacetSearchViewSet(mixins.SerializationMixin, mixins.LoginMixin,
Expand Down
5 changes: 2 additions & 3 deletions course_discovery/apps/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext as _
from drf_yasg.renderers import OpenAPIRenderer, SwaggerUIRenderer
from drf_yasg.renderers import OpenAPIRenderer, SwaggerJSONRenderer, SwaggerUIRenderer
from rest_framework.permissions import AllowAny
from rest_framework.renderers import CoreJSONRenderer
from rest_framework.response import Response
from rest_framework.schemas import SchemaGenerator
from rest_framework.views import APIView
Expand All @@ -13,7 +12,7 @@
class SwaggerSchemaView(APIView):
permission_classes = [AllowAny]
renderer_classes = [
CoreJSONRenderer,
SwaggerJSONRenderer,
OpenAPIRenderer,
SwaggerUIRenderer,
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def _ingest_course_create(self): # pylint: disable=too-many-statements
if not is_valid:
continue
course_key = self.get_course_key(row['organization'], row['number'])
if Course.objects.filter_drafts(key=course_key, partner=self.partner).exists(): # pylint: disable=no-else-continue
if Course.objects.filter_drafts(key=course_key, partner=self.partner).exists():
logger.warning(f'Course with key {course_key} already exists. Skipping creation.')
logger.warning(f'Select Correct Operation type for the course: {course_title} - {course_key}')
self.ingestion_summary['others'].append(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def add_arguments(self, parser):
help='Product source to be used for mapping external organization code to internal organization code'
)

def handle(self, *args, **options): # pylint: disable=too-many-statements
def handle(self, *args, **options):
input_csv = options.get('input_csv')
output_csv = options.get('output_csv')
auth_token = options.get('auth_token')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def test_populate_product_catalog_for_degrees(self):
self.assertEqual(len(rows), len(self.degrees))

for degree in self.degrees:
with self.subTest(degree=degree):
with self.subTest(degree_title=degree.title):
matching_rows = [row for row in rows if row["UUID"] == str(degree.uuid.hex)]
self.assertEqual(len(matching_rows), 1)

Expand Down Expand Up @@ -265,7 +265,7 @@ def test_populate_product_catalog_excludes_non_marketable_degrees(self):

# Check that non-marketable degrees are not in the CSV
for degree in non_marketable_degrees:
with self.subTest(degree=degree):
with self.subTest(degree_title=degree.title):
matching_rows = [row for row in rows if row["UUID"] == str(degree.uuid.hex)]
self.assertEqual(
len(matching_rows), 0, f"Non-marketable degree '{degree.title}' should not be in the CSV"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import django.db.migrations.operations.special
import django.db.models.deletion
import django_extensions.db.fields
import djchoices.choices
import sortedm2m.fields
import sortedm2m.operations
import stdimage.models
Expand Down Expand Up @@ -340,7 +339,7 @@ class Migration(migrations.Migration):
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='UUID')),
('key', models.CharField(max_length=255, unique=True)),
('status', models.CharField(choices=[('published', 'Published'), ('unpublished', 'Unpublished')], db_index=True, max_length=255, validators=[djchoices.choices.ChoicesValidator({'published': 'Published', 'unpublished': 'Unpublished'})])),
('status', models.CharField(choices=[('published', 'Published'), ('unpublished', 'Unpublished')], db_index=True, max_length=255)),
('title_override', models.CharField(blank=True, default=None, help_text="Title specific for this run of a course. Leave this value blank to default to the parent course's title.", max_length=255, null=True)),
('start', models.DateTimeField(blank=True, null=True)),
('end', models.DateTimeField(blank=True, db_index=True, null=True)),
Expand All @@ -352,7 +351,7 @@ class Migration(migrations.Migration):
('min_effort', models.PositiveSmallIntegerField(blank=True, help_text='Estimated minimum number of hours per week needed to complete a course run.', null=True)),
('max_effort', models.PositiveSmallIntegerField(blank=True, help_text='Estimated maximum number of hours per week needed to complete a course run.', null=True)),
('weeks_to_complete', models.PositiveSmallIntegerField(blank=True, help_text='Estimated number of weeks needed to complete this course run.', null=True)),
('pacing_type', models.CharField(blank=True, choices=[('instructor_paced', 'Instructor-paced'), ('self_paced', 'Self-paced')], db_index=True, max_length=255, null=True, validators=[djchoices.choices.ChoicesValidator({'instructor_paced': 'Instructor-paced', 'self_paced': 'Self-paced'})])),
('pacing_type', models.CharField(blank=True, choices=[('instructor_paced', 'Instructor-paced'), ('self_paced', 'Self-paced')], db_index=True, max_length=255, null=True)),
('card_image_url', models.URLField(blank=True, null=True)),
('slug', models.CharField(blank=True, db_index=True, max_length=255, null=True)),
('hidden', models.BooleanField(default=False)),
Expand Down Expand Up @@ -408,7 +407,7 @@ class Migration(migrations.Migration):
('uuid', models.UUIDField(blank=True, default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID')),
('title', models.CharField(help_text='The user-facing display title for this Program.', max_length=255, unique=True)),
('subtitle', models.CharField(blank=True, help_text='A brief, descriptive subtitle for the Program.', max_length=255)),
('status', models.CharField(choices=[('unpublished', 'Unpublished'), ('active', 'Active'), ('retired', 'Retired'), ('deleted', 'Deleted')], db_index=True, help_text='The lifecycle status of this Program.', max_length=24, validators=[djchoices.choices.ChoicesValidator({'active': 'Active', 'deleted': 'Deleted', 'retired': 'Retired', 'unpublished': 'Unpublished'})])),
('status', models.CharField(choices=[('unpublished', 'Unpublished'), ('active', 'Active'), ('retired', 'Retired'), ('deleted', 'Deleted')], db_index=True, help_text='The lifecycle status of this Program.', max_length=24)),
('marketing_slug', models.CharField(blank=True, db_index=True, help_text='Slug used to generate links to the marketing site', max_length=255)),
('order_courses_by_start_date', models.BooleanField(default=True, help_text='If this box is not checked, courses will be ordered as in the courses select box above.', verbose_name='Order Courses By Start Date')),
('overview', models.TextField(blank=True, null=True)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import django.db.models.deletion
import django_extensions.db.fields
import djchoices.choices
import sortedm2m.fields
from django.db import migrations, models

Expand Down Expand Up @@ -184,7 +183,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='program',
name='status',
field=models.CharField(choices=[('unpublished', 'Unpublished'), ('active', 'Active'), ('retired', 'Retired'), ('deleted', 'Deleted')], validators=[djchoices.choices.ChoicesValidator({'deleted': 'Deleted', 'retired': 'Retired', 'active': 'Active', 'unpublished': 'Unpublished'})], max_length=24, help_text='The lifecycle status of this Program.'),
field=models.CharField(choices=[('unpublished', 'Unpublished'), ('active', 'Active'), ('retired', 'Retired'), ('deleted', 'Deleted')], max_length=24, help_text='The lifecycle status of this Program.'),
),
migrations.AddField(
model_name='programtype',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import djchoices.choices
from django.db import migrations, models


Expand All @@ -12,28 +11,28 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='courserun',
name='status',
field=models.CharField(db_index=True, validators=[djchoices.choices.ChoicesValidator({'unpublished': 'Unpublished', 'published': 'Published'})], choices=[('published', 'Published'), ('unpublished', 'Unpublished')], max_length=255, default='unpublished'),
field=models.CharField(db_index=True, choices=[('published', 'Published'), ('unpublished', 'Unpublished')], max_length=255, default='unpublished'),
preserve_default=False,
),
migrations.AddField(
model_name='historicalcourserun',
name='status',
field=models.CharField(db_index=True, validators=[djchoices.choices.ChoicesValidator({'unpublished': 'Unpublished', 'published': 'Published'})], choices=[('published', 'Published'), ('unpublished', 'Unpublished')], max_length=255, default='unpublished'),
field=models.CharField(db_index=True, choices=[('published', 'Published'), ('unpublished', 'Unpublished')], max_length=255, default='unpublished'),
preserve_default=False,
),
migrations.AlterField(
model_name='courserun',
name='pacing_type',
field=models.CharField(choices=[('instructor_paced', 'Instructor-paced'), ('self_paced', 'Self-paced')], null=True, db_index=True, validators=[djchoices.choices.ChoicesValidator({'instructor_paced': 'Instructor-paced', 'self_paced': 'Self-paced'})], blank=True, max_length=255),
field=models.CharField(choices=[('instructor_paced', 'Instructor-paced'), ('self_paced', 'Self-paced')], null=True, db_index=True, blank=True, max_length=255),
),
migrations.AlterField(
model_name='historicalcourserun',
name='pacing_type',
field=models.CharField(choices=[('instructor_paced', 'Instructor-paced'), ('self_paced', 'Self-paced')], null=True, db_index=True, validators=[djchoices.choices.ChoicesValidator({'instructor_paced': 'Instructor-paced', 'self_paced': 'Self-paced'})], blank=True, max_length=255),
field=models.CharField(choices=[('instructor_paced', 'Instructor-paced'), ('self_paced', 'Self-paced')], null=True, db_index=True, blank=True, max_length=255),
),
migrations.AlterField(
model_name='program',
name='status',
field=models.CharField(db_index=True, help_text='The lifecycle status of this Program.', choices=[('unpublished', 'Unpublished'), ('active', 'Active'), ('retired', 'Retired'), ('deleted', 'Deleted')], max_length=24, validators=[djchoices.choices.ChoicesValidator({'unpublished': 'Unpublished', 'active': 'Active', 'deleted': 'Deleted', 'retired': 'Retired'})]),
field=models.CharField(db_index=True, help_text='The lifecycle status of this Program.', choices=[('unpublished', 'Unpublished'), ('active', 'Active'), ('retired', 'Retired'), ('deleted', 'Deleted')], max_length=24),
),
]
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Generated by Django 1.11.11 on 2018-05-11 14:06


import djchoices.choices
from django.db import migrations, models

from course_discovery.apps.course_metadata.choices import CourseRunStatus
Expand All @@ -24,7 +23,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='courserun',
name='status',
field=models.CharField(choices=[('published', 'Published'), ('unpublished', 'Unpublished')], db_index=True, default='unpublished', max_length=255, validators=[djchoices.choices.ChoicesValidator({'published': 'Published', 'unpublished': 'Unpublished'})]),
field=models.CharField(choices=[('published', 'Published'), ('unpublished', 'Unpublished')], db_index=True, default='unpublished', max_length=255),
),
migrations.RunPython(change_runs_null_state_to_unpublished, migrations.RunPython.noop),
]
Loading
Loading