This commit is contained in:
Vivek P Prakash
2025-11-27 11:53:46 +05:30
commit aa40080012
50 changed files with 1135 additions and 0 deletions

4
.env.example Normal file
View File

@@ -0,0 +1,4 @@
# Copy to .env and set values
DJANGO_SECRET_KEY=replace-me
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
__pycache__/
*.pyc
db.sqlite3
/media/
/staticfiles/
.env

32
.idea/eventify_prod.iml generated Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$" />
<option name="settingsModule" value="eventify/settings.py" />
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/templates" />
</list>
</option>
</component>
<component name="TestRunnerService">
<option name="projectConfiguration" value="Twisted Trial" />
<option name="PROJECT_TEST_RUNNER" value="Twisted Trial" />
</component>
</module>

View File

@@ -0,0 +1,28 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="cx_oracle" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E722" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyShadowingNamesInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (untitled1)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/eventify_prod.iml" filepath="$PROJECT_DIR$/.idea/eventify_prod.iml" />
</modules>
</component>
</project>

319
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,319 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="a0020f0b-7bf3-4cde-a27d-39657ebd0286" name="Default Changelist" comment="" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/accounts/models.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="260">
<caret line="13" lean-forward="true" selection-start-line="13" selection-end-line="13" />
<folding>
<element signature="e#0#51#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/accounts/login.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-103" />
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/accounts/user_form.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/accounts/user_list.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/accounts/user_confirm_delete.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/templates/base.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/master_data/models.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/master_data/views.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-183">
<caret line="6" column="47" lean-forward="true" selection-start-line="6" selection-start-column="47" selection-end-line="6" selection-end-column="47" />
<folding>
<element signature="e#0#32#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/eventify/settings.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1463">
<folding>
<element signature="e#0#9#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/master_data/migrations/0001_initial.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
</leaf>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="-85" />
<option name="y" value="14" />
<option name="width" value="1386" />
<option name="height" value="788" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="eventify_prod" type="b2602c69:ProjectViewProjectNode" />
<item name="eventify_prod" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="eventify_prod" type="b2602c69:ProjectViewProjectNode" />
<item name="eventify_prod" type="462c0819:PsiDirectoryNode" />
<item name="master_data" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="eventify_prod" type="b2602c69:ProjectViewProjectNode" />
<item name="eventify_prod" type="462c0819:PsiDirectoryNode" />
<item name="templates" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager">
<configuration name="eventify_prod" type="Python.DjangoServer" factoryName="Django server">
<module name="eventify_prod" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="launchJavascriptDebuger" value="false" />
<option name="port" value="8000" />
<option name="host" value="" />
<option name="additionalOptions" value="" />
<option name="browserUrl" value="" />
<option name="runTestServer" value="false" />
<option name="runNoReload" value="false" />
<option name="useCustomRunCommand" value="false" />
<option name="customRunCommand" value="" />
<method v="2" />
</configuration>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="a0020f0b-7bf3-4cde-a27d-39657ebd0286" name="Default Changelist" comment="" />
<created>1764194364815</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1764194364815</updated>
<workItem from="1764194366807" duration="1596000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="1596000" />
</component>
<component name="ToolWindowManager">
<frame x="-8" y="-8" width="1382" height="744" extended-state="7" />
<editor active="true" />
<layout>
<window_info id="Favorites" side_tool="true" />
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.24962178" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Docker" show_stripe_button="false" />
<window_info anchor="bottom" id="Database Changes" />
<window_info anchor="bottom" id="Version Control" />
<window_info anchor="bottom" id="Python Console" />
<window_info anchor="bottom" id="Terminal" visible="true" weight="0.32840723" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" weight="0.32840723" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="right" id="SciView" />
<window_info anchor="right" id="Database" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/accounts/views.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="660">
<caret line="33" column="25" selection-start-line="33" selection-start-column="25" selection-end-line="33" selection-end-column="25" />
<folding>
<element signature="e#0#35#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/events/views.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="197">
<caret line="16" column="51" selection-start-line="16" selection-start-column="51" selection-end-line="16" selection-end-column="51" />
<folding>
<element signature="e#0#32#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.env.example">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/events/models.py">
<provider selected="true" editor-type-id="text-editor">
<state>
<folding>
<element signature="e#0#28#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/requirements.txt">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="-240" />
<second_editor />
</state>
</provider>
</entry>
<entry file="file://C:/Python38/Lib/site-packages/django/contrib/auth/models.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="247">
<caret line="354" column="5" selection-start-line="354" selection-start-column="5" selection-end-line="354" selection-end-column="5" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/eventify/settings.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1463">
<folding>
<element signature="e#0#9#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/master_data/migrations/0001_initial.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/accounts/models.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="260">
<caret line="13" lean-forward="true" selection-start-line="13" selection-end-line="13" />
<folding>
<element signature="e#0#51#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/accounts/login.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-103" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/accounts/user_form.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/templates/accounts/user_list.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/templates/accounts/user_confirm_delete.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/master_data/models.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/master_data/views.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-183">
<caret line="6" column="47" lean-forward="true" selection-start-line="6" selection-start-column="47" selection-end-line="6" selection-end-column="47" />
<folding>
<element signature="e#0#32#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/base.html">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</component>
</project>

