diff --git a/django/accounts/views.py b/django/accounts/views.py index 006cb80..a2860e0 100644 --- a/django/accounts/views.py +++ b/django/accounts/views.py @@ -7,14 +7,20 @@ from django.contrib.auth import update_session_auth_hash from django.contrib import messages from django.contrib.auth import logout from library.models import QivipQuiz, QivipQuestion - +from django.db.models import Count # Create your views here. @login_required def home(request): + own_critisized_quizzes = QivipQuiz.objects.annotate( + criticism_count=Count('criticism_quiz') + ).filter(criticism_count__gt=0).filter(user_id=request.user) + + my_quizzes_number=QivipQuiz.objects.filter(user_id=request.user).count() my_questions_number = QivipQuestion.objects.filter(quiz_id__user_id=request.user).count() context = { + 'own_critisized_quizzes':own_critisized_quizzes, 'my_quizzes_number': my_quizzes_number, 'my_questions_number': my_questions_number, } diff --git a/django/library/admin.py b/django/library/admin.py index 0b11c31..bc85ca4 100644 --- a/django/library/admin.py +++ b/django/library/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import QivipQuiz, QivipQuestion, QivipQuizFavorite, QuestionImage, QuizCategory, QuizImage, QuizRating +from .models import QivipQuiz, QivipQuestion,QivipQuizFavorite, QuestionImage, QuizCategory, QuizImage, QuizRating, QuizCriticism # Für das QivipQuiz Modell class QivipQuizAdmin(admin.ModelAdmin): @@ -9,6 +9,11 @@ class QivipQuizAdmin(admin.ModelAdmin): autocomplete_fields = ("reference_quizzes",) +class QuizCriticismAdmin(admin.ModelAdmin): + list_display = ('quiz','question_criticism', 'user','criticism') # Welche Felder sollen in der Übersicht angezeigt werden + search_fields = ('quiz','question_criticism', 'user','criticism') # Suchfelder + list_filter=('quiz','question_criticism', 'user','criticism') + @@ -64,7 +69,7 @@ class QuizCategoryAdmin(admin.ModelAdmin): # Registrierung der Modelle im Admin admin.site.register(QivipQuiz, QivipQuizAdmin) admin.site.register(QivipQuestion, QivipQuestionAdmin) -#admin.site.register(QuizCategory, QuizCategoryAdmin) +admin.site.register(QuizCriticism, QuizCriticismAdmin) admin.site.register(QivipQuizFavorite, QivipQuizFavoriteAdmin) admin.site.register(QuizImage, QuizImageAdmin) admin.site.register(QuestionImage, QuestionImageAdmin) \ No newline at end of file diff --git a/django/library/migrations/0064_quizcriticism.py b/django/library/migrations/0064_quizcriticism.py new file mode 100644 index 0000000..9bc1e76 --- /dev/null +++ b/django/library/migrations/0064_quizcriticism.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.7 on 2025-12-30 13:36 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0063_rename_similar_quizzes_qivipquiz_reference_quizzes'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='QuizCriticism', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('criticism', models.CharField(max_length=256, verbose_name='Kritik')), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='criticism_question', to='library.qivipquestion')), + ('quiz', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='criticism_quiz', to='library.qivipquiz')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('quiz', 'user', 'question')}, + }, + ), + ] diff --git a/django/library/migrations/0065_quizcriticism_creation_date.py b/django/library/migrations/0065_quizcriticism_creation_date.py new file mode 100644 index 0000000..077da29 --- /dev/null +++ b/django/library/migrations/0065_quizcriticism_creation_date.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.7 on 2025-12-30 13:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0064_quizcriticism'), + ] + + operations = [ + migrations.AddField( + model_name='quizcriticism', + name='creation_date', + field=models.DateTimeField(auto_now_add=True, default='2025-12-30 12:00:00'), + preserve_default=False, + ), + ] diff --git a/django/library/migrations/0066_remove_quizcriticism_creation_date.py b/django/library/migrations/0066_remove_quizcriticism_creation_date.py new file mode 100644 index 0000000..55f6194 --- /dev/null +++ b/django/library/migrations/0066_remove_quizcriticism_creation_date.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.7 on 2025-12-30 13:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0065_quizcriticism_creation_date'), + ] + + operations = [ + migrations.RemoveField( + model_name='quizcriticism', + name='creation_date', + ), + ] diff --git a/django/library/migrations/0067_quizcriticism_creation_date.py b/django/library/migrations/0067_quizcriticism_creation_date.py new file mode 100644 index 0000000..a8acb95 --- /dev/null +++ b/django/library/migrations/0067_quizcriticism_creation_date.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.7 on 2025-12-30 14:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0066_remove_quizcriticism_creation_date'), + ] + + operations = [ + migrations.AddField( + model_name='quizcriticism', + name='creation_date', + field=models.DateTimeField(auto_now_add=True, default='2025-12-30 12:00:00'), + preserve_default=False, + ), + ] diff --git a/django/library/migrations/0068_rename_question_quizcriticism_question_criticism_and_more.py b/django/library/migrations/0068_rename_question_quizcriticism_question_criticism_and_more.py new file mode 100644 index 0000000..da6a0c0 --- /dev/null +++ b/django/library/migrations/0068_rename_question_quizcriticism_question_criticism_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.7 on 2025-12-31 11:56 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0067_quizcriticism_creation_date'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RenameField( + model_name='quizcriticism', + old_name='question', + new_name='question_criticism', + ), + migrations.AlterUniqueTogether( + name='quizcriticism', + unique_together={('quiz', 'user', 'question_criticism', 'criticism')}, + ), + ] diff --git a/django/library/migrations/0069_alter_quizcriticism_question_criticism.py b/django/library/migrations/0069_alter_quizcriticism_question_criticism.py new file mode 100644 index 0000000..755b54e --- /dev/null +++ b/django/library/migrations/0069_alter_quizcriticism_question_criticism.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.7 on 2025-12-31 12:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0068_rename_question_quizcriticism_question_criticism_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='quizcriticism', + name='question_criticism', + field=models.CharField(max_length=1024), + ), + ] diff --git a/django/library/migrations/0070_quizcriticism_question.py b/django/library/migrations/0070_quizcriticism_question.py new file mode 100644 index 0000000..0e44a25 --- /dev/null +++ b/django/library/migrations/0070_quizcriticism_question.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.7 on 2025-12-31 14:03 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0069_alter_quizcriticism_question_criticism'), + ] + + operations = [ + migrations.AddField( + model_name='quizcriticism', + name='question', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='library.qivipquestion'), + ), + ] diff --git a/django/library/migrations/0071_quizcriticism_id_question.py b/django/library/migrations/0071_quizcriticism_id_question.py new file mode 100644 index 0000000..5ac2c0b --- /dev/null +++ b/django/library/migrations/0071_quizcriticism_id_question.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.7 on 2025-12-31 14:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0070_quizcriticism_question'), + ] + + operations = [ + migrations.AddField( + model_name='quizcriticism', + name='id_question', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/django/library/models.py b/django/library/models.py index fc9878b..a66a8dc 100644 --- a/django/library/models.py +++ b/django/library/models.py @@ -57,7 +57,6 @@ class QivipQuiz(models.Model): uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz') - #creator = models.ForeignKey(User, on_delete=models.SET_NULL,blank=True, null=True, related_name='creator_quiz') base_quiz = models.ForeignKey('self', on_delete=models.SET_NULL,blank=True, null=True, related_name='child_quizzes') creation_date = models.DateTimeField(auto_now_add=True) update_date = models.DateTimeField(auto_now=True) @@ -87,6 +86,7 @@ class QivipQuizFavorite(models.Model): unique_together = ('user', 'quiz') # Sicherstellen, dass es nur ein Favorit pro User und Quiz gibt + # Create your models here. class QivipQuestion(models.Model): @@ -117,6 +117,21 @@ class QivipQuestion(models.Model): def __str__(self): return self.data[:50] +class QuizCriticism(models.Model): + quiz= models.ForeignKey(QivipQuiz, on_delete=models.CASCADE, related_name='criticism_quiz') + user = models.ForeignKey(User, on_delete=models.CASCADE) + question_criticism=models.CharField(max_length=1024) + criticism=models.CharField(max_length=256, verbose_name="Kritik") + creation_date = models.DateTimeField(auto_now_add=True) + question = models.ForeignKey(QivipQuestion, on_delete=models.CASCADE,null=True, blank=True) + id_question=models.IntegerField(null=True, blank=True) + + class Meta: + unique_together = ('quiz', 'user', 'question_criticism','criticism') + + def __str__(self): + return f"{self.question_criticism}" + class QuizRating(models.Model): diff --git a/django/library/urls.py b/django/library/urls.py index fde2131..ace2249 100644 --- a/django/library/urls.py +++ b/django/library/urls.py @@ -10,7 +10,6 @@ urlpatterns = [ path('', views.overview_quiz, name='overview_quiz'), path('my/', views.overview_quiz_my, name='overview_quiz_my'), path('favorites/', views.overview_quiz_favorites, name='overview_quiz_favorites'), - path('new/', views.new_quiz, name='new_quiz'), path('edit//', views.edit_quiz, name='edit_quiz'), path('delete//', views.delete_quiz, name='delete_quiz'), @@ -20,12 +19,14 @@ urlpatterns = [ path('question/delete//', views.delete_question, name='delete_question'), path('detail/copy//', views.copy_quiz, name='copy_quiz'), path('favorite_quiz//', views.favorite_quiz, name='favorite_quiz'), + path('critique/delete//', views.delete_critique, name='delete_critique'), path("quiz-names-json/", views.quiz_names_json, name="quiz_names_json"), path( "autocomplete/quiz", QuizAutocomplete.as_view(), name="quiz-autocomplete" ), + ] diff --git a/django/library/views.py b/django/library/views.py index b7fe058..3855cb9 100644 --- a/django/library/views.py +++ b/django/library/views.py @@ -6,7 +6,7 @@ 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 .models import QivipQuiz, QivipQuestion, QivipQuizFavorite, QuestionImage, QuizCriticism, QuizImage from .forms import QuizForm, QuestionForm import json from django.core.paginator import Paginator @@ -70,306 +70,7 @@ class QuizAutocomplete(autocomplete.Select2QuerySetView): # z.B. Name + ID + User return f"{result.name} (ID: {result.pk}) (User: {result.user_id})" - -""" -def pdf_solution(request, pk): - quiz = get_object_or_404(QivipQuiz, pk=pk) - - questions = QivipQuestion.objects.filter(quiz_id=quiz) - - for question in questions: - if question.data: - question.data = json.loads(question.data) - - response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = f'attachment; filename="Lösungen - {quiz.name}.pdf"' - - doc = SimpleDocTemplate(response, pagesize=A4, - rightMargin=40, leftMargin=40, - topMargin=60, bottomMargin=60) - - styles = getSampleStyleSheet() - - # Stil für Quiz-Titel - title_style = ParagraphStyle( - 'TitleStyle', - parent=styles['Title'], - alignment=1, # zentriert - fontSize=14, - spaceAfter=2, - ) - - # Stil für Fragen-Box - question_box_style = ParagraphStyle( - 'QuestionBox', - fontSize=10, - leading=16, - fontName='Helvetica-Bold', - - - spaceAfter=8, - leftIndent=6, - rightIndent=6, - - alignment=0 - ) - - # Stil für Antwort (richtig) - answer_correct_style = ParagraphStyle( - 'AnswerCorrect', - fontSize=11, - leading=14, - textColor=colors.green, - leftIndent=20, - spaceAfter=4, - ) - - # Stil für Antwort (falsch) - answer_wrong_style = ParagraphStyle( - 'AnswerWrong', - fontSize=11, - leading=14, - textColor=colors.red, - leftIndent=20, - spaceAfter=4, - ) - - # Stil für normalen Antwort-Text (ohne Antwortanzeige) - answer_neutral_style = ParagraphStyle( - 'AnswerNeutral', - fontSize=11, - leading=14, - textColor=colors.black, - leftIndent=20, - spaceAfter=4, - ) - date_style = ParagraphStyle( - name='RightAligned', - parent=styles['Normal'], - alignment=2, -) - - story = [] - - story.append(Paragraph(f"Quiz: {quiz.name}", title_style)) - story.append(Paragraph(f"- Lösungen -", title_style)) - story.append(Spacer(1, 20)) - - story.append(Paragraph(datetime.today().strftime("%d.%m.%Y"), date_style)) - story.append(Spacer(1, 20)) - - if quiz.description: - story.append(Paragraph(quiz.description, styles['Normal'])) - story.append(Spacer(1, 20)) - - - - for i, question in enumerate(questions, start=1): - data = question.data - frage_text = data.get('question', 'Frage nicht gefunden.') - - story.append(Paragraph(f"Frage {i}: {frage_text}", question_box_style)) - story.append(Spacer(1, 6)) - - rl_image = None - if hasattr(question, 'image') and question.image and question.image.image: - image_url = request.build_absolute_uri(question.image.image.url) - img_response = requests.get(image_url) - if img_response.status_code == 200: - image_data = BytesIO(img_response.content) - pil_image = PILImage.open(image_data) - pil_image = ImageOps.exif_transpose(pil_image) - orig_width, orig_height = pil_image.size - max_width = 80 - max_height = 80 - ratio = min(max_width / orig_width, max_height / orig_height) - new_width = orig_width * ratio - new_height = orig_height * ratio - output = BytesIO() - pil_image.save(output, format='JPEG') - output.seek(0) - - rl_image = Image(output, width=new_width, height=new_height) - - options = data.get('options', []) - answer_paragraphs = [] - for idx, option in enumerate(options, start=1): - opt_text = option.get('value', '') - if option.get('is_correct', False): - text = f"✔ {opt_text}" - style = answer_correct_style - else: - text = f"✘ {opt_text}" - style = answer_wrong_style - answer_paragraphs.append(Paragraph(text, style)) - - if rl_image: - # Tabelle mit Bild links und Antworten rechts - from reportlab.platypus import Table, TableStyle - from reportlab.lib.units import cm - data_table = [[rl_image, answer_paragraphs]] - table = Table(data_table, colWidths=[6*cm, 10*cm]) - table.setStyle(TableStyle([ - ('VALIGN', (0,0), (-1,-1), 'TOP'), - ('LEFTPADDING', (0,0), (-1,-1), 0), - ('RIGHTPADDING', (0,0), (-1,-1), 10), - ])) - story.append(table) - else: - # Kein Bild, Antworten einfach nacheinander - for p in answer_paragraphs: - story.append(p) - - story.append(Spacer(1, 12)) - story.append(HRFlowable(width="100%", thickness=1, color=colors.grey)) - story.append(Spacer(1, 12)) - current_url = request.build_absolute_uri(f"/library/detail/{quiz.pk}/") - escaped_url = escape(current_url) # Sicherheitshalber escapen - - link_html = f'{escaped_url}' - story.append(Paragraph(link_html, styles['Normal'])) - - - doc.build(story) - return response -def pdf_task(request, pk): - quiz = get_object_or_404(QivipQuiz, pk=pk) - questions = QivipQuestion.objects.filter(quiz_id=quiz) - - for question in questions: - if question.data: - question.data = json.loads(question.data) - - response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = f'attachment; filename="Aufgabenblatt - {quiz.name}.pdf"' - - doc = SimpleDocTemplate(response, pagesize=A4, - rightMargin=40, leftMargin=40, - topMargin=60, bottomMargin=60) - - styles = getSampleStyleSheet() - - title_style = ParagraphStyle( - 'TitleStyle', - parent=styles['Title'], - alignment=1, - fontSize=14, - spaceAfter=2, - fontName='Helvetica-Bold', - ) - - question_box_style = ParagraphStyle( - 'QuestionBox', - fontSize=10, - leading=16, - fontName='Helvetica-Bold', - spaceAfter=8, - leftIndent=6, - rightIndent=6, - alignment=0 - ) - - - - answer_neutral_style = ParagraphStyle( - 'AnswerNeutral', - fontSize=11, - leading=14, - textColor=colors.black, - leftIndent=20, - spaceAfter=4, - ) - - date_style = ParagraphStyle( - name='RightAligned', - parent=styles['Normal'], - alignment=2, - ) - - name_style = ParagraphStyle( - name='RightAligned', - parent=styles['Normal'], - alignment=0, - ) - story = [] - - # Titel & Datum - story.append(Paragraph(f"Aufgabenblatt: {quiz.name}", title_style)) - story.append(Spacer(1, 20)) - story.append(Paragraph(datetime.today().strftime("%d.%m.%Y"), date_style)) - story.append(Spacer(1, 20)) - story.append(Paragraph("Name: ", name_style)) - story.append(Spacer(1, 20)) - - if quiz.description: - story.append(Paragraph(quiz.description, styles['Normal'])) - story.append(Spacer(1, 20)) - - for i, question in enumerate(questions, start=1): - data = question.data - frage_text = data.get('question', 'Frage nicht gefunden.') - story.append(Paragraph(f"Frage {i}: {frage_text}", question_box_style)) - story.append(Spacer(1, 6)) - - rl_image = None - if hasattr(question, 'image') and question.image and question.image.image: - image_url = request.build_absolute_uri(question.image.image.url) - img_response = requests.get(image_url) - if img_response.status_code == 200: - image_data = BytesIO(img_response.content) - pil_image = PILImage.open(image_data) - pil_image = ImageOps.exif_transpose(pil_image) - orig_width, orig_height = pil_image.size - max_width = 80 # gleich wie bei Lösungen - max_height = 80 - ratio = min(max_width / orig_width, max_height / orig_height) - new_width = orig_width * ratio - new_height = orig_height * ratio - output = BytesIO() - pil_image.save(output, format='JPEG') - output.seek(0) - rl_image = Image(output, width=new_width, height=new_height) - - options = data.get('options', []) - - answer_paragraphs = [] - for idx, option in enumerate(options, start=1): - - opt_text = option.get('value', '') - if data.get("type") !="input": - opt_text = option.get('value', '') - answer_paragraphs.append(Paragraph(f"{idx}. {opt_text}", answer_neutral_style)) - else: - story.append(Paragraph("Deine Antwort: ", answer_neutral_style)) - story.append(Spacer(1, 12)) - - if rl_image: - from reportlab.platypus import Table, TableStyle - from reportlab.lib.units import cm - data_table = [[rl_image, answer_paragraphs]] - table = Table(data_table, colWidths=[6*cm, 10*cm]) - table.setStyle(TableStyle([ - ('VALIGN', (0,0), (-1,-1), 'TOP'), - ('LEFTPADDING', (0,0), (-1,-1), 0), - ('RIGHTPADDING', (0,0), (-1,-1), 10), - ])) - story.append(table) - else: - for p in answer_paragraphs: - story.append(p) - - story.append(Spacer(1, 12)) - story.append(HRFlowable(width="100%", thickness=1, color=colors.grey)) - story.append(Spacer(1, 12)) - current_url = request.build_absolute_uri(f"/library/detail/{quiz.pk}/") - escaped_url = escape(current_url) # Sicherheitshalber escapen - - link_html = f'{escaped_url}' - story.append(Paragraph(link_html, styles['Normal'])) - doc.build(story) - return response -""" - + def quiz_names_json(request): @@ -430,7 +131,82 @@ def apply_quiz_filters(queryset, form): return queryset.filter(**filters) + +def critisize_quiz(request, pk): + + quiz= get_object_or_404(QivipQuiz, pk=pk) + + + if request.method == "POST": + + question_id = request.POST.get("question_id") or "whole_quiz" + + if question_id!="whole_quiz": + question = quiz.questions.get(id=question_id) + data = json.loads(question.data) + question_text = data["question"] + + else: + question_text="Quiz(formular) kritisiert" + question = None + question_id = None + + + critic_type = request.POST.get("critic") + + CRITICISM_MAP = { + "content_error": "Die Frage oder die Antworten enthalten einen inhaltlichen Fehler.", + "spelling_error": "Die Frage oder die Antworten enthalten (einen oder mehrere) Rechtschreibfehler.", + "logic_error": "Die Frage macht keinen Sinn oder ist nur sehr schwer zu verstehen.", + "inappropriate_error": "Die Frage, die Antworten oder Bilder sind unangemessen.", + "not_completed_error":"Die Antworten sind unvollständig oder es wurde keine richtige Antwort ausgewählt.", + "quiz_error":"Das Quiz(formular) selbst ist unangebracht, falsch oder nicht verständlich." + } + + + + exists = QuizCriticism.objects.filter( + id_question=question_id, + question=question, + quiz=quiz, + user=request.user, + question_criticism=question_text, + criticism=CRITICISM_MAP[critic_type], + ).exists() + + if exists: + messages.error(request, "Diese Kritik wurde bereits abgegeben.") # nur Kritik abgeben, wenn noch nicht erstellt + else: + QuizCriticism.objects.create( + question=question, + id_question=question_id, + quiz=quiz, + user=request.user, + question_criticism=question_text, + criticism=CRITICISM_MAP[critic_type], + ) + + + +def delete_critique(request,critique_id,quiz_id): + + critique = get_object_or_404(QuizCriticism, id=critique_id) + critique.delete() + return redirect('library:detail_quiz', pk=quiz_id) + + + + +def get_critisized_quizzes(): + critisized_quizzes = QivipQuiz.objects.annotate( + criticism_count=Count('criticism_quiz') + ).filter(criticism_count__gt=0) + return critisized_quizzes + + def overview_quiz(request, connect="all"): + + critisized_quizzes = get_critisized_quizzes() form = QuizFilterForm(request.GET) user = request.user if request.user.is_authenticated else None @@ -502,6 +278,7 @@ def overview_quiz(request, connect="all"): favorite_quizzes = paginator_fav.page(1) context = { + 'critisized_quizzes':critisized_quizzes, 'form': form, 'my_quizzes': my_quizzes, 'all_quizzes': all_quizzes, @@ -552,117 +329,6 @@ def overview_quiz_my(request): def overview_quiz_favorites(request): return overview_quiz(request, connect="favorite") -""" -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_amount_questions=Count('questions')) - favorite_quizzes = favorite_quizzes.filter(id__in=favorite_quiz_ids).annotate(max_amount_questions=Count('questions')) - filter_all_quizzes = QivipQuiz.objects.exclude(user_id=request.user) - else: - favorite_quizzes = QivipQuiz.objects.none() - filter_my_quizzes = QivipQuiz.objects.none() - filter_all_quizzes = QivipQuiz.objects.all() - - # Apply common filters to all quizzes - filter_all_quizzes = filter_all_quizzes.annotate(max_amount_questions=Count('questions')) - filter_all_quizzes = filter_all_quizzes.filter(max_amount_questions__gte=1, status='öffentlich') - - if form.is_valid(): - - search = form.cleaned_data.get('search') - username= form.cleaned_data.get('user') - max_amount_questions= form.cleaned_data.get('max_amount_questions') - min_amount_questions= form.cleaned_data.get('min_amount_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_all_quizzes=QivipQuiz.objects.none() - - if search: - filters['name__icontains'] = search - if min_amount_questions: - filters['max_amount_questions__gte'] = min_amount_questions - if max_amount_questions: - filters['max_amount_questions__lte'] = max_amount_questions - - try: - filter_all_quizzes = filter_all_quizzes.filter(**filters) - except: - filter_all_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 all quizzes - all_quizzes_paginator = Paginator(filter_all_quizzes.order_by('-creation_date'), 8) # 8 quizzes per page - try: - all_quizzes = all_quizzes_paginator.page(request.GET.get('all_page', 1)) - except: - all_quizzes = all_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) # 9 quizzes per page - try: - favorite_quizzes = favorite_quizzes_paginator.page(request.GET.get('favorite_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, - 'all_quizzes': all_quizzes, - 'form': form, - } - - return render(request, 'library/overview_quiz.html', context) - """ @login_required def favorite_quiz(request, pk): @@ -802,8 +468,11 @@ def delete_quiz(request, pk): return redirect('library:overview_quiz') return render(request, 'library/delete_confirmation.html', {'object': quiz}) + + # Quiz anzeigen def detail_quiz(request, pk): + critisize_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) diff --git a/django/templates/accounts/home.html b/django/templates/accounts/home.html index 31e784f..edf9fb4 100644 --- a/django/templates/accounts/home.html +++ b/django/templates/accounts/home.html @@ -3,7 +3,7 @@ {% block content %} -
+
@@ -17,7 +17,25 @@ - + + {% if own_critisized_quizzes %} +
+
+ + + + Kritik +
+

+ Diese Quizze wurden von anderen Nutzern kritisiert, da sie als fehlerhaft oder nicht angemessen + wahrgenommen wurden. Bitte überprüfen und verbessern Sie Ihre Quizze so schnell wie möglich.

+ {% for own_critisized_quiz in own_critisized_quizzes %} + {{ own_critisized_quiz }} + {% endfor %} +
+ {% endif %} + +
diff --git a/django/templates/library/detail_quiz.html b/django/templates/library/detail_quiz.html index 903470f..0f320e6 100644 --- a/django/templates/library/detail_quiz.html +++ b/django/templates/library/detail_quiz.html @@ -272,7 +272,7 @@ {% if option.is_correct %}

Richtige Antwort: - {{ option.value }} + {{ option.value }}

{% endif %} {% endfor %} @@ -303,7 +303,7 @@
- + {% endfor %}
@@ -320,11 +320,78 @@
+ +
+{% if quiz.criticism_quiz.all %} + +
+

Kritik zum Quiz

+
+
+
+
+ +{% for critique in quiz.criticism_quiz.all %} + +
+ +{% if critique.id_question != None %} +{% if quiz.user_id == request.user %} +{{critique.question_criticism}}: +{% else %} + {{critique.question_criticism}}: +{% endif %} +{% endif %} + + + {{ critique.criticism }} + +

