Reverting back to admin pages as login and updates in the mobile api

This commit is contained in:
Vivek
2025-12-17 22:05:13 +05:30
parent 48c8abb366
commit 105da4a876
39 changed files with 2147 additions and 452 deletions

0
mobile_api/__init__.py Normal file
View File

3
mobile_api/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
mobile_api/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class MobileApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'mobile_api'

View File

@@ -0,0 +1 @@
from .user_forms import *

View File

View File

@@ -0,0 +1,75 @@
# accounts/forms.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate
User = get_user_model()
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['email', 'phone_number', 'password']
def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Email is already registered.")
return email
def clean_phone_number(self):
phone_number = self.cleaned_data.get('phone_number')
if User.objects.filter(phone_number=phone_number).exists():
raise forms.ValidationError("Phone number is already registered.")
return phone_number
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password'])
if commit:
user.save()
return user
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
print('*' * 100)
print(username, password)
print('*' * 100)
if not username or not password:
raise forms.ValidationError("Username and password are required.")
# Check if username contains '@' (email) or is a regular username
try:
if '@' in username:
print('1 **********************')
# Try to find user by email
user = User.objects.get(email=username)
print(user)
print('2 **********************')
username = user.username
print('3 **********************')
else:
print('4 **********************')# Use username as-is
user = User.objects.get(username=username)
except User.DoesNotExist:
print('5 **********************')
raise forms.ValidationError("Invalid credentials.")
# Authenticate with the resolved username
user = authenticate(username=username, password=password)
if not user:
raise forms.ValidationError("Invalid credentials.")
cleaned_data['user'] = user
return cleaned_data

View File

3
mobile_api/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
mobile_api/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

23
mobile_api/urls.py Normal file
View File

@@ -0,0 +1,23 @@
from django.urls import path
from .views import *
# User URLS
urlpatterns = [
path('user/register/', RegisterView.as_view(), name='json_register'),
path('user/login/', LoginView.as_view(), name='json_login'),
path('user/status/', StatusView.as_view(), name='user_status'),
path('user/logout/', LogoutView.as_view(), name='user_logout'),
]
# Event URLS
urlpatterns += [
path('events/type-list/', EventTypeListAPIView.as_view()),
path('events/pincode-events/', EventListAPI.as_view()),
path('events/event-details/', EventDetailAPI.as_view()),
path('events/event-images/', EventImagesListAPI.as_view()),
path('events/events-by-category/', EventsByCategoryAPI.as_view(), name='api_events_by_category'),
path('events/events-by-month-year/', EventsByMonthYearAPI.as_view(), name='events_by_month_year'),
path('events/events-by-date/', EventsByDateAPI.as_view(), name='events_by_date'),
]

90
mobile_api/utils.py Normal file
View File

@@ -0,0 +1,90 @@
"""
Utility functions for mobile API authentication and validation.
"""
import json
from django.http import JsonResponse
from rest_framework.authtoken.models import Token
from accounts.models import User
def validate_token_and_get_user(request, error_status_code=None):
"""
Validates token and username from request body.
This function handles:
- JSON parsing from request body
- Token and username extraction
- Token validation
- Username verification against token user
Args:
request: Django request object with JSON body containing 'token' and 'username'
error_status_code: Optional HTTP status code for error responses (default: None)
Returns:
tuple: On success, returns (user, token, data, None)
tuple: On error, returns (None, None, None, JsonResponse)
Error Responses:
- Invalid JSON: {"status": "error", "message": "Invalid JSON"} (400 if status_code provided)
- Missing credentials: {"status": "error", "message": "token and username required"} (400 if status_code provided)
- Invalid token: {"status": "invalid_token"} (401 if status_code provided)
- Username mismatch: {"status": "error", "message": "token does not match user"} (401 if status_code provided)
"""
try:
# Parse JSON from request body
data = json.loads(request.body)
except json.JSONDecodeError:
status = 400 if error_status_code else None
return (None, None, None, JsonResponse(
{"status": "error", "message": "Invalid JSON"},
status=status
))
# Extract token and username
token_key = data.get("token")
username = data.get("username")
# Validate both are present
if not token_key or not username:
status = 400 if error_status_code else None
return (None, None, None, JsonResponse(
{"status": "error", "message": "token and username required"},
status=status
))
try:
# Get token object
token = Token.objects.get(key=token_key)
if username:
if '@' in username:
user = User.objects.get(email=username)
else:
user = User.objects.get(username=username)
if not user:
status = 401 if error_status_code else None
return (None, None, None, JsonResponse(
{"status": "error", "message": "user not found"},
status=status
))
# Verify username matches token user
# if user.username != username:
# status = 401 if error_status_code else None
# return (None, None, None, JsonResponse(
# {"status": "error", "message": "token does not match user"},
# status=status
# ))
# Success - return user, token, data, and None for error_response
return (user, token, data, None)
except Token.DoesNotExist:
status = 401 if error_status_code else None
return (None, None, None, JsonResponse(
{"status": "invalid_token"},
status=status
))

