feat: allow unsubcribing from a course goal with just a token

* Add unsubscribe_token uuid field to CourseGoal model
* Add endpoint to unsubcribe from just a token (no login needed)
* Add admin page for the course_goals djangoapp
* Add get_course_overview_or_404 utility method
* Clean up URL handling in course_home_api

AA-907
This commit is contained in:
Michael Terry
2021-08-17 11:09:14 -04:00
parent 377dec74aa
commit 2176dd7890
33 changed files with 356 additions and 121 deletions

View File

@@ -3,6 +3,8 @@ CourseOverview api
"""
import logging
from django.http.response import Http404
from openedx.core.djangoapps.catalog.api import get_course_run_details
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.serializers import (
@@ -25,6 +27,18 @@ def get_course_overview_or_none(course_id):
return None
def get_course_overview_or_404(course_id):
"""
Retrieve and return course overview data for the provided course id.
If the course overview does not exist, raises Http404.
"""
try:
return CourseOverview.get_from_id(course_id)
except CourseOverview.DoesNotExist as e:
raise Http404(f"Course overview does not exist for {course_id}") from e
def get_pseudo_course_overview(course_id):
"""
Returns a pseudo course overview object for a deleted course.

View File

@@ -3,11 +3,13 @@ course_overview api tests
"""
from mock import patch
from django.http.response import Http404
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.api import (
get_course_overview_or_404,
get_course_overview_or_none,
get_course_overviews,
get_course_overviews_from_ids,
@@ -27,7 +29,7 @@ class TestCourseOverviewsApi(ModuleStoreTestCase):
for _ in range(3):
CourseOverviewFactory.create()
def test_get_course_overview_or_none(self):
def test_get_course_overview_or_none_success(self):
"""
Test for `test_get_course_overview_or_none` function when the overview exists.
"""
@@ -43,6 +45,22 @@ class TestCourseOverviewsApi(ModuleStoreTestCase):
retrieved_course_overview = get_course_overview_or_none(course_run_key)
assert retrieved_course_overview is None
def test_get_course_overview_or_404_success(self):
"""
Test for `test_get_course_overview_or_404` function when the overview exists.
"""
course_overview = CourseOverviewFactory.create()
retrieved_course_overview = get_course_overview_or_404(course_overview.id)
assert course_overview.id == retrieved_course_overview.id
def test_get_course_overview_or_404_missing(self):
"""
Test for `test_get_course_overview_or_404` function when the overview does not exist.
"""
course_run_key = CourseKey.from_string('course-v1:coping+with+deletions')
with self.assertRaises(Http404):
get_course_overview_or_404(course_run_key)
def test_get_course_overview_from_ids(self):
"""
Test for `get_course_overviews_from_ids` function.

View File

@@ -4,7 +4,7 @@ Course API Serializers. Representing course catalog data
from rest_framework import serializers
from lms.djangoapps.course_home_api.progress.v1.serializers import CertificateDataSerializer
from lms.djangoapps.course_home_api.progress.serializers import CertificateDataSerializer
from openedx.core.lib.api.fields import AbsoluteURLField