Reformat using black
This commit is contained in:
parent
efee4ad081
commit
a87fb49918
221 changed files with 19127 additions and 7310 deletions
|
@ -7,7 +7,7 @@ from django.db.models.functions import Coalesce
|
|||
from django.utils import timezone
|
||||
|
||||
|
||||
def tie_ranker(iterable, key=attrgetter('points')):
|
||||
def tie_ranker(iterable, key=attrgetter("points")):
|
||||
rank = 0
|
||||
delta = 1
|
||||
last = None
|
||||
|
@ -53,7 +53,9 @@ def WP(RA, RB, VA, VB):
|
|||
return (math.erf((RB - RA) / math.sqrt(2 * (VA * VA + VB * VB))) + 1) / 2.0
|
||||
|
||||
|
||||
def recalculate_ratings(old_rating, old_volatility, actual_rank, times_rated, is_disqualified):
|
||||
def recalculate_ratings(
|
||||
old_rating, old_volatility, actual_rank, times_rated, is_disqualified
|
||||
):
|
||||
# actual_rank: 1 is first place, N is last place
|
||||
# if there are ties, use the average of places (if places 2, 3, 4, 5 tie, use 3.5 for all of them)
|
||||
|
||||
|
@ -74,7 +76,9 @@ def recalculate_ratings(old_rating, old_volatility, actual_rank, times_rated, is
|
|||
for i in range(N):
|
||||
ERank = 0.5
|
||||
for j in range(N):
|
||||
ERank += WP(old_rating[i], old_rating[j], old_volatility[i], old_volatility[j])
|
||||
ERank += WP(
|
||||
old_rating[i], old_rating[j], old_volatility[i], old_volatility[j]
|
||||
)
|
||||
|
||||
EPerf = -normal_CDF_inverse((ERank - 0.5) / N)
|
||||
APerf = -normal_CDF_inverse((actual_rank[i] - 0.5) / N)
|
||||
|
@ -98,8 +102,10 @@ def recalculate_ratings(old_rating, old_volatility, actual_rank, times_rated, is
|
|||
if times_rated[i] == 0:
|
||||
new_volatility[i] = 385
|
||||
else:
|
||||
new_volatility[i] = math.sqrt(((new_rating[i] - old_rating[i]) ** 2) / Weight +
|
||||
(old_volatility[i] ** 2) / (Weight + 1))
|
||||
new_volatility[i] = math.sqrt(
|
||||
((new_rating[i] - old_rating[i]) ** 2) / Weight
|
||||
+ (old_volatility[i] ** 2) / (Weight + 1)
|
||||
)
|
||||
|
||||
if is_disqualified[i]:
|
||||
# DQed users can manipulate TopCoder ratings to get higher volatility in order to increase their rating
|
||||
|
@ -112,23 +118,49 @@ def recalculate_ratings(old_rating, old_volatility, actual_rank, times_rated, is
|
|||
# inflate a little if we have to so people who placed first don't lose rating
|
||||
best_rank = min(actual_rank)
|
||||
for i in range(N):
|
||||
if abs(actual_rank[i] - best_rank) <= 1e-3 and new_rating[i] < old_rating[i] + 1:
|
||||
if (
|
||||
abs(actual_rank[i] - best_rank) <= 1e-3
|
||||
and new_rating[i] < old_rating[i] + 1
|
||||
):
|
||||
new_rating[i] = old_rating[i] + 1
|
||||
return list(map(int, map(round, new_rating))), list(map(int, map(round, new_volatility)))
|
||||
return list(map(int, map(round, new_rating))), list(
|
||||
map(int, map(round, new_volatility))
|
||||
)
|
||||
|
||||
|
||||
def tc_rate_contest(contest, Rating, Profile):
|
||||
rating_subquery = Rating.objects.filter(user=OuterRef('user'))
|
||||
rating_sorted = rating_subquery.order_by('-contest__end_time')
|
||||
users = contest.users.order_by('is_disqualified', '-score', 'cumtime', 'tiebreaker') \
|
||||
.annotate(submissions=Count('submission'),
|
||||
last_rating=Coalesce(Subquery(rating_sorted.values('rating')[:1]), 1200),
|
||||
volatility=Coalesce(Subquery(rating_sorted.values('volatility')[:1]), 535),
|
||||
times=Coalesce(Subquery(rating_subquery.order_by().values('user_id')
|
||||
.annotate(count=Count('id')).values('count')), 0)) \
|
||||
.exclude(user_id__in=contest.rate_exclude.all()) \
|
||||
.filter(virtual=0).values('id', 'user_id', 'score', 'cumtime', 'tiebreaker', 'is_disqualified',
|
||||
'last_rating', 'volatility', 'times')
|
||||
rating_subquery = Rating.objects.filter(user=OuterRef("user"))
|
||||
rating_sorted = rating_subquery.order_by("-contest__end_time")
|
||||
users = (
|
||||
contest.users.order_by("is_disqualified", "-score", "cumtime", "tiebreaker")
|
||||
.annotate(
|
||||
submissions=Count("submission"),
|
||||
last_rating=Coalesce(Subquery(rating_sorted.values("rating")[:1]), 1200),
|
||||
volatility=Coalesce(Subquery(rating_sorted.values("volatility")[:1]), 535),
|
||||
times=Coalesce(
|
||||
Subquery(
|
||||
rating_subquery.order_by()
|
||||
.values("user_id")
|
||||
.annotate(count=Count("id"))
|
||||
.values("count")
|
||||
),
|
||||
0,
|
||||
),
|
||||
)
|
||||
.exclude(user_id__in=contest.rate_exclude.all())
|
||||
.filter(virtual=0)
|
||||
.values(
|
||||
"id",
|
||||
"user_id",
|
||||
"score",
|
||||
"cumtime",
|
||||
"tiebreaker",
|
||||
"is_disqualified",
|
||||
"last_rating",
|
||||
"volatility",
|
||||
"times",
|
||||
)
|
||||
)
|
||||
if not contest.rate_all:
|
||||
users = users.filter(submissions__gt=0)
|
||||
if contest.rating_floor is not None:
|
||||
|
@ -137,46 +169,68 @@ def tc_rate_contest(contest, Rating, Profile):
|
|||
users = users.exclude(last_rating__gt=contest.rating_ceiling)
|
||||
|
||||
users = list(users)
|
||||
participation_ids = list(map(itemgetter('id'), users))
|
||||
user_ids = list(map(itemgetter('user_id'), users))
|
||||
is_disqualified = list(map(itemgetter('is_disqualified'), users))
|
||||
ranking = list(tie_ranker(users, key=itemgetter('score', 'cumtime', 'tiebreaker')))
|
||||
old_rating = list(map(itemgetter('last_rating'), users))
|
||||
old_volatility = list(map(itemgetter('volatility'), users))
|
||||
times_ranked = list(map(itemgetter('times'), users))
|
||||
rating, volatility = recalculate_ratings(old_rating, old_volatility, ranking, times_ranked, is_disqualified)
|
||||
participation_ids = list(map(itemgetter("id"), users))
|
||||
user_ids = list(map(itemgetter("user_id"), users))
|
||||
is_disqualified = list(map(itemgetter("is_disqualified"), users))
|
||||
ranking = list(tie_ranker(users, key=itemgetter("score", "cumtime", "tiebreaker")))
|
||||
old_rating = list(map(itemgetter("last_rating"), users))
|
||||
old_volatility = list(map(itemgetter("volatility"), users))
|
||||
times_ranked = list(map(itemgetter("times"), users))
|
||||
rating, volatility = recalculate_ratings(
|
||||
old_rating, old_volatility, ranking, times_ranked, is_disqualified
|
||||
)
|
||||
|
||||
now = timezone.now()
|
||||
ratings = [Rating(user_id=i, contest=contest, rating=r, volatility=v, last_rated=now, participation_id=p, rank=z)
|
||||
for i, p, r, v, z in zip(user_ids, participation_ids, rating, volatility, ranking)]
|
||||
ratings = [
|
||||
Rating(
|
||||
user_id=i,
|
||||
contest=contest,
|
||||
rating=r,
|
||||
volatility=v,
|
||||
last_rated=now,
|
||||
participation_id=p,
|
||||
rank=z,
|
||||
)
|
||||
for i, p, r, v, z in zip(
|
||||
user_ids, participation_ids, rating, volatility, ranking
|
||||
)
|
||||
]
|
||||
|
||||
Rating.objects.bulk_create(ratings)
|
||||
|
||||
Profile.objects.filter(contest_history__contest=contest, contest_history__virtual=0).update(
|
||||
rating=Subquery(Rating.objects.filter(user=OuterRef('id'))
|
||||
.order_by('-contest__end_time').values('rating')[:1]))
|
||||
Profile.objects.filter(
|
||||
contest_history__contest=contest, contest_history__virtual=0
|
||||
).update(
|
||||
rating=Subquery(
|
||||
Rating.objects.filter(user=OuterRef("id"))
|
||||
.order_by("-contest__end_time")
|
||||
.values("rating")[:1]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# inspired by rate_all_view
|
||||
def rate_tc(apps, schema_editor):
|
||||
Contest = apps.get_model('judge', 'Contest')
|
||||
Rating = apps.get_model('judge', 'Rating')
|
||||
Profile = apps.get_model('judge', 'Profile')
|
||||
Contest = apps.get_model("judge", "Contest")
|
||||
Rating = apps.get_model("judge", "Rating")
|
||||
Profile = apps.get_model("judge", "Profile")
|
||||
|
||||
with schema_editor.connection.cursor() as cursor:
|
||||
cursor.execute('TRUNCATE TABLE `%s`' % Rating._meta.db_table)
|
||||
cursor.execute("TRUNCATE TABLE `%s`" % Rating._meta.db_table)
|
||||
Profile.objects.update(rating=None)
|
||||
for contest in Contest.objects.filter(is_rated=True, end_time__lte=timezone.now()).order_by('end_time'):
|
||||
for contest in Contest.objects.filter(
|
||||
is_rated=True, end_time__lte=timezone.now()
|
||||
).order_by("end_time"):
|
||||
tc_rate_contest(contest, Rating, Profile)
|
||||
|
||||
|
||||
# inspired by rate_all_view
|
||||
def rate_elo_mmr(apps, schema_editor):
|
||||
Rating = apps.get_model('judge', 'Rating')
|
||||
Profile = apps.get_model('judge', 'Profile')
|
||||
Rating = apps.get_model("judge", "Rating")
|
||||
Profile = apps.get_model("judge", "Profile")
|
||||
|
||||
with schema_editor.connection.cursor() as cursor:
|
||||
cursor.execute('TRUNCATE TABLE `%s`' % Rating._meta.db_table)
|
||||
cursor.execute("TRUNCATE TABLE `%s`" % Rating._meta.db_table)
|
||||
Profile.objects.update(rating=None)
|
||||
# Don't populate Rating
|
||||
|
||||
|
@ -184,25 +238,25 @@ def rate_elo_mmr(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('judge', '0117_auto_20211209_0612'),
|
||||
("judge", "0117_auto_20211209_0612"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrations.RunPython.noop, rate_tc, atomic=True),
|
||||
migrations.AddField(
|
||||
model_name='rating',
|
||||
name='mean',
|
||||
field=models.FloatField(verbose_name='raw rating'),
|
||||
model_name="rating",
|
||||
name="mean",
|
||||
field=models.FloatField(verbose_name="raw rating"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rating',
|
||||
name='performance',
|
||||
field=models.FloatField(verbose_name='contest performance'),
|
||||
model_name="rating",
|
||||
name="performance",
|
||||
field=models.FloatField(verbose_name="contest performance"),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='rating',
|
||||
name='volatility',
|
||||
field=models.IntegerField(verbose_name='volatility'),
|
||||
model_name="rating",
|
||||
name="volatility",
|
||||
field=models.IntegerField(verbose_name="volatility"),
|
||||
),
|
||||
migrations.RunPython(rate_elo_mmr, migrations.RunPython.noop, atomic=True),
|
||||
]
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue