Skip to content

Commit

Permalink
refactor: auto_save_redis_to_database
Browse files Browse the repository at this point in the history
Revival of #155
  • Loading branch information
YDX-2147483647 committed Aug 27, 2024
1 parent 97cf6b9 commit 71acac5
Showing 1 changed file with 51 additions and 55 deletions.
106 changes: 51 additions & 55 deletions contest/contest/tasks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import logging
"""celery 任务"""

from __future__ import annotations

from typing import TYPE_CHECKING

import redis
from celery import shared_task
from django.core.cache import cache
from django.shortcuts import get_object_or_404
Expand All @@ -10,59 +13,50 @@
# 导致运行的时候如果直接import contest.quiz.models会找不到
# 所以就直接默认已经在contest路径里开始索引,因此lint会报错
# 但是实际上是可以运行的,忽略Lint报错即可
from quiz.models import (
Choice,
DraftAnswer,
DraftResponse,
)
from quiz.models import Choice, DraftAnswer, DraftResponse

# Get an instance of a logger
logger = logging.getLogger("django")
if TYPE_CHECKING:
from typing import Generator


@shared_task
def auto_save_redis_to_database() -> None:
# 获取 Redis 连接
r = redis.Redis(host="127.0.0.1", port=6379, db=1)
# 使用 scan_iter 获取所有键
keys = r.scan_iter("*_ddl")
if keys is None:
return
for key in keys:
ddl_key = key.decode("utf-8")[3:]
ddl = cache.get(ddl_key)
now = timezone.now()
if ddl is not None:
if ddl < now:
try:
draft_response = DraftResponse.objects.get(id=int(ddl_key[:-4]))
cache_key = f"{ddl_key[:-4]}_json"
# # 从 Redis 获取现有的答案缓存
cached_answers = cache.get(cache_key, {})

if cached_answers is not None: # 防止未提交的是白卷
for question_id, choice_id in cached_answers.items():
# Filter out tokens
if not question_id.startswith("question-"):
continue

if not isinstance(choice_id, str) or not choice_id.startswith(
"choice-"
):
return

answer: DraftAnswer = get_object_or_404(
draft_response.answer_set,
question_id=int(question_id.removeprefix("question-")),
)

answer.choice = get_object_or_404(
Choice.objects,
pk=int(choice_id.removeprefix("choice-")),
question=answer.question,
)

answer.save()
"""提交 Redis 缓存中过期的答卷草稿"""
scanner: Generator[str, None, None] = cache.iter_keys("*_ddl") # type: ignore[attr-defined]
# `iter_keys`由 django-redis 提供,django 本身没有
for ddl_key in scanner:
pk = ddl_key.removesuffix("_ddl")
ddl = cache.get(pk)
if ddl is not None and ddl < timezone.now():
try:
draft_response = DraftResponse.objects.get(pk=pk)
cached_answers = cache.get(f"{pk}_json", {})

# 同步 Redis 缓存到数据库
# 若未作答,可能 Redis 中无记录,但数据库中仍有
if cached_answers is not None:
for question_id, choice_id in cached_answers.items():
# Filter out tokens
if not question_id.startswith("question-"):
continue

if not isinstance(choice_id, str) or not choice_id.startswith(
"choice-"
):
return

answer: DraftAnswer = get_object_or_404(
draft_response.answer_set,
question_id=int(question_id.removeprefix("question-")),
)

answer.choice = get_object_or_404(
Choice.objects,
pk=int(choice_id.removeprefix("choice-")),
question=answer.question,
)

answer.save()

# 1. Convert from draft
response, answers = draft_response.finalize(submit_at=timezone.now())
Expand All @@ -72,9 +66,11 @@ def auto_save_redis_to_database() -> None:
response.answer_set.bulk_create(answers)
draft_response.delete()

except DraftResponse.DoesNotExist as e:
print("here is tasks.py 74 line")
print(e)
except DraftResponse.DoesNotExist as e:
print("here is tasks.py 74 line")
print(e)

r.delete(key)
r.delete(":1:" + ddl_key[:-4] + "_json")
# 即使`DraftResponse.DoesNotExist`,也应尝试删除 Redis 中的记录
# 因为可能是 Django 正常处理过了
cache.delete(f"{pk}_ddl")
cache.delete(f"{pk}_json")

0 comments on commit 71acac5

Please sign in to comment.