+ Kritik von User {{ critique.user }} +

-
+
+

+ {{ critique.creation_date }} +

+
+ + {% if quiz.user_id == request.user %} +
+ {% csrf_token %} +
+ +
+
+{% endif %} + +
+{% endfor %} +
+{% endif %} +
+ + + + + +
+

Informationen über das Quiz

+
+
+ +
+
+ +

Quiz-Infos

@@ -348,16 +415,12 @@
- - -

Quiz ID: {{ quiz.pk }}

- {% if quiz.reference_quizzes.all %}

Verweis auf andere Quizze: @@ -373,7 +436,7 @@

-
+

Credits @@ -411,7 +474,7 @@

-
+

Quiz-Statistik

@@ -435,8 +498,99 @@
+
+ {% if user.is_authenticated %} + {% if quiz.user_id != request.user %} + +
+

Das Quiz kritisieren

+
+
+
+
+ +
+ {% csrf_token %} +
+

+ Quiz kritisieren +

+

+ Sie haben einen Fehler in dem Quiz entdeckt oder halten eine Frage für nicht angemessen?
+ Dann weisen Sie den Autor gern darauf hin. +

+ + + + + {% endif %} + {% elif quiz.user_id != request.user %} +
+

+ Quiz kritisieren +

+
+ Sie haben einen Fehler in dem Quiz entdeckt oder halten eine Frage für nicht angemessen?
+ Dann weisen Sie den Autor gern darauf hin. + Sie können nur Quizze anderer User kritisieren. +
+ Anmelden +
+ {% endif %} + +
+
+
+ + {% endblock %} diff --git a/django/templates/library/overview_quiz(old).html b/django/templates/library/overview_quiz(old).html deleted file mode 100644 index abdc8f2..0000000 --- a/django/templates/library/overview_quiz(old).html +++ /dev/null @@ -1,672 +0,0 @@ -{% extends 'base.html' %} -{% load static %} - -{% block extra_css %} - -{% endblock %} - - -{% block content %} - - - -
-
-
- -
- - -
-
- - -
-
- - -
-
- -
-
-
-
- - - -{% if user.is_authenticated %} -
-
-

Meine Quizze

-
-
-
- -
-
- {% for quiz in my_quizzes %} -
- -
- {% if quiz.image %} -
- -
-
-
- - - {% else %} -
{{ quiz.name }}
- {% endif %} - {% if user.is_authenticated %} - {% if quiz.id not in favorite_quiz_ids %} -
- - - - - - -
- - {% else %} - - {% endif %} - {% endif %} -
- - - - -
- -

- {{ quiz.name }} -

- -

{{ quiz.description }}

- - -
- -
- - - - {{ quiz.difficulty }} -
- - -
- - - - {{ quiz.questions.count }} Fragen -
- - -
- - - - {{ quiz.status }} -
- - -
- {% if quiz.rating_count > 0 %} - {% for i in '12345'|make_list %} - {% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %} - - {% else %} - - {% endif %} - {% endfor %} - ({{ quiz.rating_count }}) - {% else %} - Noch keine Bewertungen - {% endif %} -
-
- - - {% if quiz.authors.all %} -
- - - - - {% for author in quiz.authors.all %} - {{ author.username }}{% if not forloop.last %}, {% endif %} - {% endfor %} - -
- {% endif %} - - -
- {% if quiz.questions.count != 0 %} - - - - - - Spielen - - {% else %} - Fürs Spielen ist min. 1 Frage nötig! - {% endif %} -
- - - - - - - {% if quiz.user_id == request.user %} - - - - - - - - - - - {% endif %} -
-
-
-
- {% endfor %} - -
- - - {% if my_quizzes.paginator.num_pages > 1 %} -
- {% if my_quizzes.has_previous %} - - - - - - {% endif %} - - Seite {{ my_quizzes.number }} von {{ my_quizzes.paginator.num_pages }} - - {% if my_quizzes.has_next %} - - - - - - {% endif %} -
- {% endif %} -
-{% endif %} - - - - -{% if user.is_authenticated %} -
-
-

Meine Favoriten

-
-
-
- -
-
- {% for quiz in favorite_quizzes %} -
- -
- {% if quiz.image %} -
- -
-
-
- - - {% else %} -
{{ quiz.name }}
- {% endif %} - {% if user.is_authenticated %} - {% if quiz.favorite == False %} - -
- - - - - - -
- - {% else %} - - {% endif %} - {% endif %} -
- - - - -
- -

- {{ quiz.name }} -

- -

{{ quiz.description }}

- - -
- -
- - - - {{ quiz.difficulty }} -
- - -
- - - - {{ quiz.questions.count }} Fragen -
- - -
- - - - {{ quiz.status }} -
- - -
- {% if quiz.rating_count > 0 %} - {% for i in '12345'|make_list %} - {% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %} - - {% else %} - - {% endif %} - {% endfor %} - ({{ quiz.rating_count }}) - {% else %} - Noch keine Bewertungen - {% endif %} -
-
- - - {% if quiz.authors.all %} -
- - - - - {% for author in quiz.authors.all %} - {{ author.username }}{% if not forloop.last %}, {% endif %} - {% endfor %} - -
- {% endif %} - - -
- {% if quiz.questions.count != 0 %} - - - - - - Spielen - - {% else %} - Fürs Spielen ist min. 1 Frage nötig! - {% endif %} - -
- - - - - - - {% if quiz.user_id == request.user %} - - - - - - - - - - - {% endif %} -
-
-
-
- {% endfor %} - -
- - - {% if favorite_quizzes.paginator.num_pages > 1 %} -
- {% if favorite_quizzes.has_previous %} - - - - - - {% endif %} - - Seite {{ favorite_quizzes.number }} von {{ favorite_quizzes.paginator.num_pages }} - - {% if favorite_quizzes.has_next %} - - - - - - {% endif %} -
- {% endif %} -
-{% endif %} - - - - - - - - - - -
-
-

