import random from rest_framework import status from rest_framework.generics import CreateAPIView, GenericAPIView, ListAPIView from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers from rest_framework.permissions import AllowAny from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi from rest_framework.authtoken.models import Token from apps.account.models import User from apps.api.models import Comment, AppVersion from apps.api.serializers import ( AppVersionSerializer, CommentSerializer, SupportMessageCreateSerializer, ) from utils.pagination import StandardResultsSetPagination class HomeSerializer(serializers.Serializer): token = serializers.CharField() import json from django.conf import settings COUNTRIES_FILE_PATH = settings.BASE_DIR / 'utils' / 'country_city_list' / 'optimized_countries.json' CITIES_FILE_PATH = settings.BASE_DIR / 'utils' / 'country_city_list' / 'optimized_cities.json' def get_localized_name(translations, target_lang='en'): if not translations: return "" for t in translations: if t.get('language_code') == target_lang: return t.get('title') for t in translations: if t.get('language_code') == 'en': return t.get('title') return translations[0].get('title', '') def get_localized_cities(cities_data, country_code, target_lang='en'): country_cities = cities_data.get(country_code.upper()) if not country_cities: return [] for item in country_cities: if item.get('language_code') == target_lang: return item.get('title', []) for item in country_cities: if item.get('language_code') == 'en': return item.get('title', []) return country_cities[0].get('title', []) # test class generate token class HomeView(GenericAPIView): serializer_class = HomeSerializer @swagger_auto_schema( operation_description="Health check and token test endpoint. Optionally reads BUILD_NUMBER from headers.", manual_parameters=[ openapi.Parameter( name='BUILD_NUMBER', in_=openapi.IN_HEADER, description='Client build number', type=openapi.TYPE_STRING, required=False ) ], responses={ 200: openapi.Response( description="OK", schema=HomeSerializer() ) } ) def get(self, request): # Get build_number from headers build_number = request.META.get('HTTP_BUILD_NUMBER') # Print the build_number print(f"Build Number: {build_number}") return Response({'token': "ok", 'build_number': build_number}) class CountryView(APIView): permission_classes = [AllowAny] @swagger_auto_schema( operation_description="List of optimized countries with codes and names. Supports locale translation based on request header (Accept-Language).", responses={ 200: openapi.Response( description="List of countries sorted alphabetically by name", schema=openapi.Schema( type=openapi.TYPE_ARRAY, items=openapi.Schema( type=openapi.TYPE_OBJECT, properties={ 'code': openapi.Schema(type=openapi.TYPE_STRING, description="ISO 3166-1 alpha-2 country code", example="IR"), 'name': openapi.Schema(type=openapi.TYPE_STRING, description="Localized country name", example="Iran (Islamic Republic of)") } ) ), examples={ "application/json": [ {"code": "CN", "name": "China"}, {"code": "IR", "name": "Iran (Islamic Republic of)"}, {"code": "RU", "name": "Russian Federation (the)"}, {"code": "TR", "name": "Türkiye"} ] } ) } ) def get(self, request): try: with open(COUNTRIES_FILE_PATH, 'r', encoding='utf-8') as f: countries_data = json.load(f) except Exception as e: return Response({"error": "Failed to load countries data"}, status=500) lang = getattr(request, 'LANGUAGE_CODE', 'en') result = [] for code, translations in countries_data.items(): name = get_localized_name(translations, lang) result.append({ "code": code, "name": name }) # Sort countries alphabetically by name result.sort(key=lambda x: x["name"]) return Response(result, status=200) class CityListView(APIView): permission_classes = [AllowAny] @swagger_auto_schema( operation_description="List of cities filtered by country code. Supports locale translation based on request header (Accept-Language).", manual_parameters=[ openapi.Parameter( name='country_code', in_=openapi.IN_QUERY, description='Two-letter country code (e.g. IR, CN, TR)', type=openapi.TYPE_STRING, required=True, example="IR" ) ], responses={ 200: openapi.Response( description="List of city names sorted alphabetically", schema=openapi.Schema( type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING, description="City name", example="Abadan") ), examples={ "application/json": [ "Abadan", "Abadeh", "Abhar", "Ahvaz", "Tehran" ] } ), 400: openapi.Response( description="Bad Request - country_code missing or invalid", schema=openapi.Schema( type=openapi.TYPE_OBJECT, properties={ 'error': openapi.Schema(type=openapi.TYPE_STRING, description="Error message description", example="country_code query parameter is required") } ), examples={ "application/json": { "error": "country_code query parameter is required" } } ) } ) def get(self, request): country_code = request.query_params.get('country_code') if not country_code: return Response({"error": "country_code query parameter is required"}, status=400) try: with open(CITIES_FILE_PATH, 'r', encoding='utf-8') as f: cities_data = json.load(f) except Exception as e: return Response({"error": "Failed to load cities data"}, status=500) lang = getattr(request, 'LANGUAGE_CODE', 'en') cities = get_localized_cities(cities_data, country_code, lang) if not cities and country_code.upper() not in cities_data: return Response({"error": f"No cities found or invalid country code: {country_code}"}, status=400) # Sort cities alphabetically sorted_cities = sorted(cities) return Response(sorted_cities, status=200) class CommentListAPIView(ListAPIView): """ API view to list comments ordered by order field and creation date """ queryset = Comment.objects.all() serializer_class = CommentSerializer permission_classes = [AllowAny] ordering = ['order', '-created_at'] # Order by order field first, then by newest pagination_class = StandardResultsSetPagination @swagger_auto_schema( operation_description="List comments ordered by 'order' then '-created_at'", responses={ 200: openapi.Response( description="List of comments", schema=CommentSerializer(many=True) ) } ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) def get_queryset(self): queryset = super().get_queryset() return queryset.order_by('order', '-created_at') class AppVersionListAPIView(ListAPIView): queryset = AppVersion.objects.all().order_by('-created_at') serializer_class = AppVersionSerializer permission_classes = [AllowAny] pagination_class = StandardResultsSetPagination @swagger_auto_schema( operation_description="List all app versions with fields.", responses={ 200: openapi.Response( description="List of app versions", schema=AppVersionSerializer(many=True) ) } ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) class SupportMessageCreateAPIView(CreateAPIView): serializer_class = SupportMessageCreateSerializer permission_classes = [AllowAny] @swagger_auto_schema( tags=["Support"], operation_summary="Create support message", operation_description=( "Create a new support message from the website or mobile contact form.\n\n" "This endpoint is public and does not require authentication.\n\n" "Required fields:\n" "- `sender_name`: full name of the sender\n" "- `sender_email`: valid email address\n" "- `subject`: message subject\n" "- `message`: message body\n\n" "Optional fields:\n" "- `sender_phone`: sender phone number in international format\n\n" "Phone number validation follows the same rules used in the account module." ), request_body=SupportMessageCreateSerializer, responses={ status.HTTP_201_CREATED: openapi.Response( description="Support message created successfully.", schema=SupportMessageCreateSerializer, examples={ "application/json": { "id": 12, "sender_name": "Ali Ahmadi", "sender_email": "ali.ahmadi@example.com", "subject": "Problem accessing my purchased course", "sender_phone": "+989121234567", "message": "Hello, I purchased a course today but the lessons are not available in my account. Please check my access.", "created_at": "2026-06-17T09:30:12.421000Z", } }, ), status.HTTP_400_BAD_REQUEST: openapi.Response( description="Validation error.", examples={ "application/json": { "sender_email": ["Enter a valid email address."], "sender_phone": ["The phone number entered is not valid."], } }, ), }, ) def post(self, request, *args, **kwargs): return super().post(request, *args, **kwargs)