Compare commits

..

63 Commits

Author SHA1 Message Date
nik8
e135ff945f Kopie eines Quizzes (Datenbank) 2025-04-05 12:07:56 +02:00
nik8
fbb0787aa9 Design_Änderung_Rahmen 2025-03-28 22:29:23 +01:00
nik8
16fcaf6346 Lupe-Symbol ersetzt 2025-03-28 21:55:25 +01:00
nik8
8a6d337392 Symbole vereinheitlicht 2025-03-22 15:16:20 +01:00
ben2
5f4fd4345e Merge branch '60-refactoring-views-py-try-accept-loschen' into 'master'
Resolve "Refactoring: Views.py try accept löschen"

Closes #60

See merge request enrichment-2024/qivip!23
2025-03-22 14:01:23 +00:00
Laurenz Wolff
7e93165c58 Überflüssiger Code entfernt 2025-03-22 14:59:34 +01:00
nik8
419160d144 Merge branch '59-uniocodes-austauschen' into 'master'
Resolve "Uniocodes Austauschen"

Closes #59

See merge request enrichment-2024/qivip!22
2025-03-22 13:58:25 +00:00
nik8
0c6580e732 Ersetzen der Symbole 2025-03-22 14:58:01 +01:00
nik8
8f4b5d581d Merge branch '54-filter-fur-library' into 'master'
Resolve "Filter für library"

Closes #54

See merge request enrichment-2024/qivip!21
2025-03-22 11:59:25 +00:00
nik8
809d857e94 Swiper_package 2025-03-22 12:58:59 +01:00
Frank Poetzsch-Heffter
98142c91f3 Pfad für tailwind-Dateien angepasst 2025-03-22 11:36:25 +00:00
nik8
93b2675b9e Zwischenstand 2025-03-22 12:21:39 +01:00
nik8
157daae740 struktur_library 2025-03-20 21:34:17 +01:00
nik8
f6459965e0 Suchfunktion_update 2025-03-18 17:22:42 +01:00
nik8
a3331543f2 Filter_library 2025-03-17 18:28:12 +01:00
nik8
9d71f2bbf3 Merge branch '50-in-der-handy-ansicht-ist-qivip-zu-dicht-an-dem-text-darunter-home-seite' into 'master'
Resolve "In der Handy Ansicht ist "qivip" zu dicht an dem Text darunter (Home Seite)"

Closes #50

See merge request enrichment-2024/qivip!20
2025-03-17 12:28:07 +00:00
nik8
8cceecf1bd Abstand_qivip_slogan 2025-03-17 13:27:18 +01:00
nik8
c330c4bc70 Merge branch '49-beim-speichern-eines-neuen-quiz-richtig-weiterfuhrung-nicht-das-man-zweilmal-auf-speichern' into 'master'
Resolve "Beim Speichern eines neuen Quiz richtig weiterführung, nicht das man zweilmal auf speichern drücken muss..."

Closes #49

See merge request enrichment-2024/qivip!19
2025-03-17 12:14:18 +00:00
nik8
26bf2a2c55 unnötige_Zeile_entfernt 2025-03-17 13:13:47 +01:00
nik8
86958c6d16 Weiterleitung_nach_Quiz_erstellen_gefixt 2025-03-17 13:12:04 +01:00
nik8
83a3d1584a Merge branch '47-weiterleitung-nach-registrierung-fixen' into 'master'
Resolve "Weiterleitung nach Registrierung fixen"

Closes #47

See merge request enrichment-2024/qivip!18
2025-03-17 11:41:31 +00:00
nik8
a07c66ad1a Weiterleitung_Registrierung_gefixt_autom_Login_nach_Regestrierung 2025-03-17 12:39:20 +01:00
nik8
956e69b56f Moderieren_link_zu_library 2025-03-16 18:17:16 +01:00
nik8
526e486fd3 Text in Box 2025-03-16 17:53:48 +01:00
juhnsa
b149cbcfee Components verfuegbar machen 2025-03-16 17:49:49 +01:00
nik8
12c5cec7a1 Merge branch '51-im-impressum-einen-kasten-machen-schoner' into 'master'
Resolve "Im Impressum einen Kasten machen (schöner)"

Closes #51