Alle

-
-
-
- -{% if all_quizzes %} -
-
- {% for quiz in all_quizzes %} -
- -
- {% if quiz.image %} -
- -
-
-
- - - - {% else %} -
{{ quiz.name }}
- {% endif %} - {% if user.is_authenticated %} - {% if quiz.id not in favorite_quiz_ids %} -
- - - - - - -
- - {% else %} - - {% endif %} - {% endif %} -
- - -
- -

- {{ quiz.name }} -

- -

{{ quiz.description }}

- - -
- -
- - - - {{ quiz.difficulty }} -
- - -
- - - - {{ quiz.questions.count }} Fragen -
- - -
- - - - {{ quiz.status }} -
- - -
- {% if quiz.rating_count > 0 %} - {% for i in '12345'|make_list %} - {% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %} - - {% else %} - - {% endif %} - {% endfor %} - ({{ quiz.rating_count }}) - {% else %} - Noch keine Bewertungen - {% endif %} -
-
- - - {% if quiz.authors.all %} -
- - - - - {% for author in quiz.authors.all %} - {{ author.username }}{% if not forloop.last %}, {% endif %} - {% endfor %} - -
- {% endif %} - - -
- {% if quiz.questions.count != 0 %} - - - - - - Spielen - - {% else %} - Fürs Spielen ist min. 1 Frage nötig! - {% endif %} - -
-
-
- {% endfor %} -
- - - {% if all_quizzes.paginator.num_pages > 1 %} -
- {% if all_quizzes.has_previous %} - - - - - - {% endif %} - - Seite {{ all_quizzes.number }} von {{ all_quizzes.paginator.num_pages }} - - {% if all_quizzes.has_next %} - - - - - - {% endif %} -
- {% endif %} -
-{% else %} -
-

