Files

181 lines
5.9 KiB
Python

from django.db.models import Count
from django.shortcuts import get_object_or_404
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import ContentInteraction, ContentItem, VideoProgress
from .services import complete_video
ABILITY_META = {
ContentItem.Ability.VISION: {"label": "数学眼光", "icon": "◉", "color": "#5d73e8"},
ContentItem.Ability.HUMANITIES: {"label": "数学人文", "icon": "▤", "color": "#b76d38"},
ContentItem.Ability.DETECTION: {"label": "数学侦探", "icon": "⌕", "color": "#287f78"},
ContentItem.Ability.MODELING: {"label": "数学建模", "icon": "⌁", "color": "#d98628"},
ContentItem.Ability.CONNECTION: {"label": "数学联结", "icon": "⛓", "color": "#8667b5"},
}
def serialize_item(item, progress=None):
return {
"slug": item.slug,
"title": item.title,
"kind": item.kind,
"summary": item.summary,
"body": item.body,
"cover_url": item.cover_url,
"media_url": item.media_url,
"topics": item.topics,
"source": item.source,
"ability_dimension": item.ability_dimension,
"ability": ABILITY_META.get(item.ability_dimension),
"module": item.module,
"sub_category": item.sub_category,
"discipline": item.discipline,
"discipline_icon": item.discipline_icon,
"author": item.author,
"duration_seconds": item.duration_seconds,
"view_count": item.view_count,
"comment_count": item.comment_count,
"published_at": item.published_at,
"progress": (
{
"position_seconds": progress.position_seconds,
"completed": progress.completed,
"reward_granted": progress.reward_granted,
}
if progress
else None
),
}
class ContentListView(APIView):
permission_classes = [permissions.AllowAny]
def get(self, request):
queryset = ContentItem.objects.filter(is_published=True)
kind = request.query_params.get("kind")
ability = request.query_params.get("ability")
discipline = request.query_params.get("discipline")
if kind:
queryset = queryset.filter(kind=kind)
if ability:
queryset = queryset.filter(ability_dimension=ability)
if discipline:
queryset = queryset.filter(discipline=discipline)
progress_by_content = {}
if request.user.is_authenticated:
progress_by_content = {
item.content_id: item
for item in VideoProgress.objects.filter(
user=request.user,
content__in=queryset,
)
}
return Response(
[
serialize_item(item, progress_by_content.get(item.id))
for item in queryset[:100]
]
)
class VideoCatalogView(APIView):
permission_classes = [permissions.AllowAny]
def get(self, request):
videos = ContentItem.objects.filter(
kind=ContentItem.Kind.VIDEO,
is_published=True,
)
counts = {
row["ability_dimension"]: row["count"]
for row in videos.values("ability_dimension").annotate(count=Count("id"))
}
abilities = [
{
"id": ability,
**meta,
"count": counts.get(ability, 0),
}
for ability, meta in ABILITY_META.items()
]
disciplines = [
{
"name": row["discipline"],
"icon": row["discipline_icon"],
"count": row["count"],
}
for row in videos.values("discipline", "discipline_icon")
.annotate(count=Count("id"))
.order_by("discipline")
]
return Response(
{
"total": videos.count(),
"abilities": abilities,
"disciplines": disciplines,
}
)
class ContentDetailView(APIView):
permission_classes = [permissions.AllowAny]
def get(self, request, slug):
item = get_object_or_404(ContentItem, slug=slug, is_published=True)
progress = None
if request.user.is_authenticated:
ContentInteraction.objects.get_or_create(
user=request.user,
content=item,
action=ContentInteraction.Action.VIEW,
)
progress, _ = VideoProgress.objects.get_or_create(
user=request.user,
content=item,
)
return Response(serialize_item(item, progress))
class VideoCompleteView(APIView):
def post(self, request, slug):
item = get_object_or_404(
ContentItem,
slug=slug,
kind=ContentItem.Kind.VIDEO,
is_published=True,
)
progress, reward = complete_video(request.user, item)
return Response(
{
"completed": progress.completed,
"reward_granted": progress.reward_granted,
"reward": reward,
}
)
class FavoriteView(APIView):
def post(self, request, slug):
item = get_object_or_404(ContentItem, slug=slug, is_published=True)
_, created = ContentInteraction.objects.get_or_create(
user=request.user,
content=item,
action=ContentInteraction.Action.FAVORITE,
)
return Response(
{"favorite": True},
status=status.HTTP_201_CREATED if created else status.HTTP_200_OK,
)
def delete(self, request, slug):
ContentInteraction.objects.filter(
user=request.user,
content__slug=slug,
action=ContentInteraction.Action.FAVORITE,
).delete()
return Response(status=status.HTTP_204_NO_CONTENT)