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

BIN
.DS_Store vendored

Binary file not shown.

889
API_Documentation.md Normal file
View File

@@ -0,0 +1,889 @@
# Eventify Plus API Documentation
**Base URL:** `https://uat.eventifyplus.com/api/`
**Version:** 1.0
**Last Updated:** December 2025
---
## Table of Contents
1. [Authentication](#authentication)
2. [User APIs](#user-apis)
3. [Event APIs](#event-apis)
4. [Error Responses](#error-responses)
---
## Authentication
All API endpoints (except registration and login) require token-based authentication. The token must be included in the request body along with the username.
**Token Format:** The token is a string returned after successful login or registration.
---
## User APIs
### 1. User Registration
**Endpoint:** `POST /user/register/`
**Description:** Register a new user account.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"email": "user@example.com",
"phone_number": "+1234567890",
"password": "securepassword123"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| email | string | Yes | User's email address (must be unique) |
| phone_number | string | Yes | User's phone number (must be unique) |
| password | string | Yes | User's password |
**Success Response (201 Created):**
```json
{
"message": "User registered successfully",
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
```
**Error Response (400 Bad Request):**
```json
{
"errors": {
"email": ["Email is already registered."],
"phone_number": ["Phone number is already registered."]
}
}
```
**Error Response (500 Internal Server Error):**
```json
{
"error": "Error message here"
}
```
---
### 2. User Login
**Endpoint:** `POST /user/login/`
**Description:** Authenticate user and receive authentication token.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"username": "user@example.com",
"password": "securepassword123"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| username | string | Yes | User's email or username |
| password | string | Yes | User's password |
**Note:** The `username` field accepts either email address or username.
**Success Response (200 OK):**
```json
{
"message": "Login successful",
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"email": "user@example.com",
"phone_number": "+1234567890",
"first_name": "John",
"last_name": "Doe",
"role": "staff",
"pincode": "560001",
"district": "Bangalore Urban",
"state": "Karnataka",
"country": "India",
"place": "Bangalore",
"latitude": "12.9716",
"longitude": "77.5946"
}
```
**Error Response (401 Unauthorized):**
```json
{
"errors": {
"username": ["Invalid credentials."]
}
}
```
**Error Response (500 Internal Server Error):**
```json
{
"error": "Error message here"
}
```
---
### 3. Check User Status
**Endpoint:** `POST /user/status/`
**Description:** Verify if the authentication token is valid and get user status.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
**Success Response (200 OK):**
```json
{
"status": "logged_in",
"username": "john_doe",
"email": "user@example.com"
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 4. User Logout
**Endpoint:** `POST /user/logout/`
**Description:** Logout user and invalidate authentication token.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
**Success Response (200 OK):**
```json
{
"status": "logged_out",
"message": "Logout successful"
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
## Event APIs
### 5. Get Event Types List
**Endpoint:** `POST events/type-list/`
**Description:** Retrieve list of all available event types with icons.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
**Success Response (200 OK):**
```json
{
"status": "success",
"event_types": [
{
"id": 1,
"event_type": "Concert",
"event_type_icon": "https://uat.eventifyplus.com/media/event_type_icons/concert.png"
},
{
"id": 2,
"event_type": "Sports",
"event_type_icon": "https://uat.eventifyplus.com/media/event_type_icons/sports.png"
},
{
"id": 3,
"event_type": "Conference",
"event_type_icon": null
}
]
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 6. Get Events by Pincode
**Endpoint:** `POST events/pincode-events/`
**Description:** Retrieve list of events filtered by pincode. If pincode is not provided or set to 'all', returns all events.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"pincode": "560001"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
| pincode | string | No | Pincode to filter events (optional, use 'all' or omit to get all events) |
**Success Response (200 OK):**
```json
{
"status": "success",
"events": [
{
"id": 1,
"name": "Music Festival",
"description": "Annual music festival",
"start_date": "2025-06-15",
"end_date": "2025-06-17",
"start_time": "10:00:00",
"end_time": "22:00:00",
"latitude": "12.9716",
"longitude": "77.5946",
"pincode": "560001",
"district": "Bangalore Urban",
"state": "Karnataka",
"place": "Bangalore",
"is_bookable": true,
"is_eventify_event": true,
"outside_event_url": "NA",
"event_type": 1,
"event_status": "pending",
"cancelled_reason": "NA",
"title": "Summer Music Fest",
"important_information": "Free parking available",
"venue_name": "City Park",
"thumb_img": "https://uat.eventifyplus.com/media/event_images/festival_thumb.jpg"
}
]
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 7. Get Event Details
**Endpoint:** `POST events/event-details/`
**Description:** Retrieve detailed information about a specific event including all images.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"event_id": 1
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
| event_id | integer | Yes | ID of the event |
**Success Response (200 OK):**
```json
{
"id": 1,
"name": "Music Festival",
"description": "Annual music festival with multiple artists",
"start_date": "2025-06-15",
"end_date": "2025-06-17",
"start_time": "10:00:00",
"end_time": "22:00:00",
"latitude": "12.9716",
"longitude": "77.5946",
"pincode": "560001",
"district": "Bangalore Urban",
"state": "Karnataka",
"place": "Bangalore",
"is_bookable": true,
"is_eventify_event": true,
"outside_event_url": "NA",
"event_type": 1,
"event_status": "pending",
"cancelled_reason": "NA",
"title": "Summer Music Fest",
"important_information": "Free parking available",
"venue_name": "City Park",
"status": "success",
"images": [
{
"is_primary": true,
"image": "https://uat.eventifyplus.com/media/event_images/festival_main.jpg"
},
{
"is_primary": false,
"image": "https://uat.eventifyplus.com/media/event_images/festival_1.jpg"
},
{
"is_primary": false,
"image": "https://uat.eventifyplus.com/media/event_images/festival_2.jpg"
}
]
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 8. Get Event Images
**Endpoint:** `POST events/event-images/`
**Description:** Retrieve list of all image URLs for a specific event.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"event_id": 1
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
| event_id | integer | Yes | ID of the event |
**Success Response (200 OK):**
```json
{
"status": "success",
"images": [
"https://uat.eventifyplus.com/media/event_images/festival_main.jpg",
"https://uat.eventifyplus.com/media/event_images/festival_1.jpg",
"https://uat.eventifyplus.com/media/event_images/festival_2.jpg"
]
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 9. Get Events by Category
**Endpoint:** `POST events/events-by-category/`
**Description:** Retrieve list of events filtered by event type/category ID.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"category_id": 1
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
| category_id | integer | Yes | Event type/category ID |
**Success Response (200 OK):**
```json
{
"status": "success",
"events": [
{
"id": 1,
"name": "Music Festival",
"description": "Annual music festival",
"start_date": "2025-06-15",
"end_date": "2025-06-17",
"event_type": 1,
"event_image": "https://uat.eventifyplus.com/media/event_images/festival_main.jpg"
}
]
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (400 Bad Request - Missing category_id):**
```json
{
"status": "error",
"message": "category_id is required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 10. Get Events by Month and Year
**Endpoint:** `POST events/events-by-month-year/`
**Description:** Retrieve events for a specific month and year with date-wise breakdown. Returns dates that have events, total count, and number of events per date.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"month": "August",
"year": 2025
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
| month | string | Yes | Month name (e.g., "August", "august", "Aug") |
| year | integer | Yes | Year (e.g., 2025) |
**Success Response (200 OK):**
```json
{
"status": "success",
"dates": [
"2025-08-01",
"2025-08-10",
"2025-08-15"
],
"total_number_of_events": 10,
"date_events": [
{
"date_of_event": "2025-08-01",
"events_of_date": 8
},
{
"date_of_event": "2025-08-10",
"events_of_date": 2
},
{
"date_of_event": "2025-08-15",
"events_of_date": 1
}
]
}
```
**Error Response (400 Bad Request):**
```json
{
"status": "error",
"message": "month and year are required"
}
```
**Error Response (400 Bad Request - Invalid Month):**
```json
{
"status": "error",
"message": "Invalid month name: InvalidMonth"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
### 11. Get Events by Date
**Endpoint:** `POST events/events-by-date/`
**Description:** Retrieve complete information for all events occurring on a specific date. Returns all event fields along with primary image thumbnails.
**Request Headers:**
```
Content-Type: application/json
```
**Request Body:**
```json
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"username": "john_doe",
"date_of_event": "2025-08-15"
}
```
**Request Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | Yes | Authentication token |
| username | string | Yes | User's username |
| date_of_event | string | Yes | Date in YYYY-MM-DD format (e.g., "2025-08-15") |
**Success Response (200 OK):**
```json
{
"status": "success",
"events": [
{
"id": 1,
"created_date": "2025-07-01",
"name": "Music Festival",
"description": "Annual music festival with multiple artists",
"start_date": "2025-08-10",
"end_date": "2025-08-20",
"start_time": "10:00:00",
"end_time": "22:00:00",
"latitude": "12.9716",
"longitude": "77.5946",
"pincode": "560001",
"district": "Bangalore Urban",
"state": "Karnataka",
"place": "Bangalore",
"is_bookable": true,
"is_eventify_event": true,
"outside_event_url": "NA",
"event_type": 1,
"event_status": "pending",
"cancelled_reason": "NA",
"title": "Summer Music Fest",
"important_information": "Free parking available",
"venue_name": "City Park",
"thumb_img": "https://uat.eventifyplus.com/media/event_images/festival_thumb.jpg"
},
{
"id": 2,
"created_date": "2025-07-05",
"name": "Sports Championship",
"description": "Regional sports championship",
"start_date": "2025-08-15",
"end_date": "2025-08-15",
"start_time": "09:00:00",
"end_time": "18:00:00",
"latitude": "12.9352",
"longitude": "77.6245",
"pincode": "560001",
"district": "Bangalore Urban",
"state": "Karnataka",
"place": "Bangalore",
"is_bookable": false,
"is_eventify_event": true,
"outside_event_url": "NA",
"event_type": 2,
"event_status": "pending",
"cancelled_reason": "NA",
"title": "Regional Sports Championship",
"important_information": "Entry fee required",
"venue_name": "Sports Complex",
"thumb_img": "https://uat.eventifyplus.com/media/event_images/sports_thumb.jpg"
}
]
}
```
**Error Response (400 Bad Request - Missing date_of_event):**
```json
{
"status": "error",
"message": "date_of_event is required"
}
```
**Error Response (400 Bad Request - Invalid Date Format):**
```json
{
"status": "error",
"message": "Invalid date format. Expected YYYY-MM-DD"
}
```
**Error Response (400 Bad Request - Missing Credentials):**
```json
{
"status": "error",
"message": "token and username required"
}
```
**Error Response (401 Unauthorized):**
```json
{
"status": "invalid_token"
}
```
---
## Error Responses
### Common Error Codes
| Status Code | Description |
|-------------|-------------|
| 200 | Success |
| 201 | Created (Registration successful) |
| 400 | Bad Request (Missing or invalid parameters) |
| 401 | Unauthorized (Invalid token or credentials) |
| 500 | Internal Server Error |
### Standard Error Response Format
```json
{
"status": "error",
"message": "Error description here"
}
```
### Authentication Errors
**Invalid Token:**
```json
{
"status": "invalid_token"
}
```
**Token Mismatch:**
```json
{
"status": "error",
"message": "token does not match user"
}
```
**Missing Credentials:**
```json
{
"status": "error",
"message": "token and username required"
}
```
---
## Notes
1. **Authentication:** All endpoints except registration and login require a valid token in the request body.
2. **Content-Type:** All requests must include `Content-Type: application/json` header.
3. **Date Format:** Dates are returned in `YYYY-MM-DD` format. The `date_of_event` parameter in `events-by-date` endpoint must also be provided in `YYYY-MM-DD` format.
4. **Time Format:** Times are returned in `HH:MM:SS` format (24-hour).
5. **Image URLs:** All image URLs are returned as absolute URLs starting with the base URL.
6. **Pincode Filter:** The pincode parameter in `pincode-events` endpoint is optional. Omit it or set to `'all'` to retrieve all events.
7. **Month Names:** The month parameter accepts full month names (e.g., "August") or abbreviations (e.g., "Aug"), case-insensitive.
8. **Multi-day Events:** Events that span multiple days will be counted for each day they occur in the specified month. The `events-by-date` endpoint returns all events where the specified date falls between the event's `start_date` and `end_date` (inclusive).
---
## Support
For API support or questions, please contact the development team.
**API Base URL:** `https://uat.eventifyplus.com/api/`
---
*Document Version: 1.0*
*Last Updated: December 2025*

View File

@@ -5,18 +5,19 @@ from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
User = get_user_model()
class RegisterForm(UserCreationForm): class RegisterForm(UserCreationForm):
full_name = forms.CharField(max_length=150, required=False, label="Full name") full_name = forms.CharField(max_length=150, required=False, label="Full name")
email = forms.EmailField(required=True, label="Email") email = forms.EmailField(required=True, label="Email")
class Meta: class Meta:
model = get_user_model() model = User
fields = ("username", "full_name", "email", "password1", "password2") fields = ("username", "full_name", "email", "password1", "password2")
def clean_email(self): def clean_email(self):
email = self.cleaned_data.get("email") email = self.cleaned_data.get("email")
user = get_user_model() user = User
if user.objects.filter(email__iexact=email).exists(): if user.objects.filter(email__iexact=email).exists():
raise forms.ValidationError("A user with that email already exists.") raise forms.ValidationError("A user with that email already exists.")
return email return email
@@ -59,4 +60,35 @@ class CustomerLoginForm(AuthenticationForm):
"class": "input", "class": "input",
"autocomplete": "current-password", "autocomplete": "current-password",
}) })
) )
class CustomerProfileForm(forms.ModelForm):
class Meta:
model = User
fields = [
"first_name",
"last_name",
"email",
"phone_number",
"pincode",
"district",
"state",
"country",
"place",
"latitude",
"longitude",
]
widgets = {
"first_name": forms.TextInput(attrs={"class": "form-control"}),
"last_name": forms.TextInput(attrs={"class": "form-control"}),
"email": forms.EmailInput(attrs={"class": "form-control"}),
"phone_number": forms.TextInput(attrs={"class": "form-control"}),
"pincode": forms.TextInput(attrs={"class": "form-control"}),
"district": forms.TextInput(attrs={"class": "form-control"}),
"state": forms.TextInput(attrs={"class": "form-control"}),
"country": forms.TextInput(attrs={"class": "form-control"}),
"place": forms.TextInput(attrs={"class": "form-control"}),
"latitude": forms.NumberInput(attrs={"class": "form-control", "step": "any"}),
"longitude": forms.NumberInput(attrs={"class": "form-control", "step": "any"}),
}

View File

@@ -16,7 +16,9 @@ from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.shortcuts import render from django.shortcuts import render
from .customer_forms import RegisterForm, CustomerLoginForm from .customer_forms import RegisterForm
from .customer_forms import CustomerLoginForm
from .customer_forms import CustomerProfileForm
from django.contrib.auth import logout from django.contrib.auth import logout
from django.shortcuts import redirect from django.shortcuts import redirect
@@ -127,4 +129,27 @@ def customer_dashboard(request):
def logout_view(request): def logout_view(request):
logout(request) logout(request)
messages.success(request, "You have been logged out successfully.") messages.success(request, "You have been logged out successfully.")
return redirect("login") return redirect("login")
@login_required(login_url="login")
def customer_calendar(request):
return render(request, "customer/customer_calendar.html")
# ...existing imports...
@login_required(login_url="login")
def customer_profile(request):
user = request.user
if request.method == "POST":
form = CustomerProfileForm(request.POST, instance=user)
if form.is_valid():
form.save()
messages.success(request, "Profile updated.")
return redirect("customer_profile")
else:
form = CustomerProfileForm(instance=user)
return render(request, "customer/customer_profile.html", {"form": form})

40
db_reset.py Normal file
View File

@@ -0,0 +1,40 @@
# reset_db.py
import os
import sys
import django
from django.core.management import call_command
from django.db import connection
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eventify.settings")
django.setup()
def reset_postgres_schema():
user = connection.settings_dict.get("USER", "postgres")
with connection.cursor() as cursor:
cursor.execute("""
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
GRANT ALL ON SCHEMA public TO {user};
GRANT ALL ON SCHEMA public TO public;
""".format(user=user))
def main():
if "--force" not in sys.argv:
print("Refusing to run without --force (this DROPS ALL TABLES).")
sys.exit(1)
engine = connection.settings_dict.get("ENGINE", "")
if "postgresql" not in engine:
print("This script is intended for PostgreSQL. Aborting.")
sys.exit(1)
print("Dropping all tables by recreating public schema…")
reset_postgres_schema()
print("Running migrations…")
call_command("migrate", interactive=False)
print("Done.")
if __name__ == "__main__":
main()

View File

@@ -26,19 +26,29 @@ INSTALLED_APPS = [
'events', 'events',
'accounts', 'accounts',
'templatetags', 'templatetags',
'mobile_web_api', 'mobile_api',
'web_api',
'rest_framework', 'rest_framework',
'rest_framework.authtoken' 'rest_framework.authtoken'
] ]
INSTALLED_APPS += [
"corsheaders",
]
MIDDLEWARE = [ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
] ]
ROOT_URLCONF = 'eventify.urls' ROOT_URLCONF = 'eventify.urls'
@@ -61,24 +71,24 @@ TEMPLATES = [
WSGI_APPLICATION = 'eventify.wsgi.application' WSGI_APPLICATION = 'eventify.wsgi.application'
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'eventify_uat_db', # your DB name 'NAME': BASE_DIR / 'db.sqlite3',
'USER': 'eventify_uat', # your DB user
'PASSWORD': 'eventifyplus@!@#$', # your DB password
'HOST': '0.0.0.0', # or IP/domain
'PORT': '5440', # default PostgreSQL port
} }
} }
# 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 = [ AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},

