gapguide-api / apps /progress /serializers.py
arifRB's picture
Deploy GapGuide backend (Docker)
ffd36e0 verified
Raw
History Blame Contribute Delete
2.04 kB
from rest_framework import serializers
from .models import UserCheckpointProgress, UserProgress
class UserProgressSerializer(serializers.ModelSerializer):
resource_title = serializers.CharField(source='resource.title', read_only=True)
has_checkpoints = serializers.SerializerMethodField()
in_current_plan = serializers.SerializerMethodField()
class Meta:
model = UserProgress
fields = ['id', 'resource', 'resource_title', 'status', 'progress',
'started_at', 'completed_at', 'has_checkpoints',
'in_current_plan']
read_only_fields = ['status', 'started_at', 'completed_at',
'resource_title', 'has_checkpoints',
'in_current_plan']
def get_has_checkpoints(self, obj) -> bool:
# Prefer the annotated value from the list view's queryset; fall back
# to a direct count on single-object endpoints.
annotated = getattr(obj, 'resource_has_checkpoints', None)
if annotated is not None:
return annotated
# ResourceProgressView (detail) sets no prefetch_related, so this is
# one extra query on a single object — not an N+1, fine off the list.
return len(obj.resource.checkpoints.all()) > 0
def get_in_current_plan(self, obj) -> bool:
# Annotated by UserProgressListView.get_queryset. Single-object
# endpoints (ResourceProgressView) don't annotate it; default False —
# the frontend only consumes this flag on the list to disable
# out-of-plan rows, so a conservative False off the detail path is safe.
annotated = getattr(obj, 'in_current_plan_annot', None)
return bool(annotated) if annotated is not None else False
class UserCheckpointProgressSerializer(serializers.ModelSerializer):
class Meta:
model = UserCheckpointProgress
fields = ['id', 'checkpoint', 'completed_at']
read_only_fields = ['completed_at']