Skip to content

Commit

Permalink
Implement smart scoring mode
Browse files Browse the repository at this point in the history
  • Loading branch information
veluca93 committed Mar 12, 2017
1 parent d067ddd commit ccc0156
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 6 deletions.
120 changes: 120 additions & 0 deletions cmsocial/scripts/update_score.py
@@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
# Thanks to Federico Glaudo <dario2994@gmail.com> for the scoring algorithm

# Algorithm description:
# difficulty = sqrt(attempts) / sum (score/ability), in [0.1 - 10]
# ability = sum (difficulty*score), normalized to have average 1
# total_score = sum(points)/num_users*ability
# score = 1 if the number of points received in that task is at least 99, else
# score = points/200

import argparse
import math

from cms.db import SessionGen

from cmsocial.db.socialtask import TaskScore
try:
from cmsocial.db.socialparticipation import SocialParticipation as ScoreClass
except:
# legacy non-multicontest version
from cmsocial.db.socialuser import SocialUser as ScoreClass


def compute_standard_score(user_to_task, task_to_user):
return [(uid, int(sum(map(lambda x: x[1], lst)))) for uid, lst in user_to_task.iteritems()]


def get_score(s):
return 1 if s >= 99 else s/200.


def compute_smart_score(user_to_task, task_to_user):
maxtask = max(task_to_user.keys())+1
maxuser = max(user_to_task.keys())+1
difficulties = [1. for x in range(maxtask)]
abilities = [1. for x in range(maxuser)]
attempts = [len(task_to_user.get(i, [])) for i in range(maxtask)]
attempts_sqrt = map(math.sqrt, attempts)

pts_per_user = sum(map(
lambda x: sum(map(lambda y: y[1], x)),
user_to_task.itervalues()))/len(user_to_task)

for uid in range(maxuser):
if len(user_to_task.get(uid, [])) == 0:
continue
user_to_task[uid] = list(map(
lambda x: (x[0], get_score(x[1])),
user_to_task.get(uid, [])))
for tid in range(maxtask):
if len(task_to_user.get(tid, [])) == 0:
continue
task_to_user[tid] = list(map(
lambda x: (x[0], get_score(x[1])),
task_to_user.get(tid, [])))

for _ in range(20): # twenty iterations
# Ability pass
total_ability = 0.
for uid in range(maxuser):
abilities[uid] = 0
for tid, score in user_to_task.get(uid, []):
abilities[uid] += difficulties[tid] * score
total_ability += abilities[uid]
for uid in range(maxuser):
abilities[uid] /= total_ability/len(user_to_task)
if abilities[uid] < 0.01:
abilities[uid] = 0.01

# Difficulty pass
for tid in range(maxtask):
if len(task_to_user.get(tid, [])) == 0:
continue
sum_score_over_ability = 0
for uid, score in task_to_user.get(tid, []):
sum_score_over_ability += score/abilities[uid]
difficulties[tid] = attempts_sqrt[tid] / sum_score_over_ability
if difficulties[tid] > 10.0:
difficulties[tid] = 10.0
if difficulties[tid] < 0.1:
difficulties[tid] = 0.1

return [(uid, int(pts_per_user*abilities[uid])) for uid in user_to_task.keys()]


def main():
parser = argparse.ArgumentParser(
description="Update the score of the users/participations"
)
parser.add_argument(
"-s", "--standard", action="store_true",
help="Use the standard scoring method (sum of a task's scores)"
)
args, _ = parser.parse_known_args()

with SessionGen() as session:
scores = session.query(TaskScore).all()
if len(scores) == 0:
return 0
try:
scores = [(t.participation_id, t.task_id, t.score) for t in scores]
except:
# legacy non-multicontest version
scores = [(t.user_id, t.task_id, t.score) for t in scores]
user_to_task = dict()
task_to_user = dict()
for uid, tid, pt in scores:
user_to_task[uid] = user_to_task.get(uid, []) + [(tid, pt)]
task_to_user[tid] = task_to_user.get(tid, []) + [(uid, pt)]
if args.standard:
scores = compute_standard_score(user_to_task, task_to_user)
else:
scores = compute_smart_score(user_to_task, task_to_user)

scores = dict(scores)
sv = session.query(ScoreClass).all()
for v in sv:
if v.id in scores:
v.score = scores[v.id]
session.commit()
3 changes: 2 additions & 1 deletion setup.py
Expand Up @@ -29,7 +29,8 @@
"cmsPracticeWebServer=cmsocial.server.pws:main",
"cmsocialSyncTasks=cmsocial.scripts.synctasks:main",
"cmsocialSyncUsers=cmsocial.scripts.syncusers:main",
"cmsocialDuplicateContest=cmsocial.scripts.duplicate_contest:main"
"cmsocialDuplicateContest=cmsocial.scripts.duplicate_contest:main",
"cmsocialUpdateScore=cmsocial.scripts.update_score:main"
]
},
keywords="ioi programming contest grader management system",
Expand Down
5 changes: 0 additions & 5 deletions sql_scripts/create_triggers.sql
Expand Up @@ -96,11 +96,6 @@ begin;
update social_tasks
set nsubs = vars.nsubs, nsubscorrect = vars.nsubscorrect, nusers = vars.nusers, nuserscorrect = vars.nuserscorrect
where id = t_id;

update social_participations
set score = total_score
where id = p_id;
return new;
end;
$$ language plpgsql;
drop trigger if exists submission_scored on submission_results;
Expand Down

0 comments on commit ccc0156

Please sign in to comment.