Updates for the api and bug fixes

This commit is contained in:
Vivek
2025-12-19 19:35:38 +05:30
parent 105da4a876
commit d109df3973
10 changed files with 302 additions and 37 deletions

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-12-19 13:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0005_alter_user_role'),
]
operations = [
migrations.AddField(
model_name='user',
name='profile_picture',
field=models.ImageField(blank=True, null=True, upload_to='profile_pictures/'),
),
]

View File

@@ -29,6 +29,8 @@ class User(AbstractUser):
latitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True)
longitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True)
profile_picture = models.ImageField(upload_to='profile_pictures/', blank=True, null=True, default='default.png')
objects = UserManager()
def __str__(self):

BIN
db_1.sqlite3 Normal file

Binary file not shown.

View File

@@ -71,24 +71,24 @@ TEMPLATES = [
WSGI_APPLICATION = 'eventify.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql',
# 'NAME': 'eventify_uat_db', # your DB name
# 'USER': 'eventify_uat', # your DB user
# 'PASSWORD': 'eventifyplus@!@#$', # your DB password
# 'HOST': '0.0.0.0', # or IP/domain
# 'PORT': '5440', # default PostgreSQL port
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'eventify_uat_db', # your DB name
'USER': 'eventify_uat', # your DB user
'PASSWORD': 'eventifyplus@!@#$', # your DB password
'HOST': '0.0.0.0', # or IP/domain
'PORT': '5440', # default PostgreSQL port
}
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},

View File

@@ -6,6 +6,7 @@ from django.contrib.auth import views as auth_views
# from accounts.customer_views import login_view, logout_view, customer_dashboard, customer_calendar
# from accounts.customer_views import customer_profile
from accounts import views
from mobile_api.views.user import WebRegisterView
from django.conf.urls.static import static
from django.conf import settings
@@ -18,8 +19,8 @@ urlpatterns = [
# path('calendar/', customer_calendar, name='customer_calendar'),
# path('profile/', customer_profile, name='customer_profile'),
path('', views.login_view, name='login'),
path('register/', WebRegisterView.as_view(), name='register'),
path('logout/', views.logout_view, name='logout'),
path('dashboard/', views.dashboard, name='dashboard'),
path('users/', views.UserListView.as_view(), name='user_list'),

View File

@@ -15,7 +15,8 @@ class RegisterForm(forms.ModelForm):
def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
# Ensure both email and username do not clash, since we set username = email
if User.objects.filter(email=email).exists() or User.objects.filter(username=email).exists():
raise forms.ValidationError("Email is already registered.")
return email
@@ -27,12 +28,57 @@ class RegisterForm(forms.ModelForm):
def save(self, commit=True):
user = super().save(commit=False)
# Set username equal to email to avoid separate username errors
user.username = self.cleaned_data['email']
user.set_password(self.cleaned_data['password'])
if commit:
user.save()
return user
class WebRegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', 'phone_number', 'password', 'confirm_password']
def clean_email(self):
email = self.cleaned_data.get('email')
# Ensure both email and username do not clash, since we set username = email
if User.objects.filter(email=email).exists() or User.objects.filter(username=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 clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirm_password = cleaned_data.get('confirm_password')
if password != confirm_password:
raise forms.ValidationError("Passwords do not match.")
return cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
# Set username equal to email to avoid separate username errors
user.username = self.cleaned_data['email']
user.set_password(self.cleaned_data['password'])
print('*' * 100)
print(user.username)
print('*' * 100)
if commit:
user.save()
return user
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)

View File

@@ -8,6 +8,7 @@ urlpatterns = [
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'),
path('user/update-profile/', UpdateProfileView.as_view(), name='update_profile'),
]
# Event URLS

View File

@@ -12,13 +12,14 @@ 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
- JSON parsing from request body (for application/json requests)
- Form data parsing (for multipart/form-data requests)
- Token and username extraction
- Token validation
- Username verification against token user
Args:
request: Django request object with JSON body containing 'token' and 'username'
request: Django request object with JSON body or form data containing 'token' and 'username'
error_status_code: Optional HTTP status code for error responses (default: None)
Returns:
@@ -31,15 +32,22 @@ def validate_token_and_get_user(request, error_status_code=None):
- 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
))
# Check if it's multipart/form-data
is_multipart = request.content_type and 'multipart/form-data' in request.content_type
if is_multipart:
# For multipart/form-data, get data from POST
data = request.POST.dict()
else:
# For JSON requests, parse from body
try:
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")
@@ -62,6 +70,8 @@ def validate_token_and_get_user(request, error_status_code=None):
user = User.objects.get(email=username)
else:
user = User.objects.get(username=username)
else:
user = None
if not user:
status = 401 if error_status_code else None
@@ -87,4 +97,10 @@ def validate_token_and_get_user(request, error_status_code=None):
{"status": "invalid_token"},
status=status
))
except User.DoesNotExist:
status = 401 if error_status_code else None
return (None, None, None, JsonResponse(
{"status": "error", "message": "user not found"},
status=status
))

View File

