rating when ai is steaming
This commit is contained in:
@@ -20,6 +20,7 @@ from django.db.models import (
|
|||||||
Q,
|
Q,
|
||||||
Subquery,
|
Subquery,
|
||||||
)
|
)
|
||||||
|
from django.utils import timezone
|
||||||
from account.decorators import admin_required
|
from account.decorators import admin_required
|
||||||
from prompt.models import Conversation, Message
|
from prompt.models import Conversation, Message
|
||||||
from .classifier import classify_conversation_messages
|
from .classifier import classify_conversation_messages
|
||||||
@@ -44,6 +45,7 @@ from .schemas import (
|
|||||||
SubmissionIn,
|
SubmissionIn,
|
||||||
SubmissionOut,
|
SubmissionOut,
|
||||||
RatingScoreIn,
|
RatingScoreIn,
|
||||||
|
RandomRatingOut,
|
||||||
TaskStatsOut,
|
TaskStatsOut,
|
||||||
TopViewedItem,
|
TopViewedItem,
|
||||||
UserTag,
|
UserTag,
|
||||||
@@ -288,6 +290,37 @@ def list_by_user_task(request, user_id: int, task_id: int):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/random-for-rating/", response=Optional[RandomRatingOut])
|
||||||
|
@login_required
|
||||||
|
def get_random_for_rating(request, exclude_id: Optional[UUID] = None):
|
||||||
|
"""
|
||||||
|
随机返回一个待打分的其他同学的提交(用于AI生成期间的随手打分弹窗)
|
||||||
|
"""
|
||||||
|
if request.user.role == RoleChoices.NORMAL:
|
||||||
|
today_start = timezone.now().replace(
|
||||||
|
hour=0, minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
today_end = today_start + timezone.timedelta(days=1)
|
||||||
|
rating_count = Rating.objects.filter(
|
||||||
|
user=request.user, created__range=(today_start, today_end)
|
||||||
|
).count()
|
||||||
|
if rating_count >= 30:
|
||||||
|
return None
|
||||||
|
|
||||||
|
candidates = (
|
||||||
|
Submission.objects.select_related("task", "user")
|
||||||
|
.exclude(user=request.user)
|
||||||
|
.exclude(ratings__user=request.user)
|
||||||
|
)
|
||||||
|
if exclude_id:
|
||||||
|
candidates = candidates.exclude(pk=exclude_id)
|
||||||
|
|
||||||
|
pending = candidates.annotate(rating_count=Count("ratings")).filter(
|
||||||
|
rating_count__lt=5
|
||||||
|
)
|
||||||
|
return pending.order_by("?").first() or candidates.order_by("?").first()
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/flags")
|
@router.delete("/flags")
|
||||||
@login_required
|
@login_required
|
||||||
def clear_all_flags(request):
|
def clear_all_flags(request):
|
||||||
|
|||||||
@@ -296,3 +296,34 @@ class PromptRoundOut(Schema):
|
|||||||
html: Optional[str] = None
|
html: Optional[str] = None
|
||||||
css: Optional[str] = None
|
css: Optional[str] = None
|
||||||
js: Optional[str] = None
|
js: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class RandomRatingOut(Schema):
|
||||||
|
submission_id: UUID
|
||||||
|
username: str
|
||||||
|
task_title: str
|
||||||
|
task_display: int
|
||||||
|
task_type: Literal["tutorial", "challenge"]
|
||||||
|
html: Optional[str] = None
|
||||||
|
css: Optional[str] = None
|
||||||
|
js: Optional[str] = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_submission_id(obj):
|
||||||
|
return obj.id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_username(obj):
|
||||||
|
return obj.user.username
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_task_title(obj):
|
||||||
|
return obj.task.title
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_task_display(obj):
|
||||||
|
return obj.task.display
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_task_type(obj):
|
||||||
|
return obj.task.task_type
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from account.models import RoleChoices
|
|||||||
from prompt.models import Conversation, Message
|
from prompt.models import Conversation, Message
|
||||||
from task.models import Task
|
from task.models import Task
|
||||||
|
|
||||||
from .models import Award, Submission, SubmissionAward
|
from .models import Award, Rating, Submission, SubmissionAward
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@@ -533,3 +533,72 @@ class GradebookApiTest(TestCase):
|
|||||||
self.assertEqual(rows[1][2], "alice")
|
self.assertEqual(rows[1][2], "alice")
|
||||||
self.assertEqual(rows[1][4], "4")
|
self.assertEqual(rows[1][4], "4")
|
||||||
self.assertEqual(rows[1][7], "4")
|
self.assertEqual(rows[1][7], "4")
|
||||||
|
|
||||||
|
|
||||||
|
class RandomForRatingApiTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.viewer = _make_user("viewer")
|
||||||
|
self.author = _make_user("author")
|
||||||
|
self.task = _make_task()
|
||||||
|
|
||||||
|
def _submission(self, user, html="<div>work</div>"):
|
||||||
|
return Submission.objects.create(
|
||||||
|
user=user, task=self.task, html=html, css="", js=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_excludes_own_and_already_rated_submissions(self):
|
||||||
|
self._submission(self.viewer) # 自己的提交,应排除
|
||||||
|
rated = self._submission(self.author)
|
||||||
|
Rating.objects.create(user=self.viewer, submission=rated, score=4)
|
||||||
|
eligible = self._submission(self.author)
|
||||||
|
|
||||||
|
self.client.force_login(self.viewer)
|
||||||
|
resp = self.client.get("/api/submission/random-for-rating/")
|
||||||
|
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
data = resp.json()
|
||||||
|
self.assertIsNotNone(data)
|
||||||
|
self.assertEqual(data["submission_id"], str(eligible.id))
|
||||||
|
self.assertEqual(data["username"], "author")
|
||||||
|
self.assertEqual(data["task_title"], self.task.title)
|
||||||
|
self.assertEqual(data["task_display"], self.task.display)
|
||||||
|
self.assertEqual(data["task_type"], "challenge")
|
||||||
|
|
||||||
|
def test_prefers_submission_with_fewer_than_five_ratings(self):
|
||||||
|
raters = [_make_user(f"rater{i}") for i in range(5)]
|
||||||
|
many_ratings = self._submission(self.author, html="<div>many</div>")
|
||||||
|
for rater in raters:
|
||||||
|
Rating.objects.create(user=rater, submission=many_ratings, score=3)
|
||||||
|
few_ratings = self._submission(self.author, html="<div>few</div>")
|
||||||
|
for rater in raters[:2]:
|
||||||
|
Rating.objects.create(user=rater, submission=few_ratings, score=3)
|
||||||
|
|
||||||
|
self.client.force_login(self.viewer)
|
||||||
|
resp = self.client.get("/api/submission/random-for-rating/")
|
||||||
|
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
data = resp.json()
|
||||||
|
self.assertEqual(data["submission_id"], str(few_ratings.id))
|
||||||
|
|
||||||
|
def test_exclude_id_param_excludes_given_submission(self):
|
||||||
|
only = self._submission(self.author)
|
||||||
|
|
||||||
|
self.client.force_login(self.viewer)
|
||||||
|
resp = self.client.get(
|
||||||
|
f"/api/submission/random-for-rating/?exclude_id={only.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
self.assertIsNone(resp.json())
|
||||||
|
|
||||||
|
def test_normal_user_reaching_daily_cap_returns_none(self):
|
||||||
|
self._submission(self.author, html="<div>extra</div>")
|
||||||
|
for i in range(30):
|
||||||
|
target = self._submission(self.author, html=f"<div>{i}</div>")
|
||||||
|
Rating.objects.create(user=self.viewer, submission=target, score=3)
|
||||||
|
|
||||||
|
self.client.force_login(self.viewer)
|
||||||
|
resp = self.client.get("/api/submission/random-for-rating/")
|
||||||
|
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
self.assertIsNone(resp.json())
|
||||||
|
|||||||
Reference in New Issue
Block a user