View File

@@ -1,23 +1,37 @@
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from accounts.views import dashboard # from accounts.views import dashboard, login_view, logout_view, UserListView, UserCreateView, UserUpdateView, UserDeleteView
from accounts.customer_views import RegisterView # from accounts.customer_views import RegisterView
from accounts.customer_views import login_view, logout_view, customer_dashboard # 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 django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path("", login_view, name="login"), # path("", login_view, name="login"),
path("logout/", logout_view, name="logout"), # path("logout/", logout_view, name="logout"),
path("register/", RegisterView.as_view(), name="register"), # path("register/", RegisterView.as_view(), name="register"),
path('dashboard/', customer_dashboard, name='customer_dashboard'), # path('dashboard/', customer_dashboard, name='customer_dashboard'),
# path('calendar/', customer_calendar, name='customer_calendar'),
# path('profile/', customer_profile, name='customer_profile'),
path('', views.login_view, name='login'),
path('logout/', views.logout_view, name='logout'),
path('dashboard/', views.dashboard, name='dashboard'),
path('users/', views.UserListView.as_view(), name='user_list'),
path('users/add/', views.UserCreateView.as_view(), name='user_add'),
path('users/<int:pk>/edit/', views.UserUpdateView.as_view(), name='user_edit'),
path('users/<int:pk>/delete/', views.UserDeleteView.as_view(), name='user_delete'),
path('master-data/', include('master_data.urls')), path('master-data/', include('master_data.urls')),
path('events/', include('events.urls')), path('events/', include('events.urls')),
path('accounts/', include('accounts.urls')), path('accounts/', include('accounts.urls')),
path('api/', include('mobile_web_api.urls')), path('api/', include('mobile_api.urls')),
# path('web-api/', include('web_api.urls')),
] ]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

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

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

