Spaces:
Sleeping
Sleeping
| 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 | |
| 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) | |
| 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) | |
| class RoleSuggestionsView(APIView): | |
| permission_classes = [permissions.IsAuthenticated] | |
| def get(self, request): | |
| return Response(suggest_roles(request.user), status=status.HTTP_200_OK) | |