View File

@@ -0,0 +1,2 @@
from .user import *
from .events import *

361
mobile_api/views/events.py Normal file
View File

@@ -0,0 +1,361 @@
from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from events.models import Event, EventImages
from master_data.models import EventType
from django.forms.models import model_to_dict
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.db.models import Q
from datetime import datetime, timedelta
import calendar
from mobile_api.utils import validate_token_and_get_user
@method_decorator(csrf_exempt, name='dispatch')
class EventTypeListAPIView(APIView):
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
# Fetch event types manually without serializer
event_types_queryset = EventType.objects.all()
event_types = []
for event_type in event_types_queryset:
event_type_data = {
"id": event_type.id,
"event_type": event_type.event_type,
"event_type_icon": request.build_absolute_uri(event_type.event_type_icon.url) if event_type.event_type_icon else None
}
event_types.append(event_type_data)
print(event_types)
return JsonResponse({
"status": "success",
"event_types": event_types
})
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
class EventListAPI(APIView):
def post(self, request):
try:
print('*' * 100)
print(request.body)
print('*' * 100)
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
pincode = data.get("pincode")
print('*' * 100)
print(pincode)
print('*' * 100)
# pincode is optional - if not provided or 'all', return all events
if not pincode or pincode == 'all':
events = Event.objects.all().order_by('-created_date')
else:
events = Event.objects.filter(pincode=pincode).order_by('-created_date')
event_list = []
for e in events:
data_dict = model_to_dict(e)
try:
thumb_img = EventImages.objects.get(event=e.id, is_primary=True)
data_dict['thumb_img'] = request.build_absolute_uri(thumb_img.event_image.url)
except EventImages.DoesNotExist:
data_dict['thumb_img'] = ''
event_list.append(data_dict)
print('*' * 100)
print(event_list)
print('*' * 100)
return JsonResponse({
"status": "success",
"events": event_list
})
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
class EventDetailAPI(APIView):
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
event_id = data.get("event_id")
events = Event.objects.get(id=event_id)
event_images = EventImages.objects.filter(event=event_id)
event_data = model_to_dict(events)
event_data["status"] = "success"
event_images_list = []
for ei in event_images:
event_img = {}
event_img['is_primary'] = ei.is_primary
event_img['image'] = request.build_absolute_uri(ei.event_image.url)
event_images_list.append(event_img)
event_data["images"] = event_images_list
print(event_data)
return JsonResponse(event_data)
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
class EventImagesListAPI(APIView):
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
event_id = data.get("event_id")
event_images = EventImages.objects.filter(event=event_id)
res_data = {}
res_data["status"] = "success"
event_images_list = []
for ei in event_images:
event_images_list.append(request.build_absolute_uri(ei.event_image.url))
res_data["images"] = event_images_list
print(res_data)
return JsonResponse(res_data)
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
@method_decorator(csrf_exempt, name='dispatch')
class EventsByCategoryAPI(APIView):
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
category_id = data.get("category_id")
if not category_id:
return JsonResponse(
{"status": "error", "message": "category_id is required"}
)
events = Event.objects.filter(event_type=category_id)
events_dict = [model_to_dict(obj) for obj in events]
for event in events_dict:
try:
event['event_image'] = request.build_absolute_uri(
EventImages.objects.get(event=event['id'], is_primary=True).event_image.url
)
except EventImages.DoesNotExist:
event['event_image'] = ''
# event['start_date'] = convert_date_to_dd_mm_yyyy(event['start_date'])
print(events_dict)
return JsonResponse({
"status": "success",
"events": events_dict
})
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
@method_decorator(csrf_exempt, name='dispatch')
class EventsByMonthYearAPI(APIView):
"""
API to get events by month and year.
Returns dates that have events, total count, and date-wise breakdown.
"""
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
month_name = data.get("month") # e.g., "August", "august", "Aug"
year = data.get("year") # e.g., 2025
if not month_name or not year:
return JsonResponse(
{"status": "error", "message": "month and year are required"}
)
# Convert month name to month number
month_name_lower = month_name.lower().capitalize()
month_abbr = month_name_lower[:3]
# Try full month name first, then abbreviation
month_number = None
for i in range(1, 13):
if calendar.month_name[i].lower() == month_name_lower or calendar.month_abbr[i].lower() == month_abbr.lower():
month_number = i
break
if not month_number:
return JsonResponse(
{"status": "error", "message": f"Invalid month name: {month_name}"}
)
# Convert year to integer
try:
year = int(year)
except (ValueError, TypeError):
return JsonResponse(
{"status": "error", "message": "Invalid year format"}
)
# Filter events where start_date or end_date falls in the given month/year
# An event is included if any part of it (start_date to end_date) overlaps with the month
events = Event.objects.filter(
Q(start_date__year=year, start_date__month=month_number) |
Q(end_date__year=year, end_date__month=month_number) |
Q(start_date__lte=datetime(year, month_number, 1).date(),
end_date__gte=datetime(year, month_number, calendar.monthrange(year, month_number)[1]).date())
).distinct()
# Group events by date
date_events_dict = {}
all_dates = set()
# Calculate month boundaries
month_start = datetime(year, month_number, 1).date()
month_end = datetime(year, month_number, calendar.monthrange(year, month_number)[1]).date()
for event in events:
# Get all dates between start_date and end_date that fall in the target month
current_date = max(event.start_date, month_start)
end_date = min(event.end_date, month_end)
# Iterate through each date in the event's date range that falls in the target month
while current_date <= end_date:
if current_date.year == year and current_date.month == month_number:
date_str = current_date.strftime('%Y-%m-%d')
all_dates.add(date_str)
if date_str not in date_events_dict:
date_events_dict[date_str] = 0
date_events_dict[date_str] += 1
# Move to next day
current_date += timedelta(days=1)
# Sort dates
sorted_dates = sorted(all_dates)
# Build date_events list
date_events = [
{
"date_of_event": date_str,
"events_of_date": date_events_dict[date_str]
}
for date_str in sorted_dates
]
# Calculate total number of events (unique events, not date occurrences)
total_events = events.count()
print(sorted_dates)
print(total_events)
print(date_events)
return JsonResponse({
"status": "success",
"dates": sorted_dates,
"total_number_of_events": total_events,
"date_events": date_events
})
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
@method_decorator(csrf_exempt, name='dispatch')
class EventsByDateAPI(APIView):
"""
API to get events occurring on a specific date.
Returns complete event information with primary images.
"""
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request)
if error_response:
return error_response
date_of_event = data.get("date_of_event")
if not date_of_event:
return JsonResponse(
{"status": "error", "message": "date_of_event is required"}
)
# Parse date_of_event in YYYY-MM-DD format
try:
event_date = datetime.strptime(date_of_event, "%Y-%m-%d").date()
except ValueError:
return JsonResponse(
{"status": "error", "message": "Invalid date format. Expected YYYY-MM-DD"}
)
# Filter events where the provided date falls between start_date and end_date (inclusive)
events = Event.objects.filter(
start_date__lte=event_date,
end_date__gte=event_date
).order_by('start_date', 'start_time')
event_list = []
for e in events:
data_dict = model_to_dict(e)
try:
thumb_img = EventImages.objects.get(event=e.id, is_primary=True)
data_dict['thumb_img'] = request.build_absolute_uri(thumb_img.event_image.url)
except EventImages.DoesNotExist:
data_dict['thumb_img'] = ''
event_list.append(data_dict)
return JsonResponse({
"status": "success",
"events": event_list
})
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)