@@ -17,5 +17,7 @@ urlpatterns += [
path('events/pincode-events/', EventListAPI.as_view()), path('events/pincode-events/', EventListAPI.as_view()),
path('events/event-details/', EventDetailAPI.as_view()), path('events/event-details/', EventDetailAPI.as_view()),
path('events/event-images/', EventImagesListAPI.as_view()), path('events/event-images/', EventImagesListAPI.as_view()),
path('events/events-by-category/<int:slug>/', api_events_by_category, name='api_events_by_category'), 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
))

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)

View File

@@ -1,42 +0,0 @@
# 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):
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')
user = authenticate(username=username, password=password)
if not user:
raise forms.ValidationError("Invalid credentials.")
cleaned_data['user'] = user
return cleaned_data

View File

@@ -1,231 +0,0 @@
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 rest_framework.authtoken.models import Token
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
import json
from django.contrib.auth.decorators import login_required
@method_decorator(csrf_exempt, name='dispatch')
class EventTypeListAPIView(APIView):
def post(self, request):
try:
# Manually load JSON because we are not using parsers
data = json.loads(request.body)
token_key = data.get("token")
username = data.get("username")
if not token_key or not username:
return JsonResponse(
{"status": "error", "message": "token and username required"}
)
try:
token = Token.objects.get(key=token_key)
user = token.user
if user.username != username:
return JsonResponse(
{"status": "error", "message": "token does not match user"}
)
# Fetch event types manually without serializer
event_types = list(EventType.objects.values("id", "event_type"))
return JsonResponse({
"status": "success",
"event_types": event_types
})
except Token.DoesNotExist:
return JsonResponse({"status": "invalid_token"})
except json.JSONDecodeError:
return JsonResponse(
{"status": "error", "message": "Invalid JSON"}
)
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
class EventListAPI(APIView):
def post(self, request):
try:
data = json.loads(request.body)
token_key = data.get("token")
username = data.get("username")
pincode = data.get("pincode")
if not token_key or not username:
return JsonResponse(
{"status": "error", "message": "token and username required"}
)
try:
token = Token.objects.get(key=token_key)
user = token.user
if user.username != username:
return JsonResponse(
{"status": "error", "message": "token does not match user"}
)
events = Event.objects.filter(pincode=pincode).order_by('-created_date')
event_list = []
for e in events:
data_dict = model_to_dict(e)
print('*' * 10)
print(e.id)
print('*' * 10)
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 Token.DoesNotExist:
return JsonResponse({"status": "invalid_token"})
except json.JSONDecodeError:
return JsonResponse(
{"status": "error", "message": "Invalid JSON"}
)
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
class EventDetailAPI(APIView):
def post(self, request):
try:
data = json.loads(request.body)
token_key = data.get("token")
username = data.get("username")
event_id = data.get("event_id")
if not token_key or not username:
return JsonResponse(
{"status": "error", "message": "token and username required"}
)
try:
token = Token.objects.get(key=token_key)
user = token.user
if user.username != username:
return JsonResponse(
{"status": "error", "message": "token does not match user"}
)
events = Event.objects.get(id=event_id)
event_images = EventImages.objects.filter(event=event_id)
data = model_to_dict(events)
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)
data["images"] = event_images_list
return JsonResponse(data)
except Token.DoesNotExist:
return JsonResponse({"status": "invalid_token"})
except json.JSONDecodeError:
return JsonResponse(
{"status": "error", "message": "Invalid JSON"}
)
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
class EventImagesListAPI(APIView):
def post(self, request):
try:
data = json.loads(request.body)
token_key = data.get("token")
username = data.get("username")
event_id = data.get("event_id")
if not token_key or not username:
return JsonResponse(
{"status": "error", "message": "token and username required"}
)
try:
token = Token.objects.get(key=token_key)
user = token.user
if user.username != username:
return JsonResponse(
{"status": "error", "message": "token does not match user"}
)
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
return JsonResponse(res_data)
except Token.DoesNotExist:
return JsonResponse({"status": "invalid_token"})
except json.JSONDecodeError:
return JsonResponse(
{"status": "error", "message": "Invalid JSON"}
)
except Exception as e:
return JsonResponse(
{"status": "error", "message": str(e)},
)
@login_required(login_url="login")
def api_events_by_category(request, slug):
events = Event.objects.filter(event_type=slug)
events_dict = [model_to_dict(obj) for obj in events]
for event in events_dict:
event['event_image'] = EventImages.objects.get(event=event['id'], is_primary=True).event_image.url
# event['start_date'] = convert_date_to_dd_mm_yyyy(event['start_date'])
return JsonResponse({"events": events_dict})

