refactor score
This commit is contained in:
@@ -86,6 +86,8 @@ def list_submissions(request, filters: SubmissionFilter = Query(...)):
|
||||
submissions = submissions.filter(flag__isnull=False)
|
||||
else:
|
||||
submissions = submissions.filter(flag=filters.flag)
|
||||
if filters.zone:
|
||||
submissions = submissions.filter(zone=filters.zone)
|
||||
|
||||
if filters.score_lt_threshold is not None:
|
||||
submissions = submissions.filter(score__lt=filters.score_lt_threshold)
|
||||
|
||||
27
submission/management/commands/recalculate_scores.py
Normal file
27
submission/management/commands/recalculate_scores.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from submission.models import Submission
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Recalculate score, raw_score, and zone for all rated submissions"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--dry-run", action="store_true", help="Show what would change without saving")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run = options["dry_run"]
|
||||
qs = Submission.objects.filter(ratings__isnull=False).distinct()
|
||||
total = qs.count()
|
||||
self.stdout.write(f"Found {total} rated submission(s).")
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write("Dry run — no changes made.")
|
||||
return
|
||||
|
||||
for i, s in enumerate(qs, 1):
|
||||
old_score = s.score
|
||||
s.update_score()
|
||||
self.stdout.write(f"[{i}/{total}] {s.user.username}/{s.task.title}: {old_score:.3f} → {s.score:.3f} zone={s.zone}")
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("Done."))
|
||||
18
submission/migrations/0008_add_zone.py
Normal file
18
submission/migrations/0008_add_zone.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-03-30 11:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('submission', '0007_remove_nominated'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='submission',
|
||||
name='zone',
|
||||
field=models.CharField(blank=True, choices=[('featured', '精选'), ('low', '待改进'), ('pending', '待评')], db_index=True, default=None, max_length=10, null=True, verbose_name='分区'),
|
||||
),
|
||||
]
|
||||
@@ -19,6 +19,12 @@ class FlagChoices(models.TextChoices):
|
||||
YELLOW = "yellow", "需要改进"
|
||||
|
||||
|
||||
class ZoneChoices(models.TextChoices):
|
||||
FEATURED = "featured", "精选"
|
||||
LOW = "low", "待改进"
|
||||
PENDING = "pending", "待评"
|
||||
|
||||
|
||||
class Submission(TimeStampedModel):
|
||||
id = models.UUIDField(
|
||||
primary_key=True,
|
||||
@@ -49,6 +55,15 @@ class Submission(TimeStampedModel):
|
||||
verbose_name="标记",
|
||||
)
|
||||
raw_score = models.FloatField(default=0.0, verbose_name="原始加权分")
|
||||
zone = models.CharField(
|
||||
max_length=10,
|
||||
choices=ZoneChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
db_index=True,
|
||||
verbose_name="分区",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ("-created",)
|
||||
@@ -62,6 +77,19 @@ class Submission(TimeStampedModel):
|
||||
"""
|
||||
return self.task.task_type
|
||||
|
||||
def _update_zone(self, n: int):
|
||||
if n < 5:
|
||||
new_zone = ZoneChoices.PENDING
|
||||
elif self.score >= 4.0:
|
||||
new_zone = ZoneChoices.FEATURED
|
||||
elif self.score < 3.0:
|
||||
new_zone = ZoneChoices.LOW
|
||||
else:
|
||||
new_zone = None
|
||||
if self.zone != new_zone:
|
||||
self.zone = new_zone
|
||||
self.save(update_fields=["zone"])
|
||||
|
||||
def update_score(self):
|
||||
ratings = list(self.ratings.select_related("user").all())
|
||||
n = len(ratings)
|
||||
@@ -70,15 +98,18 @@ class Submission(TimeStampedModel):
|
||||
self.raw_score = 0.0
|
||||
self.score = 0.0
|
||||
self.save(update_fields=["raw_score", "score"])
|
||||
self._update_zone(n)
|
||||
return
|
||||
|
||||
weighted_sum = sum(
|
||||
r.score * (0.5 if r.user.role == RoleChoices.SUPER
|
||||
role_weights = [
|
||||
0.5 if r.user.role == RoleChoices.SUPER
|
||||
else 0.3 if r.user.role == RoleChoices.ADMIN
|
||||
else 0.2)
|
||||
else 0.2
|
||||
for r in ratings
|
||||
)
|
||||
self.raw_score = weighted_sum / n
|
||||
]
|
||||
weighted_sum = sum(r.score * w for r, w in zip(ratings, role_weights))
|
||||
weight_total = sum(role_weights)
|
||||
self.raw_score = weighted_sum / weight_total
|
||||
|
||||
C = 3
|
||||
global_mean = (
|
||||
@@ -89,6 +120,7 @@ class Submission(TimeStampedModel):
|
||||
|
||||
self.score = (C * global_mean + n * self.raw_score) / (C + n)
|
||||
self.save(update_fields=["raw_score", "score"])
|
||||
self._update_zone(n)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@@ -26,6 +26,7 @@ class SubmissionOut(Schema):
|
||||
js: Optional[str] = None
|
||||
conversation_id: Optional[UUID] = None
|
||||
flag: Optional[str] = None
|
||||
zone: Optional[str] = None
|
||||
submit_count: int = 0
|
||||
created: str
|
||||
modified: str
|
||||
@@ -107,6 +108,7 @@ class SubmissionFilter(Schema):
|
||||
username: Optional[str] = None
|
||||
user_id: Optional[int] = None
|
||||
flag: Optional[Literal["red", "blue", "green", "yellow", "any"]] = None
|
||||
zone: Optional[Literal["featured", "low", "pending"]] = None
|
||||
score_min: Optional[float] = None
|
||||
score_max_exclusive: Optional[float] = None
|
||||
score_lt_threshold: Optional[float] = None
|
||||
|
||||
Reference in New Issue
Block a user