107
mobile_api/views/user.py Normal file
View File

@@ -0,0 +1,107 @@
# accounts/views.py
import json
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from rest_framework.authtoken.models import Token
from mobile_api.forms import RegisterForm, LoginForm
from rest_framework.authentication import TokenAuthentication
from django.contrib.auth import logout
from mobile_api.utils import validate_token_and_get_user
from utils.errors_json_convertor import simplify_form_errors
@method_decorator(csrf_exempt, name='dispatch')
class RegisterView(View):
def post(self, request):
try:
data = json.loads(request.body)
form = RegisterForm(data)
if form.is_valid():
user = form.save()
token, _ = Token.objects.get_or_create(user=user)
return JsonResponse({'message': 'User registered successfully', 'token': token.key}, status=201)
return JsonResponse({'errors': form.errors}, status=400)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
@method_decorator(csrf_exempt, name='dispatch')
class LoginView(View):
def post(self, request):
print('0')
try:
data = json.loads(request.body)
form = LoginForm(data)
print('1')
if form.is_valid():
print('2')
user = form.cleaned_data['user']
token, _ = Token.objects.get_or_create(user=user)
print('3')
response = {
'message': 'Login successful',
'token': token.key,
'username': user.username,
'email': user.email,
'phone_number': user.phone_number,
'first_name': user.first_name,
'last_name': user.last_name,
'role': user.role,
'pincode': user.pincode,
'district': user.district,
'state': user.state,
'country': user.country,
'place': user.place,
'latitude': user.latitude,
'longitude': user.longitude,
}
print('4')
print(response)
return JsonResponse(response, status=200)
return JsonResponse(simplify_form_errors(form), status=401)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
@method_decorator(csrf_exempt, name='dispatch')
class StatusView(View):
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
if error_response:
return error_response
return JsonResponse({
"status": "logged_in",
"username": user.username,
"email": user.email
})
except Exception as e:
return JsonResponse({"status": "error", "message": str(e)}, status=500)
@method_decorator(csrf_exempt, name='dispatch')
class LogoutView(View):
def post(self, request):
try:
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
if error_response:
return error_response
# 🔍 Call Django's built-in logout
logout(request)
# 🗑 Delete the token to invalidate future access
token.delete()
return JsonResponse({
"status": "logged_out",
"message": "Logout successful"
})
except Exception as e:
return JsonResponse({"status": "error", "message": str(e)}, status=500)