mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-25 00:28:42 +00:00
feat: contest self reviews
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
419be8ab49
commit
9c2b2a96ab
@ -333,6 +333,30 @@ LOCK TABLES `contests_submissions` WRITE;
|
|||||||
/*!40000 ALTER TABLE `contests_submissions` ENABLE KEYS */;
|
/*!40000 ALTER TABLE `contests_submissions` ENABLE KEYS */;
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `contests_reviews`
|
||||||
|
--
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `contests_reviews` (
|
||||||
|
`contest_id` int(11) NOT NULL,
|
||||||
|
`problem_id` int(11) NOT NULL DEFAULT 0,
|
||||||
|
`poster` varchar(20) NOT NULL,
|
||||||
|
`content` text NOT NULL,
|
||||||
|
PRIMARY KEY (`contest_id`,`problem_id`,`poster`)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `contests_reviews`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `contests_reviews` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `contests_reviews` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `contests_reviews` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `countdowns`
|
-- Table structure for table `countdowns`
|
||||||
--
|
--
|
||||||
|
@ -44,11 +44,18 @@
|
|||||||
'name' => UOJLocale::get('contests::contest standings'),
|
'name' => UOJLocale::get('contests::contest standings'),
|
||||||
'url' => "/contest/{$contest['id']}/standings"
|
'url' => "/contest/{$contest['id']}/standings"
|
||||||
),
|
),
|
||||||
'after_contest_standings' => array(
|
);
|
||||||
|
|
||||||
|
if ($contest['cur_progress'] > CONTEST_TESTING) {
|
||||||
|
$tabs_info['after_contest_standings'] = array(
|
||||||
'name' => UOJLocale::get('contests::after contest standings'),
|
'name' => UOJLocale::get('contests::after contest standings'),
|
||||||
'url' => "/contest/{$contest['id']}/after_contest_standings"
|
'url' => "/contest/{$contest['id']}/after_contest_standings"
|
||||||
)
|
);
|
||||||
);
|
$tabs_info['self_reviews'] = array(
|
||||||
|
'name' => UOJLocale::get('contests::contest self reviews'),
|
||||||
|
'url' => "/contest/{$contest['id']}/self_reviews"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasContestPermission(Auth::user(), $contest)) {
|
if (hasContestPermission(Auth::user(), $contest)) {
|
||||||
$tabs_info['backstage'] = array(
|
$tabs_info['backstage'] = array(
|
||||||
@ -392,6 +399,21 @@ EOD;
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function echoReviews() {
|
||||||
|
global $contest;
|
||||||
|
|
||||||
|
$contest_data = queryContestData($contest, array());
|
||||||
|
calcStandings($contest, $contest_data, $score, $standings, false, true);
|
||||||
|
|
||||||
|
uojIncludeView('contest-standings', [
|
||||||
|
'contest' => $contest,
|
||||||
|
'standings' => $standings,
|
||||||
|
'score' => $score,
|
||||||
|
'contest_data' => $contest_data,
|
||||||
|
'show_self_reviews' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
function echoContestCountdown() {
|
function echoContestCountdown() {
|
||||||
global $contest;
|
global $contest;
|
||||||
$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
||||||
@ -454,7 +476,7 @@ EOD;
|
|||||||
<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
|
<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<?php if ($cur_tab == 'standings' || $cur_tab == 'after_contest_standings'): ?>
|
<?php if ($cur_tab == 'standings' || $cur_tab == 'after_contest_standings' || $cur_tab == 'self_reviews'): ?>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -472,12 +494,14 @@ EOD;
|
|||||||
echoStandings(true);
|
echoStandings(true);
|
||||||
} elseif ($cur_tab == 'backstage') {
|
} elseif ($cur_tab == 'backstage') {
|
||||||
echoBackstage();
|
echoBackstage();
|
||||||
|
} elseif ($cur_tab == 'self_reviews') {
|
||||||
|
echoReviews();
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($cur_tab == 'standings' || $cur_tab == 'after_contest_standings'): ?>
|
<?php if ($cur_tab == 'standings' || $cur_tab == 'after_contest_standings' || $cur_tab == 'self_reviews'): ?>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,8 +92,8 @@ function queryContestData($contest, $config = array(), $is_after_contest_query =
|
|||||||
return ['problems' => $problems, 'data' => $data, 'people' => $people];
|
return ['problems' => $problems, 'data' => $data, 'people' => $people];
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcStandings($contest, $contest_data, &$score, &$standings, $update_contests_submissions = false) {
|
function calcStandings($contest, $contest_data, &$score, &$standings, $update_contests_submissions = false, $show_reviews = false) {
|
||||||
// score: username, problem_pos => score, penalty, id
|
// score: username, problem_pos => score, penalty, id, ?review
|
||||||
$score = array();
|
$score = array();
|
||||||
$n_people = count($contest_data['people']);
|
$n_people = count($contest_data['people']);
|
||||||
$n_problems = count($contest_data['problems']);
|
$n_problems = count($contest_data['problems']);
|
||||||
@ -107,10 +107,19 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $update_co
|
|||||||
$penalty = 0;
|
$penalty = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$score[$submission[2]][$submission[3]] = array($submission[4], $penalty, $submission[0]);
|
$score[$submission[2]][$submission[3]] = array($submission[4], $penalty, $submission[0]);
|
||||||
|
|
||||||
|
if ($show_reviews) {
|
||||||
|
$review_result = DB::selectFirst("select content from contests_reviews where contest_id = {$contest['id']} and problem_id = {$contest_data['problems'][$submission[3]]} and poster = '{$person[0]}'");
|
||||||
|
|
||||||
|
if ($review_result['content']) {
|
||||||
|
$score[$submission[2]][$submission[3]][] = $review_result['content'];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// standings: rank => score, penalty, [username, realname], virtual_rank
|
// standings: rank => score, penalty, [username, realname], virtual_rank, ?review
|
||||||
$standings = array();
|
$standings = array();
|
||||||
foreach ($contest_data['people'] as $person) {
|
foreach ($contest_data['people'] as $person) {
|
||||||
$cur = array(0, 0, $person);
|
$cur = array(0, 0, $person);
|
||||||
@ -124,6 +133,15 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $update_co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($show_reviews) {
|
||||||
|
$review_result = DB::selectFirst("select content from contests_reviews where contest_id = {$contest['id']} and poster = '{$person[0]}'");
|
||||||
|
|
||||||
|
if ($review_result['content']) {
|
||||||
|
$cur[] = $review_result['content'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$standings[] = $cur;
|
$standings[] = $cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +161,10 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $update_co
|
|||||||
|
|
||||||
for ($i = 0; $i < $n_people; $i++) {
|
for ($i = 0; $i < $n_people; $i++) {
|
||||||
if ($i == 0 || !$is_same_rank($standings[$i - 1], $standings[$i])) {
|
if ($i == 0 || !$is_same_rank($standings[$i - 1], $standings[$i])) {
|
||||||
$standings[$i][] = $i + 1;
|
$standings[$i][] = $standings[$i][3];
|
||||||
|
$standings[$i][3] = $i + 1;
|
||||||
} else {
|
} else {
|
||||||
|
$standings[$i][] = $standings[$i][3];
|
||||||
$standings[$i][] = $standings[$i - 1][3];
|
$standings[$i][] = $standings[$i - 1][3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,5 +26,6 @@ return [
|
|||||||
'contest pending final test' => 'Pending final test',
|
'contest pending final test' => 'Pending final test',
|
||||||
'contest final testing' => 'Final testing',
|
'contest final testing' => 'Final testing',
|
||||||
'contest ended' => 'Contest Ended',
|
'contest ended' => 'Contest Ended',
|
||||||
'contest registrants' => 'Registrants'
|
'contest registrants' => 'Registrants',
|
||||||
|
'contest self reviews' => 'Contest self reviews'
|
||||||
];
|
];
|
||||||
|
@ -26,5 +26,6 @@ return [
|
|||||||
'contest pending final test' => '等待评测',
|
'contest pending final test' => '等待评测',
|
||||||
'contest final testing' => '正在测评',
|
'contest final testing' => '正在测评',
|
||||||
'contest ended' => '比赛已结束',
|
'contest ended' => '比赛已结束',
|
||||||
'contest registrants' => '报名选手列表'
|
'contest registrants' => '报名选手列表',
|
||||||
|
'contest self reviews' => '赛后总结'
|
||||||
];
|
];
|
||||||
|
@ -31,6 +31,7 @@ Route::group([
|
|||||||
Route::any('/contest/{id}/submissions', '/contest_inside.php?tab=submissions');
|
Route::any('/contest/{id}/submissions', '/contest_inside.php?tab=submissions');
|
||||||
Route::any('/contest/{id}/standings', '/contest_inside.php?tab=standings');
|
Route::any('/contest/{id}/standings', '/contest_inside.php?tab=standings');
|
||||||
Route::any('/contest/{id}/after_contest_standings', '/contest_inside.php?tab=after_contest_standings');
|
Route::any('/contest/{id}/after_contest_standings', '/contest_inside.php?tab=after_contest_standings');
|
||||||
|
Route::any('/contest/{id}/self_reviews', '/contest_inside.php?tab=self_reviews');
|
||||||
Route::any('/contest/{id}/backstage', '/contest_inside.php?tab=backstage');
|
Route::any('/contest/{id}/backstage', '/contest_inside.php?tab=backstage');
|
||||||
Route::any('/contest/{contest_id}/problem/{id}', '/problem.php');
|
Route::any('/contest/{contest_id}/problem/{id}', '/problem.php');
|
||||||
Route::any('/contest/{contest_id}/problem/{id}/statistics', '/problem_statistics.php');
|
Route::any('/contest/{contest_id}/problem/{id}/statistics', '/problem_statistics.php');
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
standings_version=<?=$contest['extra_config']['standings_version']?>;
|
standings_version=<?=$contest['extra_config']['standings_version']?>;
|
||||||
|
show_self_reviews=<?=isset($show_self_reviews) && $show_self_reviews ? 'true' : 'false' ?>;
|
||||||
contest_id=<?=$contest['id']?>;
|
contest_id=<?=$contest['id']?>;
|
||||||
standings=<?=json_encode($standings)?>;
|
standings=<?=json_encode($standings)?>;
|
||||||
score=<?=json_encode($score)?>;
|
score=<?=json_encode($score)?>;
|
||||||
|
@ -1071,6 +1071,7 @@ function showStandings() {
|
|||||||
$.map(problems, function(col, idx) {
|
$.map(problems, function(col, idx) {
|
||||||
return '<th style="width:8em;">' + '<a href="/contest/' + contest_id + '/problem/' + col + '">' + String.fromCharCode('A'.charCodeAt(0) + idx) + '</a>' + '</th>';
|
return '<th style="width:8em;">' + '<a href="/contest/' + contest_id + '/problem/' + col + '">' + String.fromCharCode('A'.charCodeAt(0) + idx) + '</a>' + '</th>';
|
||||||
}).join('') +
|
}).join('') +
|
||||||
|
(show_self_reviews ? '<th style="width:16em;">赛后总结</th>' : '') +
|
||||||
'</tr>',
|
'</tr>',
|
||||||
function(row) {
|
function(row) {
|
||||||
var col_tr = '<tr>';
|
var col_tr = '<tr>';
|
||||||
@ -1089,9 +1090,26 @@ function showStandings() {
|
|||||||
col_tr += '<div>' + getPenaltyTimeStr(col[1]) + '</div>';
|
col_tr += '<div>' + getPenaltyTimeStr(col[1]) + '</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (show_self_reviews) {
|
||||||
|
col_tr += '<div id="review-' + row[2][0] + '-' + i + '"></div>'
|
||||||
|
+ '<script>'
|
||||||
|
+ '$(function() {'
|
||||||
|
+ 'var purify_result = DOMPurify.sanitize(\'' + String(col[3] || '').replace(/'/g, '\\\'').replace(new RegExp('</scr' + 'ipt>', 'gi'), '</scr\' + \'ipt>') + '\', {ALLOWED_TAGS: ["a", "b", "i", "u", "em", "strong", "sub", "sup", "small", "del"], ALLOWED_ATTR: ["href"]});'
|
||||||
|
+ '$("#review-' + row[2][0] + '-' + i + '")'
|
||||||
|
+ '.html(purify_result ? \'<div class="mt-3 pt-2 border-top">\' + purify_result + \'</div>\' : \'\'); });'
|
||||||
|
+ '</scr' + 'ipt>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
col_tr += '</td>';
|
col_tr += '</td>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (show_self_reviews) {
|
||||||
|
col_tr += '<td><div id="review-' + row[2][0] + '"></div>'
|
||||||
|
+ '<script>'
|
||||||
|
+ '$(function() { $("#review-' + row[2][0] + '")'
|
||||||
|
+ '.html(DOMPurify.sanitize(\'' + String(row[4] || '').replace(/'/g, '\\\'').replace(new RegExp('</scr' + 'ipt>', 'gi'), '</scr\' + \'ipt>') + '\', {ALLOWED_TAGS: ["a", "b", "i", "u", "em", "strong", "sub", "sup", "small", "del"], ALLOWED_ATTR: ["href"]})); });'
|
||||||
|
+ '</scr' + 'ipt></td>';
|
||||||
|
}
|
||||||
col_tr += '</tr>';
|
col_tr += '</tr>';
|
||||||
return col_tr;
|
return col_tr;
|
||||||
}, {
|
}, {
|
||||||
|
Loading…
Reference in New Issue
Block a user