38146-vm/core/views.py
2026-02-04 16:21:56 +00:00

449 lines
17 KiB
Python

from django.core.exceptions import PermissionDenied
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import translation
from django.conf import settings
from django.http import JsonResponse, FileResponse, Http404
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.contrib.auth import logout, login
from django.urls import reverse
from django.db.models import Q
from .models import Classroom, Subject, Teacher, Student, City, Resource, Package
from .forms import StudentRegistrationForm, TeacherProfileForm, ResourceForm
from .wablas import send_whatsapp_message
from .thawani import ThawaniClient
import random
import string
import mimetypes
import os
def index(request):
levels = Classroom.objects.prefetch_related('subjects').all()
teachers = Teacher.objects.select_related('user').all()
context = {'levels': levels, 'teachers': teachers}
return render(request, 'core/index.html', context)
def set_language(request, lang_code):
next_url = request.GET.get('next', '/')
response = redirect(next_url)
if lang_code in [lang[0] for lang in settings.LANGUAGES]:
translation.activate(lang_code)
if hasattr(request, 'session'):
request.session['_language'] = lang_code
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code)
return response
def subject_detail(request, pk):
subject = get_object_or_404(Subject, pk=pk)
is_teacher = False
if request.user.is_authenticated and hasattr(request.user, 'teacher_profile'):
if request.user.teacher_profile in subject.teachers.all():
is_teacher = True
return render(request, 'core/subject_detail.html', {'subject': subject, 'is_teacher': is_teacher})
@staff_member_required
def get_subjects_by_level(request):
level_id = request.GET.get('level_id') or request.GET.get('classroom_id')
if not level_id:
return JsonResponse([], safe=False)
subjects = Subject.objects.filter(classroom_id=level_id).values('id', 'name_en', 'name_ar')
return JsonResponse(list(subjects), safe=False)
def get_classroom_subjects(request):
classroom_id = request.GET.get('classroom_id')
if not classroom_id:
return JsonResponse([], safe=False)
subjects = Subject.objects.filter(classroom_id=classroom_id).values('id', 'name_en', 'name_ar', 'price')
return JsonResponse(list(subjects), safe=False)
def get_cities_by_governorate(request):
governorate_id = request.GET.get('governorate_id')
if not governorate_id:
return JsonResponse([], safe=False)
cities = City.objects.filter(governorate_id=governorate_id).values('id', 'name_en', 'name_ar')
return JsonResponse(list(cities), safe=False)
def generate_otp():
return ''.join(random.choices(string.digits, k=6))
def register_student(request):
if request.method == 'POST':
form = StudentRegistrationForm(request.POST, request.FILES)
if form.is_valid():
student = form.save()
# Generate OTPs
mobile_otp = generate_otp()
email_otp = generate_otp()
student.mobile_otp_code = mobile_otp
student.email_otp_code = email_otp
student.save()
# Send OTP via WhatsApp
if student.mobile_number:
# Attempt to send WhatsApp message
# Note: This will only work if Wablas is configured in Admin
send_whatsapp_message(
student.mobile_number,
f"رمز التحقق الخاص بك هو: {mobile_otp}"
)
# Simulate sending OTPs (Log to console)
print(f"========================================")
print(f"SIMULATED OTP SENDING:")
print(f"User: {student.user.username}")
print(f"Mobile OTP for {student.mobile_number}: {mobile_otp}")
print(f"Email OTP for {student.user.email}: {email_otp}")
print(f"========================================")
# Log the user in
login(request, student.user)
# Redirect to verification page
return redirect('verify_otp')
else:
form = StudentRegistrationForm()
return render(request, 'core/registration.html', {'form': form})
@login_required
def verify_otp(request):
try:
student = request.user.student_profile
except Student.DoesNotExist:
return redirect('index')
if student.is_mobile_verified and student.is_email_verified:
return redirect('profile')
error = None
if request.method == 'POST':
entered_mobile_otp = request.POST.get('mobile_otp')
entered_email_otp = request.POST.get('email_otp')
mobile_ok = student.is_mobile_verified
email_ok = student.is_email_verified
if not mobile_ok:
if entered_mobile_otp == student.mobile_otp_code:
student.is_mobile_verified = True
mobile_ok = True
else:
error = "رمز الهاتف غير صحيح"
if not email_ok and (error is None or "الهاتف" not in error):
if entered_email_otp == student.email_otp_code:
student.is_email_verified = True
email_ok = True
else:
error = "رمز البريد غير صحيح"
if mobile_ok and email_ok:
student.mobile_otp_code = "" # Clear codes
student.email_otp_code = ""
student.save()
return redirect('profile')
else:
student.save() # Save partial verification if any
return render(request, 'core/verify_otp.html', {'error': error, 'student': student})
@login_required
def profile(request):
user = request.user
# Check for Teacher
if hasattr(user, 'teacher_profile'):
teacher_profile = user.teacher_profile
subjects = teacher_profile.subjects.all()
return render(request, 'core/teacher_dashboard.html', {
'teacher_profile': teacher_profile,
'subjects': subjects
})
# Check for Student
elif hasattr(user, 'student_profile'):
student_profile = user.student_profile
subscribed_subjects = student_profile.subscribed_subjects.all()
# Get available subjects (in same classroom, not yet subscribed)
available_subjects = []
if student_profile.classroom:
available_subjects = Subject.objects.filter(
classroom=student_profile.classroom
).exclude(
id__in=subscribed_subjects.values_list('id', flat=True)
)
# Get active packages
# Filter by student's classroom or global packages (classroom is None)
packages = Package.objects.filter(is_active=True)
if student_profile.classroom:
packages = packages.filter(Q(classroom=student_profile.classroom) | Q(classroom__isnull=True))
else:
# If student has no classroom, show only global packages
packages = packages.filter(classroom__isnull=True)
return render(request, 'core/student_dashboard.html', {
'student_profile': student_profile,
'subscribed_subjects': subscribed_subjects,
'available_subjects': available_subjects,
'packages': packages
})
# Fallback (Superuser or Admin without profile)
else:
student_profile = None
return render(request, 'core/profile.html', {'student_profile': student_profile})
@login_required
def edit_teacher_profile(request):
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
return redirect('profile')
if request.method == 'POST':
form = TeacherProfileForm(request.POST, request.FILES, instance=teacher)
if form.is_valid():
form.save()
return redirect('profile')
else:
form = TeacherProfileForm(instance=teacher)
return render(request, 'core/edit_teacher_profile.html', {'form': form})
def custom_logout(request):
logout(request)
return redirect('index')
@login_required
def subscribe_subject(request, subject_id):
try:
student = request.user.student_profile
except Student.DoesNotExist:
return redirect('index')
subject = get_object_or_404(Subject, pk=subject_id)
# Check if already subscribed
if subject in student.subscribed_subjects.all():
return redirect('profile')
try:
thawani = ThawaniClient()
success_url = request.build_absolute_uri(reverse('payment_success')) + '?session_id={session_id}'
cancel_url = request.build_absolute_uri(reverse('payment_cancel'))
session = thawani.create_checkout_session(subject, request.user, success_url, cancel_url)
session_id = session.get('data', {}).get('session_id')
if not session_id:
return render(request, 'core/error.html', {'message': 'تعذر إنشاء جلسة الدفع.'})
return redirect(f"{thawani.checkout_base_url}/{session_id}")
except Exception as e:
print(f"Payment Error: {e}")
return render(request, 'core/error.html', {'message': f'فشل بدء عملية الدفع: {str(e)}'})
@login_required
def subscribe_package(request, package_id):
try:
student = request.user.student_profile
except Student.DoesNotExist:
return redirect('index')
package = get_object_or_404(Package, pk=package_id)
try:
thawani = ThawaniClient()
success_url = request.build_absolute_uri(reverse('payment_success')) + '?session_id={session_id}'
cancel_url = request.build_absolute_uri(reverse('payment_cancel'))
session = thawani.create_checkout_session(package, request.user, success_url, cancel_url)
session_id = session.get('data', {}).get('session_id')
if not session_id:
return render(request, 'core/error.html', {'message': 'تعذر إنشاء جلسة الدفع.'})
return redirect(f"{thawani.checkout_base_url}/{session_id}")
except Exception as e:
print(f"Payment Error: {e}")
return render(request, 'core/error.html', {'message': f'فشل بدء عملية الدفع: {str(e)}'})
@login_required
def payment_success(request):
session_id = request.GET.get('session_id')
if not session_id:
return redirect('profile')
try:
thawani = ThawaniClient()
response = thawani.get_checkout_session(session_id)
data = response.get('data', {})
payment_status = data.get('payment_status')
metadata = data.get('metadata', {})
subject_id = metadata.get('subject_id')
package_id = metadata.get('package_id')
if payment_status == 'paid':
student = request.user.student_profile
if subject_id:
try:
subject = get_object_or_404(Subject, pk=subject_id)
student.subscribed_subjects.add(subject)
except Exception:
pass
if package_id:
try:
package = get_object_or_404(Package, pk=package_id)
for subject in package.subjects.all():
student.subscribed_subjects.add(subject)
except Exception:
pass
return redirect('profile')
else:
return render(request, 'core/error.html', {'message': f'لم تتم عملية الدفع بنجاح. الحالة: {payment_status}'})
except Exception as e:
print(f"Payment Verification Error: {e}")
return render(request, 'core/error.html', {'message': f'فشل التحقق من الدفع: {str(e)}'})
@login_required
def payment_cancel(request):
return redirect('profile')
@login_required
def live_classroom(request, subject_id):
subject = get_object_or_404(Subject, pk=subject_id)
is_teacher = False
is_student = False
if hasattr(request.user, 'teacher_profile') and request.user.teacher_profile in subject.teachers.all():
is_teacher = True
if hasattr(request.user, 'student_profile') and request.user.student_profile.subscribed_subjects.filter(pk=subject_id).exists():
is_student = True
if not (is_teacher or is_student):
raise PermissionDenied("ليس لديك صلاحية الانضمام لهذا الفصل.")
# Generate Room Name
# We use a consistent room name based on Subject ID
room_name = f"EduPlatform_Live_Subject_{subject.id}"
return render(request, 'core/live_classroom.html', {
'subject': subject,
'room_name': room_name,
'user_display_name': request.user.get_full_name() or request.user.username,
'is_teacher': is_teacher
})
@login_required
def add_resource(request, subject_id):
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
raise PermissionDenied("يجب أن تكون معلماً للوصول إلى هذه الصفحة.")
subject = get_object_or_404(Subject, pk=subject_id)
if teacher not in subject.teachers.all():
raise PermissionDenied("ليس لديك صلاحية لإضافة مصادر لهذه المادة.")
if request.method == 'POST':
form = ResourceForm(request.POST, request.FILES)
if form.is_valid():
resource = form.save(commit=False)
resource.subject = subject
resource.save()
return redirect('subject_detail', pk=subject.id)
else:
form = ResourceForm()
return render(request, 'core/add_resource.html', {'form': form, 'subject': subject})
@login_required
def edit_resource(request, resource_id):
resource = get_object_or_404(Resource, pk=resource_id)
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
raise PermissionDenied("يجب أن تكون معلماً للوصول إلى هذه الصفحة.")
if teacher not in resource.subject.teachers.all():
raise PermissionDenied("ليس لديك صلاحية لتعديل هذا المصدر.")
if request.method == 'POST':
form = ResourceForm(request.POST, request.FILES, instance=resource)
if form.is_valid():
form.save()
return redirect('subject_detail', pk=resource.subject.id)
else:
form = ResourceForm(instance=resource)
return render(request, 'core/edit_resource.html', {'form': form, 'resource': resource, 'subject': resource.subject})
@login_required
def delete_resource(request, resource_id):
resource = get_object_or_404(Resource, pk=resource_id)
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
raise PermissionDenied("يجب أن تكون معلماً للوصول إلى هذه الصفحة.")
if teacher not in resource.subject.teachers.all():
raise PermissionDenied("ليس لديك صلاحية لحذف هذا المصدر.")
subject_id = resource.subject.id
if request.method == 'POST':
resource.delete()
return redirect('subject_detail', pk=subject_id)
return render(request, 'core/delete_resource.html', {'resource': resource})
@login_required
def view_resource(request, resource_id):
resource = get_object_or_404(Resource, pk=resource_id)
has_access = False
# Check Teacher Access
if hasattr(request.user, 'teacher_profile') and request.user.teacher_profile in resource.subject.teachers.all():
has_access = True
# Check Student Access
elif hasattr(request.user, 'student_profile') and resource.subject in request.user.student_profile.subscribed_subjects.all():
has_access = True
if not has_access:
raise PermissionDenied("ليس لديك صلاحية لعرض هذا المصدر.")
if resource.resource_type == 'FILE' and resource.file:
try:
# Open file
file_handle = resource.file.open('rb')
response = FileResponse(file_handle)
# Check for PDF to force inline display
filename = resource.file.name
if filename.lower().endswith('.pdf'):
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = f'inline; filename="{os.path.basename(filename)}"'
return response
except FileNotFoundError:
raise Http404("الملف غير موجود على الخادم.")
elif resource.resource_type in ['LINK', 'VIDEO']:
return redirect(resource.link)
return redirect('subject_detail', pk=resource.subject.id)