Keine öffentlichen Quiz gefunden.

-
-{% endif %} - -{% endblock content %} diff --git a/django/templates/library/overview_quiz.html b/django/templates/library/overview_quiz.html index c46e0e7..4212a53 100644 --- a/django/templates/library/overview_quiz.html +++ b/django/templates/library/overview_quiz.html @@ -60,7 +60,7 @@
{% for quiz in all_quizzes %} -
+
{% if quiz.image %} @@ -111,6 +111,7 @@
+

diff --git a/django/templates/library/overview_quiz_favorite.html b/django/templates/library/overview_quiz_favorite.html index 063ce64..3cd9ae4 100644 --- a/django/templates/library/overview_quiz_favorite.html +++ b/django/templates/library/overview_quiz_favorite.html @@ -54,7 +54,7 @@
{% for quiz in favorite_quizzes %} -
+
{% if quiz.image %} diff --git a/django/templates/library/overview_quiz_my.html b/django/templates/library/overview_quiz_my.html index 7b70f38..072d2b4 100644 --- a/django/templates/library/overview_quiz_my.html +++ b/django/templates/library/overview_quiz_my.html @@ -52,7 +52,7 @@
{% for quiz in my_quizzes %} -
+
{% if quiz.image %} diff --git a/temp-498.rdb b/temp-498.rdb new file mode 100644 index 0000000..e69de29