Spaces:
Runtime error
Runtime error
File size: 3,977 Bytes
ffd36e0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView
from apps.roles.models import Role, UserTargetRole
from .services import (
DEFAULT_LIMIT_PER_SKILL,
MAX_LIMIT_PER_SKILL,
compute_gap_report,
compute_recommendations,
suggest_roles,
)
_ROLE_ID_PARAM = OpenApiParameter(
name='role_id',
type=int,
location=OpenApiParameter.QUERY,
required=False,
description='Role to analyze. Defaults to the user\'s active target role.',
)
def _resolve_role(request):
"""Return (role, error_response). role_id query param overrides active target."""
role_id = (
request.query_params.get('role_id')
or request.query_params.get('role')
)
if role_id:
try:
role = Role.objects.get(id=role_id, is_active=True)
except (Role.DoesNotExist, ValueError):
return None, Response(
{'detail': 'Role not found or inactive.'},
status=status.HTTP_404_NOT_FOUND,
)
return role, None
try:
target = UserTargetRole.objects.select_related('role').get(
user=request.user, is_active=True
)
except UserTargetRole.DoesNotExist:
return None, Response(
{'detail': 'No active target role. Select a target role first.'},
status=status.HTTP_400_BAD_REQUEST,
)
return target.role, None
@extend_schema(
parameters=[_ROLE_ID_PARAM],
responses=OpenApiTypes.OBJECT,
description='Weighted competency-fulfillment gap report for the user against a role.',
)
class GapAnalysisView(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
role, err = _resolve_role(request)
if err is not None:
return err
report = compute_gap_report(request.user, role)
return Response(report.to_dict(), status=status.HTTP_200_OK)
@extend_schema(
parameters=[
_ROLE_ID_PARAM,
OpenApiParameter(
name='limit',
type=int,
location=OpenApiParameter.QUERY,
required=False,
description=f'Resources returned per skill gap (default {DEFAULT_LIMIT_PER_SKILL}).',
),
],
responses=OpenApiTypes.OBJECT,
description='Ranked learning-resource recommendations per skill gap.',
)
class RecommendationsView(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
role, err = _resolve_role(request)
if err is not None:
return err
limit_raw = request.query_params.get('limit')
limit = DEFAULT_LIMIT_PER_SKILL
if limit_raw is not None:
try:
limit = int(limit_raw)
except ValueError:
return Response(
{'detail': 'limit must be a positive integer.'},
status=status.HTTP_400_BAD_REQUEST,
)
if limit <= 0:
return Response(
{'detail': 'limit must be a positive integer.'},
status=status.HTTP_400_BAD_REQUEST,
)
limit = min(limit, MAX_LIMIT_PER_SKILL)
payload = compute_recommendations(request.user, role, limit_per_skill=limit)
return Response(payload, status=status.HTTP_200_OK)
@extend_schema(
responses=OpenApiTypes.OBJECT,
description=(
'Active roles ranked by how well the user\'s current skills fit them. '
'Powers the "Recommended for you" section on the role-selection page.'
),
)
class RoleSuggestionsView(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
return Response(suggest_roles(request.user), status=status.HTTP_200_OK)
|