Updates for the api and bug fixes
This commit is contained in:
18
accounts/migrations/0006_user_profile_picture.py
Normal file
18
accounts/migrations/0006_user_profile_picture.py
Normal 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/'),
|
||||
),
|
||||
]
|
||||
@@ -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
BIN
db_1.sqlite3
Normal file
Binary file not shown.
@@ -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'},
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,8 +32,15 @@ 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)
|
||||
"""
|
||||
# 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:
|
||||
# Parse JSON from request body
|
||||
data = json.loads(request.body)
|
||||
except json.JSONDecodeError:
|
||||
status = 400 if error_status_code else None
|
||||
@@ -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
|
||||
))
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user