See merge request enrichment-2024/qivip!17
2025-03-16 16:41:59 +00:00
nik8
e0bfda0dee Impressum_Datenschutzerklärung_ansprechend 2025-03-16 17:41:11 +01:00
nik8
23675bc69e Merge branch '36-verwalten-mit-konto-mit-der-bibliothek-verbinden' into 'master'
Resolve "Verwalten mit Erstellbutton ersetzen und Verknüpfung herstellen"

Closes #36

See merge request enrichment-2024/qivip!16
2025-03-16 16:04:05 +00:00
nik8
94659791e6 fix#36 2025-03-16 17:03:26 +01:00
nik8
17cc0af02d Merge branch '40-offentliche-bibliothek' into 'master'
Resolve "Öffentliche Bibliothek"

Closes #40

See merge request enrichment-2024/qivip!14
2025-03-16 15:30:41 +00:00
nik8
f28878e1b0 small_changes_Datenbank 2025-03-16 16:27:50 +01:00
nik8
2a8b0b813c öffentliche_Library_und_Datenbankchanges 2025-03-16 14:51:27 +01:00
Juhn-Vitus Saß
f18ed83f9d Merge branch '41-quiz-id-updaten-wenn-participant-ein-anderes-quiz-beitritt' into 'master'
Resolve "Quiz ID updaten wenn Participant einem anderen Quiz beitritt"

Closes #41

See merge request enrichment-2024/qivip!13
2025-03-16 12:18:35 +00:00
juhnsa
d1cca38595 Quiz Titel anzeigen 2025-03-16 11:54:00 +01:00
juhnsa
1f1344522f Quiz Instanz von Teilnehmer updaten, wenn dieser einem anderen Quiz beitritt 2025-03-16 09:55:36 +01:00
Juhn-Vitus Saß
7cad46063e Merge branch '31-projektordner-core-umbenennen-in-z-b-django-2' into 'master'
Resolve "Projektordner "core" umbenennen in z.B. Django"

Closes #31

See merge request enrichment-2024/qivip!12
2025-03-16 08:23:23 +00:00
juhnsa
55750bcf1f Django Projektordner von 'core' in ''django' umbenennen 2025-03-15 21:48:35 +01:00
Juhn-Vitus Saß
230944cbcd Merge branch '35-seite-fur-namen-und-avatar-auswahl-erstellen' into 'master'
Resolve "Seite für Namen und Avatar Auswahl erstellen"

Closes #35

See merge request enrichment-2024/qivip!11
2025-03-15 20:16:28 +00:00
Juhn-Vitus Saß
2f47ff8523 Merge branch '34-der-code-soll-nur-aus-zahlen-bestehen' into 'master'
Resolve "Der Code soll nur aus Zahlen bestehen!"

Closes #34

See merge request enrichment-2024/qivip!10
2025-03-15 20:15:57 +00:00
juhnsa
3ef7d61da6 Teilnehmlogik grundlegend fertiggestellt 2025-03-15 20:30:49 +01:00
juhnsa
7fe29c5bd4 Warnmeldung beheben 2025-03-15 19:41:13 +01:00
Juhn-Vitus Saß
5775e53bb8 Fix template name 2025-03-15 16:30:31 +01:00
Juhn-Vitus Saß
57682d4607 Fix routing issue 2025-03-15 16:30:09 +01:00
ben8
915552ac4a fix#37 2025-03-15 16:18:28 +01:00
861e89ae4d Merge branch '35-seite-fur-namen-und-avatar-auswahl-erstellen' of edugit.org:enrichment-2024/qivip into 35-seite-fur-namen-und-avatar-auswahl-erstellen 2025-03-15 16:13:44 +01:00
97fd326561 Create initialize_participant.html 2025-03-15 16:11:35 +01:00
Juhn-Vitus Saß
34a9ab6a43 View erstellen 2025-03-15 16:10:34 +01:00
ben8
4c597473b7 Merge branch 'master' of edugit.org:enrichment-2024/qivip 2025-03-15 15:53:19 +01:00
ben8
8119f202d3 design_changes 2025-03-15 15:53:16 +01:00
6beccc772a Add initialize_participant.html 2025-03-15 15:50:41 +01:00
Frank Poetzsch-Heffter
4797a74111 almost 6 digits 2025-03-15 15:42:21 +01:00
Juhn-Vitus Saß
30b5cd9731 Fix Button 2025-03-15 15:31:52 +01:00
Juhn-Vitus Saß
6b83f26fc0 Merge branch '21-host-und-spiel-app-erstellen-und-grundlegende-templates-erstellen' into 'master'
Resolve "Host und Spiel App erstellen und Grundlegende Templates erstellen"

