Skip to content

Commit

Permalink
feat: Add Library Collections REST endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
yusuf-musleh committed Aug 19, 2024
1 parent 2a6f0f2 commit fcff07b
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 1 deletion.
6 changes: 5 additions & 1 deletion openedx/core/djangoapps/content_libraries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
LIBRARY_BLOCK_UPDATED,
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Component, MediaType
from openedx_learning.api.authoring_models import Component, MediaType, LearningPackage
from organizations.models import Organization
from xblock.core import XBlock
from xblock.exceptions import XBlockNotFoundError
Expand Down Expand Up @@ -150,6 +150,7 @@ class ContentLibraryMetadata:
Class that represents the metadata about a content library.
"""
key = attr.ib(type=LibraryLocatorV2)
learning_package = attr.ib(type=LearningPackage)
title = attr.ib("")
description = attr.ib("")
num_blocks = attr.ib(0)
Expand Down Expand Up @@ -323,6 +324,7 @@ def get_metadata(queryset, text_search=None):
has_unpublished_changes=False,
has_unpublished_deletes=False,
license=lib.license,
learning_package=lib.learning_package,
)
for lib in queryset
]
Expand Down Expand Up @@ -408,6 +410,7 @@ def get_library(library_key):
license=ref.license,
created=learning_package.created,
updated=learning_package.updated,
learning_package=learning_package
)


Expand Down Expand Up @@ -479,6 +482,7 @@ def create_library(
allow_public_learning=ref.allow_public_learning,
allow_public_read=ref.allow_public_read,
license=library_license,
learning_package=ref.learning_package
)


Expand Down
29 changes: 29 additions & 0 deletions openedx/core/djangoapps/content_libraries/collections/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# """
# Collections APIs
# """

# from __future__ import annotations

# from openedx_learning.api.authoring_models import Collection
# from openedx_learning.api import authoring as authoring_api


# # def get_library_collection(collection_id: int) -> Collection:

# # library_collection = authoring_api.get_collection(collection_id)
# # library_collection.learning_package




# def get_all_library_collections():
# pass


# def create_library_collection():
# pass


# def update_library_collection():
# pass

Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
Collections API Views
"""

from __future__ import annotations

from django.http import Http404

# from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.status import HTTP_405_METHOD_NOT_ALLOWED

from opaque_keys.edx.locator import LibraryLocatorV2

from openedx.core.djangoapps.content_libraries import api, permissions
from openedx.core.djangoapps.content_libraries.serializers import (
ContentLibraryCollectionSerializer,
ContentLibraryCollectionCreateOrUpdateSerializer,
)

from openedx_learning.api.authoring_models import Collection
from openedx_learning.api import authoring as authoring_api


class LibraryCollectionsView(ModelViewSet):
"""
Views to get, create and update Library Collections.
"""

serializer_class = ContentLibraryCollectionSerializer

def retrieve(self, request, lib_key_str, pk=None):
"""
Retrieve the Content Library Collection
"""
try:
collection = authoring_api.get_collection(pk)
except Collection.DoesNotExist as exc:
raise Http404 from exc

# Check if user has permissions to view this collection by checking if
# user has permission to view the Content Library it belongs to
library_key = LibraryLocatorV2.from_string(lib_key_str)
api.require_permission_for_library_key(library_key, request.user, permissions.CAN_VIEW_THIS_CONTENT_LIBRARY)
serializer = self.get_serializer(collection)
return Response(serializer.data)

def list(self, request, lib_key_str):
"""
List Collections that belong to Content Library
"""
# Check if user has permissions to view collections by checking if user
# has permission to view the Content Library they belong to
library_key = LibraryLocatorV2.from_string(lib_key_str)
api.require_permission_for_library_key(library_key, request.user, permissions.CAN_VIEW_THIS_CONTENT_LIBRARY)
content_library = api.get_library(library_key)
collections = authoring_api.get_learning_package_collections(content_library.learning_package.id)
serializer = self.get_serializer(collections, many=True)
return Response(serializer.data)

def create(self, request, lib_key_str):
"""
Create a Collection that belongs to a Content Library
"""
# Check if user has permissions to create a collection in the Content Library
# by checking if user has permission to edit the Content Library
library_key = LibraryLocatorV2.from_string(lib_key_str)
api.require_permission_for_library_key(library_key, request.user, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY)
create_serializer = ContentLibraryCollectionCreateOrUpdateSerializer(data=request.data)
create_serializer.is_valid(raise_exception=True)
content_library = api.get_library(library_key)
collection = authoring_api.create_collection(
content_library.learning_package.id,
request.user,
**create_serializer.validated_data
)
serializer = self.get_serializer(collection)
return Response(serializer.data)

def partial_update(self, request, lib_key_str, pk=None):
"""
Update a Collection that belongs to a Content Library
"""
# Check if user has permissions to update a collection in the Content Library
# by checking if user has permission to edit the Content Library
library_key = LibraryLocatorV2.from_string(lib_key_str)
api.require_permission_for_library_key(library_key, request.user, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY)

try:
collection = authoring_api.get_collection(pk)
except Collection.DoesNotExist as exc:
raise Http404 from exc

update_serializer = ContentLibraryCollectionCreateOrUpdateSerializer(
collection, data=request.data, partial=True
)
update_serializer.is_valid(raise_exception=True)
collection = authoring_api.update_collection(pk, **update_serializer.validated_data)
serializer = self.get_serializer(collection)
return Response(serializer.data)

def destroy(self, request, lib_key_str, pk=None):
"""
Deletes a Collection that belongs to a Content Library
Note: (currently not allowed)
"""
return Response(None, status=HTTP_405_METHOD_NOT_ALLOWED)
21 changes: 21 additions & 0 deletions openedx/core/djangoapps/content_libraries/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from django.core.validators import validate_unicode_slug
from rest_framework import serializers


from openedx_learning.api.authoring_models import Collection
from openedx.core.djangoapps.content_libraries.constants import (
LIBRARY_TYPES,
COMPLEX,
Expand Down Expand Up @@ -234,3 +236,22 @@ class ContentLibraryBlockImportTaskCreateSerializer(serializers.Serializer):
"""

course_key = CourseKeyField()


class ContentLibraryCollectionSerializer(serializers.ModelSerializer):
"""
Serializer for a Content Library Collection
"""

class Meta:
model = Collection
fields = '__all__'


class ContentLibraryCollectionCreateOrUpdateSerializer(serializers.Serializer):
"""
Serializer for add/update a Collection in a Content Library
"""

title = serializers.CharField()
description = serializers.CharField()
6 changes: 6 additions & 0 deletions openedx/core/djangoapps/content_libraries/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from rest_framework import routers

from . import views
from .collections.rest_api.v1 import views as collection_views


# Django application name.
Expand All @@ -18,6 +19,9 @@
import_blocks_router = routers.DefaultRouter()
import_blocks_router.register(r'tasks', views.LibraryImportTaskViewSet, basename='import-block-task')

library_collections_router = routers.DefaultRouter()
library_collections_router.register(r'collections', collection_views.LibraryCollectionsView, basename="library-collections")

# These URLs are only used in Studio. The LMS already provides all the
# API endpoints needed to serve XBlocks from content libraries using the
# standard XBlock REST API (see openedx.core.django_apps.xblock.rest_api.urls)
Expand All @@ -43,6 +47,8 @@
path('team/group/<str:group_name>/', views.LibraryTeamGroupView.as_view()),
# Import blocks into this library.
path('import_blocks/', include(import_blocks_router.urls)),
# Library Collections
path('', include(library_collections_router.urls)),
])),
path('blocks/<str:usage_key_str>/', include([
# Get metadata about a specific XBlock in this library, or delete the block:
Expand Down

0 comments on commit fcff07b

Please sign in to comment.