|
40 | 40 | 400: Downstream block is not linked to upstream content. |
41 | 41 | 404: Downstream block not found or user lacks permission to edit it. |
42 | 42 |
|
| 43 | + /api/contentstore/v2/upstream/{usage_key_string}/downstream-contexts |
| 44 | +
|
| 45 | + GET: List all downstream contexts (Courses) linked to a library block. |
| 46 | + 200: A list of Course IDs and their display names, along with the number of times the block |
| 47 | + is linked to each. |
| 48 | +
|
43 | 49 | # NOT YET IMPLEMENTED -- Will be needed for full Libraries Relaunch in ~Teak. |
44 | 50 | /api/contentstore/v2/downstreams |
45 | 51 | /api/contentstore/v2/downstreams?course_id=course-v1:A+B+C&ready_to_sync=true |
|
60 | 66 | import logging |
61 | 67 |
|
62 | 68 | from attrs import asdict as attrs_asdict |
| 69 | +from collections import Counter |
63 | 70 | from django.contrib.auth.models import User # pylint: disable=imported-auth-user |
64 | 71 | from opaque_keys import InvalidKeyError |
65 | 72 | from opaque_keys.edx.keys import CourseKey, UsageKey |
|
71 | 78 |
|
72 | 79 | from cms.djangoapps.contentstore.helpers import import_static_assets_for_library_sync |
73 | 80 | from cms.djangoapps.contentstore.models import PublishableEntityLink |
| 81 | +from cms.djangoapps.contentstore.utils import reverse_course_url |
74 | 82 | from cms.djangoapps.contentstore.rest_api.v2.serializers import PublishableEntityLinksSerializer |
75 | 83 | from cms.lib.xblock.upstream_sync import ( |
76 | 84 | BadDownstream, |
|
91 | 99 | from xmodule.modulestore.django import modulestore |
92 | 100 | from xmodule.modulestore.exceptions import ItemNotFoundError |
93 | 101 |
|
| 102 | + |
94 | 103 | logger = logging.getLogger(__name__) |
95 | 104 |
|
96 | 105 |
|
@@ -137,6 +146,40 @@ def get(self, request: _AuthenticatedRequest, course_key_string: str): |
137 | 146 | return Response(serializer.data) |
138 | 147 |
|
139 | 148 |
|
| 149 | +@view_auth_classes() |
| 150 | +class DownstreamContextListView(DeveloperErrorViewMixin, APIView): |
| 151 | + """ |
| 152 | + Serves library block->courses links |
| 153 | + """ |
| 154 | + def get(self, _: _AuthenticatedRequest, usage_key_string: str) -> Response: |
| 155 | + """ |
| 156 | + Fetches downstream context links for given publishable entity |
| 157 | + """ |
| 158 | + try: |
| 159 | + usage_key = UsageKey.from_string(usage_key_string) |
| 160 | + print(usage_key) |
| 161 | + except InvalidKeyError as exc: |
| 162 | + raise ValidationError(detail=f"Malformed usage key: {usage_key_string}") from exc |
| 163 | + links = PublishableEntityLink.get_by_upstream_usage_key(upstream_usage_key=usage_key) |
| 164 | + downstream_key_list = [link.downstream_context_key for link in links] |
| 165 | + |
| 166 | + # Count the number of times each course is linked to the library block |
| 167 | + counter = Counter(downstream_key_list) |
| 168 | + |
| 169 | + result = [] |
| 170 | + for context_key, count in counter.most_common(): |
| 171 | + # The following code only can handle the correct display_name for Courses as context |
| 172 | + course = modulestore().get_course(context_key) |
| 173 | + result.append({ |
| 174 | + "id": str(context_key), |
| 175 | + "display_name": course.display_name, |
| 176 | + "url": reverse_course_url('course_handler', context_key), |
| 177 | + "count": count, |
| 178 | + }) |
| 179 | + |
| 180 | + return Response(result) |
| 181 | + |
| 182 | + |
140 | 183 | @view_auth_classes(is_authenticated=True) |
141 | 184 | class DownstreamView(DeveloperErrorViewMixin, APIView): |
142 | 185 | """ |
|
0 commit comments