Closes #21

See merge request enrichment-2024/qivip!9
2025-03-15 14:26:45 +00:00
Juhn-Vitus Saß
346ac69046 Join Mechanik erstellen 2025-03-15 15:25:39 +01:00
Juhn-Vitus Saß
838eaab9be Fix Code generation 2025-03-15 14:59:53 +01:00
Juhn-Vitus Saß
eba34fa2d4 Merge branch '21-host-und-spiel-app-erstellen-und-grundlegende-templates-erstellen' of edugit.org:enrichment-2024/qivip into 21-host-und-spiel-app-erstellen-und-grundlegende-templates-erstellen 2025-03-15 14:53:00 +01:00
Juhn-Vitus Saß
9f51def959 Fix Admin 2025-03-15 14:52:58 +01:00
c8bf38dd36 Structure Installed Apps 2025-03-15 14:51:36 +01:00
Juhn-Vitus Saß
7120dcd317 DB Model für Quiz Spiele 2025-03-15 14:50:57 +01:00
ben8
da419533e9 Merge branch 'master' of edugit.org:enrichment-2024/qivip 2025-03-15 14:14:43 +01:00
ben8
bd0aa651af Teilnhame_verbunden 2025-03-15 14:14:40 +01:00
Juhn-Vitus Saß
d1b11a0c51 Update Gitignore 2025-03-15 13:46:34 +01:00
Juhn-Vitus Saß
5dc849f551 Update Gitignore 2025-03-15 13:45:49 +01:00
135 changed files with 1245 additions and 391 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ db.sqlite3
tailwindcss.exe tailwindcss.exe
t-style.css t-style.css
dump.rdb dump.rdb
media
node_modules

View File

@@ -100,10 +100,10 @@ npx @tailwindcss/cli -i <INPUT-DATEI> -o <OUTPUT-DATEI> --watch --minify
```sh ```sh
# unter Linux mit Tailwind Binärdatei: # unter Linux mit Tailwind Binärdatei:
tailwindcss -i core/static/homepage/t-input.css -o core/static/homepage/t-style.css --watch --minify tailwindcss -i static/css/t-input.css -o static/css/t-style.css --watch --minify
# unter Windows mit NPM: # unter Windows mit NPM (in bash mit '/' statt '\'):
npx @tailwindcss/cli -i .\core\static\homepage\t-input.css -o .\core\static\homepage\t-style.css --watch --minify npx @tailwindcss/cli -i .\static\css\t-input.css -o .\static\css\t-style.css --watch --minify
``` ```
## Issue - Merge Request - Merge ## Issue - Merge Request - Merge

View File

@@ -1,40 +0,0 @@
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
self.room_group_name = f"chat_{self.room_name}"
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name, self.channel_name
)
self.accept()
def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name, self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name, {"type": "chat.message", "message": message}
)
# Receive message from room group
def chat_message(self, event):
message = event["message"]
# Send message to WebSocket
self.send(text_data=json.dumps({"message": message}))

View File

@@ -1,8 +0,0 @@
# chat/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

View File

@@ -1,9 +0,0 @@
# chat/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("<str:room_name>/", views.room, name="room"),
]

View File

@@ -1,8 +0,0 @@
# chat/views.py
from django.shortcuts import render
def index(request):
return render(request, "chat/index.html")
def room(request, room_name):
return render(request, "chat/room.html", {"room_name": room_name})

View File