@@ -240,12 +240,23 @@ class EventsByMonthYearAPI(APIView):
# 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()
# 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()
events = Event.objects.filter(start_date__year=year, start_date__month=month_number).distinct()
print('*' * 100)
print(f'Total events: {events.count()}')
print('*' * 100)
unique_start_dates = events.values_list('start_date', flat=True).distinct()
date_strings = [d.strftime('%Y-%m-%d') for d in unique_start_dates]
print('*' * 100)
print(f'Unique start dates: {date_strings}')
print('*' * 100)
# Group events by date
date_events_dict = {}
@@ -293,7 +304,7 @@ class EventsByMonthYearAPI(APIView):
return JsonResponse({
"status": "success",
"dates": sorted_dates,
"dates": date_strings,
"total_number_of_events": total_events,
"date_events": date_events
})
@@ -334,9 +345,8 @@ class EventsByDateAPI(APIView):
# 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')
start_date=event_date
).order_by('start_date')
event_list = []

View File

@@ -5,11 +5,12 @@ 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 mobile_api.forms import RegisterForm, LoginForm, WebRegisterForm
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
from accounts.models import User
@method_decorator(csrf_exempt, name='dispatch')
@@ -27,6 +28,38 @@ class RegisterView(View):
return JsonResponse({'error': str(e)}, status=500)
@method_decorator(csrf_exempt, name='dispatch')
class WebRegisterView(View):
def post(self, request):
print('0')
print('*' * 100)
print(request.body)
print('*' * 100)
try:
data = json.loads(request.body)
form = WebRegisterForm(data)
print('1')
print('*' * 100)
print(form.errors)
print('*' * 100)
if form.is_valid():
print('2')
user = form.save()
token, _ = Token.objects.get_or_create(user=user)
print('3')
response = {
'message': 'User registered successfully',
'token': token.key,
'username': user.username,
'email': user.email,
'phone_number': user.phone_number,
}
return JsonResponse(response, 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):
@@ -56,6 +89,7 @@ class LoginView(View):
'place': user.place,
'latitude': user.latitude,
'longitude': user.longitude,
'profile_photo': request.build_absolute_uri(user.profile_picture.url) if user.profile_picture else ''
}
print('4')
print(response)
@@ -105,3 +139,140 @@ class LogoutView(View):
except Exception as e:
return JsonResponse({"status": "error", "message": str(e)}, status=500)
@method_decorator(csrf_exempt, name='dispatch')
class UpdateProfileView(View):
def post(self, request):
try:
# Authenticate user using validate_token_and_get_user
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
if error_response:
# Convert error response format to match our API response format
error_data = json.loads(error_response.content)
return JsonResponse({
'success': False,
'error': error_data.get('message', error_data.get('status', 'Authentication failed'))
}, status=error_response.status_code)
errors = {}
updated_fields = []
# Get update data - handle both JSON and multipart/form-data
is_multipart = request.content_type and 'multipart/form-data' in request.content_type
if is_multipart:
# For multipart, get data from POST (data already contains token/username from validation)
json_data = request.POST.dict()
else:
# For JSON, use data from validate_token_and_get_user
json_data = data if data else {}
# Update first_name
if 'first_name' in json_data:
first_name = json_data.get('first_name', '').strip()
if first_name:
user.first_name = first_name
updated_fields.append('first_name')
elif first_name == '':
user.first_name = ''
updated_fields.append('first_name')
# Update last_name
if 'last_name' in json_data:
last_name = json_data.get('last_name', '').strip()
if last_name:
user.last_name = last_name
updated_fields.append('last_name')
elif last_name == '':
user.last_name = ''
updated_fields.append('last_name')
# Update phone_number
if 'phone_number' in json_data:
phone_number = json_data.get('phone_number', '').strip()
if phone_number:
# Check if phone number is already taken by another user
if User.objects.filter(phone_number=phone_number).exclude(id=user.id).exists():
errors['phone_number'] = 'Phone number is already registered.'
else:
user.phone_number = phone_number
updated_fields.append('phone_number')
elif phone_number == '':
user.phone_number = None
updated_fields.append('phone_number')
# Update email
if 'email' in json_data:
email = json_data.get('email', '').strip().lower()
if email:
# Validate email format
if '@' not in email:
errors['email'] = 'Invalid email format.'
# Check if email is already taken by another user
elif User.objects.filter(email=email).exclude(id=user.id).exists():
errors['email'] = 'Email is already registered.'
else:
user.email = email
# Also update username if it was set to email
if user.username == user.email or not user.username:
user.username = email
updated_fields.append('email')
elif email == '':
errors['email'] = 'Email cannot be empty.'
# Update pincode
if 'pincode' in json_data:
pincode = json_data.get('pincode', '').strip()
if pincode:
user.pincode = pincode
updated_fields.append('pincode')
elif pincode == '':
user.pincode = None
updated_fields.append('pincode')
# Handle profile_picture (multipart form-data only)
if 'profile_photo' in request.FILES:
# Handle file upload from multipart/form-data
profile_photo = request.FILES['profile_photo']
# Validate file type
if not profile_photo.content_type.startswith('image/'):
errors['profile_photo'] = 'File must be an image.'
else:
user.profile_picture = profile_photo
updated_fields.append('profile_photo')
# Return errors if any
if errors:
return JsonResponse({
'success': False,
'errors': errors
}, status=400)
# Save user if any fields were updated
if updated_fields:
user.save()
return JsonResponse({
'success': True,
'message': 'Profile updated successfully',
'updated_fields': updated_fields,
'user': {
'username': user.username,
'email': user.email,
'first_name': user.first_name,
'last_name': user.last_name,
'phone_number': user.phone_number,
'pincode': user.pincode,
'profile_picture': user.profile_picture.url if user.profile_picture else None,
}
}, status=200)
else:
return JsonResponse({
'success': False,
'error': 'No fields provided for update'
}, status=400)
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=500)