From c39fdfee6f6617de0f9ef6540aa0726416897fb9 Mon Sep 17 00:00:00 2001 From: Anna Gavrilman Date: Mon, 3 Jun 2024 20:18:22 +0200 Subject: [PATCH] Adding topics to course and program api (#2228) * Adding topics to course and program api * test * fixing * number of queries * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- courses/admin.py | 1 + courses/serializers/v2/courses.py | 11 +++++++++++ courses/serializers/v2/programs.py | 12 ++++++++++++ courses/serializers/v2/programs_test.py | 12 +++++++++++- courses/views/test_utils.py | 2 +- 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/courses/admin.py b/courses/admin.py index a33abd187d..3d529febef 100644 --- a/courses/admin.py +++ b/courses/admin.py @@ -231,6 +231,7 @@ class CourseRunEnrollmentAdmin(AuditableModelAdmin): "get_run_courseware_id", "enrollment_mode", "change_status", + "created_on", ) raw_id_fields = ( "user", diff --git a/courses/serializers/v2/courses.py b/courses/serializers/v2/courses.py index 4b30b674c7..9388e5add7 100644 --- a/courses/serializers/v2/courses.py +++ b/courses/serializers/v2/courses.py @@ -30,6 +30,7 @@ class CourseSerializer(BaseCourseSerializer): next_run_id = serializers.SerializerMethodField() page = BaseCoursePageSerializer(read_only=True) programs = serializers.SerializerMethodField() + topics = serializers.SerializerMethodField() def get_next_run_id(self, instance): """Get next run id""" @@ -44,6 +45,15 @@ def get_programs(self, instance): return None + def get_topics(self, instance): + """List topics of a course""" + if instance.page: + return sorted( + [{"name": topic.name} for topic in instance.page.topics.all()], + key=lambda topic: topic["name"], + ) + return [] + class Meta: model = models.Course fields = [ @@ -54,6 +64,7 @@ class Meta: "departments", "page", "programs", + "topics", ] diff --git a/courses/serializers/v2/programs.py b/courses/serializers/v2/programs.py index a1a0eb7016..478ed7df3c 100644 --- a/courses/serializers/v2/programs.py +++ b/courses/serializers/v2/programs.py @@ -21,6 +21,7 @@ class ProgramSerializer(serializers.ModelSerializer): req_tree = serializers.SerializerMethodField() page = serializers.SerializerMethodField() departments = serializers.StringRelatedField(many=True, read_only=True) + topics = serializers.SerializerMethodField() def get_courses(self, instance): return [course[0].id for course in instance.courses if course[0].live] @@ -45,6 +46,16 @@ def get_page(self, instance): else: return {"feature_image_src": get_thumbnail_url(None)} + def get_topics(self, instance): + """List all topics in all courses in the program""" + topics = set( # noqa: C401 + topic.name + for course in instance.courses + if course[0].page + for topic in course[0].page.topics.all() + ) + return [{"name": topic} for topic in sorted(topics)] + class Meta: model = Program fields = [ @@ -58,6 +69,7 @@ class Meta: "program_type", "departments", "live", + "topics", ] diff --git a/courses/serializers/v2/programs_test.py b/courses/serializers/v2/programs_test.py index 5687b8546b..fa75cfbf3a 100644 --- a/courses/serializers/v2/programs_test.py +++ b/courses/serializers/v2/programs_test.py @@ -3,12 +3,13 @@ import pytest from django.utils.timezone import now +from cms.factories import CoursePageFactory from cms.serializers import ProgramPageSerializer from courses.factories import ( # noqa: F401 CourseRunFactory, program_with_empty_requirements, ) -from courses.models import Department +from courses.models import CoursesTopic, Department from courses.serializers.v2.programs import ( ProgramRequirementTreeSerializer, ProgramSerializer, @@ -29,11 +30,13 @@ def test_serialize_program(mock_context, remove_tree, program_with_empty_require start_date=now() + timedelta(hours=1), ) course1 = run1.course + CoursePageFactory.create(course=run1.course) run2 = CourseRunFactory.create( course__page=None, start_date=now() + timedelta(hours=2), ) course2 = run2.course + CoursePageFactory.create(course=run2.course) departments = [ Department.objects.create(name=f"department{num}") for num in range(3) ] @@ -42,6 +45,7 @@ def test_serialize_program(mock_context, remove_tree, program_with_empty_require formatted_reqs = {"required": [], "electives": []} + topics = [] if not remove_tree: program_with_empty_requirements.add_requirement(course1) program_with_empty_requirements.add_requirement(course2) @@ -51,6 +55,11 @@ def test_serialize_program(mock_context, remove_tree, program_with_empty_require formatted_reqs["electives"] = [ course.id for course in program_with_empty_requirements.elective_courses ] + topics = [CoursesTopic.objects.create(name=f"topic{num}") for num in range(3)] + course1.page.topics.set([topics[0], topics[1]]) + course2.page.topics.set([topics[1], topics[2]]) + course1.page.save() + course2.page.save() data = ProgramSerializer( instance=program_with_empty_requirements, context=mock_context @@ -73,5 +82,6 @@ def test_serialize_program(mock_context, remove_tree, program_with_empty_require "program_type": "Series", "departments": [], "live": True, + "topics": [{"name": topic.name} for topic in topics], }, ) diff --git a/courses/views/test_utils.py b/courses/views/test_utils.py index 92a89a5790..0bf0c11179 100644 --- a/courses/views/test_utils.py +++ b/courses/views/test_utils.py @@ -54,7 +54,7 @@ def num_queries_from_programs(programs, version="v1"): num_queries += num_queries_from_course(course) num_queries += 4 + (6 * num_courses) + 1 if version == "v2": - num_queries += 6 + (12 * num_courses) + 1 + num_queries += 6 + (17 * num_courses) + 1 return num_queries