@@ -1,34 +0,0 @@
"""
ASGI config for core project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
#application = get_asgi_application()
django_asgi_app = get_asgi_application()
from chat.routing import websocket_urlpatterns
application = ProtocolTypeRouter(
{
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
),
}
)

View File

@@ -1,12 +0,0 @@
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

View File

@@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View File

@@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@@ -1,9 +0,0 @@
from django.urls import path
from . import views
app_name = 'play'
urlpatterns = [
path('lobby', views.lobby, name='lobby'),
path('join', views.join_game, name='join_game'),
]

View File

@@ -1,8 +0,0 @@
from django.shortcuts import render
# Create your views here.
def lobby(request):
return render(request, 'play/lobby.html')
def join_game(request):
return render(request, 'play/join_game.html')

View File

@@ -1,27 +0,0 @@
<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Rooms</title>
</head>
<body>
What chat room would you like to enter?<br>
<input id="room-name-input" type="text" size="100"><br>
<input id="room-name-submit" type="button" value="Enter">
<script>
document.querySelector('#room-name-input').focus();
document.querySelector('#room-name-input').onkeyup = function(e) {
if (e.key === 'Enter') { // enter, return
document.querySelector('#room-name-submit').click();
}
};
document.querySelector('#room-name-submit').onclick = function(e) {
var roomName = document.querySelector('#room-name-input').value;
window.location.pathname = '/chat/' + roomName + '/';
};
</script>
</body>
</html>

View File

@@ -1,50 +0,0 @@
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.key === 'Enter') { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>

View File

@@ -1,15 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="grid place-content-center md:h-screen h-150 w-full -z-50 top-0 p-4">
<div class="text-center">
<h2 class="font-bold md:text-8xl text-6xl md:mb-8 text-blue-600">qivip</h2>
<h3 class="italic font-extralight sm:text-2xl text-md">Interaktives Lernen neu definiert.</h3>
</div>
<div class="grid sm:grid-cols-3 place-items-stretch text-center mt-8 gap-4 p-4 border-3 bg-blue-100 border-blue-100 rounded-md">
<a class="qp-a-button bg-green-500 text-white" href="#">Teilnehmen</a>
<a class="qp-a-button bg-indigo-500 text-white" href="#">Moderieren</a>
<a class="qp-a-button bg-purple-500 text-white" href="#">Verwalten</a>
</div>
</div>
{% endblock %}

View File

@@ -1,22 +0,0 @@
{% extends 'base.html' %}
{% block content%}
<div>
<div>
<h1>Impressum</h1>
</div>
<div>
<div>
<p>
Katharineum zu Lübeck
</p>
<p>
Königsstraße 27-31
</p>
<p>
23552 Lübeck
</p>
</div>
</div>
{% endblock %}
</body>
</html>

View File

@@ -1,13 +0,0 @@
{% extends 'base.html' %}
{% block content%}
<div>
<p>
Hallo, ich bin die Datenschutzerklärung!
</p>
</div
{% endblock %}
>
</div>
</body>
</html>

View File

@@ -1,28 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<h1>Übersicht</h1>
<div class="flex justify-end">
<a href="{% url 'library:new_quiz' %}" class="text-white bg-blue-500 px-4 py-2 rounded-md mb-12">Neues Quiz erstellen</a>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4 place-items-center">
{% for quiz in quizzes %}
<div class="bg-white w-full max-w-md rounded-lg p-4 shadow-md border-blue-600 border-2 rounded-xl">
<h2 class="text-lg font-bold"><a href="{% url 'library:edit_quiz' quiz.id %}">{{ quiz.name }}</a></h2>
<p class="text-sm text-gray-600 py-12">{{ quiz.description }}</p>
<div>
<div class="flex justify-between items-center gap-2">
<a href="#" class="qp-a-button-small border-2 border-blue-600 text-black">Spiel starten</a>
<div class="flex gap-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white">&#x270F;</a>
<a href="{% url 'library:delete_quiz' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white">&#x1F5D1;</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@@ -1,15 +0,0 @@
<nav class="flex justify-between bg-blue-600 h-12 px-4 sm:px-6 lg:px-8 rounded-lg m-2 shadow-md">
<div class="flex items-center">
<h2 class="text-xl font-bold text-white hover:scale-110 transition duration-200"><a href="{% url 'homepage:home' %}">qivip</a></h2>
</div>
<div class="flex items-center">
<ul class="flex space-x-4 qp-nav-list">
<li><a href="{% url 'library:overview_quiz' %}">Bibliothek</a></li>
{% if user.is_authenticated %}
<li><a href="{% url 'accounts:home' %}">Konto</a></li>
{% else %}
<li><a href="{% url 'accounts:login' %}">Anmelden</a></li>
{% endif %}
</ul>
</div>
</nav>

View File

@@ -1,15 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="container mx-auto px-4">
<h1>Bundeslaender Deutschland Quiz</h1>
<div id="player-list">
<ul>
<li>Spieler 1</li>
<li>Spieler 2</li>
<li>Spieler 3</li>
</ul>
</div>
<button class="w-full p-3 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200" type="submit">Starten</button>
</div>
{% endblock %}

View File

@@ -1,5 +1,6 @@
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate
from .forms import RegisterForm from .forms import RegisterForm
# Create your views here. # Create your views here.
@@ -11,8 +12,9 @@ def register(response):
if response.method == "POST": if response.method == "POST":
form = RegisterForm(response.POST) form = RegisterForm(response.POST)
if form.is_valid(): if form.is_valid():
form.save() user=form.save()
return redirect("home") login(response, user) #automatischer Login nach Registrierung
return redirect("accounts:home")
else: else:
form = RegisterForm() form = RegisterForm()

16
django/core/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for core project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
application = get_asgi_application()

View File

@@ -25,19 +25,17 @@ SECRET_KEY = 'django-insecure-#+s307$rxkts=8@+_^i)8)n1b4*y4za1xz!mvv(h@!+n9!mp9)
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*', '172.22.10.111'] ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'daphne',
'chat',
'library.apps.LibraryConfig', 'library.apps.LibraryConfig',
'homepage.apps.HomepageConfig', 'homepage.apps.HomepageConfig',
'components.apps.ComponentsConfig', 'components.apps.ComponentsConfig',
'accounts.apps.AccountsConfig', 'accounts.apps.AccountsConfig',
'play.apps.PlayConfig', 'play',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@@ -131,17 +129,7 @@ STATICFILES_DIRS = [BASE_DIR / 'static']
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = '/account/login/' LOGIN_URL = '/account/login/'
import os
# Daphne MEDIA_URL = '/media/' # URL, um auf Medien-Dateien zuzugreifen
ASGI_APPLICATION = "core.asgi.application" MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Speicherort für hochgeladene Dateien
# Channels
ASGI_APPLICATION = "core.asgi.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 8000)],
},
},
}

View File

@@ -23,5 +23,5 @@ urlpatterns = [
path('', include('homepage.urls')), path('', include('homepage.urls')),
path('play/', include('play.urls')), path('play/', include('play.urls')),
path('library/', include('library.urls')), path('library/', include('library.urls')),
path('chat/', include('chat.urls')), path('components/', include('components.urls'))
] ]

View File

@@ -6,7 +6,6 @@ class QivipQuizAdmin(admin.ModelAdmin):
list_display = ('name', 'user_id', 'status', 'category', 'creation_date', 'update_date') # Welche Felder sollen in der Übersicht angezeigt werden 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 search_fields = ('name', 'user_id__username', 'category__name') # Suchfelder
list_filter = ('status', 'category') # Filteroptionen list_filter = ('status', 'category') # Filteroptionen
# Für das QivipQuestion Modell # Für das QivipQuestion Modell
class QivipQuestionAdmin(admin.ModelAdmin): class QivipQuestionAdmin(admin.ModelAdmin):
list_display = ('quiz_id', 'data', 'creation_date', 'update_date') list_display = ('quiz_id', 'data', 'creation_date', 'update_date')

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

@@ -0,0 +1,31 @@
from django import forms
from .models import QivipQuiz, QivipQuestion
class QuizForm(forms.ModelForm):
class Meta:
model = QivipQuiz
fields = ['name', 'description','image', 'status', 'category', 'tags',"difficulty","credits"]
class QuestionForm(forms.ModelForm):
class Meta:
model = QivipQuestion
fields = ['quiz_id', 'data']
from django import forms
class QuizFilterForm(forms.Form):
search = forms.CharField(required=False, label="Suche", widget=forms.TextInput(attrs={
'class': 'flex flex-grow rounded-lg p-2' ,'placeholder': 'Suche ...',
}))
min_amout_questions = forms.IntegerField(required=False, min_value=1, label="Minimale Anzahl an Fragen", widget=forms.NumberInput(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
}))
max_amout_questions = forms.IntegerField(required=False, min_value=1, label="Maximale Anzahl an Fragen", widget=forms.NumberInput(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
}))
user = forms.CharField(required=False, label="von User", widget=forms.TextInput(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
}))

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 12:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0015_alter_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='status',
field=models.CharField(choices=[('öffentlich', 'Öffentlich'), ('versteckt', 'Versteckt'), ('privat', 'Privat')], default='öffentlich', max_length=10),
),
]

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.7 on 2025-03-16 13:08
import library.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0019_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(default='In dem Quiz geht es um ...', max_length=255, validators=[library.models.QivipQuiz.validate_description]),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 14:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0020_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='name',
field=models.CharField(max_length=100),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.1.7 on 2025-03-16 14:35
import library.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0021_alter_qivipquiz_name'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(default='In dem Quiz geht es um ...', max_length=200, validators=[library.models.QivipQuiz.validate_description]),
),
migrations.AlterField(
model_name='qivipquiz',
name='name',
field=models.CharField(max_length=75),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-21 17:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0022_alter_qivipquiz_description_alter_qivipquiz_name'),
]
operations = [
migrations.AddField(
model_name='qivipquiz',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='quiz_images/'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-22 10:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0023_qivipquiz_image'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='image',
field=models.ImageField(blank=True, max_length=256, null=True, upload_to='quiz_images/'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-04-03 16:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0024_alter_qivipquiz_image'),
]
operations = [
migrations.AddField(
model_name='qivipquiz',
name='modified_description',
field=models.TextField(blank=True, default='Das Quiz wurde modifiziert von:', editable=False),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-04-04 13:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0025_qivipquiz_modified_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='modified_description',
field=models.TextField(blank=True, editable=False),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.1.7 on 2025-04-04 13:37
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0026_alter_qivipquiz_modified_description'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='qivipquiz',
name='creator',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='creator_quiz', to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.1.7 on 2025-04-04 13:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0027_qivipquiz_creator'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='creator',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='creator_quiz', to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-04-04 16:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0028_alter_qivipquiz_creator'),
]
operations = [
migrations.AddField(
model_name='qivipquiz',
name='credits',
field=models.TextField(blank=True),
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 5.1.7 on 2025-04-05 09:39
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0029_qivipquiz_credits'),
]
operations = [
migrations.RemoveField(
model_name='qivipquiz',
name='creator',
),
migrations.RemoveField(
model_name='qivipquiz',
name='modified_description',
),
migrations.AddField(
model_name='qivipquiz',
name='base_quiz',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_quizzes', to='library.qivipquiz'),
),
]

View File

@@ -2,12 +2,13 @@ from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.text import slugify from django.utils.text import slugify
import uuid import uuid
from django.core.exceptions import ValidationError
class QivipQuiz(models.Model): class QivipQuiz(models.Model):
STATUS_VALUES = { STATUS_VALUES = {
"public": "Öffentlich", "öffentlich": "Öffentlich",
"hidden": "Versteckt", "versteckt": "Versteckt",
"private": "Privat", "privat": "Privat",
} }
DIFFICULTY_VALUES = { DIFFICULTY_VALUES = {
"1": "1", "1": "1",
@@ -15,25 +16,34 @@ class QivipQuiz(models.Model):
"3": "3", "3": "3",
"4": "4", "4": "4",
"5": "5", "5": "5",
"not defined":"nicht gesetzt", "nicht gesetzt":"nicht gesetzt",
} }
def validate_description(value):
if value.strip() == "In dem Quiz geht es um ...":
raise ValidationError("Bitte gib eine aussagekräftige Beschreibung ein.")
image = models.ImageField(max_length=256, upload_to='quiz_images/', blank=True, null=True) # Bild speichern in media/quiz_images/
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz') 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) creation_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True) update_date = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=list(STATUS_VALUES.items()), default="public") status = models.CharField(max_length=10, choices=list(STATUS_VALUES.items()), default="öffentlich")
category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE) category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', blank=True) tags = models.ManyToManyField('Tag', blank=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=75)
description = models.TextField(blank=True, null=True, default="In dem Quiz geht es um ...") description = models.TextField(validators=[validate_description],max_length=200,blank=False, 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") difficulty= models.CharField(max_length=13, choices=list(DIFFICULTY_VALUES.items()), default="nicht gesetzt", help_text="1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit")
credits=models.TextField(blank=True, editable=True)
def __str__(self): def __str__(self):
return self.name return self.name
# Create your models here. # Create your models here.
class QivipQuestion(models.Model): class QivipQuestion(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)

View File

@@ -1,6 +1,8 @@
from django.urls import path from django.urls import path
from . import views
from django.conf import settings
from . import views
from django.conf.urls.static import static
app_name = 'library' app_name = 'library'
urlpatterns = [ urlpatterns = [
@@ -12,4 +14,8 @@ urlpatterns = [
path('question/new/', views.new_question, name='new_question'), path('question/new/', views.new_question, name='new_question'),
path('question/edit/<int:pk>/', views.edit_question, name='edit_question'), path('question/edit/<int:pk>/', views.edit_question, name='edit_question'),
path('question/delete/<int:pk>/', views.delete_question, name='delete_question'), path('question/delete/<int:pk>/', views.delete_question, name='delete_question'),
] path('detail/copy/<int:pk>/', views.copy_quiz, name='copy_quiz'),
]# Nur für die Entwicklungsumgebung
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -1,3 +1,4 @@
import uuid
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -6,24 +7,96 @@ from django.contrib import messages
from .models import QivipQuiz, QivipQuestion from .models import QivipQuiz, QivipQuestion
from .forms import QuizForm, QuestionForm from .forms import QuizForm, QuestionForm
import json 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 # Ü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})
def overview_quiz(request):
#Filter
form = QuizFilterForm(request.GET)
try:
filter_my_quizzes = QivipQuiz.objects.filter(user_id=request.user).annotate(max_amout_questions=Count('question'))
except:
filter_my_quizzes = QivipQuiz.objects.none()
filter_other_quizzes = QivipQuiz.objects.annotate(max_amout_questions=Count('question'))
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')
if search: # Suche nach Namen
filter_other_quizzes = filter_other_quizzes.filter(name__icontains=search)
filter_my_quizzes = filter_my_quizzes.filter(name__icontains=search)
if min_amout_questions:
filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__gte=min_amout_questions)
try:
filter_my_quizzes =filter_my_quizzes.filter(max_amout_questions__gte=min_amout_questions)
except:
filter_my_quizzes = QivipQuiz.objects.none()
if max_amout_questions:
filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__lte=max_amout_questions)
try:
filter_my_quizzes =filter_my_quizzes.filter(max_amout_questions__lte=max_amout_questions)
except:
filter_my_quizzes = QivipQuiz.objects.none()
if username:
try:
user = User.objects.get(username=username) # Benutzer anhand des Namens holen
filter_other_quizzes = filter_other_quizzes.filter(user_id=user.id)
filter_my_quizzes = filter_my_quizzes.filter(user_id=user.id) # Nach der user_id filtern
except User.DoesNotExist:
filter_other_quizzes = QivipQuiz.objects.none() # Falls User nicht existiert, leere Query zurückgeben
filter_my_quizzes = QivipQuiz.objects.none()
try:
filter_other_quizzes=filter_other_quizzes.exclude(user_id=request.user)
except:
filter_other_quizzes=filter_other_quizzes
filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__gte=1).filter(status='öffentlich')
context = {
'show_search': True,
'filter_my_quizzes': filter_my_quizzes.order_by('-creation_date'),
'filter_other_quizzes': filter_other_quizzes.order_by('-creation_date'),
'form': form,
}
return render(request, 'library/overview_quiz.html', context)
# Neues Quiz erstellen # Neues Quiz erstellen
@login_required @login_required
def new_quiz(request): def new_quiz(request):
if request.method == 'POST': if request.method == 'POST':
form = QuizForm(request.POST) form = QuizForm(request.POST, request.FILES)
if form.is_valid(): if form.is_valid():
quiz = form.save(commit=False) quiz = form.save(commit=False)
quiz.user_id = request.user quiz.user_id = request.user
quiz.creator = request.user
quiz.save() quiz.save()
form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags) form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags)
return redirect('library:edit_quiz', pk=quiz.pk) return redirect('library:detail_quiz', pk=quiz.pk)
else: else:
form = QuizForm() form = QuizForm()
return render(request, 'library/form.html', {'form': form}) return render(request, 'library/form.html', {'form': form})
@@ -33,14 +106,17 @@ def new_quiz(request):
def edit_quiz(request, pk): def edit_quiz(request, pk):
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user) quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
if request.method == 'POST': if request.method == 'POST':
form = QuizForm(request.POST, instance=quiz) form = QuizForm(request.POST, instance=quiz, files=request.FILES)
if form.is_valid(): if form.is_valid():
form.save() form.save()
return redirect('library:overview_quiz') return redirect('library:detail_quiz', pk=pk)
#return modified(request, pk)
else: else:
form = QuizForm(instance=quiz) form = QuizForm(instance=quiz)
return render(request, 'library/form.html', {'form': form}) return render(request, 'library/form.html', {'form': form})
# Quiz löschen # Quiz löschen
@login_required @login_required
def delete_quiz(request, pk): def delete_quiz(request, pk):
@@ -51,9 +127,9 @@ def delete_quiz(request, pk):
return render(request, 'library/delete_confirmation.html', {'object': quiz}) return render(request, 'library/delete_confirmation.html', {'object': quiz})
# Quiz anzeigen # Quiz anzeigen
@login_required
def detail_quiz(request, pk): def detail_quiz(request, pk):
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user) #quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
quiz = get_object_or_404(QivipQuiz, pk=pk)
questions = QivipQuestion.objects.filter(quiz_id=quiz) questions = QivipQuestion.objects.filter(quiz_id=quiz)
# Parse JSON data for each question # Parse JSON data for each question
@@ -66,6 +142,63 @@ def detail_quiz(request, pk):
'questions': questions 'questions': questions
} }
return render(request, 'library/detail_quiz.html', context) return render(request, 'library/detail_quiz.html', context)
"""
def modified(request, pk):
original_quiz = get_object_or_404(QivipQuiz, pk=pk)
words=original_quiz.modified_description.split()
word_exist=0
print(original_quiz.creator.username)
print(request.user.username)
for word in words:
if word==request.user.username or word+","==request.user.username:
word_exist=1
if word_exist==0 and len(words)!=0 and request.user.username!=original_quiz.creator.username:
original_quiz.modified_description +=", "+ request.user.username
elif word_exist==0 and request.user.username!=original_quiz.creator.username:
original_quiz.modified_description +=" "+ request.user.username
original_quiz.save()
return redirect('library:detail_quiz', pk=pk)
"""
@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" # Optional: Name anpassen
new_quiz.base_quiz_id = pk
#new_quiz.creator = original_quiz.creator or request.user
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()
return redirect('library:detail_quiz', pk=new_quiz.pk)
# Übersicht aller Fragen # Übersicht aller Fragen
@login_required @login_required
@@ -141,6 +274,7 @@ def new_question(request):
question.data = json.dumps(json_data) question.data = json.dumps(json_data)
question.quiz_id = quiz question.quiz_id = quiz
question.save() question.save()
#return modified(request, pk=quiz.pk)
return redirect('library:detail_quiz', pk=quiz.pk) return redirect('library:detail_quiz', pk=quiz.pk)
else: else:
# Initialize empty question data for new questions # Initialize empty question data for new questions
@@ -269,7 +403,9 @@ def edit_question(request, pk):
question.data = json.dumps(json_data) question.data = json.dumps(json_data)
question.save() question.save()
#return modified(request, question.quiz_id.pk)
return redirect('library:detail_quiz', pk=question.quiz_id.pk) return redirect('library:detail_quiz', pk=question.quiz_id.pk)
else: else:
template_name = f'library/question/question_{question_type}.html' template_name = f'library/question/question_{question_type}.html'
context = { context = {
@@ -280,6 +416,7 @@ def edit_question(request, pk):
return render(request, template_name, context) return render(request, template_name, context)
# Frage löschen # Frage löschen
@login_required @login_required
def delete_question(request, pk): def delete_question(request, pk):

35
django/package-lock.json generated Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "django",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "django",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"swiper": "^11.2.6"
}
},
"node_modules/swiper": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.6.tgz",
"integrity": "sha512-8aXpYKtjy3DjcbzZfz+/OX/GhcU5h+looA6PbAzHMZT6ESSycSp9nAjPCenczgJyslV+rUGse64LMGpWE3PX9Q==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"license": "MIT",
"engines": {
"node": ">= 4.7.0"
}
}
}
}

15
django/package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "django",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"swiper": "^11.2.6"
}
}

6
django/play/admin.py Normal file
View File

@@ -0,0 +1,6 @@
from django.contrib import admin
from .models import QuizGame, QuizGameParticipant
# Register your models here.
admin.site.register(QuizGame)
admin.site.register(QuizGameParticipant)

View File

@@ -0,0 +1,37 @@
# Generated by Django 5.1.7 on 2025-03-15 13:48
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('library', '0015_alter_qivipquiz_difficulty'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='QuizGame',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('join_code', models.CharField(max_length=6, unique=True)),
('host_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='host_user', to=settings.AUTH_USER_MODEL)),
('quiz_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='library.qivipquiz')),
],
),
migrations.CreateModel(
name='QuizGameParticipant',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('display_name', models.CharField(max_length=15, verbose_name='Anzeigename')),
('score', models.IntegerField(max_length=10, verbose_name='Punkte')),
('avatar', models.CharField(max_length=200)),
('quiz_game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participant', to='play.quizgame')),
],
),
]

Some files were not shown because too many files have changed in this diff Show More