mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-27 15:16:21 +00:00
Compare commits
4 Commits
ce0c47821f
...
9c2b2a96ab
Author | SHA1 | Date | |
---|---|---|---|
9c2b2a96ab | |||
419be8ab49 | |||
46f6923d97 | |||
62b6c3c182 |
@ -35,7 +35,7 @@ docker-compose up -d
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
docker-compose up --build -f docker-compose.development.yml
|
||||
docker-compose -f docker-compose.development.yml up --build
|
||||
```
|
||||
|
||||
## 感谢
|
||||
|
@ -269,6 +269,7 @@ UNLOCK TABLES;
|
||||
CREATE TABLE `contests_problems` (
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`dfn` int(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`problem_id`,`contest_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
@ -332,6 +333,30 @@ LOCK TABLES `contests_submissions` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_submissions` ENABLE KEYS */;
|
||||
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`
|
||||
--
|
||||
|
@ -20,6 +20,7 @@ services:
|
||||
- SYS_PTRACE
|
||||
volumes:
|
||||
- ./uoj_data/judger/log:/opt/uoj_judger/log
|
||||
- ./uoj_data/judger/data:/opt/uoj_judger/uoj_judger/data
|
||||
environment:
|
||||
- UOJ_PROTOCOL=http
|
||||
- UOJ_HOST=uoj-web
|
||||
|
@ -44,11 +44,18 @@
|
||||
'name' => UOJLocale::get('contests::contest 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'),
|
||||
'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)) {
|
||||
$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() {
|
||||
global $contest;
|
||||
$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
||||
@ -454,7 +476,7 @@ EOD;
|
||||
<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
|
||||
</div>
|
||||
<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">
|
||||
<?php else: ?>
|
||||
<div class="col-sm-9">
|
||||
@ -472,12 +494,14 @@ EOD;
|
||||
echoStandings(true);
|
||||
} elseif ($cur_tab == 'backstage') {
|
||||
echoBackstage();
|
||||
} elseif ($cur_tab == 'self_reviews') {
|
||||
echoReviews();
|
||||
}
|
||||
?>
|
||||
</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">
|
||||
<hr />
|
||||
</div>
|
||||
|
@ -67,6 +67,7 @@
|
||||
},
|
||||
function($type, $username) {
|
||||
global $contest;
|
||||
|
||||
if ($type == '+') {
|
||||
DB::query("insert into contests_permissions (contest_id, username) values (${contest['id']}, '$username')");
|
||||
} elseif ($type == '-') {
|
||||
@ -99,7 +100,8 @@
|
||||
$problem_id = $matches[1];
|
||||
|
||||
if ($type == '+') {
|
||||
DB::insert("insert into contests_problems (contest_id, problem_id) values ({$contest['id']}, '$problem_id')");
|
||||
$dfn = DB::selectFirst("select max(dfn) from contests_problems where contest_id = {$contest['id']}")['max(dfn)'] + 1;
|
||||
DB::insert("insert into contests_problems (contest_id, problem_id, dfn) values ({$contest['id']}, '$problem_id', $dfn)");
|
||||
} elseif ($type == '-') {
|
||||
DB::delete("delete from contests_problems where contest_id = {$contest['id']} and problem_id = '$problem_id'");
|
||||
}
|
||||
@ -216,7 +218,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$result = DB::query("select problem_id from contests_problems where contest_id = ${contest['id']} order by dfn asc");
|
||||
$result = DB::query("select problem_id from contests_problems where contest_id = ${contest['id']} order by dfn, problem_id");
|
||||
while ($row = DB::fetch($result, MYSQLI_ASSOC)) {
|
||||
$problem = queryProblemBrief($row['problem_id']);
|
||||
$problem_config_str = isset($contest['extra_config']["problem_{$problem['id']}"]) ? $contest['extra_config']["problem_{$problem['id']}"] : 'sample';
|
||||
|
@ -46,7 +46,7 @@ function queryContestData($contest, $config = array(), $is_after_contest_query =
|
||||
$problems = [];
|
||||
$prob_pos = [];
|
||||
$n_problems = 0;
|
||||
$result = DB::query("select problem_id from contests_problems where contest_id = {$contest['id']} order by dfn");
|
||||
$result = DB::query("select problem_id from contests_problems where contest_id = {$contest['id']} order by dfn, problem_id");
|
||||
while ($row = DB::fetch($result, MYSQLI_NUM)) {
|
||||
$prob_pos[$problems[] = (int)$row[0]] = $n_problems++;
|
||||
}
|
||||
@ -92,8 +92,8 @@ function queryContestData($contest, $config = array(), $is_after_contest_query =
|
||||
return ['problems' => $problems, 'data' => $data, 'people' => $people];
|
||||
}
|
||||
|
||||
function calcStandings($contest, $contest_data, &$score, &$standings, $update_contests_submissions = false) {
|
||||
// score: username, problem_pos => score, penalty, id
|
||||
function calcStandings($contest, $contest_data, &$score, &$standings, $update_contests_submissions = false, $show_reviews = false) {
|
||||
// score: username, problem_pos => score, penalty, id, ?review
|
||||
$score = array();
|
||||
$n_people = count($contest_data['people']);
|
||||
$n_problems = count($contest_data['problems']);
|
||||
@ -107,10 +107,19 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $update_co
|
||||
$penalty = 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();
|
||||
foreach ($contest_data['people'] as $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;
|
||||
}
|
||||
|
||||
@ -143,8 +161,10 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $update_co
|
||||
|
||||
for ($i = 0; $i < $n_people; $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 {
|
||||
$standings[$i][] = $standings[$i][3];
|
||||
$standings[$i][] = $standings[$i - 1][3];
|
||||
}
|
||||
}
|
||||
|
@ -26,5 +26,6 @@ return [
|
||||
'contest pending final test' => 'Pending final test',
|
||||
'contest final testing' => 'Final testing',
|
||||
'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 final testing' => '正在测评',
|
||||
'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}/standings', '/contest_inside.php?tab=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/{contest_id}/problem/{id}', '/problem.php');
|
||||
Route::any('/contest/{contest_id}/problem/{id}/statistics', '/problem_statistics.php');
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
standings_version=<?=$contest['extra_config']['standings_version']?>;
|
||||
show_self_reviews=<?=isset($show_self_reviews) && $show_self_reviews ? 'true' : 'false' ?>;
|
||||
contest_id=<?=$contest['id']?>;
|
||||
standings=<?=json_encode($standings)?>;
|
||||
score=<?=json_encode($score)?>;
|
||||
|
@ -1071,6 +1071,7 @@ function showStandings() {
|
||||
$.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>';
|
||||
}).join('') +
|
||||
(show_self_reviews ? '<th style="width:16em;">赛后总结</th>' : '') +
|
||||
'</tr>',
|
||||
function(row) {
|
||||
var col_tr = '<tr>';
|
||||
@ -1089,9 +1090,26 @@ function showStandings() {
|
||||
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>';
|
||||
}
|
||||
|
||||
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>';
|
||||
return col_tr;
|
||||
}, {
|
||||
|
Loading…
Reference in New Issue
Block a user