30
README.md Normal file
View File

@@ -0,0 +1,30 @@
# Eventify - Django
This repository contains a production-oriented Django project skeleton for the Eventify application.
## Features
- Custom `User` model
- EventType (categories), Event, EventImages models
- CRUD for EventType, Event, and Users
- Bootstrap-based templates and navigation
- Settings prepared to use environment variables for production
## Quick start (development)
1. Create a virtualenv and activate it
```bash
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
2. Run migrations and create superuser
```bash
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
```
## Production notes
- Set `DJANGO_SECRET_KEY`, `DJANGO_DEBUG`, and `DJANGO_ALLOWED_HOSTS` environment variables
- Collect static files with `python manage.py collectstatic`
- Serve via uWSGI/gunicorn + nginx or any WSGI server

10
accounts/admin.py Normal file
View File

@@ -0,0 +1,10 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User
@admin.register(User)
class UserAdmin(BaseUserAdmin):
fieldsets = BaseUserAdmin.fieldsets + (
('Extra', {'fields':('phone_number','role','is_customer','is_user')}),
)
list_display = ('username','email','phone_number','role','is_staff')

12
accounts/forms.py Normal file
View File

@@ -0,0 +1,12 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import User
class UserForm(UserCreationForm):
class Meta:
model = User
fields = ('username','email','phone_number','role','is_staff','is_customer','is_user')
from django.contrib.auth.forms import AuthenticationForm
class LoginForm(AuthenticationForm):
pass

View File

@@ -0,0 +1,48 @@
# Generated by Django 4.2.21 on 2025-11-26 22:07
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('phone_number', models.CharField(blank=True, max_length=15, null=True)),
('role', models.CharField(blank=True, max_length=50, null=True)),
('is_staff', models.BooleanField(default=False)),
('is_customer', models.BooleanField(default=False)),
('is_user', models.BooleanField(default=False)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

13
accounts/models.py Normal file
View File

@@ -0,0 +1,13 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
phone_number = models.CharField(max_length=15, blank=True, null=True)
role = models.CharField(max_length=50, blank=True, null=True)
is_staff = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
is_user = models.BooleanField(default=False)
def __str__(self):
return self.username

11
accounts/urls.py Normal file
View File

@@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
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'),
]

41
accounts/views.py Normal file
View File

@@ -0,0 +1,41 @@
from django.shortcuts import render
from django.views import generic
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import User
from .forms import UserForm
from events.models import Event
from master_data.models import EventType
def dashboard(request):
total_events = Event.objects.count()
total_categories = EventType.objects.count()
total_users = User.objects.count()
return render(request, 'dashboard.html', {
'total_events': total_events,
'total_categories': total_categories,
'total_users': total_users,
})
class UserListView(LoginRequiredMixin, generic.ListView):
model = User
template_name = 'accounts/user_list.html'
context_object_name = 'users'
paginate_by = 20
class UserCreateView(LoginRequiredMixin, generic.CreateView):
model = User
form_class = UserForm
template_name = 'accounts/user_form.html'
success_url = reverse_lazy('accounts:user_list')
class UserUpdateView(LoginRequiredMixin, generic.UpdateView):
model = User
form_class = UserForm
template_name = 'accounts/user_form.html'
success_url = reverse_lazy('accounts:user_list')
class UserDeleteView(LoginRequiredMixin, generic.DeleteView):
model = User
template_name = 'accounts/user_confirm_delete.html'
success_url = reverse_lazy('accounts:user_list')

0
eventify/__init__.py Normal file
View File

4
eventify/asgi.py Normal file
View File

@@ -0,0 +1,4 @@
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eventify.settings')
application = get_asgi_application()

85
eventify/settings.py Normal file
View File

@@ -0,0 +1,85 @@
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'change-me-in-production')
DEBUG = os.environ.get('DJANGO_DEBUG', 'False') == 'True'
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', '*').split(',')
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'master_data',
'events',
'accounts',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'eventify.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'eventify.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / 'static']
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
AUTH_USER_MODEL = 'accounts.User'
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'dashboard'
LOGOUT_REDIRECT_URL = 'login'

15
eventify/urls.py Normal file
View File

@@ -0,0 +1,15 @@
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
from accounts.views import dashboard
urlpatterns = [
path('admin/', admin.site.urls),
path('', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('dashboard/', dashboard, name='dashboard'),
path('master-data/', include('master_data.urls')),
path('events/', include('events.urls')),
path('accounts/', include('accounts.urls')),
]

4
eventify/wsgi.py Normal file
View File

@@ -0,0 +1,4 @@
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eventify.settings')
application = get_wsgi_application()

12
events/admin.py Normal file
View File

@@ -0,0 +1,12 @@
from django.contrib import admin
from .models import Event, EventImages
@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
list_display = ('id','name','start_date','end_date','event_type','event_status')
list_filter = ('event_status','event_type')
search_fields = ('name','place','district')
@admin.register(EventImages)
class EventImagesAdmin(admin.ModelAdmin):
list_display = ('id','event','is_primary')

11
events/forms.py Normal file
View File

@@ -0,0 +1,11 @@
from django import forms
from .models import Event
class EventForm(forms.ModelForm):
class Meta:
model = Event
exclude = ['created_date']
widgets = {
'start_date': forms.DateInput(attrs={'type':'date'}),
'end_date': forms.DateInput(attrs={'type':'date'}),
}

View File

@@ -0,0 +1,48 @@
# Generated by Django 4.2.21 on 2025-11-26 22:07
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('master_data', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Event',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_date', models.DateField(auto_now_add=True)),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('start_date', models.DateField()),
('end_date', models.DateField()),
('latitude', models.DecimalField(decimal_places=6, max_digits=9)),
('longitude', models.DecimalField(decimal_places=6, max_digits=9)),
('pincode', models.CharField(max_length=10)),
('district', models.CharField(max_length=100)),
('state', models.CharField(max_length=100)),
('place', models.CharField(max_length=200)),
('is_bookable', models.BooleanField(default=False)),
('is_eventify_event', models.BooleanField(default=True)),
('outside_event_url', models.URLField(default='NA')),
('event_status', models.CharField(choices=[('created', 'Created'), ('cancelled', 'Cancelled'), ('pending', 'Pending'), ('completed', 'Completed'), ('postponed', 'Postponed')], default='pending', max_length=250)),
('cancelled_reason', models.TextField(default='NA')),
('event_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='master_data.eventtype')),
],
),
migrations.CreateModel(
name='EventImages',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_primary', models.BooleanField(default=False)),
('event_image', models.ImageField(upload_to='event_images')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')),
],
),
]

View File

42
events/models.py Normal file
View File

@@ -0,0 +1,42 @@
from django.db import models
from master_data.models import EventType
class Event(models.Model):
created_date = models.DateField(auto_now_add=True)
name = models.CharField(max_length=200)
description = models.TextField()
start_date = models.DateField()
end_date = models.DateField()
latitude = models.DecimalField(max_digits=9, decimal_places=6)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
pincode = models.CharField(max_length=10)
district = models.CharField(max_length=100)
state = models.CharField(max_length=100)
place = models.CharField(max_length=200)
is_bookable = models.BooleanField(default=False)
is_eventify_event = models.BooleanField(default=True)
outside_event_url = models.URLField(default='NA')
event_type = models.ForeignKey(EventType, on_delete=models.CASCADE)
event_status = models.CharField(max_length=250, choices=[
('created', 'Created'),
('cancelled', 'Cancelled'),
('pending', 'Pending'),
('completed', 'Completed'),
('postponed', 'Postponed'),
], default='pending')
cancelled_reason = models.TextField(default='NA')
def __str__(self):
return f"{self.name} ({self.start_date})"
class EventImages(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
is_primary = models.BooleanField(default=False)
event_image = models.ImageField(upload_to='event_images')
def __str__(self):
return f"{self.event_image}"

11
events/urls.py Normal file
View File

@@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'events'
urlpatterns = [
path('', views.EventListView.as_view(), name='event_list'),
path('add/', views.EventCreateView.as_view(), name='event_add'),
path('<int:pk>/edit/', views.EventUpdateView.as_view(), name='event_edit'),
path('<int:pk>/delete/', views.EventDeleteView.as_view(), name='event_delete'),
]

28
events/views.py Normal file
View File

@@ -0,0 +1,28 @@
from django.views import generic
from django.urls import reverse_lazy
from .models import Event
from .forms import EventForm
from django.contrib.auth.mixins import LoginRequiredMixin
class EventListView(LoginRequiredMixin, generic.ListView):
model = Event
context_object_name = 'events'
template_name = 'events/event_list.html'
paginate_by = 10
class EventCreateView(LoginRequiredMixin, generic.CreateView):
model = Event
form_class = EventForm
template_name = 'events/event_form.html'
success_url = reverse_lazy('events:event_list')
class EventUpdateView(LoginRequiredMixin, generic.UpdateView):
model = Event
form_class = EventForm
template_name = 'events/event_form.html'
success_url = reverse_lazy('events:event_list')
class EventDeleteView(LoginRequiredMixin, generic.DeleteView):
model = Event
template_name = 'events/event_confirm_delete.html'
success_url = reverse_lazy('events:event_list')

8
manage.py Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python
import os, sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eventify.settings')
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

7
master_data/admin.py Normal file
View File

@@ -0,0 +1,7 @@
from django.contrib import admin
from .models import EventType
@admin.register(EventType)
class EventTypeAdmin(admin.ModelAdmin):
list_display = ('id','event_type')
search_fields = ('event_type',)

7
master_data/forms.py Normal file
View File

@@ -0,0 +1,7 @@
from django import forms
from .models import EventType
class EventTypeForm(forms.ModelForm):
class Meta:
model = EventType
fields = ['event_type']

View File

@@ -0,0 +1,24 @@
# Generated by Django 4.2.21 on 2025-11-26 22:07
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='EventType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_type', models.CharField(max_length=50)),
],
options={
'db_table': 'master_data_event_type',
},
),
]

View File

10
master_data/models.py Normal file
View File

@@ -0,0 +1,10 @@
from django.db import models
class EventType(models.Model):
event_type = models.CharField(max_length=50, null=False, blank=False)
def __str__(self):
return self.event_type
class Meta:
db_table = 'master_data_event_type'

11
master_data/urls.py Normal file
View File

@@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'master_data'
urlpatterns = [
path('event-types/', views.EventTypeListView.as_view(), name='event_type_list'),
path('event-types/add/', views.EventTypeCreateView.as_view(), name='event_type_add'),
path('event-types/<int:pk>/edit/', views.EventTypeUpdateView.as_view(), name='event_type_edit'),
path('event-types/<int:pk>/delete/', views.EventTypeDeleteView.as_view(), name='event_type_delete'),
]

27
master_data/views.py Normal file
View File

@@ -0,0 +1,27 @@
from django.views import generic
from django.urls import reverse_lazy
from .models import EventType
from .forms import EventTypeForm
from django.contrib.auth.mixins import LoginRequiredMixin
class EventTypeListView(LoginRequiredMixin, generic.ListView):
model = EventType
template_name = 'master_data/event_type_list.html'
context_object_name = 'categories'
class EventTypeCreateView(LoginRequiredMixin, generic.CreateView):
model = EventType
form_class = EventTypeForm
template_name = 'master_data/event_type_form.html'
success_url = reverse_lazy('master_data:event_type_list')
class EventTypeUpdateView(LoginRequiredMixin, generic.UpdateView):
model = EventType
form_class = EventTypeForm
template_name = 'master_data/event_type_form.html'
success_url = reverse_lazy('master_data:event_type_list')
class EventTypeDeleteView(LoginRequiredMixin, generic.DeleteView):
model = EventType
template_name = 'master_data/event_type_confirm_delete.html'
success_url = reverse_lazy('master_data:event_type_list')

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
Django>=4.2
Pillow

0
static/.gitkeep Normal file
View File

BIN
templates.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-4">
<div class="card p-4 shadow-sm">
<h3 class="text-center mb-3">Login</h3>
<form method="post" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
<div class="mb-3">{{ form.username.label_tag }}{{ form.username }}</div>
<div class="mb-3">{{ form.password.label_tag }}{{ form.password }}</div>
<button class="btn btn-primary w-100">Login</button>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<h3>Delete User</h3>
<p>Are you sure you want to delete <strong>{{ object.username }}</strong>?</p>
<form method="post">{% csrf_token %}<button class="btn btn-danger">Yes, delete</button>
<a class="btn btn-secondary" href="{% url 'accounts:user_list' %}">Cancel</a></form>
{% endblock %}

View File

@@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block content %}
<h3>{% if object %}Edit{% else %}Add{% endif %} User</h3>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary">Save</button>
<a class="btn btn-secondary" href="{% url 'accounts:user_list' %}">Cancel</a>
</form>
{% endblock %}

View File

@@ -0,0 +1,27 @@
{% extends 'base.html' %}
{% block content %}
<div class="d-flex justify-content-between mb-3">
<h3>Users</h3>
<a class="btn btn-success" href="{% url 'accounts:user_add' %}">Add User</a>
</div>
<table class="table table-striped">
<thead><tr><th>#</th><th>Username</th><th>Email</th><th>Phone</th><th>Role</th><th>Actions</th></tr></thead>
<tbody>
{% for u in users %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ u.username }}</td>
<td>{{ u.email }}</td>
<td>{{ u.phone_number }}</td>
<td>{{ u.role }}</td>
<td>
<a class="btn btn-sm btn-primary" href="{% url 'accounts:user_edit' u.pk %}">Edit</a>
<a class="btn btn-sm btn-danger" href="{% url 'accounts:user_delete' u.pk %}">Delete</a>
</td>
</tr>
{% empty %}
<tr><td colspan="6">No users yet.</td></tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

44
templates/base.html Normal file
View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Eventify</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'dashboard' %}">Eventify</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navmenu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navmenu">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link" href="{% url 'dashboard' %}">Dashboard</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'master_data:event_type_list' %}">Categories</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'events:event_list' %}">Events</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'accounts:user_list' %}">Users</a></li>
</ul>
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="#">{{ user.username }}</a></li>
<li class="nav-item"><a class="nav-link text-danger" href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="{% url 'login' %}">Login</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

23
templates/dashboard.html Normal file
View File

@@ -0,0 +1,23 @@
{% extends 'base.html' %}
{% block content %}
<div class="row text-center">
<div class="col-md-4">
<div class="card p-3 shadow-sm">
<h5>Total Events</h5>
<h2>{{ total_events }}</h2>
</div>
</div>
<div class="col-md-4">
<div class="card p-3 shadow-sm">
<h5>Total Categories</h5>
<h2>{{ total_categories }}</h2>
</div>
</div>
<div class="col-md-4">
<div class="card p-3 shadow-sm">
<h5>Total Users</h5>
<h2>{{ total_users }}</h2>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<h3>Delete Event</h3>
<p>Are you sure you want to delete <strong>{{ object.name }}</strong>?</p>
<form method="post">{% csrf_token %}<button class="btn btn-danger">Yes, delete</button>
<a class="btn btn-secondary" href="{% url 'events:event_list' %}">Cancel</a></form>
{% endblock %}

View File

@@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block content %}
<h3>{% if object %}Edit{% else %}Add{% endif %} Event</h3>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary">Save</button>
<a class="btn btn-secondary" href="{% url 'events:event_list' %}">Cancel</a>
</form>
{% endblock %}

View File

@@ -0,0 +1,27 @@
{% extends 'base.html' %}
{% block content %}
<div class="d-flex justify-content-between mb-3">
<h3>Events</h3>
<a class="btn btn-success" href="{% url 'events:event_add' %}">Add Event</a>
</div>
<table class="table table-hover">
<thead><tr><th>#</th><th>Name</th><th>Type</th><th>Dates</th><th>Place</th><th>Actions</th></tr></thead>
<tbody>
{% for e in events %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ e.name }}</td>
<td>{{ e.event_type }}</td>
<td>{{ e.start_date }} - {{ e.end_date }}</td>
<td>{{ e.place }}</td>
<td>
<a class="btn btn-sm btn-primary" href="{% url 'events:event_edit' e.pk %}">Edit</a>
<a class="btn btn-sm btn-danger" href="{% url 'events:event_delete' e.pk %}">Delete</a>
</td>
</tr>
{% empty %}
<tr><td colspan="6">No events found.</td></tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<h3>Delete Category</h3>
<p>Are you sure you want to delete <strong>{{ object.event_type }}</strong>?</p>
<form method="post">{% csrf_token %}<button class="btn btn-danger">Yes, delete</button>
<a class="btn btn-secondary" href="{% url 'master_data:event_type_list' %}">Cancel</a></form>
{% endblock %}

View File

@@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block content %}
<h3>{% if object %}Edit{% else %}Add{% endif %} Category</h3>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary">Save</button>
<a class="btn btn-secondary" href="{% url 'master_data:event_type_list' %}">Cancel</a>
</form>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'base.html' %}
{% block content %}
<div class="d-flex justify-content-between mb-3">
<h3>Event Categories</h3>
<a class="btn btn-success" href="{% url 'master_data:event_type_add' %}">Add Category</a>
</div>
<table class="table table-striped">
<thead><tr><th>#</th><th>Event Type</th><th>Actions</th></tr></thead>
<tbody>
{% for c in categories %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ c.event_type }}</td>
<td>
<a class="btn btn-sm btn-primary" href="{% url 'master_data:event_type_edit' c.pk %}">Edit</a>
<a class="btn btn-sm btn-danger" href="{% url 'master_data:event_type_delete' c.pk %}">Delete</a>
</td>
</tr>
{% empty %}
<tr><td colspan="3">No categories yet.</td></tr>
{% endfor %}
</tbody>
</table>
{% endblock %}