Merge remote-tracking branch 'origin/master'

This commit is contained in:
Mawoka
2022-04-24 20:46:57 +02:00
10 changed files with 62 additions and 27 deletions
+8 -8
View File
@@ -14,14 +14,14 @@
<p align='center'>
The open-source quiz-platform!
<br/>
<a href='https://classquiz.mawoka.eu/'><strong>Visit the website »</strong></a>
<a href='https://classquiz.de/'><strong>Visit the website »</strong></a>
<br />
<br />
<a href='https://classquiz.mawoka.eu/docs'>Docs</a>
<a href='https://classquiz.de/docs'>Docs</a>
·
<a href='https://classquiz.mawoka.eu/account/register'>Register</a>
<a href='https://classquiz.de/account/register'>Register</a>
·
<a href='https://classquiz.mawoka.eu/docs/self-host'>Self-Hosting</a>
<a href='https://classquiz.de/docs/self-host'>Self-Hosting</a>
</p>
</div>
@@ -33,21 +33,21 @@ quiz, so students can compete with their knowledge against each other.
## Try it
There is a hosted version at [ClassQuiz.Mawoka.eu](https://classquiz.mawoka.eu?utm_medium=Github&utm_source=Readme). The
There is a hosted version at [classquiz.de](https://classquiz.de?utm_medium=Github&utm_source=Readme). The
server is located in Karlsruhe, Germany and hosted by [netcup](https://mawoka.eu/redir?token=2), so expect some latency
depending on your location.
## Self-Host
Please see https://classquiz.mawoka.eu/docs/self-host
Please see https://classquiz.de/docs/self-host
## Development
See https://classquiz.mawoka.eu/docs/develop
See https://classquiz.de/docs/develop
## Docs
The docs are online at https://classquiz.mawoka.eu/docs
The docs are online at https://classquiz.de/docs
### Things to know about the structure
+2 -1
View File
@@ -3,7 +3,7 @@ from fastapi import FastAPI, Request
from sentry_sdk.integrations.redis import RedisIntegration
from socketio import ASGIApp
from classquiz.config import settings
from classquiz.config import settings, meilisearch
from classquiz.db import database
from classquiz.routers import users, quiz, utils, stats, storage, search
from classquiz.socket_server import sio
@@ -32,6 +32,7 @@ async def startup() -> None:
database_ = app.state.database
if not database_.is_connected:
await database_.connect()
meilisearch.index(settings.meilisearch_index).update_settings({"sortableAttributes": ["created_at"]})
@app.on_event("shutdown")
+2
View File
@@ -30,6 +30,8 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
return "quiz not found"
quiz_questions: list[dict] = []
quiz_id = uuid.uuid4()
meilisearch.delete_index(settings.meilisearch_index)
meilisearch.create_index(settings.meilisearch_index)
for q in quiz.kahoot.questions:
answers: list[QuizAnswer] = []
+2 -1
View File
@@ -37,7 +37,8 @@ async def create_quiz_lol(quiz_input: QuizInput, user: User = Depends(get_curren
raise HTTPException(status_code=400, detail="image url is not valid")
quiz = Quiz(**quiz_input.dict(), user_id=user.id, id=uuid.uuid4())
await redis.delete("global_quiz_count")
meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz)])
if quiz_input.public:
meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz)])
return await quiz.save()
+23 -2
View File
@@ -16,11 +16,11 @@ from classquiz.auth import (
get_current_user,
)
from classquiz.cache import clear_cache_for_account
from classquiz.config import redis, settings
from classquiz.config import redis, settings, meilisearch
import uuid
import bleach
from pydantic import BaseModel
from classquiz.db.models import User, UserSession, UpdatePassword, Token
from classquiz.db.models import User, UserSession, UpdatePassword, Token, Quiz
from classquiz.emails import send_register_email, send_forgotten_password_email
settings = settings()
@@ -259,3 +259,24 @@ async def delete_session(session_id: str, user: User = Depends(get_current_user)
async def get_session(user: User = Depends(get_current_user)):
session = await UserSession.objects.filter(user=user).first()
return session
class DeleteUserInput(BaseModel):
password: str
@router.delete("/me")
async def delete_user_account(input_data: DeleteUserInput, user: User = Depends(get_current_user)):
if not verify_password(input_data.password, user.password):
raise HTTPException(status_code=400, detail="Incorrect password")
user = await User.objects.filter(id=user.id).get_or_none()
await UserSession.objects.filter(user=user).delete()
quizzes = await Quiz.objects.filter(user_id=user).all()
quizzes_to_delete = []
for quiz in quizzes:
if quiz.is_public:
quizzes_to_delete.append(quiz.id)
if len(quizzes_to_delete) > 0:
meilisearch.index(settings.meilisearch_index).delete_documents(quizzes_to_delete)
await User.objects.filter(id=user.id).delete()
await user.delete()
+1 -1
View File
@@ -3,7 +3,7 @@ import pytest
from classquiz.kahoot_importer.get import get
@pytest.mark.order("last")
@pytest.mark.order(-2)
@pytest.mark.asyncio
async def test_get():
await get("1f95eb0b-fcf4-4db2-879b-5418ef75116b")
+1 -1
View File
@@ -4,7 +4,7 @@ from classquiz.kahoot_importer.search import search
@pytest.mark.asyncio
@pytest.mark.order("last")
@pytest.mark.order(-2)
async def test_search():
res = await search(query="Python", limit=100)
assert len(res.entities) == 100
+13
View File
@@ -359,3 +359,16 @@ class TestQuiz:
ValueStorage.imported_quizzes.append(resp.json()["id"])
resp = test_client.post("/api/v1/quiz/import/1f95eb0bdassdadasdas", cookies={"access_token": token})
assert resp.text == '"quiz not found"'
class TestDeleteUser:
@pytest.mark.asyncio
async def test_delete_user(self, test_client):
resp = test_client.post(
"/api/v1/users/token/cookie", data={"username": test_user_email, "password": test_user_password}
)
token = resp.cookies["access_token"]
resp = test_client.delete(
"/api/v1/users/me", cookies={"access_token": token}, json={"password": test_user_password}
)
assert resp.status_code == 200
+9 -12
View File
@@ -31,29 +31,26 @@
</thead>
<tbody>
<tr>
<td> Your IP-Address </td>
<td> The IP-Address of the user</td>
<td>
<ul>
<li>
I store them because I am too lazy to configure them to be not stored.
</li>
<li>To prevent abuse</li>
<li>To provide user-sessions</li>
</ul>
</td>
<td> For a very long time </td>
<td> On my server, by Netcup, in Germany. </td>
<td> For a very long time</td>
<td> On the developers server, by Netcup, in Germany.</td>
</tr>
<tr>
<td> The data you enter (quizzes, email, username) </td>
<td> Self-explaining </td>
<td> Untill you delete your account </td>
<td> On my server, by Netcup, in Germany. </td>
<td> The data the user enters (quizzes, email, username)</td>
<td> Self-explaining</td>
<td> Maximum of 30 days after you've deleted your account</td>
<td> On the developers server, by Netcup, in Germany.</td>
</tr>
<tr>
<td>Your hashed password</td>
<td>The <b>hashed</b> password of the user</td>
<td>Self-explaining</td>
<td>Untill you delete your account</td>
<td>Maximum of 30 days after you've deleted your account</td>
<td>On my server, by Netcup, in Germany.</td>
</tr>
</tbody>
+1 -1
View File
@@ -15,7 +15,7 @@ async def __main__():
quizzes = await Quiz.objects.filter(public=True).all()
for quiz in quizzes:
meili_data.append(await get_meili_data(quiz))
print(meili_data)
print(len(meili_data))
client = meilisearch.Client(settings.meilisearch_url)
client.index(settings.meilisearch_index).add_documents(meili_data)
client.index(settings.meilisearch_index).update_settings({"sortableAttributes": ["created_at"]})