Umbenennen des Projektordners

This commit is contained in:
juhnsa
2025-03-15 21:21:53 +01:00
parent 35617470c4
commit defbc5b95d
87 changed files with 0 additions and 0 deletions

View File

31
django/library/admin.py Normal file
View File

@@ -0,0 +1,31 @@
from django.contrib import admin
from .models import QivipQuiz, QivipQuestion, QuizCategory, Tag
# Für das QivipQuiz Modell
class QivipQuizAdmin(admin.ModelAdmin):
list_display = ('name', 'user_id', 'status', 'category', 'creation_date', 'update_date') # Welche Felder sollen in der Übersicht angezeigt werden
search_fields = ('name', 'user_id__username', 'category__name') # Suchfelder
list_filter = ('status', 'category') # Filteroptionen
# Für das QivipQuestion Modell
class QivipQuestionAdmin(admin.ModelAdmin):
list_display = ('quiz_id', 'data', 'creation_date', 'update_date')
search_fields = ('data',)
list_filter = ('quiz_id',)
# Für das QuizCategory Modell
class QuizCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'description') # Zeige Name, Slug und Beschreibung an
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)} # Erstelle automatisch den Slug basierend auf dem Namen
# Für das Tag Modell
class TagAdmin(admin.ModelAdmin):
list_display = ('name',) # Zeige nur den Namen des Tags
search_fields = ('name',)
# Registrierung der Modelle im Admin
admin.site.register(QivipQuiz, QivipQuizAdmin)
admin.site.register(QivipQuestion, QivipQuestionAdmin)
admin.site.register(QuizCategory, QuizCategoryAdmin)
admin.site.register(Tag, TagAdmin)

6
django/library/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class LibraryConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'library'

12
django/library/forms.py Normal file
View File

@@ -0,0 +1,12 @@
from django import forms
from .models import QivipQuiz, QivipQuestion
class QuizForm(forms.ModelForm):
class Meta:
model = QivipQuiz
fields = ['name', 'description', 'status', 'category', 'tags',"difficulty"]
class QuestionForm(forms.ModelForm):
class Meta:
model = QivipQuestion
fields = ['quiz_id', 'data']

View File

@@ -0,0 +1,60 @@
# Generated by Django 5.1.6 on 2025-02-14 17:00
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='QuizCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('description', models.TextField(blank=True, null=True)),
('slug', models.SlugField(blank=True, unique=True)),
],
),
migrations.CreateModel(
name='Tag',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
],
),
migrations.CreateModel(
name='QivipQuiz',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
('creation_date', models.DateTimeField(auto_now_add=True)),
('update_date', models.DateTimeField(auto_now=True)),
('status', models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], max_length=10)),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True, null=True)),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quiz', to=settings.AUTH_USER_MODEL)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quiz', to='library.quizcategory')),
('tags', models.ManyToManyField(blank=True, to='library.tag')),
],
),
migrations.CreateModel(
name='QivipQuestion',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
('creation_date', models.DateTimeField(auto_now_add=True)),
('update_date', models.DateTimeField(auto_now=True)),
('data', models.TextField()),
('quiz_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='question', to='library.qivipquiz')),
],
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='status',
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='private', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0002_alter_qivipquiz_status'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='status',
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='Privat', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0003_alter_qivipquiz_status'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='status',
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='private', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0004_alter_qivipquiz_status'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='status',
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='public', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0005_alter_qivipquiz_status'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(blank=True, default='In dem Quiz geht es um ...', null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0006_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(blank=True, default='Das Quiz ist ein ... Quiz. In dem Quiz geht es um ...', null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0007_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(blank=True, default='Das Quiz ist ein ... Quiz. \n In dem Quiz geht es um ...', null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0008_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(blank=True, default='Das Quiz ist ein ... Quiz. \nIn dem Quiz geht es um ...', null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0009_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(blank=True, default='In dem Quiz geht es um ...', null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0010_alter_qivipquiz_description'),
]
operations = [
migrations.AddField(
model_name='qivipquiz',
name='difficulty',
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='public', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0011_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='difficulty',
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='not defined', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0012_alter_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='difficulty',
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('not defined', 'nicht gesetzt')], default='not defined', max_length=11),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0013_alter_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='difficulty',
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('not defined', 'nicht gesetzt')], default='not defined', help_text='1: niedrigste Schwierigkeit, 2: höchste Schwierigkeit', max_length=11),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-03-08 13:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0014_alter_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='difficulty',
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('not defined', 'nicht gesetzt')], default='not defined', help_text='1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit', max_length=11),
),
]

