from django.utils import timezone import uuid from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.urls import reverse from django.contrib import messages from .models import QivipQuiz, QivipQuestion, QivipQuizFavorite, QuestionImage, QuizImage from .forms import QuizForm, QuestionForm import json from django.core.paginator import Paginator from .models import QivipQuiz from .forms import QuizFilterForm from django.db.models import Count from django.contrib.auth.models import User # Übersicht aller Quizze def quiz_names_json(request): form = QuizFilterForm(request.GET) if form.is_valid(): search = form.cleaned_data.get('search') names = QivipQuiz.objects.all().annotate(max_amout_questions=Count('questions')).filter(max_amout_questions__gte=1, status='öffentlich', name__icontains=search) if request.user.is_authenticated: names = names | QivipQuiz.objects.filter( user_id=request.user.id, status='privat', name__icontains=search ) names=list(names.values_list('name', flat=True).distinct()) return JsonResponse(names, safe=False) def overview_quiz(request): # Filter form = QuizFilterForm(request.GET) try: favorite_quizzes_data = QivipQuizFavorite.objects.filter(user=request.user, favorite=True).order_by("-favorite_date") favorite_quiz_ids = favorite_quizzes_data.values_list('quiz', flat=True) favorite_quizzes = QivipQuiz.objects.filter(id__in=favorite_quiz_ids) except: favorite_quizzes_data = QivipQuizFavorite.objects.none() favorite_quizzes = QivipQuizFavorite.objects.none() favorite_quiz_ids = favorite_quizzes.none() favorite_quizzes = QivipQuiz.objects.none() # Initialize querysets if request.user.is_authenticated: filter_my_quizzes = QivipQuiz.objects.filter(user_id=request.user).annotate(max_amout_questions=Count('questions')) favorite_quizzes = favorite_quizzes.filter(id__in=favorite_quiz_ids).annotate(max_amout_questions=Count('questions')) filter_other_quizzes = QivipQuiz.objects.exclude(user_id=request.user) else: favorite_quizzes = QivipQuiz.objects.none() filter_my_quizzes = QivipQuiz.objects.none() filter_other_quizzes = QivipQuiz.objects.all() # Apply common filters to other quizzes filter_other_quizzes = filter_other_quizzes.annotate(max_amout_questions=Count('questions')) filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__gte=1, status='öffentlich') if form.is_valid(): search = form.cleaned_data.get('search') username= form.cleaned_data.get('user') max_amout_questions= form.cleaned_data.get('max_amout_questions') min_amout_questions= form.cleaned_data.get('min_amout_questions') filters = {} if username: try: user = User.objects.get(username=username) filters['user_id'] = user.id except User.DoesNotExist: favorite_quizzes = QivipQuiz.objects.none() filter_my_quizzes = QivipQuiz.objects.none() filter_other_quizzes=QivipQuiz.objects.none() if search: filters['name__icontains'] = search if min_amout_questions: filters['max_amout_questions__gte'] = min_amout_questions if max_amout_questions: filters['max_amout_questions__lte'] = max_amout_questions try: filter_other_quizzes = filter_other_quizzes.filter(**filters) except: filter_other_quizzes=QivipQuiz.objects.none() try: filter_my_quizzes = filter_my_quizzes.filter(**filters) except: filter_my_quizzes=QivipQuiz.objects.none() try: favorite_quizzes = favorite_quizzes.filter(**filters) except: favorite_quizzes=QivipQuiz.objects.none() # Pagination for my quizzes my_quizzes_paginator = Paginator(filter_my_quizzes.order_by('-creation_date'), 8) # 8 quizzes per page try: my_quizzes = my_quizzes_paginator.page(request.GET.get('my_page', 1)) except: my_quizzes = my_quizzes_paginator.page(1) # Pagination for other quizzes other_quizzes_paginator = Paginator(filter_other_quizzes.order_by('-creation_date'), 8) # 8 quizzes per page try: other_quizzes = other_quizzes_paginator.page(request.GET.get('other_page', 1)) except: other_quizzes = other_quizzes_paginator.page(1) try: favorite_quizzes = favorite_quizzes.filter( favorites__user=request.user, # Filtere nach dem User favorites__favorite=True # Filtere nach favorisierten Quizzes ).order_by('-favorites__favorite_date') except: pass # Pagination for favorite_quizzes favorite_quizzes_paginator = Paginator(favorite_quizzes, 8) # 8 quizzes per page try: favorite_quizzes = favorite_quizzes_paginator.page(request.GET.get('my_page', 1)) except: favorite_quizzes = favorite_quizzes_paginator.page(1) context = { 'show_search': True, 'favorite_quiz_ids': favorite_quiz_ids, 'favorite_quizzes':favorite_quizzes, 'my_quizzes': my_quizzes, 'other_quizzes': other_quizzes, 'form': form, } return render(request, 'library/overview_quiz.html', context) # Neues Quiz erstellen @login_required def favorite_quiz(request, pk): quiz= get_object_or_404(QivipQuiz, pk=pk) favorite = QivipQuizFavorite.objects.filter(user=request.user, quiz=quiz).first() if favorite: favorite.favorite=not favorite.favorite if favorite.favorite==True: favorite.favorite_date = timezone.now() favorite.save() else: favorite.favorite_date = None favorite.delete() statement="Das Quiz '"+ favorite.quiz.name+ "' wurde erfolgreich von den Favoriten entfernt!" messages.error(request, statement) else: favorite=QivipQuizFavorite.objects.create(user=request.user, quiz=quiz, favorite=True, favorite_date=timezone.now()) statement="Das Quiz '"+ favorite.quiz.name+ "' wurde erfolgreich zu den Favoriten hinzugefügt!" messages.success(request,statement ) return redirect(request.META.get('HTTP_REFERER', 'library:overview_quiz')) # Neues Quiz erstellen @login_required def new_quiz(request): if request.method == 'POST': form = QuizForm(request.POST, request.FILES) if form.is_valid(): quiz = form.save(commit=False) try: quiz_image=request.FILES['quiz_image'] imagequiz=QuizImage.objects.create(image=quiz_image) quiz.image=imagequiz except: pass quiz.user_id = request.user #quiz.creator = request.user quiz.save() #form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags) return redirect('library:detail_quiz', pk=quiz.pk) else: form = QuizForm() return render(request, 'library/form.html', {'form': form}) # Quiz bearbeiten @login_required def edit_quiz(request, pk): quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user) if request.method == 'POST': form = QuizForm(request.POST, instance=quiz, files=request.FILES) if form.is_valid(): if 'reset_image' in request.POST: quiz.image = None try: quiz_image=request.FILES['quiz_image'] imagequiz=QuizImage.objects.create(image=quiz_image) quiz.image=imagequiz except: pass form.save() for img in QuizImage.objects.all(): try: img.delete() img.image.delete(save=False) except: pass return redirect('library:detail_quiz', pk=pk) #return modified(request, pk) else: form = QuizForm(instance=quiz) return render(request, 'library/form.html', {'form': form}) # Quiz löschen @login_required def delete_quiz(request, pk): quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user) if request.method == 'POST': quiz.delete() for img in QuizImage.objects.all(): try: img.delete() img.image.delete(save=False) except: pass for img in QuestionImage.objects.all(): try: img.delete() img.image.delete(save=False) except: pass return redirect('library:overview_quiz') return render(request, 'library/delete_confirmation.html', {'object': quiz}) # Quiz anzeigen def detail_quiz(request, pk): quiz = get_object_or_404(QivipQuiz, pk=pk) show_answers = request.GET.get('show_answers', 'false').lower() == 'true' questions = QivipQuestion.objects.filter(quiz_id=quiz) # Parse JSON data for each question for question in questions: if question.data: question.data = json.loads(question.data) context = { 'quiz': quiz, 'questions': questions, 'detail':show_answers, } if quiz.status!="privat" or quiz.user_id==request.user: return render(request, 'library/detail_quiz.html', context) else: return redirect('library:overview_quiz') @login_required def copy_quiz(request, pk): original_quiz = get_object_or_404(QivipQuiz, pk=pk) # Quiz kopieren (ohne ID, damit ein neues Objekt erstellt wird) # Quiz kopieren (ohne ID, damit ein neues Objekt erstellt wird) new_quiz = original_quiz # Kopie erstellen (aber Achtung: Noch gleiche Referenz!) new_quiz.pk = None # Setzt die ID auf None, damit Django es als neues Objekt erkennt new_quiz.uuid = uuid.uuid4() # Neue UUID generieren new_quiz.user_id = request.user # Neuer Besitzer ist der aktuelle User new_quiz.name += " - Kopie" new_quiz.base_quiz_id = pk new_quiz.rating_count = 0 new_quiz.average_rating = 3.0 new_quiz.save() # Speichern als neues Objekt # Optional: Beschreibung anpassen # Alle zugehörigen Fragen kopieren questions = QivipQuestion.objects.filter(quiz_id=pk) for question in questions: question.uuid = uuid.uuid4() question.pk = None # Setze pk auf None, damit es als neues Objekt gespeichert wird question.quiz_id = original_quiz # Verknüpfe mit dem neuen Quiz question.data = question.data # `data`-Feld wird explizit übernommen # Verknüpfe mit dem neuen Quiz question.save() messages.success(request, "Quiz wurde erfolgreich kopiert!") return redirect('library:detail_quiz', pk=new_quiz.pk) # Übersicht aller Fragen @login_required def question_list(request): questions = QivipQuestion.objects.filter(quiz_id__user_id=request.user) # Parse JSON data for each question for question in questions: if question.data: question.data = json.loads(question.data) return render(request, 'library/question_list.html', {'questions': questions}) # Neue Frage erstellen @login_required def new_question(request): question_type = request.GET.get('type') quiz_id = request.GET.get('quiz_id') if not quiz_id: messages.error(request, 'Quiz ID muss angegeben werden.') return redirect('library:overview_quiz') try: quiz = QivipQuiz.objects.get(pk=quiz_id, user_id=request.user) except QivipQuiz.DoesNotExist: messages.error(request, 'Quiz nicht gefunden oder keine Berechtigung.') return redirect('library:overview_quiz') if question_type not in ['multiple_choice', 'true_false','input']: base_url = reverse('library:new_question') return redirect(f'{base_url}?type=multiple_choice&quiz_id={quiz_id}') if request.method == 'POST': question_text = request.POST.get('question', '') # Handle different question types if question_type == 'true_false': correct_answer = request.POST.get('correct_answer') == 'true' json_data = { 'type': question_type, 'question': question_text, 'options': [ {'value': 'Wahr', 'is_correct': correct_answer}, {'value': 'Falsch', 'is_correct': not correct_answer} ] } elif question_type == 'input': correct_answer = request.POST.get('correct_answer') json_data = { 'type': question_type, 'question': question_text, 'options': [ {'value': correct_answer, 'is_correct': True} ] } elif question_type == 'multiple_choice': options = [] i = 1 # Limit to maximum 6 options while request.POST.get(f'option_{i}') and i <= 6: is_correct = request.POST.get(f'correct_{i}') == '1' options.append({ 'order': i, 'value': request.POST.get(f'option_{i}'), 'is_correct': is_correct }) i += 1 # Validate minimum 2 options if len(options) < 2: messages.error(request, 'Mindestens zwei Optionen müssen angegeben werden.') return redirect(request.get_full_path()) json_data = { 'type': question_type, 'question': question_text, 'options': options } question = QivipQuestion() question.data = json.dumps(json_data) question.quiz_id = quiz try: question_image=request.FILES['question_image'] imagequestion=QuestionImage.objects.create(image=question_image) question.image=imagequestion except: pass question.time_per_question = request.POST.get('time_per_question', '30') question.save() #return modified(request, pk=quiz.pk) return redirect('library:detail_quiz', pk=quiz.pk) else: # Initialize empty question data for new questions if question_type == 'true_false': question_data = { 'type': question_type, 'question': '', 'options': [ {'value': 'Wahr', 'is_correct': True}, {'value': 'Falsch', 'is_correct': False} ] } standard_time_per_question = 30 elif question_type == 'input': question_data = { 'type': question_type, 'question': '', 'options': [ ] } standard_time_per_question = 30 elif question_type == 'multiple_choice': question_data = { 'type': question_type, 'question': '', 'options': [ {'value': '', 'is_correct': False}, {'value': '', 'is_correct': False} ] } standard_time_per_question = 30 template_name = f'library/question/question_{question_type}.html' context = { 'question': question_data, 'quiz_id': quiz_id, 'quiz': quiz, 'standard_time_per_question': standard_time_per_question, } return render(request, template_name, context) # Frage bearbeiten @login_required def edit_question(request, pk): question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user) if 'reset_ques_image' in request.POST: question.image = None if question.quiz_id.user_id != request.user: return redirect('library:question_list') # Parse the existing JSON data try: question_data = json.loads(question.data) if question.data else {} question_type = question_data.get('type', 'multiple_choice') question_image = question.image # For true/false questions, get the correct answer from the options if question_type == 'true_false': # Default to true if no options exist correct_answer = True if question_data.get('options'): correct_answer = question_data['options'][0].get('is_correct', True) question_data = { 'type': question_type, 'question': question_data.get('question', ''), 'correct_answer': correct_answer, # Add this for template compatibility 'options': [ {'value': 'Wahr', 'is_correct': correct_answer}, {'value': 'Falsch', 'is_correct': not correct_answer} ] } else: # For multiple choice questions question_data.setdefault('type', question_type) question_data.setdefault('question', '') question_data.setdefault('options', []) except (json.JSONDecodeError, AttributeError): # Handle invalid JSON or None data question_type = question_data.get('type', 'multiple_choice') if question_type == 'true_false': question_data = { 'type': question_type, 'question': '', 'correct_answer': True, # Default to true for new questions 'options': [ {'value': 'Wahr', 'is_correct': True}, {'value': 'Falsch', 'is_correct': False} ] } else: question_data = { 'type': question_type, 'question': '', 'options': [] } if request.method == 'POST': question_text = request.POST.get('question', '') try: question_image=request.FILES['question_image'] imagequestion=QuestionImage.objects.create(image=question_image) question.image=imagequestion except Exception: pass # Handle different question types if question_type == 'true_false': correct_answer = request.POST.get('correct_answer') == 'true' json_data = { 'type': question_type, 'question': question_text, 'options': [ {'value': 'Wahr', 'is_correct': correct_answer}, {'value': 'Falsch', 'is_correct': not correct_answer} ] } elif question_type == 'input': correct_answer = request.POST.get('correct_answer') json_data = { 'type': question_type, 'question': question_text, 'options': [ {'value': correct_answer,'is_correct':True} ] } elif question_type == 'multiple_choice': options = [] i = 1 # Limit to maximum 6 options while request.POST.get(f'option_{i}') and i <= 6: options.append({ 'order': i, 'value': request.POST.get(f'option_{i}'), 'is_correct': request.POST.get(f'correct_{i}') == '1' }) i += 1 # Validate minimum 2 options if len(options) < 2: messages.error(request, 'Mindestens zwei Optionen müssen angegeben werden.') return redirect(request.get_full_path()) # Validate maximum 6 options if len(options) > 6: messages.error(request, 'Maximal 6 Optionen sind erlaubt.') return redirect(request.get_full_path()) json_data = { 'type': question_type, 'question': question_text, 'options': options } question.data = json.dumps(json_data) question.time_per_question = request.POST.get('time_per_question', '30') question.save() for img in QuestionImage.objects.all(): try: img.delete() img.image.delete(save=False) except: pass #return modified(request, question.quiz_id.pk) return redirect('library:detail_quiz', pk=question.quiz_id.pk) else: template_name = f'library/question/question_{question_type}.html' context = { 'question': {'data': question_data}, # Wrap in data structure expected by template 'quiz_id': question.quiz_id.pk, 'quiz': question.quiz_id, 'time_per_question': question.time_per_question, 'image': question_image, } return render(request, template_name, context) # Frage löschen @login_required def delete_question(request, pk): question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user) # Parse JSON data for question if question.data: question.data = json.loads(question.data) if request.method == 'POST': question.delete() for img in QuestionImage.objects.all(): try: img.delete() img.image.delete(save=False) except: pass return redirect('library:detail_quiz', pk=question.quiz_id.pk) return render(request, 'library/delete_confirmation.html', {'object': question})