Skip to content

Commit

Permalink
Merge pull request #28 from Phoupraw/auto-submission
Browse files Browse the repository at this point in the history
  • Loading branch information
YDX-2147483647 authored Aug 9, 2023
2 parents 19aa41c + 208d147 commit 7616c99
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 36 deletions.
2 changes: 1 addition & 1 deletion contest/js/static_src/rollup.config.dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import commonjs from '@rollup/plugin-commonjs'

export default {
input: {
index: './src/index.js',
status: './src/status.js',
contest: './src/contest.js',
toggle_mobile_menu: './src/toggle_mobile_menu.js',
},
Expand Down
17 changes: 17 additions & 0 deletions contest/js/static_src/src/contest.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Swal from 'sweetalert2'

import { get_data } from "./util"

/** 时间允差,毫秒 */
Expand Down Expand Up @@ -30,7 +32,22 @@ async function update_contest_progress () {
body: new FormData(form)
})
if (!response.ok && response.status == 403) {
// 锁定表单
filed_sets.forEach(set => set.disabled = true)

// 提示提交
const result = await Swal.fire({
title: '此次作答已超时',
html: '<p>无法再更改答卷,但您还可以查看自己的答卷。</p><p>(截止前作答部分已尽量保存)</p>',
icon: 'warning',
confirmButtonText: '现在提交',
showCancelButton: true,
cancelButtonText: '再看看答卷',
showCloseButton: true,
})
if (result.isConfirmed) {
form.submit()
}
}
}

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion contest/quiz/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% load my_humanize %}
{% load static %}
{% block scripts %}
<script type='module' src="{% static 'js/dist/index.js' %}"></script>
<script type='module' src="{% static 'js/dist/status.js' %}"></script>
{{ status | json_script:"data:status" }}
{% endblock scripts %}
{% block header %}
Expand Down
4 changes: 4 additions & 0 deletions contest/quiz/templates/info.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{% extends "base.html" %}
{% load my_humanize humanize %}
{% load static %}
{% block scripts %}
<script type='module' src="{% static 'js/dist/status.js' %}"></script>
{{ status | json_script:"data:status" }}
{% endblock scripts %}
{% block content %}
<div class="prose">
<h2>成绩</h2>
Expand Down
38 changes: 37 additions & 1 deletion contest/quiz/tests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import timedelta
from http import HTTPStatus

from django.test import TestCase
from django.urls import reverse
from django.utils import timezone

from .constants import constants
from .models import (
Answer,
Choice,
Expand Down Expand Up @@ -66,6 +68,40 @@ def test_contest_view(self):
response = self.client.get(reverse("quiz:contest"))
self.assertEqual(response.status_code, HTTPStatus.OK)

def test_auto_submission(self):
"""自动提交"""
response = self.client.get(reverse("quiz:index"))
self.assertEqual(response.context["status"], "")

assert len(self.user.student.response_set.all()) == 0

self.client.force_login(self.user)
response = self.client.get(reverse("quiz:index"))
self.assertEqual(response.context["status"], "not taking")

# 前往答题
self.client.get(reverse("quiz:contest"))
response = self.client.get(reverse("quiz:index"))
self.assertEqual(response.context["status"], "taking contest")

# “时光飞逝”
self.user.student.draft_response.deadline -= constants.DEADLINE_DURATION
# -1 s
self.user.student.draft_response.deadline -= timedelta(seconds=1)
self.user.student.draft_response.save()

response = self.client.get(reverse("quiz:index"))
self.assertEqual(response.context["status"], "deadline passed")
self.assertEqual(len(self.user.student.response_set.all()), 1)
# `self.user.student.draft_response`访问在先,自动提交在后。
# 两边的 student 在数据库中相同,但并非 python 类的同一实例。
# 故必须刷新缓存的关系,不然`student.draft_response`总仍存在。
self.user.student.refresh_from_db()
self.assertFalse(hasattr(self.user.student, "draft_response"))

response = self.client.get(reverse("quiz:index"))
self.assertEqual(response.context["status"], "not taking")

def test_empty_response(self):
"""正常作答,但交白卷"""
self.client.force_login(self.user)
Expand All @@ -74,7 +110,7 @@ def test_empty_response(self):
self.assertEqual(response.status_code, HTTPStatus.OK)

response = self.client.post(reverse("quiz:contest_submit"))
self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, reverse("quiz:info"))