View File

65
django/library/models.py Normal file
View File

@@ -0,0 +1,65 @@
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import slugify
import uuid
class QivipQuiz(models.Model):
STATUS_VALUES = {
"public": "Öffentlich",
"hidden": "Versteckt",
"private": "Privat",
}
DIFFICULTY_VALUES = {
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"not defined":"nicht gesetzt",
}
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz')
creation_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=list(STATUS_VALUES.items()), default="public")
category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', blank=True)
name = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True, default="In dem Quiz geht es um ...")
difficulty= models.CharField(max_length=11, choices=list(DIFFICULTY_VALUES.items()), default="not defined", help_text="1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit")
def __str__(self):
return self.name
# Create your models here.
class QivipQuestion(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
quiz_id = models.ForeignKey(QivipQuiz, on_delete=models.CASCADE, related_name='question')
creation_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
data = models.TextField()
def __str__(self):
return self.data[:50]
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
class QuizCategory(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True, null=True)
slug = models.SlugField(unique=True, blank=True)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def __str__(self):
return self.name

3
django/library/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

15
django/library/urls.py Normal file
View File

@@ -0,0 +1,15 @@
from django.urls import path
from . import views
app_name = 'library'
urlpatterns = [
path('', views.overview_quiz, name='overview_quiz'),
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'),
path('detail/<int:pk>/', views.detail_quiz, name='detail_quiz'), # Hier werden Fragen gelistet (Quiz-Spezifisch)
path('question/new/', views.new_question, name='new_question'),
path('question/edit/<int:pk>/', views.edit_question, name='edit_question'),
path('question/delete/<int:pk>/', views.delete_question, name='delete_question'),
]

295
django/library/views.py Normal file
View File

@@ -0,0 +1,295 @@
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
from .forms import QuizForm, QuestionForm
import json
# Übersicht aller Quizze
@login_required
def overview_quiz(request):
quizzes = QivipQuiz.objects.filter(user_id=request.user)
return render(request, 'library/overview_quiz.html', {'quizzes': quizzes})
# Neues Quiz erstellen
@login_required
def new_quiz(request):
if request.method == 'POST':
form = QuizForm(request.POST)
if form.is_valid():
quiz = form.save(commit=False)
quiz.user_id = request.user
quiz.save()
form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags)
return redirect('library:edit_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)
if form.is_valid():
form.save()
return redirect('library:overview_quiz')
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()
return redirect('library:overview_quiz')
return render(request, 'library/delete_confirmation.html', {'object': quiz})
# Quiz anzeigen
@login_required
def detail_quiz(request, pk):
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
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
}
return render(request, 'library/detail_quiz.html', context)
# Ü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']:
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 == '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
question.save()
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}
]
}
elif question_type == 'multiple_choice':
question_data = {
'type': question_type,
'question': '',
'options': [
{'value': '', 'is_correct': False},
{'value': '', 'is_correct': False}
]
}
template_name = f'library/question/question_{question_type}.html'
context = {
'question': question_data,
'quiz_id': quiz_id,
'quiz': quiz
}
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 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')
# 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', '')
# 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 == '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.save()
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
}
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()
return redirect('library:detail_quiz', pk=question.quiz_id.pk)
return render(request, 'library/delete_confirmation.html', {'object': question})