View File

@@ -1,124 +0,0 @@
# 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_web_api.forms import RegisterForm, LoginForm
from rest_framework.authentication import TokenAuthentication
from django.contrib.auth import logout
@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):
try:
data = json.loads(request.body)
form = LoginForm(data)
if form.is_valid():
user = form.cleaned_data['user']
token, _ = Token.objects.get_or_create(user=user)
return JsonResponse({'message': 'Login successful', 'token': token.key})
return JsonResponse({'errors': form.errors}, 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:
data = json.loads(request.body)
token_key = data.get("token")
username = data.get("username")
if not token_key or not username:
return JsonResponse(
{"status": "error", "message": "token and username required"},
status=400
)
try:
token = Token.objects.get(key=token_key)
if token.user.username != username:
return JsonResponse(
{"status": "error", "message": "token does not match user"},
status=401
)
return JsonResponse({
"status": "logged_in",
"username": token.user.username,
"email": token.user.email
})
except Token.DoesNotExist:
return JsonResponse({"status": "invalid_token"}, status=401)
except json.JSONDecodeError:
return JsonResponse({"status": "error", "message": "Invalid JSON"}, status=400)
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:
data = json.loads(request.body)
token_key = data.get("token")
username = data.get("username")
if not token_key or not username:
return JsonResponse(
{"status": "error", "message": "token and username required"},
status=400
)
try:
token = Token.objects.get(key=token_key)
user = token.user
if user.username != username:
return JsonResponse(
{"status": "error", "message": "token does not match user"},
status=401
)
# 🔍 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 Token.DoesNotExist:
return JsonResponse({"status": "invalid_token"}, status=401)
except json.JSONDecodeError:
return JsonResponse({"status": "error", "message": "Invalid JSON"}, status=400)
except Exception as e:
return JsonResponse({"status": "error", "message": str(e)}, status=500)

80
pg_to_sqlite_backup.py Normal file
View File

@@ -0,0 +1,80 @@
# pg_to_sqlite_backup.py
import os
import sys
import django
from django.conf import settings
from django.core.management import call_command
from django.db import connections, DEFAULT_DB_ALIAS
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eventify.settings")
django.setup()
SQLITE_PATH = "backup.sqlite3"
BACKUP_ALIAS = "backup"
def ensure_backup_db():
# Add a SQLite backup database alias
settings.DATABASES[BACKUP_ALIAS] = {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.abspath(SQLITE_PATH),
}
# Remove existing file so migrate creates a fresh DB
if os.path.exists(SQLITE_PATH):
os.remove(SQLITE_PATH)
# Run migrations on the backup DB
print("Running migrations on backup (SQLite)…")
call_command("migrate", database=BACKUP_ALIAS, interactive=False, verbosity=0)
print("SQLite schema created at", SQLITE_PATH)
def copy_all_tables():
pg_conn = connections[DEFAULT_DB_ALIAS] # default = PostgreSQL
sqlite_conn = connections[BACKUP_ALIAS] # new SQLite
pg_cursor = pg_conn.cursor()
sqlite_cursor = sqlite_conn.cursor()
tables = pg_conn.introspection.table_names()
for table in tables:
# Fetch column names from PostgreSQL
cols = [c.name for c in pg_conn.introspection.get_table_description(pg_cursor, table)]
col_list = ", ".join(f'"{c}"' for c in cols)
placeholders = ", ".join(["?"] * len(cols))
# Fetch all rows
pg_cursor.execute(f'SELECT {col_list} FROM "{table}"')
rows = pg_cursor.fetchall()
# Insert into SQLite
if rows:
insert_sql = f'INSERT INTO "{table}" ({col_list}) VALUES ({placeholders})'
sqlite_cursor.executemany(insert_sql, rows)
print(f"Copied {len(rows)} rows from {table}")
sqlite_conn.commit()
pg_cursor.close()
sqlite_cursor.close()
def main():
if "--force" not in sys.argv:
print("Refusing to run without --force (destructive for existing backup file).")
sys.exit(1)
# Safety: ensure default DB is PostgreSQL
engine = settings.DATABASES[DEFAULT_DB_ALIAS]["ENGINE"]
if "postgresql" not in engine:
print("Default database is not PostgreSQL. Aborting.")
sys.exit(1)
ensure_backup_db()
copy_all_tables()
print("Done. SQLite backup at", SQLITE_PATH)
if __name__ == "__main__":
main()

View File

@@ -15,6 +15,8 @@
<div class="card shadow-sm p-4"> <div class="card shadow-sm p-4">
<h2 class="text-center mb-3">Welcome To Eventify Admin Panel</h2>
<h4 class="text-center mb-3">Login</h4> <h4 class="text-center mb-3">Login</h4>
{% if messages %} {% if messages %}

View File

@@ -1,9 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -26,36 +25,44 @@
.sidebar { .sidebar {
width: 260px; width: 260px;
background: linear-gradient(180deg, #1e3cfa, #1436c7); background: linear-gradient(180deg, #1e3cfa, #1436c7);
color: white; color: #fff;
padding: 30px 20px; padding: 30px 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 30px;
border-radius: 12px;
margin-bottom: 8px;
font-size: 15px;
line-height: 1.5;
text-decoration: none;
color: #000000;
/* force white text */
background: transparent;
}
.nav-item:hover,
.nav-item.active {
background: #fff;
color: #000000;
padding: 16px 30px;
}
.nav-item:visited {
color: #000000;
}
.logo { .logo {
font-size: 28px; font-size: 28px;
font-weight: 700; font-weight: 700;
margin-bottom: 40px; margin-bottom: 40px;
} }
.nav-item {
padding: 12px 15px;
border-radius: 12px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
margin-bottom: 8px;
font-size: 15px;
transition: 0.2s;
}
.nav-item:hover,
.nav-item.active {
background: white;
color: #1436c7;
}
.bottom-nav { .bottom-nav {
margin-top: auto; margin-top: auto;
} }
@@ -146,9 +153,14 @@
<div class="logo">EVENTIFY</div> <div class="logo">EVENTIFY</div>
<div class="nav"> <div class="nav">
<div class="nav-item active">🏠 Home</div> <a class="nav-item {% if request.resolver_match.url_name == 'customer_dashboard' %}active{% endif %}"
<div class="nav-item">📅 Calendar</div> href="{% url 'customer_dashboard' %}">🏠 Home</a>
<div class="nav-item">👤 Profile</div>
<a class="nav-item {% if request.resolver_match.url_name == 'customer_calendar' %}active{% endif %}"
href="{% url 'customer_calendar' %}">📅 Calendar</a>
<a class="nav-item {% if request.resolver_match.url_name == 'customer_profile' %}active{% endif %}"
href="{% url 'customer_profile' %}">👤 Profile</a>
</div> </div>
<div class="bottom-nav"> <div class="bottom-nav">

View File

@@ -0,0 +1,256 @@
{% extends "customer/base_dashboard.html" %}
{% load static %}
{% block content %}
<style>
.calendar-wrapper {
display: flex;
width: 100%;
padding: 40px;
gap: 30px;
}
/* LEFT CALENDAR PANEL */
.calendar-card {
flex: 0.58;
background: white;
border-radius: 22px;
padding: 30px;
box-shadow: 0 4px 20px rgba(0,0,0,0.05);
}
.calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.calendar-header button {
background: none;
border: none;
font-size: 20px;
color: #555;
cursor: pointer;
}
.calendar-title h2 {
margin: 0;
font-size: 24px;
font-weight: 600;
}
.calendar-title p {
margin: 0;
color: #777;
font-size: 14px;
}
.weekday-row,
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
}
.weekday-row {
font-weight: 600;
color: #666;
margin-bottom: 15px;
}
.calendar-day {
height: 65px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
color: #333;
}
.calendar-day.inactive {
color: #ccc;
}
.selected-day {
position: absolute;
inset: 0;
background: #e7f0ff;
border-radius: 14px;
z-index: 0;
}
.calendar-day span {
z-index: 2;
font-weight: 600;
color: #1a47d1;
}
.day-icons {
display: flex;
gap: -8px;
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
}
.day-icons img {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid white;
}
/* RIGHT EVENTS PANEL */
.events-card {
flex: 0.42;
background: white;
border-radius: 22px;
padding: 25px;
box-shadow: 0 4px 20px rgba(0,0,0,0.05);
max-height: 80vh;
overflow-y: auto;
}
.events-header {
display: flex;
gap: 15px;
margin-bottom: 25px;
align-items: center;
}
.date-tag {
background: #1e3cfa;
color: white;
padding: 8px 14px;
text-align: center;
border-radius: 10px;
font-size: 13px;
font-weight: 600;
}
.event-card {
background: white;
border-radius: 16px;
margin-bottom: 20px;
border: 1px solid #eee;
box-shadow: 0 3px 10px rgba(0,0,0,0.05);
overflow: hidden;
}
.event-card img {
width: 100%;
height: 160px;
object-fit: cover;
}
.event-content {
padding: 12px 15px;
}
.event-content h4 {
margin: 0 0 5px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.event-meta {
font-size: 14px;
color: #777;
display: flex;
gap: 10px;
align-items: center;
}
</style>
<div class="calendar-wrapper">
<!-- LEFT CALENDAR -->
<div class="calendar-card">
<div class="calendar-header">
<button>&lt;</button>
<div class="calendar-title">
<h2>{{ month_name }}</h2>
<p>{{ year }}</p>
</div>
<button>&gt;</button>
</div>
<!-- Weekdays -->
<div class="weekday-row">
<div>M</div><div>T</div><div>W</div><div>T</div><div>F</div>
<div style="color:red;">S</div>
<div style="color:red;">S</div>
</div>
<!-- Calendar Grid -->
<div class="calendar-grid">
{% for week in calendar_weeks %}
{% for day in week %}
{% if day.month == current_month %}
<div class="calendar-day">
{% if day.day == selected_day %}
<div class="selected-day"></div>
{% endif %}
<span>{{ day.day }}</span>
{% if day.events %}
<div class="day-icons">
{% for ev in day.events %}
<img src="{{ ev.icon }}">
{% endfor %}
</div>
{% endif %}
</div>
{% else %}
<div class="calendar-day inactive">{{ day.day }}</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
</div>
<!-- RIGHT EVENTS -->
<div class="events-card">
<div class="events-header">
<div class="date-tag">{{ selected_day }} {{ selected_month_short }}</div>
<div>
<h3 style="margin:0;font-size:18px;font-weight:700;">
{{ selected_date_verbose }}
</h3>
<p style="margin:0;color:#777;font-size:14px;">
{{ events|length }} Events
</p>
</div>
</div>
{% for event in events %}
<div class="event-card">
<img src="{{ event.image }}">
<div class="event-content">
<h4>{{ event.title }}</h4>
<div class="event-meta">
📅 {{ event.date }}
📍 {{ event.location }}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends "customer/base_dashboard.html" %}
{% load static %}
{% block content %}
<div class="card p-4">
<h2 class="mb-3">Profile</h2>
<form method="post" novalidate>
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">{{ form.non_field_errors }}</div>
{% endif %}
<div class="row g-3">
{% for field in form %}
<div class="col-md-6">
<div class="form-group">
<label class="form-label">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<div class="text-danger small">{{ field.errors|striptags }}</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="mt-3">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
{% endblock %}

View File

View File

@@ -0,0 +1,47 @@
def simplify_errors(error_response):
"""
Convert nested 'errors' dict like:
{
"errors": {
"__all__": ["Invalid credentials."]
}
}
into:
{
"errors": "Invalid credentials."
}
"""
errors = error_response.get("errors", {})
# Collect all messages into a flat list
messages = []
if isinstance(errors, dict):
for field_errors in errors.values():
if isinstance(field_errors, (list, tuple)):
messages.extend(str(msg) for msg in field_errors)
else:
messages.append(str(field_errors))
else:
# If 'errors' is not a dict, just stringify it
messages.append(str(errors))
# Join multiple messages if needed
combined = " ".join(messages).strip()
return {"errors": combined}
def simplify_form_errors(form):
"""
Flatten Django form.errors into a single 'errors' string.
"""
errors = form.errors # ErrorDict
messages = []
for field_errors in errors.values():
# field_errors is usually an ErrorList (list-like)
for msg in field_errors:
messages.append(str(msg))
combined = " ".join(messages).strip()
return {"errors": combined}

0
web_api/__init__.py Normal file
View File

3
web_api/admin.py Normal file
View File

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

6
web_api/apps.py Normal file
View File

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

View File

3
web_api/models.py Normal file
View File

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

3
web_api/tests.py Normal file
View File

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

View File

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

0
web_api/views/events.py Normal file
View File