def test_non_student_user(self):
Expand Down
7 changes: 2 additions & 5 deletions contest/quiz/urls.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from django.urls import path

from . import views
from .constants import constants

app_name = "quiz"

extra_context = {"constants": constants}

urlpatterns = [
path("", views.index, name="index"),
path("info/", views.InfoView.as_view(extra_context=extra_context), name="info"),
path("", views.IndexView.as_view(), name="index"),
path("info/", views.InfoView.as_view(), name="info"),
path("contest/", views.contest, name="contest"),
path("contest/update/", views.contest_update, name="contest_update"),
path("contest/submit/", views.contest_submit, name="contest_submit"),
Expand Down
90 changes: 62 additions & 28 deletions contest/quiz/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,47 +23,81 @@
from .util import is_student, is_student_taking_contest, student_only

if TYPE_CHECKING:
from django.http import HttpRequest
from typing import Any, Literal

from .models import DraftAnswer, Student
from django.contrib.auth.models import AnonymousUser

from .models import DraftAnswer, Student, User
from .util import AuthenticatedHttpRequest


@require_GET
def index(request: HttpRequest) -> HttpResponse:
if request.user.is_authenticated and is_student(request.user):
student = request.user.student
def continue_or_finalize(draft: DraftResponse) -> bool:
"""自动提交
若草稿已超期,则定稿,否则什么也不做。
:return: 是否如此操作(定稿)了
定稿只会修改数据库,而 python 实例仍存在。
可用其它 model 的`refresh_from_db`刷新缓存的关系。
https://docs.djangoproject.com/en/4.2/ref/models/instances/#django.db.models.Model.delete
https://docs.djangoproject.com/en/4.2/ref/models/instances/#refreshing-objects-from-database
"""

if draft.outdated():
# 提交之前的草稿
response, answers = draft.finalize(submit_at=draft.deadline)

response.save()
response.answer_set.bulk_create(answers)

draft.delete()

return True

return False


def manage_status(
user: User | AnonymousUser,
) -> (
Literal["not taking"]
| Literal["deadline passed"]
| Literal["taking contest"]
| Literal[""]
):
"""检查状态及自动提交"""

if user.is_authenticated and is_student(user):
student = user.student

if not hasattr(student, "draft_response"):
status = "not taking"
elif not student.draft_response.outdated():
status = "taking contest"
return "not taking"
else:
status = "deadline passed"
finalized = continue_or_finalize(student.draft_response)
if finalized:
return "deadline passed"
else:
return "taking contest"
else:
return ""

# 提交之前的草稿
response, answers = student.draft_response.finalize(
submit_at=student.draft_response.deadline + constants.DEADLINE_DURATION
)

response.save()
response.answer_set.bulk_create(answers)
student.draft_response.delete()
else:
status = ""
class IndexView(TemplateView):
template_name = "index.html"

return render(
request,
"index.html",
{
"constants": constants,
"status": status,
},
)
def get_context_data(self, **kwargs) -> dict[str, Any]:
context = super().get_context_data(**kwargs)

context["status"] = manage_status(self.request.user)
context["constants"] = constants

return context


@method_decorator(student_only, name="dispatch")
class InfoView(LoginRequiredMixin, TemplateView):
class InfoView(LoginRequiredMixin, IndexView):
template_name = "info.html"


Expand Down

0 comments on commit 7616c99

Please sign in to comment.