# Commit Message #24

Merged
Saani merged 1 commits from feature/meetings into main 2025-11-23 23:07:04 +00:00
7 changed files with 78 additions and 27 deletions
Showing only changes of commit d736c1681c - Show all commits

2
Procfile Normal file
View File

@ -0,0 +1,2 @@
release: python manage.py migrate
web: gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT --workers 3

View File

@ -9,10 +9,16 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv('JWT_SECRET', 'django-insecure-fallback-secret-key') SECRET_KEY = os.getenv('JWT_SECRET', 'django-insecure-fallback-secret-key')
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' DEBUG = os.getenv('DEBUG')
ALLOWED_HOSTS = ["*"] # Update ALLOWED_HOSTS for production
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '*').split(',')
# Update CORS for production
CORS_ALLOWED_ORIGINS = os.getenv(
'CORS_ALLOWED_ORIGINS',
'http://localhost:3000,http://127.0.0.1:3000'
).split(',')
@ -71,24 +77,24 @@ if not DEBUG:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# 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': os.getenv('POSTGRES_DB'), 'NAME': BASE_DIR / 'db.sqlite3',
'USER': os.getenv('POSTGRES_USER'),
'PASSWORD': os.getenv('POSTGRES_PASSWORD'),
'HOST': os.getenv('POSTGRES_HOST', 'postgres'),
'PORT': os.getenv('POSTGRES_PORT', 5432),
} }
} }
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql',
# 'NAME': os.getenv('POSTGRES_DB'),
# 'USER': os.getenv('POSTGRES_USER'),
# 'PASSWORD': os.getenv('POSTGRES_PASSWORD'),
# 'HOST': os.getenv('POSTGRES_HOST', 'postgres'),
# 'PORT': os.getenv('POSTGRES_PORT', 5432),
# }
# }
ENCRYPTION_KEY = os.getenv('ENCRYPTION_KEY') ENCRYPTION_KEY = os.getenv('ENCRYPTION_KEY')

View File

@ -293,7 +293,8 @@ def api_root(request, format=None):
"rejected": "Number of rejected requests", "rejected": "Number of rejected requests",
"completion_rate": "Percentage of requests that were scheduled" "completion_rate": "Percentage of requests that were scheduled"
} }
} },
}, },
"jitsi_integration": { "jitsi_integration": {
"description": "Automatic Jitsi video meeting integration", "description": "Automatic Jitsi video meeting integration",

View File

@ -1,20 +1,35 @@
FROM python:3.12-slim FROM python:3.11-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
# Set work directory
WORKDIR /app WORKDIR /app
RUN apt-get update && apt-get install -y \ # Install dependencies
build-essential \ COPY requirements.txt /app/
curl wget \ RUN pip install --upgrade pip && \
&& rm -rf /var/lib/apt/lists/* pip install -r requirements.txt
COPY requirements.txt . # Copy project
RUN pip install --no-cache-dir -r requirements.txt COPY . /app/
COPY . . # Collect static files
RUN python manage.py collectstatic --noinput
# Expose port
EXPOSE 8000 EXPOSE 8000
CMD ["bash", "-c", "python manage.py migrate && python manage.py collectstatic --noinput && python manage.py runserver 0.0.0.0:8000"] # Run gunicorn
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]
```
**Optional: Create `.dockerignore`:**
```
*.pyc
__pycache__
db.sqlite3
.env
.git
venv/

View File

@ -8,7 +8,8 @@ from .views import (
reject_appointment, reject_appointment,
available_dates, available_dates,
user_appointments, user_appointments,
appointment_stats appointment_stats,
user_apointment_stats
) )
urlpatterns = [ urlpatterns = [
@ -25,4 +26,5 @@ urlpatterns = [
path('user/appointments/', user_appointments, name='user-appointments'), path('user/appointments/', user_appointments, name='user-appointments'),
path('appointments/stats/', appointment_stats, name='appointment-stats'), path('appointments/stats/', appointment_stats, name='appointment-stats'),
path('user/appointments/stats/', user_apointment_stats, name='user-appointment-stats'),
] ]

View File

@ -168,4 +168,28 @@ def appointment_stats(request):
'rejected': rejected, 'rejected': rejected,
'users': users, 'users': users,
'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0 'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0
}) })
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def user_apointment_stats(request):
if not request.user.is_staff:
return Response(
{'error': 'Unauthorized'},
status=status.HTTP_403_FORBIDDEN
)
total = AppointmentRequest.objects.filter(email=request.user.email).count()
pending = AppointmentRequest.objects.filter(email=request.user.email, status='pending_review').count()
scheduled = AppointmentRequest.objects.filter(email=request.user.email, status='scheduled').count()
rejected = AppointmentRequest.objects.filter(email=request.user.email, status='rejected').count()
completed = AppointmentRequest.objects.filter(email=request.user.email, status='completed').count()
return Response({
'total_requests': total,
'pending_review': pending,
'scheduled': scheduled,
'rejected': rejected,
'completed': completed,
'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0
})

View File

@ -24,6 +24,7 @@ class CustomUserManager(BaseUserManager):
extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True) extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True) extra_fields.setdefault('is_active', True)
extra_fields.setdefault('isVerified', True)
if extra_fields.get('is_staff') is not True: if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.')) raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True: if extra_fields.get('is_superuser') is not True: