critizise Quiz
This commit is contained in:
@@ -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)
|
||||
29
django/library/migrations/0064_quizcriticism.py
Normal file
29
django/library/migrations/0064_quizcriticism.py
Normal file
@@ -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')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -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,
|
||||
),
|
||||
]
|
||||
@@ -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',
|
||||
),
|
||||
]
|
||||
@@ -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,
|
||||
),
|
||||
]
|
||||
@@ -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')},
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
19
django/library/migrations/0070_quizcriticism_question.py
Normal file
19
django/library/migrations/0070_quizcriticism_question.py
Normal file
@@ -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'),
|
||||
),
|
||||
]
|
||||
18
django/library/migrations/0071_quizcriticism_id_question.py
Normal file
18
django/library/migrations/0071_quizcriticism_id_question.py
Normal file
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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):
|
||||
|
||||
@@ -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/<int:pk>/', views.edit_quiz, name='edit_quiz'),
|
||||
path('delete/<int:pk>/', views.delete_quiz, name='delete_quiz'),
|
||||
@@ -20,12 +19,14 @@ urlpatterns = [
|
||||
path('question/delete/<int:pk>/', views.delete_question, name='delete_question'),
|
||||
path('detail/copy/<int:pk>/', views.copy_quiz, name='copy_quiz'),
|
||||
path('favorite_quiz/<int:pk>/', views.favorite_quiz, name='favorite_quiz'),
|
||||
path('critique/delete/<int:critique_id>/<int:quiz_id>', 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"
|
||||
),
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
@@ -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'<a href="{escaped_url}" color="blue">{escaped_url}</a>'
|
||||
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'<a href="{escaped_url}" color="blue">{escaped_url}</a>'
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user