Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 75 additions & 21 deletions src/handlers/contests/proset.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,98 @@
from decimal import Decimal

from msgpack import unpackb

from handlers.base import reqenv, RequestHandler
from services.pro import ProConst, ProService
from services.rate import RateService
from services.contests import ContestMode

PERMISSION_DENIED_ERROR = ('Eacces', 'Permission denied')

class ContestProsetHandler(RequestHandler):
async def randomset_proset(self, pageoff: int):
if not self.contest.is_member(self.acct):
return self.error(PERMISSION_DENIED_ERROR)

scoreboard_key = f'contest_{self.contest.contest_id}_randomset_scoreboard'
randomset_prolist = self.contest.get_randomset_prolist_from_acct_by_ip(self.acct)
if randomset_prolist is None:
return self.error(('Etodo', 'TODO: Assign problem set for out of range IP not implemented. Call Yushiuan9499.'))
randomset_prolist_set = set(randomset_prolist)

prolist_order = {pro_id: idx for idx, pro_id in enumerate(randomset_prolist)}
_, prolist = await ProService.inst.list_pro(ProConst.PRO_STATUS_CONTEST_USER)
prolist = sorted(filter(lambda pro: pro.pro_id in randomset_prolist_set, prolist),
key=lambda pro: prolist_order[pro.pro_id])

keys = [f'{self.acct.acct_id}_{pro_order}' for pro_order in range(len(prolist))]
score_values = await self.rs.hmget(scoreboard_key, keys)

score_map: dict[int, dict] = {}
for pro_order, pro in enumerate(prolist):
score_map[pro.pro_id] = {'score': Decimal('0'), 'state': None}
assert 0 <= pro_order < len(score_values)

best_record = score_values[pro_order]
if best_record is not None:
best_record = unpackb(best_record)
score_map[pro.pro_id]['score'] += Decimal(best_record['score'])
score_map[pro.pro_id]['state'] = best_record['state']

pro_total_cnt = len(prolist)
prolist = prolist[pageoff: pageoff + 40]

pro_idx_map = {pro_id: idx for idx, pro_id in enumerate(randomset_prolist)}
await self.render('contests/proset', contest=self.contest, show_ac_ratio=False,
prolist=prolist, pro_idx_map=pro_idx_map, pro_total_cnt=pro_total_cnt, score_map=score_map, pageoff=pageoff)


@reqenv
async def get(self):
pageoff = int(self.get_argument('pageoff', default=0))

show_ac_ratio = False

if not self.contest.is_member(self.acct):
return self.error(PERMISSION_DENIED_ERROR)

if not self.contest.is_start() and not self.contest.is_admin(self.acct):
return self.error(('Eacces', 'Permission denied'))
return self.error(PERMISSION_DENIED_ERROR)

elif self.contest.is_running() and not self.contest.is_member(self.acct):
return self.error(('Eacces', 'Permission denied'))
return self.error(PERMISSION_DENIED_ERROR)

else:
_, acct_rates = await RateService.inst.map_rate_acct(self.acct, contest_id=self.contest.contest_id)
_, prolist = await ProService.inst.list_pro(ProConst.PRO_STATUS_CONTEST_USER)
elif self.contest.contest_mode == ContestMode.RANDOM_SET and not self.contest.is_admin(self.acct):
return await self.randomset_proset(pageoff)

_, acct_rates = await RateService.inst.map_rate_acct(self.acct, contest_id=self.contest.contest_id)
_, prolist = await ProService.inst.list_pro(ProConst.PRO_STATUS_CONTEST_USER)

prolist_order = {pro_id: idx for idx, pro_id in enumerate(self.contest.pro_list.keys())}
prolist = sorted(filter(lambda pro: self.contest.is_pro(pro.pro_id), prolist),
key=lambda pro: prolist_order[pro.pro_id])

prolist_order = {pro_id: idx for idx, pro_id in enumerate(self.contest.pro_list.keys())}
prolist = sorted(filter(lambda pro: self.contest.is_pro(pro.pro_id), prolist),
key=lambda pro: prolist_order[pro.pro_id])
score_map: dict[int, dict] = {}
for pro in prolist:
pro_id = pro.pro_id
score_map[pro_id] = {'score': 0, 'state': None}
if pro_id in acct_rates:
score_map[pro_id]['score'] += acct_rates[pro.pro_id]['rate']
score_map[pro_id]['state'] = acct_rates[pro.pro_id]['state']

score_map: dict[int, dict] = {}
if self.contest.is_public_scoreboard or self.contest.is_admin(self.acct):
show_ac_ratio = True
for pro in prolist:
pro_id = pro.pro_id
score_map[pro_id] = {'score': 0, 'state': None}
if pro_id in acct_rates:
score_map[pro_id]['score'] += acct_rates[pro.pro_id]['rate']
score_map[pro_id]['state'] = acct_rates[pro.pro_id]['state']

if self.contest.is_public_scoreboard or self.contest.is_admin(self.acct):
show_ac_ratio = True
for pro in prolist:
_, rate = await RateService.inst.get_pro_ac_rate(pro.pro_id, contest_id=self.contest.contest_id)
score_map[pro.pro_id]['rate_data'] = rate
_, rate = await RateService.inst.get_pro_ac_rate(pro.pro_id, contest_id=self.contest.contest_id)
score_map[pro.pro_id]['rate_data'] = rate

if self.contest.contest_mode == ContestMode.RANDOM_SET:
pro_idx_map = {pro_id: idx for idx, pro_set in enumerate(self.contest.pro_sets) for pro_id in pro_set}
else:
pro_idx_map = {pro_id: idx for idx, pro_id in enumerate(self.contest.pro_list.keys())}

pro_total_cnt = len(prolist)
prolist = prolist[pageoff: pageoff + 40]

await self.render('contests/proset', contest=self.contest, show_ac_ratio=show_ac_ratio,
prolist=prolist, pro_total_cnt=pro_total_cnt, score_map=score_map, pageoff=pageoff)
prolist=prolist, pro_idx_map=pro_idx_map, pro_total_cnt=pro_total_cnt, score_map=score_map, pageoff=pageoff)
64 changes: 62 additions & 2 deletions src/handlers/contests/scoreboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import config
from handlers.base import RequestHandler, UnifiedWebSocketHandler, reqenv
from services.contests import ContestService, ProblemScoreType, UserStatus
from services.contests import ContestService, ProblemScoreType, UserStatus, ContestMode
from services.user import UserService


Expand Down Expand Up @@ -104,6 +104,63 @@ def _encoder(self, obj):

return obj

async def random_set_scoreboard(self):
contest = self.contest
acct = self.acct
if contest.is_member(acct) and not contest.is_admin(acct) and not contest.is_randomset_pro_allocated(acct):
return self.error(('Etodo', 'TODO: Assign problem set for out of range IP not implemented. Call Yushiuan9499.'))

if self.contest.is_public_scoreboard:
acct_list = [acct_id for acct_id, v in self.contest.user_list.items() if v['status'] == UserStatus.APPROVED]
else:
acct_list = [self.acct.acct_id]

scoreboard_key = f'contest_{self.contest.contest_id}_randomset_scoreboard'
pro_sets_len = len(self.contest.pro_sets)
keys = [f'{acct_id}_{pro_order}' for acct_id in acct_list for pro_order in range(pro_sets_len)]
score_values = await self.rs.hmget(scoreboard_key, keys)

start_time = self.contest.contest_start
all_scores = []
for acct_cnt, acct_id in enumerate(acct_list):
_, acct = await UserService.inst.info_acct(acct_id)
assert acct
prolist = self.contest.get_randomset_prolist_from_acct_by_ip(acct)
if prolist is None:
continue

total_score = Decimal('0')
scores = {}
for pro_order, pro_id in enumerate(prolist):
idx = acct_cnt * pro_sets_len + pro_order
assert 0 <= idx < len(score_values)

best_record = score_values[idx]
if best_record is None:
continue

best_record = unpackb(best_record)
best_record['timestamp'] = datetime.datetime.fromtimestamp(best_record['timestamp'])
best_record['score'] = Decimal(best_record['score'])
scores[pro_order] = {
'pro_id': pro_id,
'chal_id': best_record['chal_id'],
'timestamp': best_record['timestamp'].astimezone(config.TIMEZONE) - start_time,
'score': best_record['score'],
'state': best_record['state'],
'fail_count': best_record['fail_count']
}
total_score += best_record['score']

all_scores.append({
'acct_id': acct_id,
'name': acct.name,
'scores': scores,
'total_score': total_score
})

self.error(('S', all_scores), encoder=_JsonDatetimeEncoder)

@reqenv
async def get(self):
await self.render('contests/scoreboard', contest=self.contest)
Expand All @@ -115,6 +172,9 @@ async def post(self):
elif not self.contest.is_public_scoreboard and not self.contest.is_member(self.acct):
return self.error(('Eacces', 'Permission denied'))

if self.contest.contest_mode == ContestMode.RANDOM_SET:
return await self.random_set_scoreboard()

has_end_time = True
start_time = self.contest.contest_start
try:
Expand Down Expand Up @@ -186,7 +246,7 @@ async def post(self):
'chal_id': p['chal_id'],
'timestamp': p['timestamp'].astimezone(config.TIMEZONE) - start_time,
'score': p['score'],
'fail_cnt': p['fail_cnt']
'fail_count': p['fail_count']
}
total_score += p['score']

Expand Down
15 changes: 15 additions & 0 deletions src/handlers/pro.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from services.pro import ProClassService, ProClassConst, ProConst, ProService, Problem
from services.rate import RateService
from services.user import UserService, UserConst
from services.contests import ContestMode

PERMISSION_DENIED_ERROR = ("Eacces", "Permission denied")

Expand Down Expand Up @@ -175,6 +176,13 @@ async def get(self, pro_id: int, path: str):
if not self.contest.is_admin(self.acct) and not self.contest.is_running():
return self.error(PERMISSION_DENIED_ERROR)

if self.contest.contest_mode == ContestMode.RANDOM_SET:
contest_prolist = self.contest.get_randomset_prolist_from_acct_by_ip(self.acct)
if contest_prolist is None:
return self.error(('Etodo', 'TODO: Assign problem set for out of range IP not implemented. Call Yushiuan9499.'))
if pro_id not in contest_prolist:
return self.error(("Enoext", "Problem not in your problem set"))

allow_statuses = ProConst.PRO_STATUS_CONTEST_USER
else:
if self.acct.is_kernel():
Expand Down Expand Up @@ -239,6 +247,13 @@ async def get(self, pro_id):
if not self.contest.is_admin(self.acct) and not self.contest.is_running():
return self.error(PERMISSION_DENIED_ERROR)

if self.contest.contest_mode == ContestMode.RANDOM_SET:
contest_prolist = self.contest.get_randomset_prolist_from_acct_by_ip(self.acct)
if contest_prolist is None:
return self.error(('Etodo', 'TODO: Assign problem set for out of range IP not implemented. Call Yushiuan9499.'))
if pro_id not in contest_prolist:
return self.error(PERMISSION_DENIED_ERROR)

allow_statuses = ProConst.PRO_STATUS_CONTEST_USER

else:
Expand Down
Loading