Commit 32ad0e53 authored by Cornee Traas's avatar Cornee Traas
Browse files

Rewrite API to allow for different platforms.

Use /p/platform_id/ to access the analytics server with the desired platform_id.
parent e9812a9a
Pipeline #2373 passed with stage
in 52 seconds
# Generated by Django 2.1.3 on 2018-12-05 13:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('courses', '0003_registeredaction'),
]
operations = [
migrations.AddField(
model_name='course',
name='course_platform',
field=models.CharField(default='default', max_length=30),
preserve_default=False,
),
migrations.AlterField(
model_name='registeredaction',
name='description',
field=models.TextField(blank=True),
),
]
......@@ -3,6 +3,7 @@ from django.db import models
class Course(models.Model):
course_id = models.CharField(unique=True, max_length=30)
course_platform = models.CharField(max_length=30)
course_slug = models.CharField(max_length=100)
course_name = models.CharField(max_length=255)
......
......@@ -6,12 +6,24 @@ from courses.views import CourseViewSet, RegisteredActionViewSet
app_name = "courses-api"
router = DefaultRouter()
# TODO: This is kept for backwards compatibility, remove if not needed anymore.
router.register("courses", CourseViewSet)
router.register("actions", RegisteredActionViewSet)
urlpatterns = router.urls + [
path(
"actions/<slug:platform>/<slug:course_id>/",
RegisteredActionViewSet.as_view({"get": "list"}),
name="registeredaction-list",
)
),
path(
"p/<slug:platform>/courses/",
CourseViewSet.as_view({"get": "list"}),
name="course-list",
),
path(
"p/<slug:platform>/courses/<int:pk>/",
CourseViewSet.as_view({"get": "retrieve"}),
name="course-detail",
),
]
......@@ -6,7 +6,7 @@ from courses.models import Course, RegisteredAction
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = ["pk", "course_id", "course_slug", "course_name"]
fields = ["pk", "course_id", "course_platform", "course_slug", "course_name"]
extra_kwargs = {"course_id": {"validators": []}}
......
......@@ -10,6 +10,13 @@ class CourseViewSet(ModelViewSet):
serializer_class = CourseSerializer
permission_classes = [IsAdminUser]
def get_queryset(self):
queryset = super().get_queryset()
if "platform" in self.kwargs:
return queryset.filter(course_platform=self.kwargs["platform"])
else:
return queryset.filter(course_platform="default")
class RegisteredActionViewSet(ModelViewSet):
queryset = RegisteredAction.objects.order_by("date")
......@@ -18,6 +25,10 @@ class RegisteredActionViewSet(ModelViewSet):
def get_queryset(self):
queryset = super().get_queryset().filter(course__user=self.request.user)
if "platform" in self.kwargs:
queryset = queryset.filter(course__course_platform=self.kwargs["platform"])
else:
queryset = queryset.filter(course__course_platform="default")
if "course_id" in self.kwargs:
queryset = queryset.filter(course_id=self.kwargs["course_id"])
return queryset
......@@ -40,8 +40,9 @@ except (IOError, ValueError) as e:
logger.error("platforms.json not found or invalid! Reverting to fallback.")
PLATFORMS = [
{
"id": "coursera",
"name": "Coursera Fallback (platforms.json not found)",
"id": "default",
"name": "Default platform (platforms.json not found)",
"type": "coursera",
"url": "http://localhost:8001/api/",
}
]
......
......@@ -29,7 +29,7 @@ urlpatterns = [
path("o/", include(users.urls)),
path("o/", include((oauth2_provider.urls.base_urlpatterns, "oauth2_provider"))),
path("p/", PlatformListView, name="platform-list-view"),
path("p/<platform>/<path>/", ApiProxyView),
path("", include(users.routers)),
path("", include(courses.routers)),
path("p/<slug:platform>/<path>/", ApiProxyView),
]
......@@ -20,4 +20,5 @@ def ApiProxyView(request, platform, path): # pragma: no cover
@api_view(["GET"])
@permission_classes((IsAuthenticated,))
def PlatformListView(request):
return JsonResponse(PLATFORMS, safe=False)
fields = ["id", "name", "type"]
return JsonResponse([{y: x[y] for y in fields} for x in PLATFORMS], safe=False)
[{
"id": "coursera",
"name": "Coursera Local Test Platform",
"id": "default",
"name": "Default platform",
"type": "coursera",
"url": "http://localhost:8001/api/"
}]
......@@ -64,6 +64,7 @@ def teacher(_teacher, coursera_course_id):
course_id=coursera_course_id,
defaults={
"course_slug": "design-thinking-entrepreneurship",
"course_platform": "default",
"course_name": "Innovation & Entrepreneurship - From Design Thinking to Funding",
},
)
......
......@@ -10,6 +10,7 @@ class CourseFactory(DjangoModelFactory):
model = "courses.Course"
course_id = factory.LazyFunction(get_random_string)
course_platform = "default"
course_slug = "capstone-recommender-systems"
course_name = "Capstone Recommender Systems"
......
......@@ -12,6 +12,7 @@ def test_can_create_course():
"""
course = Course.objects.create(
course_id="bmHtyVrIEee3CwoIJ_9DVg",
course_platform="default",
course_slug="capstone-recommender-systems",
course_name="Capstone Recommender Systems",
)
......
......@@ -11,6 +11,7 @@ def test_can_serialize_course(course):
assert CourseSerializer(course).data == {
"pk": course.pk,
"course_id": course.course_id,
"course_platform": "default",
"course_slug": "capstone-recommender-systems",
"course_name": "Capstone Recommender Systems",
}
......
......@@ -8,16 +8,49 @@ def test_can_view_course(admin_api_client, course):
Test that an admin can see the course details.
"""
response = admin_api_client.get(
reverse("courses-api:course-detail", kwargs={"pk": course.pk})
reverse(
"courses-api:course-detail", kwargs={"platform": "default", "pk": course.pk}
)
)
assert response.status_code == 200, str(response.content)
assert response.data == {
"pk": course.pk,
"course_id": course.course_id,
"course_platform": "default",
"course_slug": "capstone-recommender-systems",
"course_name": "Capstone Recommender Systems",
}, "view did not return correct data"
response = admin_api_client.get(
reverse("courses-api:course-detail", kwargs={"pk": course.pk})
)
assert response.status_code == 200, str(response.content)
response = admin_api_client.get(
reverse(
"courses-api:course-detail",
kwargs={"platform": "INVALID_PLATFORM", "pk": course.pk},
)
)
assert response.status_code == 404, str(response.content)
@pytest.mark.django_db
def test_can_view_course_list(admin_api_client, course):
"""
Test that an admin can see the course details.
"""
response = admin_api_client.get(
reverse("courses-api:course-list", kwargs={"platform": "default"})
)
assert response.status_code == 200, str(response.content)
assert len(response.data) == 1, str(response.content)
response = admin_api_client.get(
reverse("courses-api:course-list", kwargs={"platform": "INVALID_PLATFORM"})
)
assert len(response.data) == 0, str(response.content)
@pytest.mark.django_db
def test_can_view_registered_action(teacher, teacher_api_client, registered_action):
......@@ -47,11 +80,22 @@ def test_can_filter_action_list(teacher, teacher_api_client, registered_action):
response = teacher_api_client.get(
reverse(
"courses-api:registeredaction-list",
kwargs={"platform": "coursera", "course_id": registered_action.course_id},
kwargs={"platform": "default", "course_id": registered_action.course_id},
)
)
assert response.status_code == 200, str(response.content)
response = teacher_api_client.get(
reverse(
"courses-api:registeredaction-list",
kwargs={
"platform": "INVALID_PLATFORM",
"course_id": registered_action.course_id,
},
)
)
assert len(response.data) == 0, str(response.content)
@pytest.mark.django_db
def test_can_create_registered_action(teacher_api_client, teacher, course):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment