Merge pull request #141 from mawoka-myblock/132-add-a-field-for-optional-data-to-be-entered-by-the-players
This commit is contained in:
@@ -183,6 +183,7 @@ class PlayGame(BaseModel):
|
||||
game_mode: str | None
|
||||
current_question: int = -1
|
||||
background_color: str | None
|
||||
custom_field: str | None
|
||||
|
||||
|
||||
class GamePlayer(BaseModel):
|
||||
|
||||
@@ -29,9 +29,18 @@ async def get_meili_data(quiz: Quiz) -> dict:
|
||||
}
|
||||
|
||||
|
||||
async def generate_spreadsheet(quiz_results: dict, quiz: Quiz) -> BytesIO:
|
||||
async def generate_spreadsheet(quiz_results: dict, quiz: Quiz, player_fields: dict) -> BytesIO:
|
||||
storage = BytesIO()
|
||||
workbook = xlsxwriter.Workbook(storage, {"in_memory": True})
|
||||
player_worksheet = workbook.add_worksheet()
|
||||
player_worksheet.name = "Players"
|
||||
player_worksheet.write(0, 0, "Username")
|
||||
player_worksheet.write(0, 1, "Score")
|
||||
player_worksheet.write(0, 2, "Custom-Field")
|
||||
for i, player in enumerate(player_fields.keys()):
|
||||
player_worksheet.write(i + 1, 0, player)
|
||||
player_worksheet.write(i + 1, 2, player_fields[player])
|
||||
|
||||
worksheet = workbook.add_worksheet()
|
||||
worksheet.name = "Questions"
|
||||
worksheet.write(0, 0, "Question")
|
||||
|
||||
@@ -89,7 +89,11 @@ async def get_public_quiz(quiz_id: str):
|
||||
|
||||
@router.post("/start/{quiz_id}")
|
||||
async def start_quiz(
|
||||
quiz_id: str, game_mode: str, captcha_enabled: bool = True, user: User = Depends(get_current_user)
|
||||
quiz_id: str,
|
||||
game_mode: str,
|
||||
captcha_enabled: bool = True,
|
||||
custom_field: str | None = None,
|
||||
user: User = Depends(get_current_user),
|
||||
):
|
||||
try:
|
||||
quiz_id = uuid.UUID(quiz_id)
|
||||
@@ -101,6 +105,8 @@ async def start_quiz(
|
||||
if quiz is None:
|
||||
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
|
||||
game_pin = randint(10000000, 99999999)
|
||||
if custom_field == "":
|
||||
custom_field = None
|
||||
game = PlayGame(
|
||||
quiz_id=quiz_id,
|
||||
game_pin=str(game_pin),
|
||||
@@ -113,6 +119,7 @@ async def start_quiz(
|
||||
game_mode=game_mode,
|
||||
user_id=user.id,
|
||||
background_color=quiz.background_color,
|
||||
custom_field=custom_field,
|
||||
)
|
||||
await redis.set(f"game:{str(game.game_pin)}", game.json(), ex=18000)
|
||||
await redis.set(f"game_pin:{user.id}:{quiz_id}", game_pin, ex=18000)
|
||||
@@ -122,6 +129,7 @@ async def start_quiz(
|
||||
class CheckIfCaptchaEnabledResponse(BaseModel):
|
||||
enabled: bool
|
||||
game_mode: str | None
|
||||
custom_field: str | None
|
||||
|
||||
|
||||
@router.get("/play/check_captcha/{game_pin}", response_model=CheckIfCaptchaEnabledResponse)
|
||||
@@ -131,9 +139,9 @@ async def check_if_captcha_enabled(game_pin: str):
|
||||
return JSONResponse(status_code=404, content={"detail": "game not found"})
|
||||
game = PlayGame.parse_raw(game)
|
||||
if game.captcha_enabled:
|
||||
return CheckIfCaptchaEnabledResponse(enabled=True, game_mode=game.game_mode)
|
||||
return CheckIfCaptchaEnabledResponse(enabled=True, game_mode=game.game_mode, custom_field=game.custom_field)
|
||||
else:
|
||||
return CheckIfCaptchaEnabledResponse(enabled=False, game_mode=game.game_mode)
|
||||
return CheckIfCaptchaEnabledResponse(enabled=False, game_mode=game.game_mode, custom_field=game.custom_field)
|
||||
|
||||
|
||||
@router.get("/join/{game_pin}", deprecated=True)
|
||||
@@ -238,11 +246,14 @@ async def export_quiz_answers(export_token: str, game_pin: str):
|
||||
if data is None:
|
||||
raise HTTPException(status_code=404, detail="export token not found")
|
||||
data = json.loads(data)
|
||||
game_data = PlayGame(**json.loads(await redis.get(f"game:{game_pin}")))
|
||||
data2 = await redis.get(f"game:{game_pin}")
|
||||
game_data = PlayGame.parse_raw(data2)
|
||||
quiz = await Quiz.objects.get_or_none(id=game_data.quiz_id)
|
||||
if quiz is None:
|
||||
raise HTTPException(status_code=404, detail="quiz not found")
|
||||
spreadsheet = await generate_spreadsheet(quiz=quiz, quiz_results=data)
|
||||
|
||||
player_fields = await redis.hgetall(f"game:{game_pin}:player:custom_fields")
|
||||
spreadsheet = await generate_spreadsheet(quiz=quiz, quiz_results=data, player_fields=player_fields)
|
||||
|
||||
def iter_file():
|
||||
yield from spreadsheet
|
||||
|
||||
@@ -43,6 +43,7 @@ class _JoinGameData(BaseModel):
|
||||
username: str
|
||||
game_pin: str
|
||||
captcha: str | None
|
||||
custom_field: str | None
|
||||
|
||||
|
||||
@sio.event
|
||||
@@ -104,7 +105,11 @@ async def join_game(sid: str, data: dict):
|
||||
redis_res = GameSession.parse_raw(redis_res)
|
||||
await redis.set(f"game_session:{data.game_pin}:players:{data.username}", sid, ex=18000)
|
||||
await redis.sadd(f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).json())
|
||||
print(GamePlayer(username=data.username, sid=sid).json())
|
||||
if data.custom_field == "":
|
||||
data.custom_field = None
|
||||
print(data.custom_field)
|
||||
if data.custom_field is not None:
|
||||
await redis.hset(f"game:{data.game_pin}:player:custom_fields", data.username, data.custom_field)
|
||||
# await redis.set(
|
||||
# f"game_session:{data.game_pin}",
|
||||
# GameSession(admin=redis_res.admin, game_id=redis_res.game_id, answers=[]).json(),
|
||||
|
||||
@@ -14,26 +14,26 @@
|
||||
let captcha_selected = false;
|
||||
let selected_game_mode = 'kahoot';
|
||||
let loading = false;
|
||||
let custom_field = '';
|
||||
|
||||
const start_game = async (id: string) => {
|
||||
let res;
|
||||
loading = true;
|
||||
if (captcha_enabled && captcha_selected) {
|
||||
res = await fetch(
|
||||
`/api/v1/quiz/start/${id}?captcha_enabled=True&game_mode=${selected_game_mode}`,
|
||||
`/api/v1/quiz/start/${id}?captcha_enabled=True&game_mode=${selected_game_mode}&custom_field=${custom_field}`,
|
||||
{
|
||||
method: 'POST'
|
||||
}
|
||||
);
|
||||
} else {
|
||||
res = await fetch(
|
||||
`/api/v1/quiz/start/${id}?captcha_enabled=False&game_mode=${selected_game_mode}`,
|
||||
`/api/v1/quiz/start/${id}?captcha_enabled=False&game_mode=${selected_game_mode}&custom_field=${custom_field}`,
|
||||
{
|
||||
method: 'POST'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (res.status !== 200) {
|
||||
alertModal.set({
|
||||
open: true,
|
||||
@@ -111,6 +111,9 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<input bind:value={custom_field} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="mt-auto mx-auto bg-green-500 p-4 rounded-lg shadow-lg hover:bg-green-400 transition-all marck-script text-2xl"
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
export let game_mode;
|
||||
|
||||
export let username;
|
||||
let custom_field;
|
||||
let custom_field_value;
|
||||
let captcha_enabled;
|
||||
|
||||
let hcaptchaSitekey = import.meta.env.VITE_HCAPTCHA;
|
||||
|
||||
let hcaptcha = {
|
||||
@@ -48,17 +52,13 @@
|
||||
}
|
||||
});
|
||||
|
||||
const setUsername = async () => {
|
||||
if (username.length <= 3) {
|
||||
return;
|
||||
}
|
||||
let captcha_resp: string;
|
||||
const set_game_pin = async () => {
|
||||
const res = await fetch(`/api/v1/quiz/play/check_captcha/${game_pin}`);
|
||||
let captcha_enabled;
|
||||
const json = await res.json();
|
||||
game_mode = json.game_mode;
|
||||
if (res.status === 200) {
|
||||
captcha_enabled = json.enabled;
|
||||
custom_field = json.custom_field;
|
||||
}
|
||||
if (res.status === 404) {
|
||||
alertModal.set({
|
||||
@@ -77,6 +77,17 @@
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
$: if (game_pin.length >= 7) {
|
||||
set_game_pin();
|
||||
}
|
||||
|
||||
const setUsername = async () => {
|
||||
if (username.length <= 3) {
|
||||
return;
|
||||
}
|
||||
let captcha_resp: string;
|
||||
|
||||
if (captcha_enabled) {
|
||||
try {
|
||||
@@ -103,7 +114,8 @@
|
||||
socket.emit('join_game', {
|
||||
username: username,
|
||||
game_pin: game_pin,
|
||||
captcha: captcha_resp
|
||||
captcha: captcha_resp,
|
||||
custom_field: custom_field ? custom_field_value : undefined
|
||||
});
|
||||
};
|
||||
socket.on('game_not_found', () => {
|
||||
@@ -151,6 +163,13 @@
|
||||
bind:value={username}
|
||||
maxlength="17"
|
||||
/>
|
||||
{#if custom_field}
|
||||
<h1 class="text-lg text-center">{custom_field}</h1>
|
||||
<input
|
||||
class="border border-gray-400 self-center text-center text-black ring-0 outline-none p-2 rounded-lg focus:shadow-2xl transition-all"
|
||||
bind:value={custom_field_value}
|
||||
/>
|
||||
{/if}
|
||||
<button
|
||||
class="bg-amber-800 hover:bg-amber-700 text-white font-bold py-2 px-4 rounded disabled:cursor-not-allowed disabled:opacity-50 mt-2"
|
||||
type="submit"
|
||||
|
||||
Reference in New Issue
Block a user