mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-25 02:28:42 +00:00
fixed the port issue
This commit is contained in:
parent
74c091c911
commit
63142dd042
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
85
README.md
85
README.md
@ -1,2 +1,83 @@
|
||||
# uoj
|
||||
Universal Online Judge
|
||||
# Universal Online Judge
|
||||
|
||||
## Dependence
|
||||
This is a dockerized version of UOJ. Before installation, please make sure that [Docker](https://www.docker.com/) has already been installed on your OS.
|
||||
|
||||
The docker image of UOJ is **64-bit**, so a **32-bit** host OS may cause installation failure.
|
||||
|
||||
## Installation
|
||||
First please download [JDK7u76](http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html#jdk-7u76-oth-JPR) and [JDK8u31](http://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase8-2177648.html#jdk-8u31-oth-JPR), and put them to `docker/jdk-7u76-linux-x64.tar.gz` and `docker/jdk-8u31-linux-x64.tar.gz`. These two compressed files are used by judge\_client for judging Java program. If you are too lazy to download these two huge files, you can simply place two empty .tar.gz files there.
|
||||
|
||||
Next, you can run the following command in your terminal: (not the one in the `docker/` directory!)
|
||||
```sh
|
||||
./install
|
||||
```
|
||||
If everything goes well, you will see `Successfully built <image-id>` in the last line of the output.
|
||||
|
||||
To start your UOJ main server, please run:
|
||||
```sh
|
||||
docker run -it -p 80:80 -p 3690:3690 <image-id>
|
||||
```
|
||||
If you are using docker on Mac OS or having 'std: compile error. no comment' message on uploading problem data, you could possibly use this alternative command:
|
||||
```sh
|
||||
docker run -it -p 80:80 -p 3690:3690 --cap-add SYS_PTRACE <image-id>
|
||||
```
|
||||
|
||||
The default hostname of UOJ is `local_uoj.ac`, so you need to modify your host file in your OS in order to map `127.0.0.1` to `local_uoj.ac`. (It is `/etc/hosts` on Linux.) After that, you can access UOJ in your web browser.
|
||||
|
||||
The first user registered after the installation of UOJ will be a super user. If you need another super user, please register a user and change its `usergroup` to "<samp>S</samp>" in the table `user_info`. Run
|
||||
```sh
|
||||
mysql app_uoj233 -u root -p
|
||||
```
|
||||
to login mysql in the terminal.
|
||||
|
||||
Notice that if you want only one judge client, then everything is ok now. Cheers!
|
||||
|
||||
However, if you want more judge clients, you need to set up them one by one. First run:
|
||||
```sh
|
||||
./config_judge_client
|
||||
```
|
||||
and answer the questions.
|
||||
|
||||
* uoj container id: the container id of the main server.
|
||||
* uoj ip: the ip address of the main server.
|
||||
* judger name: you can take a name you like, such as judger, judger\_2, very\_strong\_judger. (containing special characters may cause unforeseeable consequence.)
|
||||
|
||||
After that, a sql command is given, we will talk about it later.
|
||||
|
||||
Next, we need to run:
|
||||
```sh
|
||||
./install_judge_client
|
||||
```
|
||||
to build the docker image. If you want to run judger at the same server, you just need to run
|
||||
```sh
|
||||
docker run -it <image-id>
|
||||
```
|
||||
And, you need to complete the sql command given just now with the ip address of the judger docker, and modify the database. To someone who do not know how to get the ip address of a docker container, here is the answer:
|
||||
```sh
|
||||
docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container-id>
|
||||
```
|
||||
|
||||
Or, if you want to run judger at different server, you need to copy the image to the other server, and run
|
||||
```sh
|
||||
docker run -p 2333 -it <image-id>
|
||||
```
|
||||
Similarly, you need to complete the sql command and modify the database. This time, you need to fill with the ip address of the host machine of the judger docker.
|
||||
|
||||
You may meet many difficulties during the installation. Good luck and have fun!
|
||||
|
||||
## Notes
|
||||
|
||||
mysql default password: root
|
||||
|
||||
local\_main\_judger password: judger
|
||||
|
||||
You can change the default hostname and something else in `/var/www/uoj/app/.config.php`. However, not all the config is here, haha.
|
||||
|
||||
## More Documentation
|
||||
As you know, my Yingyu is not very hao. Suoyi only the README file is En(Chi)nglish for internationalization.
|
||||
|
||||
More documentation is here: [https://vfleaking.github.io/uoj/](https://vfleaking.github.io/uoj/)
|
||||
|
||||
## License
|
||||
MIT License.
|
||||
|
38
config_judge_client
Normal file
38
config_judge_client
Normal file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys, json, random, os
|
||||
|
||||
def translate(filename, target, tab):
|
||||
with open(filename, 'r') as f:
|
||||
content = f.read()
|
||||
for k, v in tab.items():
|
||||
content = content.replace('__' + k + '__', v)
|
||||
with open(target, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
cid = raw_input('uoj container id: ')
|
||||
ip = raw_input('uoj ip: ')
|
||||
name = raw_input('judger name: ')
|
||||
|
||||
os.system("docker cp " + cid + ":/home/local_main_judger/judge_client/.conf.json docker/judge_client/conf.json")
|
||||
|
||||
with open('docker/judge_client/conf.json', 'r') as f:
|
||||
conf = json.load(f)
|
||||
|
||||
conf['uoj_host'] = ip
|
||||
conf['judger_name'] = name
|
||||
conf['judger_password'] = ''.join(random.choice('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(32))
|
||||
|
||||
with open('docker/judge_client/conf.json', 'w') as f:
|
||||
json.dump(conf, f, indent=4, separators=(',', ': '))
|
||||
print >>f
|
||||
|
||||
translate_table = {
|
||||
'svn_cert': '--username %s --password %s' % (conf['svn_username'], conf['svn_password']),
|
||||
'uoj_host': ip
|
||||
}
|
||||
|
||||
translate('docker/judge_client/install', 'docker/judge_client/cur_install', translate_table)
|
||||
|
||||
print "please modify the database after getting the judger server ready:"
|
||||
print "insert into judger_info (judger_name, password, ip) values ('%s', '%s', '__judger_ip_here__');" % (name, conf['judger_password'])
|
2
docker/.gitignore
vendored
Normal file
2
docker/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
jdk-7u76-linux-x64.tar.gz
|
||||
jdk-8u31-linux-x64.tar.gz
|
30
docker/Dockerfile
Normal file
30
docker/Dockerfile
Normal file
@ -0,0 +1,30 @@
|
||||
FROM vfleaking/uoj
|
||||
MAINTAINER vfleaking vfleaking@163.com
|
||||
COPY docker/sources.list /etc/apt/sources.list
|
||||
COPY uoj/1 /root/uoj_1
|
||||
COPY judge_client/1 /root/judge_client_1
|
||||
|
||||
COPY docker/new_problem.sh \
|
||||
docker/post-commit.sh \
|
||||
docker/uoj-passwd \
|
||||
docker/uoj-post-commit \
|
||||
docker/gen-uoj-config.php \
|
||||
docker/app_uoj233.sql \
|
||||
/root/
|
||||
|
||||
COPY docker/jdk-7u76-linux-x64.tar.gz \
|
||||
docker/jdk-8u31-linux-x64.tar.gz \
|
||||
/home/local_main_judger/
|
||||
|
||||
COPY docker/install /root/install
|
||||
|
||||
RUN cd /root && php gen-uoj-config.php && chmod +x install
|
||||
RUN cd /root && ./install && rm * -rf
|
||||
|
||||
COPY docker/up /root/up
|
||||
|
||||
RUN chmod +x /root/up
|
||||
|
||||
EXPOSE 80 3690
|
||||
|
||||
CMD /root/up
|
611
docker/app_uoj233.sql
Normal file
611
docker/app_uoj233.sql
Normal file
@ -0,0 +1,611 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 4.2.7.1
|
||||
-- http://www.phpmyadmin.net
|
||||
--
|
||||
-- Host: localhost
|
||||
-- Generation Time: 2016-08-29 09:19:31
|
||||
-- 服务器版本: 5.5.38-0ubuntu0.14.04.1
|
||||
-- PHP Version: 5.5.9-1ubuntu4.4
|
||||
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
|
||||
--
|
||||
-- Database: `app_uoj233`
|
||||
--
|
||||
CREATE DATABASE IF NOT EXISTS `app_uoj233` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
||||
USE `app_uoj233`;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `best_ac_submissions`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `best_ac_submissions` (
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`submitter` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`submission_id` int(11) NOT NULL,
|
||||
`used_time` int(11) NOT NULL,
|
||||
`used_memory` int(11) NOT NULL,
|
||||
`tot_size` int(11) NOT NULL,
|
||||
`shortest_id` int(11) NOT NULL,
|
||||
`shortest_used_time` int(11) NOT NULL,
|
||||
`shortest_used_memory` int(11) NOT NULL,
|
||||
`shortest_tot_size` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `blogs`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `blogs` (
|
||||
`id` int(11) NOT NULL,
|
||||
`title` text COLLATE utf8_unicode_ci NOT NULL,
|
||||
`content` mediumtext COLLATE utf8_unicode_ci NOT NULL,
|
||||
`post_time` datetime NOT NULL,
|
||||
`poster` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`content_md` mediumtext COLLATE utf8_unicode_ci NOT NULL,
|
||||
`zan` int(11) NOT NULL,
|
||||
`is_hidden` tinyint(1) NOT NULL,
|
||||
`type` char(1) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'B',
|
||||
`is_draft` tinyint(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `blogs_comments`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `blogs_comments` (
|
||||
`id` int(11) NOT NULL,
|
||||
`blog_id` int(11) NOT NULL,
|
||||
`content` text COLLATE utf8_unicode_ci NOT NULL,
|
||||
`post_time` datetime NOT NULL,
|
||||
`poster` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`zan` int(11) NOT NULL,
|
||||
`reply_id` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `blogs_tags`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `blogs_tags` (
|
||||
`id` int(11) NOT NULL,
|
||||
`blog_id` int(11) NOT NULL,
|
||||
`tag` varchar(30) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `click_zans`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `click_zans` (
|
||||
`type` char(2) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`username` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`target_id` int(11) NOT NULL,
|
||||
`val` tinyint(4) NOT NULL DEFAULT '1'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `contests`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `contests` (
|
||||
`id` int(11) NOT NULL,
|
||||
`name` varchar(50) NOT NULL,
|
||||
`start_time` datetime NOT NULL,
|
||||
`last_min` int(11) NOT NULL,
|
||||
`player_num` int(11) NOT NULL,
|
||||
`status` varchar(50) NOT NULL,
|
||||
`extra_config` varchar(200) NOT NULL,
|
||||
`zan` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `contests_notice`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `contests_notice` (
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`title` varchar(30) NOT NULL,
|
||||
`content` varchar(500) NOT NULL,
|
||||
`time` datetime NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `contests_permissions`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `contests_permissions` (
|
||||
`username` varchar(20) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `contests_problems`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `contests_problems` (
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `contests_registrants`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `contests_registrants` (
|
||||
`username` varchar(20) NOT NULL,
|
||||
`user_rating` int(11) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`has_participated` tinyint(1) NOT NULL,
|
||||
`rank` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `contests_submissions`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `contests_submissions` (
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`submitter` varchar(20) NOT NULL,
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`submission_id` int(11) NOT NULL,
|
||||
`score` int(11) NOT NULL,
|
||||
`penalty` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `custom_test_submissions`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `custom_test_submissions` (
|
||||
`id` int(10) unsigned NOT NULL,
|
||||
`problem_id` int(10) unsigned NOT NULL,
|
||||
`submit_time` datetime NOT NULL,
|
||||
`submitter` varchar(20) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`judge_time` datetime DEFAULT NULL,
|
||||
`result` blob NOT NULL,
|
||||
`status` varchar(20) NOT NULL,
|
||||
`status_details` varchar(100) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `hacks`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `hacks` (
|
||||
`id` int(10) unsigned NOT NULL,
|
||||
`problem_id` int(10) unsigned NOT NULL,
|
||||
`contest_id` int(10) unsigned DEFAULT NULL,
|
||||
`submission_id` int(10) unsigned NOT NULL,
|
||||
`hacker` varchar(20) NOT NULL,
|
||||
`owner` varchar(20) NOT NULL,
|
||||
`input` varchar(150) NOT NULL,
|
||||
`input_type` char(20) NOT NULL,
|
||||
`submit_time` datetime NOT NULL,
|
||||
`judge_time` datetime DEFAULT NULL,
|
||||
`success` tinyint(1) DEFAULT NULL,
|
||||
`details` blob NOT NULL,
|
||||
`is_hidden` tinyint(1) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `important_blogs`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `important_blogs` (
|
||||
`blog_id` int(11) NOT NULL,
|
||||
`level` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `judger_info`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `judger_info` (
|
||||
`judger_name` varchar(50) NOT NULL,
|
||||
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
||||
`ip` char(20) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `problems`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `problems` (
|
||||
`id` int(10) unsigned NOT NULL,
|
||||
`title` text NOT NULL,
|
||||
`is_hidden` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`submission_requirement` text,
|
||||
`hackable` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`extra_config` varchar(500) NOT NULL DEFAULT '{"view_content_type":"ALL","view_details_type":"ALL"}',
|
||||
`zan` int(11) NOT NULL,
|
||||
`ac_num` int(11) NOT NULL DEFAULT '0',
|
||||
`submit_num` int(11) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `problems_contents`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `problems_contents` (
|
||||
`id` int(11) NOT NULL,
|
||||
`statement` mediumtext NOT NULL,
|
||||
`statement_md` mediumtext NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `problems_permissions`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `problems_permissions` (
|
||||
`username` varchar(20) NOT NULL,
|
||||
`problem_id` int(11) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `problems_tags`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `problems_tags` (
|
||||
`id` int(11) NOT NULL,
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`tag` varchar(30) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `search_requests`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `search_requests` (
|
||||
`id` int(11) NOT NULL,
|
||||
`created_at` datetime NOT NULL,
|
||||
`remote_addr` varchar(50) NOT NULL,
|
||||
`type` enum('search','autocomplete') NOT NULL,
|
||||
`cache_id` int(11) NOT NULL,
|
||||
`q` varchar(100) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`result` mediumtext NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `submissions`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `submissions` (
|
||||
`id` int(10) unsigned NOT NULL,
|
||||
`problem_id` int(10) unsigned NOT NULL,
|
||||
`contest_id` int(10) unsigned DEFAULT NULL,
|
||||
`submit_time` datetime NOT NULL,
|
||||
`submitter` varchar(20) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`language` varchar(15) NOT NULL,
|
||||
`tot_size` int(11) NOT NULL,
|
||||
`judge_time` datetime DEFAULT NULL,
|
||||
`result` blob NOT NULL,
|
||||
`status` varchar(20) NOT NULL,
|
||||
`result_error` varchar(20) DEFAULT NULL,
|
||||
`score` int(11) DEFAULT NULL,
|
||||
`used_time` int(11) NOT NULL DEFAULT '0',
|
||||
`used_memory` int(11) NOT NULL DEFAULT '0',
|
||||
`is_hidden` tinyint(1) NOT NULL,
|
||||
`status_details` varchar(100) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `user_info`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_info` (
|
||||
`usergroup` char(1) NOT NULL DEFAULT 'U',
|
||||
`username` varchar(20) NOT NULL,
|
||||
`email` varchar(50) NOT NULL,
|
||||
`password` char(32) NOT NULL,
|
||||
`svn_password` char(10) NOT NULL,
|
||||
`rating` int(11) NOT NULL DEFAULT '1500',
|
||||
`qq` bigint(20) NOT NULL,
|
||||
`sex` char(1) NOT NULL DEFAULT 'U',
|
||||
`ac_num` int(11) NOT NULL,
|
||||
`register_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`remote_addr` varchar(50) NOT NULL,
|
||||
`http_x_forwarded_for` varchar(50) NOT NULL,
|
||||
`remember_token` char(60) NOT NULL,
|
||||
`motto` varchar(200) NOT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `user_msg`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_msg` (
|
||||
`id` int(10) unsigned NOT NULL,
|
||||
`sender` varchar(20) NOT NULL,
|
||||
`receiver` varchar(20) NOT NULL,
|
||||
`message` varchar(5000) NOT NULL,
|
||||
`send_time` datetime NOT NULL,
|
||||
`read_time` datetime DEFAULT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `user_system_msg`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_system_msg` (
|
||||
`id` int(11) NOT NULL,
|
||||
`title` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`content` varchar(300) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`receiver` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`send_time` datetime NOT NULL,
|
||||
`read_time` datetime DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
--
|
||||
-- Indexes for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- Indexes for table `best_ac_submissions`
|
||||
--
|
||||
ALTER TABLE `best_ac_submissions`
|
||||
ADD PRIMARY KEY (`problem_id`,`submitter`);
|
||||
|
||||
--
|
||||
-- Indexes for table `blogs`
|
||||
--
|
||||
ALTER TABLE `blogs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `blogs_comments`
|
||||
--
|
||||
ALTER TABLE `blogs_comments`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `blogs_tags`
|
||||
--
|
||||
ALTER TABLE `blogs_tags`
|
||||
ADD PRIMARY KEY (`id`), ADD KEY `blog_id` (`blog_id`), ADD KEY `tag` (`tag`);
|
||||
|
||||
--
|
||||
-- Indexes for table `click_zans`
|
||||
--
|
||||
ALTER TABLE `click_zans`
|
||||
ADD PRIMARY KEY (`type`,`target_id`,`username`);
|
||||
|
||||
--
|
||||
-- Indexes for table `contests`
|
||||
--
|
||||
ALTER TABLE `contests`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `contests_notice`
|
||||
--
|
||||
ALTER TABLE `contests_notice`
|
||||
ADD KEY `contest_id` (`contest_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `contests_permissions`
|
||||
--
|
||||
ALTER TABLE `contests_permissions`
|
||||
ADD PRIMARY KEY (`username`,`contest_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `contests_problems`
|
||||
--
|
||||
ALTER TABLE `contests_problems`
|
||||
ADD PRIMARY KEY (`problem_id`,`contest_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `contests_registrants`
|
||||
--
|
||||
ALTER TABLE `contests_registrants`
|
||||
ADD PRIMARY KEY (`contest_id`,`username`);
|
||||
|
||||
--
|
||||
-- Indexes for table `contests_submissions`
|
||||
--
|
||||
ALTER TABLE `contests_submissions`
|
||||
ADD PRIMARY KEY (`contest_id`,`submitter`,`problem_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `custom_test_submissions`
|
||||
--
|
||||
ALTER TABLE `custom_test_submissions`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `hacks`
|
||||
--
|
||||
ALTER TABLE `hacks`
|
||||
ADD PRIMARY KEY (`id`), ADD KEY `submission_id` (`submission_id`), ADD KEY `is_hidden` (`is_hidden`,`problem_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `important_blogs`
|
||||
--
|
||||
ALTER TABLE `important_blogs`
|
||||
ADD PRIMARY KEY (`blog_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `judger_info`
|
||||
--
|
||||
ALTER TABLE `judger_info`
|
||||
ADD PRIMARY KEY (`judger_name`);
|
||||
|
||||
--
|
||||
-- Indexes for table `problems`
|
||||
--
|
||||
ALTER TABLE `problems`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `problems_contents`
|
||||
--
|
||||
ALTER TABLE `problems_contents`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `problems_permissions`
|
||||
--
|
||||
ALTER TABLE `problems_permissions`
|
||||
ADD PRIMARY KEY (`username`,`problem_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `problems_tags`
|
||||
--
|
||||
ALTER TABLE `problems_tags`
|
||||
ADD PRIMARY KEY (`id`), ADD KEY `problem_id` (`problem_id`), ADD KEY `tag` (`tag`);
|
||||
|
||||
--
|
||||
-- Indexes for table `search_requests`
|
||||
--
|
||||
ALTER TABLE `search_requests`
|
||||
ADD PRIMARY KEY (`id`), ADD KEY `remote_addr` (`remote_addr`,`created_at`), ADD KEY `created_at` (`created_at`);
|
||||
|
||||
--
|
||||
-- Indexes for table `submissions`
|
||||
--
|
||||
ALTER TABLE `submissions`
|
||||
ADD PRIMARY KEY (`id`), ADD KEY `is_hidden` (`is_hidden`,`problem_id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `user_info`
|
||||
--
|
||||
ALTER TABLE `user_info`
|
||||
ADD PRIMARY KEY (`username`), ADD KEY `rating` (`rating`,`username`), ADD KEY `ac_num` (`ac_num`,`username`);
|
||||
|
||||
--
|
||||
-- Indexes for table `user_msg`
|
||||
--
|
||||
ALTER TABLE `user_msg`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `user_system_msg`
|
||||
--
|
||||
ALTER TABLE `user_system_msg`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for table `blogs`
|
||||
--
|
||||
ALTER TABLE `blogs`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `blogs_comments`
|
||||
--
|
||||
ALTER TABLE `blogs_comments`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `blogs_tags`
|
||||
--
|
||||
ALTER TABLE `blogs_tags`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `contests`
|
||||
--
|
||||
ALTER TABLE `contests`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `custom_test_submissions`
|
||||
--
|
||||
ALTER TABLE `custom_test_submissions`
|
||||
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `hacks`
|
||||
--
|
||||
ALTER TABLE `hacks`
|
||||
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `problems`
|
||||
--
|
||||
ALTER TABLE `problems`
|
||||
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `problems_tags`
|
||||
--
|
||||
ALTER TABLE `problems_tags`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `search_requests`
|
||||
--
|
||||
ALTER TABLE `search_requests`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `submissions`
|
||||
--
|
||||
ALTER TABLE `submissions`
|
||||
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `user_msg`
|
||||
--
|
||||
ALTER TABLE `user_msg`
|
||||
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
|
||||
--
|
||||
-- AUTO_INCREMENT for table `user_system_msg`
|
||||
--
|
||||
ALTER TABLE `user_system_msg`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
93
docker/gen-uoj-config.php
Normal file
93
docker/gen-uoj-config.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
function rand_str($len, $charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
|
||||
$n_chars = strlen($charset);
|
||||
$str = '';
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$str .= $charset[mt_rand(0, $n_chars - 1)];
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
function translate($filename, $target, $tab) {
|
||||
$content = file_get_contents($filename);
|
||||
foreach ($tab as $k => $v) {
|
||||
$content = str_replace("__{$k}__", $v, $content);
|
||||
}
|
||||
file_put_contents($target, $content);
|
||||
}
|
||||
|
||||
$svn_pwd = rand_str(32);
|
||||
$svn_cert = '--username root --password '.$svn_pwd;
|
||||
|
||||
$config = [
|
||||
'database' => [
|
||||
'database' => 'app_uoj233',
|
||||
'username' => 'root',
|
||||
'password' => 'root',
|
||||
'host' => '127.0.0.1'
|
||||
],
|
||||
'web' => [
|
||||
'main' => [
|
||||
'protocol' => 'http',
|
||||
'host' => 'local_uoj.ac'
|
||||
],
|
||||
'blog' => [
|
||||
'protocol' => 'http',
|
||||
'host' => 'blog.local_uoj.ac'
|
||||
]
|
||||
],
|
||||
'security' => [
|
||||
'user' => [
|
||||
'client_salt' => rand_str(32)
|
||||
],
|
||||
'cookie' => [
|
||||
'checksum_salt' => [rand_str(16), rand_str(16), rand_str(16)]
|
||||
],
|
||||
],
|
||||
'mail' => [
|
||||
'noreply' => [
|
||||
'username' => 'noreply@none',
|
||||
'password' => 'noreply'
|
||||
]
|
||||
],
|
||||
'judger' => [
|
||||
'socket' => [
|
||||
'port' => 2333,
|
||||
'password' => rand_str(32)
|
||||
]
|
||||
],
|
||||
'svn' => [
|
||||
'our-root' => [
|
||||
'username' => 'our-root',
|
||||
'password' => rand_str(32)
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$judge_client_config = [
|
||||
'uoj_protocol' => 'http',
|
||||
'uoj_host' => '127.0.0.1',
|
||||
'judger_name' => 'main_judger',
|
||||
'judger_password' => rand_str(32),
|
||||
'socket_port' => $config['judger']['socket']['port'],
|
||||
'socket_password' => $config['judger']['socket']['password'],
|
||||
'svn_username' => 'root',
|
||||
'svn_password' => $svn_pwd
|
||||
];
|
||||
|
||||
$translate_table = [
|
||||
'svn_cert' => $svn_cert,
|
||||
'svn_pwd' => $svn_pwd,
|
||||
'our_root_password' => $config['svn']['our-root']['password'],
|
||||
'main_judger_password' => $judge_client_config['judger_password']
|
||||
];
|
||||
|
||||
translate('new_problem.sh', '/var/svn/problem/new_problem.sh', $translate_table);
|
||||
translate('post-commit.sh', '/var/svn/problem/post-commit.sh', $translate_table);
|
||||
|
||||
translate('uoj-passwd', '/var/svn/uoj/conf/passwd', $translate_table);
|
||||
translate('uoj-post-commit', '/var/svn/uoj/hooks/post-commit', $translate_table);
|
||||
file_put_contents('uoj_config.php', "<?php\nreturn ".var_export($config, true).";\n");
|
||||
file_put_contents('judge_client_config.json', json_encode($judge_client_config, JSON_PRETTY_PRINT));
|
||||
translate('install', 'install', $translate_table);
|
65
docker/install
Normal file
65
docker/install
Normal file
@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
|
||||
chmod +x /var/svn/uoj/hooks/post-commit
|
||||
chmod +x /var/svn/problem/new_problem.sh
|
||||
chmod +x /var/svn/problem/post-commit.sh
|
||||
|
||||
svnserve -d -r /var/svn
|
||||
service mysql start
|
||||
service apache2 start
|
||||
|
||||
cd /root
|
||||
|
||||
svn co svn://127.0.0.1/uoj __svn_cert__
|
||||
mv uoj_1 uoj/1
|
||||
cd uoj
|
||||
svn add 1
|
||||
svn ci -m "first commit" __svn_cert__
|
||||
cd ..
|
||||
rm uoj -r
|
||||
|
||||
rm /var/www/uoj -r && ln -s /var/svn/uoj/cur/uoj/1 /var/www/uoj
|
||||
|
||||
while [ ! -d "/var/www/uoj/app" ]
|
||||
do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
mv uoj_config.php /var/www/uoj/app/.config.php
|
||||
|
||||
mysql -u root --password=root <app_uoj233.sql
|
||||
chown www-data /var/www/uoj/app/storage -R
|
||||
|
||||
svn co svn://127.0.0.1/judge_client __svn_cert__
|
||||
mv judge_client_1 judge_client/1
|
||||
cd judge_client
|
||||
svn add 1
|
||||
svn ci -m "first commit" __svn_cert__
|
||||
cd ..
|
||||
rm judge_client -r
|
||||
|
||||
chown local_main_judger /home/local_main_judger/jdk-7u76-linux-x64.tar.gz /home/local_main_judger/jdk-8u31-linux-x64.tar.gz
|
||||
|
||||
su local_main_judger <<EOD
|
||||
svn update /var/svn/judge_client/cur/judge_client __svn_cert__
|
||||
ln -s /var/svn/judge_client/cur/judge_client/1 ~/judge_client
|
||||
cd ~/judge_client
|
||||
chmod +x judge_client
|
||||
echo '#define UOJ_WORK_PATH "/home/local_main_judger/judge_client/uoj_judger"' >uoj_judger/include/uoj_work_path.h
|
||||
make
|
||||
ln -s /var/uoj_data ~/judge_client/uoj_judger/data
|
||||
|
||||
mkdir ~/judge_client/uoj_judger/run/runtime
|
||||
mv ~/jdk-7u76-linux-x64.tar.gz ~/jdk-8u31-linux-x64.tar.gz ~/judge_client/uoj_judger/run/runtime
|
||||
cd ~/judge_client/uoj_judger/run/runtime
|
||||
tar -xzf jdk-7u76-linux-x64.tar.gz
|
||||
tar -xzf jdk-8u31-linux-x64.tar.gz
|
||||
EOD
|
||||
|
||||
mv judge_client_config.json /home/local_main_judger/judge_client/.conf.json
|
||||
chown local_main_judger /home/local_main_judger/judge_client/.conf.json
|
||||
chmod 600 /home/local_main_judger/judge_client/.conf.json
|
||||
|
||||
echo 'insert into judger_info (judger_name, password) values ("main_judger", "__main_judger_password__")' | mysql app_uoj233 -u root --password=root
|
||||
|
||||
php /var/www/uoj/app/cli.php upgrade:latest
|
30
docker/judge_client/Dockerfile
Normal file
30
docker/judge_client/Dockerfile
Normal file
@ -0,0 +1,30 @@
|
||||
FROM ubuntu:14.04
|
||||
MAINTAINER vfleaking vfleaking@163.com
|
||||
|
||||
COPY docker/sources.list /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y vim \
|
||||
ntp \
|
||||
build-essential \
|
||||
python \
|
||||
python-requests \
|
||||
subversion \
|
||||
unzip
|
||||
|
||||
COPY docker/jdk-7u76-linux-x64.tar.gz \
|
||||
docker/jdk-8u31-linux-x64.tar.gz \
|
||||
docker/judge_client/conf.json \
|
||||
/root/
|
||||
COPY docker/judge_client/cur_install /root/install
|
||||
|
||||
RUN cd /root && chmod +x install
|
||||
RUN cd /root && ./install && rm * -rf
|
||||
|
||||
COPY docker/judge_client/up /root/up
|
||||
|
||||
RUN chmod +x /root/up
|
||||
|
||||
EXPOSE 2333
|
||||
|
||||
CMD /root/up
|
32
docker/judge_client/install
Normal file
32
docker/judge_client/install
Normal file
@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo adduser judger --gecos "" --disabled-password
|
||||
|
||||
mv /root/jdk-7u76-linux-x64.tar.gz /root/jdk-8u31-linux-x64.tar.gz /root/conf.json /home/judger
|
||||
chown judger /home/judger/jdk-7u76-linux-x64.tar.gz /home/judger/jdk-8u31-linux-x64.tar.gz /home/judger/conf.json
|
||||
chgrp judger /home/judger/jdk-7u76-linux-x64.tar.gz /home/judger/jdk-8u31-linux-x64.tar.gz /home/judger/conf.json
|
||||
|
||||
mkdir /var/uoj_data_copy
|
||||
chown judger /var/uoj_data_copy
|
||||
|
||||
su judger <<EOD
|
||||
cd ~
|
||||
mkdir judge_client_svn
|
||||
cd judge_client_svn
|
||||
svn co svn://__uoj_host__/judge_client __svn_cert__
|
||||
cd ~
|
||||
ln -s judge_client_svn/judge_client/1 judge_client
|
||||
cd judge_client
|
||||
echo '#define UOJ_WORK_PATH "/home/judger/judge_client/uoj_judger"' >uoj_judger/include/uoj_work_path.h
|
||||
make
|
||||
ln -s /var/uoj_data_copy ~/judge_client/uoj_judger/data
|
||||
|
||||
mkdir ~/judge_client/uoj_judger/run/runtime
|
||||
mv ~/jdk-7u76-linux-x64.tar.gz ~/jdk-8u31-linux-x64.tar.gz ~/judge_client/uoj_judger/run/runtime
|
||||
cd ~/judge_client/uoj_judger/run/runtime
|
||||
tar -xzf jdk-7u76-linux-x64.tar.gz
|
||||
tar -xzf jdk-8u31-linux-x64.tar.gz
|
||||
|
||||
mv ~/conf.json ~/judge_client/.conf.json
|
||||
chmod 600 ~/judge_client/.conf.json
|
||||
EOD
|
7
docker/judge_client/up
Normal file
7
docker/judge_client/up
Normal file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
service ntp start
|
||||
|
||||
su judger -c '~/judge_client/judge_client start'
|
||||
|
||||
exec bash
|
34
docker/new_problem.sh
Normal file
34
docker/new_problem.sh
Normal file
@ -0,0 +1,34 @@
|
||||
if [ $# -ne 1 ]
|
||||
then
|
||||
echo 'invalid argument'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
path=/var/svn/problem/$1
|
||||
mkdir $path
|
||||
svnadmin create $path
|
||||
cat >$path/conf/svnserve.conf <<EOD
|
||||
[general]
|
||||
anon-access = none
|
||||
auth-access = write
|
||||
password-db = passwd
|
||||
EOD
|
||||
|
||||
svnusr="our-root"
|
||||
svnpwd="__our_root_password__"
|
||||
|
||||
cat >$path/conf/passwd <<EOD
|
||||
[users]
|
||||
$svnusr = $svnpwd
|
||||
EOD
|
||||
|
||||
mkdir $path/cur
|
||||
cd $path/cur
|
||||
svn checkout svn://127.0.0.1/problem/$1 --username $svnusr --password $svnpwd
|
||||
mkdir /var/uoj_data/$1
|
||||
|
||||
cat >$path/hooks/post-commit <<EODEOD
|
||||
#!/bin/sh
|
||||
/var/svn/problem/post-commit.sh $1
|
||||
EODEOD
|
||||
chmod +x $path/hooks/post-commit
|
6
docker/post-commit.sh
Normal file
6
docker/post-commit.sh
Normal file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
svnusr="our-root"
|
||||
svnpwd="__our_root_password__"
|
||||
cd /var/svn/problem/$1/cur/$1
|
||||
svn update --username $svnusr --password $svnpwd
|
||||
chown www-data /var/svn/problem/$1 -R
|
35
docker/sources.list
Normal file
35
docker/sources.list
Normal file
@ -0,0 +1,35 @@
|
||||
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
|
||||
# newer versions of the distribution.
|
||||
|
||||
deb http://cn.archive.ubuntu.com/ubuntu/ trusty main restricted
|
||||
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty main restricted
|
||||
|
||||
## Major bug fix updates produced after the final release of the
|
||||
## distribution.
|
||||
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-updates main restricted
|
||||
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-updates main restricted
|
||||
|
||||
## Uncomment the following two lines to add software from the 'universe'
|
||||
## repository.
|
||||
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
|
||||
## team. Also, please note that software in universe WILL NOT receive any
|
||||
## review or updates from the Ubuntu security team.
|
||||
deb http://cn.archive.ubuntu.com/ubuntu/ trusty universe
|
||||
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty universe
|
||||
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-updates universe
|
||||
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-updates universe
|
||||
|
||||
## N.B. software from this repository may not have been tested as
|
||||
## extensively as that contained in the main release, although it includes
|
||||
## newer versions of some applications which may provide useful features.
|
||||
## Also, please note that software in backports WILL NOT receive any review
|
||||
## or updates from the Ubuntu security team.
|
||||
# deb http://cn.archive.ubuntu.com/ubuntu/ trusty-backports main restricted
|
||||
# deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-backports main restricted
|
||||
|
||||
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-security main restricted
|
||||
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-security main restricted
|
||||
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-security universe
|
||||
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-security universe
|
||||
# deb http://cn.archive.ubuntu.com/ubuntu/ trusty-security multiverse
|
||||
# deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-security multiverse
|
2
docker/uoj-passwd
Normal file
2
docker/uoj-passwd
Normal file
@ -0,0 +1,2 @@
|
||||
[users]
|
||||
root = __svn_pwd__
|
2
docker/uoj-post-commit
Normal file
2
docker/uoj-post-commit
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
cd /var/svn/uoj/cur/uoj && svn update --username root --password __svn_pwd__
|
10
docker/up
Normal file
10
docker/up
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
service ntp start
|
||||
service apache2 start
|
||||
service mysql start
|
||||
svnserve -d -r /var/svn
|
||||
|
||||
su local_main_judger -c '~/judge_client/judge_client start'
|
||||
|
||||
exec bash
|
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
site/
|
4
docs/docs/about.md
Normal file
4
docs/docs/about.md
Normal file
@ -0,0 +1,4 @@
|
||||
# 关于
|
||||
文档编写:vfleaking
|
||||
|
||||
邮件:vfleaking@163.com
|
3
docs/docs/contest.md
Normal file
3
docs/docs/contest.md
Normal file
@ -0,0 +1,3 @@
|
||||
# 比赛
|
||||
|
||||
<p style="font-size:233px">坑</p>
|
50
docs/docs/dev.md
Normal file
50
docs/docs/dev.md
Normal file
@ -0,0 +1,50 @@
|
||||
# 开发
|
||||
听说你想写代码?
|
||||
|
||||
svn://local\_uoj.ac/uoj 和 svn://local\_uoj.ac/judge_client 欢迎你(如果你没改 UOJ 的 hostname 的话),这是两个 svn 仓库,至于 svn 仓库权限问题……你需要手工加一下……
|
||||
|
||||
在 /var/svn/uoj/conf/passwd 这个文件中你可以加一行
|
||||
<pre>
|
||||
uoj = 666666
|
||||
</pre>
|
||||
来增加一位名为 "<samp>uoj</samp>",密码为 "<samp>666666</samp>" 的 svn 仓库管理员。
|
||||
|
||||
之后你就可以随意用 svn 玩耍了哇咔咔……
|
||||
|
||||
在本地写完之后,如果你想与别人分享,你可以把代码再放到 git 中(什么鬼?)再 push 到 github。
|
||||
|
||||
如果你想更新的东西已经不局限于网站代码,还想对数据库、文件系统之类的折腾一番,请在 app/upgrade 目录下建立文件夹,举个例子,叫 `2333_create_table_qaq`。即,以一串数字开头,后面加一个下划线,再后面随便取名字吧,仅能包含数字字母和下划线。
|
||||
|
||||
在这个文件夹下你可以放一些小脚本。大概 UOJ 运行这些小脚本是这么个逻辑:
|
||||
```php
|
||||
if (is_file("{$dir}/upgrade.php")) {
|
||||
$fun = include "{$dir}/upgrade.php";
|
||||
$fun("up");
|
||||
}
|
||||
if (is_file("{$dir}/up.sql")) {
|
||||
runSQL("{$dir}/up.sql");
|
||||
}
|
||||
if (is_file("{$dir}/upgrade.sh")) {
|
||||
runShell("/bin/bash {$dir}/upgrade.sh up");
|
||||
}
|
||||
```
|
||||
你只需要在 /var/www/uoj/app 下执行 `php cli.php upgrade:up 2333_create_table_qaq` 就可以运行了,这个运行过程我们称为 `up`。
|
||||
|
||||
请务必再写一下还原的小脚本,我们称为 `down`。写完代码后你需要保证 `up` 再 `down` 能回到原来的系统。与 `up` 类似,你需要执行 `php cli.php upgrade:down 2333_create_table_qaq`,执行 `down` 的逻辑如下:
|
||||
```php
|
||||
if (is_file("{$dir}/upgrade.php")) {
|
||||
$fun = include "{$dir}/upgrade.php";
|
||||
$fun("down");
|
||||
}
|
||||
if (is_file("{$dir}/down.sql")) {
|
||||
runSQL("{$dir}/down.sql");
|
||||
}
|
||||
if (is_file("{$dir}/upgrade.sh")) {
|
||||
runShell("/bin/bash {$dir}/upgrade.sh down");
|
||||
}
|
||||
```
|
||||
|
||||
在数据库中,UOJ 会记录自己已经加载了哪些 upgrade,当你执行 `php cli.php upgrade:latest` 的时候,UOJ 会把所有已经在 app/upgrade 文件夹下但还没有加载的 upgrade 都给 `up` 一下,并且是按前缀上的那一串数字从小到大执行。所以写好了这种小 upgrade 之后,你就可以跟别人分享了!
|
||||
|
||||
不过关于架构什么的介绍我还是先
|
||||
<p style="font-size:233px">坑着</p>
|
4
docs/docs/extra.css
Normal file
4
docs/docs/extra.css
Normal file
@ -0,0 +1,4 @@
|
||||
.hljs-preprocessor .hljs-keyword {
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
11
docs/docs/index.md
Normal file
11
docs/docs/index.md
Normal file
@ -0,0 +1,11 @@
|
||||
# 概述
|
||||
|
||||
写点 UOJ 的文档方便大家。
|
||||
|
||||
(喂怎么概述就这么短这么懒啊?)
|
||||
|
||||
哎呀懒得废话那么多啦……
|
||||
|
||||
要想竞赛搞得好,网上刷题不可少;在线题库哪里找?通用测评大法好!
|
||||
|
||||
[UOJ](http://uoj.ac/)
|
106
docs/docs/install.md
Normal file
106
docs/docs/install.md
Normal file
@ -0,0 +1,106 @@
|
||||
# 安装
|
||||
|
||||
## 教练我想安装!
|
||||
|
||||
请参见 [README.md](https://github.com/vfleaking/uoj/blob/master/README.md) 来安装。
|
||||
|
||||
嘿嘿现在我假设你已经是超级管理员了!
|
||||
|
||||
那么你就可以新建题目了!
|
||||
|
||||
看看看,problem/1 这个文件夹,里面有一道样题,赶紧动手上传!(啊感觉我应该多给几道样题的)
|
||||
|
||||
关于如何传题请参见[题目文档](problem/)
|
||||
|
||||
啊如果你在 “与svn仓库同步” 的过程中发现 <samp>compile error</samp> 并且还说 <samp>no comment</samp> 的话……多半是……UOJ 尝试 ptrace 然而 ptrace 被禁了……
|
||||
|
||||
被禁了可能是被 docker 禁了,可以加一句 `--cap-add SYS_PTRACE` (见 [README.md](https://github.com/vfleaking/uoj/blob/master/README.md))……要是这样不能解决问题……是 Ubuntu/openSUSE 环境嘛?请尝试用下面的命令阻止 AppArmor 对 docker 的行为产生影响。当然把第二行加到 `rc.local` 里就不用每次重启都输入一遍啦~(详细解释戳 [→ 这里](https://github.com/docker/docker/issues/7276))
|
||||
|
||||
```sh
|
||||
sudo apt-get install apparmor-utils
|
||||
aa-complain /etc/apparmor.d/docker
|
||||
```
|
||||
|
||||
要是这样还是不能解决问题……估计就是被黑恶势力禁掉了。。。大概就不是咱们这边的锅了,找找是哪个黑恶势力禁的吧。。。
|
||||
|
||||
另:如果上述方法不能解决 ptrace 问题也可以考虑使用 `--privileged`,但是这么做会导致 docker 失去其原有的隔离性而带来潜在的风险,不建议在生产环境中使用。
|
||||
|
||||
传题都没问题的话,感觉就可以愉快使用 UOJ 了!
|
||||
|
||||
## 教练我还想折腾!
|
||||
|
||||
嗯!来来来我们来折腾一波。
|
||||
|
||||
有个很厉害的配置文件,在 /var/www/uoj/app/.config.php 里,你可以配置一波……一个完全体如下:(有可能本文档没有跟上代码更新的速度,请以旁边的那个 .default-config.php 为准)
|
||||
|
||||
```php
|
||||
<?php
|
||||
return [
|
||||
'database' => [ // 数据库相关
|
||||
'database' => 'app_uoj233', // 数据库名称
|
||||
'username' => 'root', // 用户名
|
||||
'password' => '', // 密码
|
||||
'host' => '127.0.0.1' // 数据库主机名
|
||||
],
|
||||
'web' => [ // 网址相关
|
||||
'main' => [ // 网站主体
|
||||
'protocol' => 'http', // 传输协议
|
||||
'host' => 'local_uoj.ac', // 主机名
|
||||
'port' => 80 // 端口
|
||||
],
|
||||
'blog' => [ // UOJ 博客 (用户名放前面之后成为完整的域名)
|
||||
'protocol' => 'http', // 传输协议
|
||||
'host' => 'blog.local_uoj.ac', // 主机名
|
||||
'port' => 80 // 端口
|
||||
]
|
||||
],
|
||||
'security' => [ // 安全相关(不要动)
|
||||
'user' => [
|
||||
'client_salt' => 'salt0'
|
||||
],
|
||||
'cookie' => [
|
||||
'checksum_salt' => ['salt1', 'salt2', 'salt3']
|
||||
],
|
||||
],
|
||||
'mail' => [ // 邮件相关(SMTP 协议发送)
|
||||
'noreply' => [ // noreply 邮箱
|
||||
'username' => 'noreply@none',
|
||||
'password' => 'noreply',
|
||||
'host' => 'smtp.sina.com',
|
||||
'port' => 25
|
||||
]
|
||||
],
|
||||
'judger' => [ // 测评相关(不要动)
|
||||
'socket' => [ // 与测评机的 socket 服务器通讯的设置
|
||||
'port' => '233', // 端口
|
||||
'password' => 'password233' // 认证密码(证明自己 UOJ 服务器)
|
||||
]
|
||||
],
|
||||
'svn' => [ // svn 相关(不要动)
|
||||
'our-root' => [ // 每个题目的 svn 仓库自带的仓库管理员
|
||||
'username' => 'our-root', // 管理员用户名
|
||||
'password' => 'our-root' // 密码
|
||||
]
|
||||
],
|
||||
'switch' => [ // 一些开关
|
||||
'ICP-license' => false, // ICP 备案信息的显示
|
||||
'web-analytics' => false // 网站流量统计(记 uoj.ac 名下……想统计自己的得改代码)
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
### 域名
|
||||
如果你想使用自己的域名,改 config 即可。
|
||||
|
||||
如果你是搭校内网站这种的,可能没有 DNS。你可以选择自己搭校内的 DNS 解析,或者自己改 hosts。
|
||||
|
||||
或者你想成为一条咸鱼,可以直接在 config 里的 host 那里写 ip。
|
||||
|
||||
谜之声:为什么博客用不了?
|
||||
|
||||
嗯。。。博客的话需要给每个用户的博客域名进行解析……最好是用泛解析解决。如果没这条件,我帮你点根蜡烛 23333……期待有为青年码一个“博客以子域名还是子目录形式出现”的开关。
|
||||
|
||||
### 邮箱
|
||||
noreply 邮箱的目的是发一些莫名其妙的邮件,比如 svn 密码和 “找回密码”。
|
||||
|
||||
你可以随便找一个邮箱小号把账号密码塞这里,但是记得查查邮件服务商的 SMTP 服务器名和端口。以及,有些邮箱是需要手动开启某个开关才能允许 SMTP 发送邮件的。
|
35
docs/docs/maintain.md
Normal file
35
docs/docs/maintain.md
Normal file
@ -0,0 +1,35 @@
|
||||
# 维护
|
||||
|
||||
## 数据可持久化
|
||||
|
||||
(我并不是在说函数式线段树233)
|
||||
|
||||
有同学表示重启后 docker 丢失了数据。在这里要科普下 docker 的 image 和 container 的概念。
|
||||
|
||||
`run` 命令的功能是读一个 image 并运行,跑起来的 image 是一名光荣的 container,跑完的 container 还是一名光荣的 container。
|
||||
|
||||
所以当你关掉了你的 container 之后,使用 `run` 命令试图让它复活是不对的。。。
|
||||
|
||||
首先,你需要
|
||||
```sh
|
||||
docker ps -a
|
||||
```
|
||||
来查看所有正在运行或运行结束的 container,找到你刚才掐掉的那个(通常就是第一个),然后复制下它的 container id。然后:
|
||||
```sh
|
||||
docker restart <container-id>
|
||||
```
|
||||
这家伙就在后台默默跑起来了!如果你想让它回到前台,请用
|
||||
```sh
|
||||
docker attach <container-id>
|
||||
```
|
||||
当当!复活了!你以为丢失了的数据都回来了!(container 其实可以起名的,这种进阶用法自己探索吧233)
|
||||
|
||||
## 更新
|
||||
|
||||
当 github 上有更新的时候,如何更新自己本地的版本呢?
|
||||
|
||||
求看眼[开发](/dev/)最前面几行关于使用 svn 仓库的。
|
||||
|
||||
嗯好,假设你已经看完了。如果是 uoj 和 judge_client 的更新,那么你可以 svn checkout 一下,然后把 git 里的新版本 commit 上去……不过你以前 checkout 过的话就可以留着那个文件夹,下次就不用重新 checkout 了。
|
||||
|
||||
随着 UOJ 代码的更新,可能会有一些数据哇文件系统哇之类的更新,这种会被打包放在 app/upgrade 目录下。你只需要在 /var/www/uoj/app 下执行 `php cli.php upgrade:latest` 就好啦!
|
263
docs/docs/problem.md
Normal file
263
docs/docs/problem.md
Normal file
@ -0,0 +1,263 @@
|
||||
# 题目
|
||||
## 上传题目
|
||||
新建题目只有超级管理员有权限,所以想加题先要联系超级管理员君,点一下题库右下方的 “新建题目” 按钮。
|
||||
|
||||
新建题目后,超级管理员或该题目的题目管理员可以看到题目后台。后台分为三个选项卡:
|
||||
|
||||
1. 编辑:题面编辑页面
|
||||
2. 管理者:题目管理员列表管理页面
|
||||
3. 数据:题目数据管理页面
|
||||
|
||||
当然还有最后个 “返回” 选项卡退出后台。下面将逐一介绍这三个选项卡。
|
||||
|
||||
###编辑
|
||||
该页面可以对题面本身进行编辑,还可以管理题目的标签。
|
||||
|
||||
####编辑题面
|
||||
题面用 Markdown 编写。
|
||||
|
||||
理论上题面是可以自由编写的,但还是有一些推荐的格式。可参照 UOJ 上对应类型的题面(传统题,交互题,提交答案题等)
|
||||
|
||||
一些推荐的规则:
|
||||
|
||||
1. 中文与英文、数字之间加一个空格隔开。
|
||||
2. 输入输出样例用 `<pre>` 标签包围,并且以一个空行结尾。(方便大家复制样例到终端后不用再打回车)
|
||||
3. 题面中最高级标题为三级标题。这是因为题目名称是二级标题。
|
||||
4. 名称超过一个字符的数学符号要用 mathrm。例如 `\mathrm{sgn}`, `\mathrm{lca}`。
|
||||
4. 注意 `\max`, `\min`, `\gcd` 都是有现成的。
|
||||
5. 注意 xor 这种名称超过一个字符的二元运算符请用 `\mathbin{\mathrm{xor}}`。
|
||||
6. 一行内的计算机输入输出和常量字符串表达式请用 `<samp>` 标签,例如 `请输出 “<samp>NO SOLUTION</samp>”`, `<samp>aaa</samp> 中有三个 <samp>a</samp>`。
|
||||
7. 一行内的计算机代码请用 <code>`</code> 括起来,就像上面的规则那样。
|
||||
|
||||
可参考下面这个例子:
|
||||
```markdown
|
||||
读入一个整数 $n$,表示题目中提到的 $3$ 位大爷 AC 的总题数。请输出他们分别 AC 的总题数。如果你不能正确读入,那么将获得 $0$ 分。前 $3$ 个测试点你正确读入即可获得 $6$ 分,第 4 个测试点你正确读入只能获得 $3$ 分。如果你不会做这道题,请直接输出 “<samp>WOBUHUI</samp>”
|
||||
|
||||
下面有一个样例:
|
||||
<pre>
|
||||
233
|
||||
|
||||
</pre>
|
||||
```
|
||||
|
||||
|
||||
####编辑标签
|
||||
只需要用英文逗号隔开标签填入文本框就行。
|
||||
|
||||
推荐你使用如下几条规则填写标签:
|
||||
|
||||
1. 标签的目的是标出题目类型,方便用户检索题目。一般来说,标签顺序基本为从小范围到大范围。
|
||||
2. 最前面的几个标签是这题所需要的前置技能,这里假定 “二分查找” 之类过于基础的技能选手已经掌握。
|
||||
3. 接下来是这道题的大方法,比如 “贪心”、“DP”、“乱搞”、“构造”、“分治”……
|
||||
4. 接下来,如果这道题是非传统题,用一个标签注明非传统题类型,比如 “提交答案”、“交互式”、“通讯”。
|
||||
5. 接下来,如果这道题是模板题,用一个标签注明 “模板题”。
|
||||
6. 接下来,如果这道题是不用脑子想就能做出的题,例如 NOIP 第一题难度,用一个标签注明 “水题”。
|
||||
7. 最后,如果这题的来源比较重要,用一个标签注明。比如 “UOJ Round”、“NOI”、“WC”。
|
||||
8. 前置技能中,“数学” 太过宽泛不能作为标签,但 “数论” 可以作为前置技能。
|
||||
9. 如果有多个解法,每个解法的前值技能和大方法都不太一样,那么尽可能都标上去。
|
||||
10. “乱搞” 标签不宜滥用。
|
||||
|
||||
|
||||
###管理者
|
||||
只有题目管理员和超级管理员才能看到后台,但只有题目管理员才能管理题目数据。
|
||||
|
||||
可以通过这个页面增加或删除题目管理员。
|
||||
|
||||
###数据
|
||||
UOJ 使用 svn 管理题目数据,svn 仓库地址可以在本页面上找到。当你点了本页面的某个按钮后,UOJ 将把 svn 密码发送到你的邮箱。这个 svn 密码是不随题目变化而变化的。
|
||||
|
||||
但是,初始时 UOJ 是没有配置过用于给大家发邮件的邮箱的。所以如果你还没有配置过,请直接在数据库的 user\_info 表中找到 svn\_password 一栏存储的值;如果你愿意折腾一下,可以在 /var/www/uoj/app/.config.php 里配置一下邮箱,详情见[安装](/install/)咯~
|
||||
|
||||
1. 下载 svn,即 subversion。如果是 win 可以用 TortoiseSVN,如果是 ubuntu 直接 apt-get 一发,也可以用 smartsvn。
|
||||
2. 学习怎么使用 svn。(蛮好懂的吧,会用 checkout,add,remove,commit 什么的就行了)
|
||||
3. 在题目数据管理页面,获取 svn 密码。
|
||||
4. checkout 这道题。
|
||||
5. 在你的 working copy 下,建立文件夹 1,然后在 1 这个文件夹下放置题目数据什么的。
|
||||
6. 搞完之后 commit。
|
||||
7. 在网页上点“与SVN仓库同步”,就可以啦!
|
||||
|
||||
####题目配置格式
|
||||
主要麻烦是要写个 problem.conf,下面主要介绍传统题的配置。
|
||||
|
||||
例如:
|
||||
|
||||
<pre>
|
||||
use_builtin_judger on
|
||||
use_builtin_checker ncmp
|
||||
n_tests 10
|
||||
n_ex_tests 5
|
||||
n_sample_tests 1
|
||||
input_pre www
|
||||
input_suf in
|
||||
output_pre www
|
||||
output_suf out
|
||||
time_limit 1
|
||||
memory_limit 256
|
||||
output_limit 64
|
||||
|
||||
</pre>
|
||||
|
||||
假如你 input 文件是 www1.in, www2.in, ... 这个样子,那么你需要在 problem.conf 里记录:
|
||||
|
||||
<pre>
|
||||
input_pre www
|
||||
input_suf in
|
||||
|
||||
</pre>
|
||||
|
||||
output 文件同理。
|
||||
|
||||
extra test 是指额外数据,在 AC 的情况下会测额外数据,如果某个额外数据通不过会被倒扣3分。
|
||||
|
||||
至于样例,样例一定是前几个额外数据,所以有一个 `n_sample_tests` 表示是前多少组是样例。你需要把题面中的样例和大样例放进额外数据里。
|
||||
|
||||
额外数据命名形如 ex\_www1.in, ex\_www2.in, ... 这个样子。
|
||||
|
||||
checker 是指判断选手输出是否正确的。一般来说输出结果为整数序列,用 ncmp 就够了,它会比较标准答案的整数序列和选手输出的整数序列。如果是忽略所有空白字符,进行字符串序列的比较,可以用wcmp。如果你想按行比较(不忽略行末空格,但忽略文末回车),可以使用 fcmp。如果想手写 checker,请使用 Codeforces 的 [testlib](http://codeforces.com/testlib) 库写 checker。
|
||||
|
||||
你需要写好后命名为 chk.cpp,然后在 problem.conf 中去掉 “use\_builtin\_checker” 一行。
|
||||
|
||||
如果你的题目的输入数据可以被检验是否满足题目要求,那么请写一个 validator。比如“保证数据随机”“保证数据均为人手工输入”等就无法检验输入数据是否满足题目要求。
|
||||
|
||||
validator 的作用就是检查你输入数据造得是否合法。比如你输入保证是个连通图,validator 就得检验这个图是不是连通的。还是请用 testlib 写,教程还是之前提到的那个教程。写好后命名为 val.cpp 就行。
|
||||
|
||||
另外注意时间限制**不能为小数**。
|
||||
|
||||
#### 提交答案题的配置
|
||||
范例:
|
||||
|
||||
<pre>
|
||||
use_builtin_judger on
|
||||
submit_answer on
|
||||
n_tests 10
|
||||
input_pre www
|
||||
input_suf in
|
||||
output_pre www
|
||||
output_suf out
|
||||
|
||||
</pre>
|
||||
|
||||
So easy!
|
||||
|
||||
不过还没完……你要写个 checker。当然如果你是道 [最小割计数](http://uoj.ac/problem/85) 就不用写了。。。老样子写个 use\_builtin\_checker ncmp 就行。
|
||||
|
||||
提答题经常会面临给部分分,请使用 quitp 函数。由于一些历史原因(求不吐槽 QAQ),假设测试点满分为 a,则
|
||||
```cpp
|
||||
quitp(x, "haha");
|
||||
```
|
||||
将会给 floor(a * round(100x) / 100) 分。是不是很复杂……其实很简单,当你这个测试点想给 p 分的时候,只要
|
||||
```cpp
|
||||
quitp(ceil(100.0 * p / a) / 100, "haha");
|
||||
```
|
||||
就行了。(好麻烦啊)
|
||||
|
||||
假设你已经写好了,赞!
|
||||
|
||||
但是下发的本地 checker 怎么办?
|
||||
|
||||
首先你得会写本地 checker。如果只是传参进来一个测试点编号,检查测试点是否正确,那么只要善用 registerTestlib 就行了。如果你想让 checker 与用户交互,请使用 registerInteraction。特别地,如果你想让 checker 通过终端与用户进行交互,请使用如下代码(好丑啊)
|
||||
```cpp
|
||||
char *targv[] = {argv[0], "inf_file.out", (char*)"stdout"};
|
||||
#ifdef __EMSCRIPTEN__
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
#endif
|
||||
registerInteraction(3, targv);
|
||||
```
|
||||
|
||||
那么怎么下发文件呢?你可以在文件夹 1/ 下新建一个文件夹 download,里面放的所有东西都会打包发给选手啦~
|
||||
|
||||
#### 交叉编译
|
||||
|
||||
由于你的本地 checker 可能需要发布到各个平台,所以你也许需要交叉编译。交叉编译的意思是在一个平台上编译出其他平台上的可执行文件。好!下面是 Ubuntu 上交叉编译小教程时间。
|
||||
|
||||
首先不管三七二十一装一些库:(请注意 libc 和 libc++ 可能有更新的版本)
|
||||
```sh
|
||||
sudo apt-get install libc6-dev-i386
|
||||
sudo apt-get install lib32stdc++6 lib32stdc++-4.8-dev
|
||||
sudo apt-get install mingw32
|
||||
```
|
||||
|
||||
然后就能编译啦:
|
||||
```sh
|
||||
g++ localchk.cpp -o checker_linux64 -O2
|
||||
g++ localchk.cpp -m32 -o checker_linux32 -O2
|
||||
i586-mingw32msvc-g++ localchk.cpp -o checker_win32.exe -O2
|
||||
```
|
||||
|
||||
问:那其他系统怎么办?有个叫 emscripten 的东西,可以把 C++ 代码编译成 javascript。请务必使用 UOJ 上的 testlib,而不是 CF 上的那个版本。由于 testlib 有些黑科技,UOJ 上的 testlib 对 emscripten 才是兼容的。
|
||||
|
||||
[emscripten 下载地址](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html) (建议翻墙提升下载速度)
|
||||
|
||||
编译就用
|
||||
```sh
|
||||
em++ localchk.cpp -o checker.js -O2
|
||||
```
|
||||
|
||||
#### 交互题的配置
|
||||
嘛……其实并没有 UOJ 内部并没有显式地支持交互式(真正的程序与程序交互的版本还没发布出来),只是提供了 require、implementer 和 token 这两个东西。
|
||||
|
||||
除了前文所述的 download 这个文件夹,还有一个叫 require 的神奇文件夹。在测评时,这个文件夹里的所有文件均会被移动到与选手源程序同一目录下。在这个文件夹下,你可以放置交互库,供编译时使用。
|
||||
|
||||
再来说 implementer 的用途。如果你在 problem.conf 里设置 `with_implementer on` 的话,各个语言的编译命令将变为:
|
||||
|
||||
* C++: g++ code implementer.cpp code.cpp -lm -O2 -DONLINE_JUDGE
|
||||
* C: gcc code implementer.c code.c -lm -O2 -DONLINE_JUDGE
|
||||
* Pascal: fpc implementer.pas -o code -O2
|
||||
|
||||
好……不管那些奇怪的选手的话,一个交互题就搞好了……你需要写 implementer.cpp、implementer.c 和 implementer.pas。当然你不想兹瓷某个语言的话不写对应的交互库就好了。
|
||||
|
||||
如果是 C/C++,正确姿势是搞一个统一的头文件放置接口,让 implementer 和选手程序都 include 这个头文件。把主函数写在 implementer 里,连起来编译时程序就是从 implementer 开始执行的了。
|
||||
|
||||
如果是 Pascal,正确姿势是让选手写一个 pascal unit 上来,在 implementer.pas 里 uses 一下。除此之外也要搞另外一个 pascal unit,里面存交互库的各种接口,让 implementer 和选手程序都 uses 一下。
|
||||
|
||||
哦另外 require 文件夹的内容显然不会被下发……如果你想下发一个样例交互库,可以放到 download 文件夹里。
|
||||
|
||||
好下面来解释什么是 token……考虑一道典型的交互题,交互库跟选手函数交流得很愉快,最后给了个满分。此时交互库输出了 “AC!!!” 的字样,也可能输出了选手一共调用了几次接口。但是聪明的选手发现,只要自己手动输出一下 “AC!!!” 然后果断退出似乎就能骗过测评系统了娃哈哈。
|
||||
|
||||
这是因为选手函数和交互库已经融为一体成为了一个统一的程序,测评系统分辨出谁是谁。这时候,token 就出来拯救世界了。
|
||||
|
||||
在 problem.conf 里配置一个奇妙的 token,比如 `token wahaha233`,然后把这个 token 写进交互库的代码里(线上交互库,不是样例交互库)。在交互库准备输出任何东西之前,先输出一下这个 token。当测评系统要给选手判分时,首先判断文件的第一行是不是 token,如果不是,直接零分。这样就避免了奇怪的选手 hack 测评系统了。这里的 token 判定是在调用 checker 之前,测评系统会把 token 去掉之后的选手输出喂给 checker。(所以一道交互题的 token 要好好保护好)
|
||||
|
||||
另外另外,记得在 C/C++ 的交互库里给全局变量开 static,意为仅本文件可访问。
|
||||
|
||||
#### 意想不到的非传统题
|
||||
吼,假如你有一道题不属于上述任何一种题目类型 —— 恭喜你中奖啦!得自己写 judger 啦!
|
||||
|
||||
judger 的任务:给你选手提交的文件,把测评结果告诉我。
|
||||
|
||||
把 problem.conf 里的 `use_builtin_judger on` 这行去掉,搞一个 judger 放在题目目录下,就好了。
|
||||
|
||||
噢对怎么搞这个 judger 呢。。。全自动啦233。。。你需要写个 Makefile……比如
|
||||
```makefile
|
||||
export INCLUDE_PATH
|
||||
CXXFLAGS = -I$(INCLUDE_PATH) -O2
|
||||
|
||||
all: chk judger
|
||||
|
||||
% : %.cpp
|
||||
$(CXX) $(CXXFLAGS) $< -o $@
|
||||
```
|
||||
|
||||
什么?你问 judger 应该怎么写?
|
||||
|
||||
很抱歉的是这部分我封装得并不是很方便……请 include 一下 uoj_judger.h 来辅助你写 judger……参照 QUINE 这个样题……
|
||||
|
||||
说实在的 uoj_judger.h 写得太丑了(不过反正我会用2333)。期待神犇再来造一遍轮子,或者用 python 码个框架(需要解决 python 启动就耗时 40ms 的坑爹问题)
|
||||
|
||||
哦对,要使用自己的 judger 的话就只有超级管理员能 “与 SVN 仓库同步” 了。另外无论你的 judger 如何折腾,测评也是有 10 分钟 的时间限制的。
|
||||
|
||||
## 样题
|
||||
在 problem 文件夹下有我们给出的几道样题。
|
||||
|
||||
1. 一个典型的传统题
|
||||
* \#1. A + B Problem。
|
||||
2. 一个典型的非传统题:
|
||||
* \#8. Quine。
|
||||
3. 一个典型的交互题:
|
||||
* \#52. 【UR \#4】元旦激光炮。
|
||||
4. 一个典型的 ACM 赛制题(错一个就 0 分):
|
||||
* \#79. 一般图最大匹配。
|
||||
5. 一个典型的提交答案题:
|
||||
* \#116. 【ZJOI2015】黑客技术。
|
||||
6. 一个典型的 subtask 制的题:
|
||||
* \#225. 【UR #15】奥林匹克五子棋。
|
11
docs/mkdocs.yml
Normal file
11
docs/mkdocs.yml
Normal file
@ -0,0 +1,11 @@
|
||||
site_name: UOJ Documentation
|
||||
pages:
|
||||
- 主页: 'index.md'
|
||||
- 安装: 'install.md'
|
||||
- 维护: 'maintain.md'
|
||||
- 题目: 'problem.md'
|
||||
- 比赛: 'contest.md'
|
||||
- 开发: 'dev.md'
|
||||
- 关于: 'about.md'
|
||||
theme: cerulean
|
||||
extra_css: [extra.css]
|
3
install
Normal file
3
install
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
docker build -f docker/Dockerfile --no-cache=true .
|
3
install_judge_client
Normal file
3
install_judge_client
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
docker build -f docker/judge_client/Dockerfile .
|
5
judge_client/1/Makefile
Normal file
5
judge_client/1/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
all:
|
||||
cd uoj_judger && $(MAKE)
|
||||
|
||||
clean:
|
||||
cd uoj_judger && make clean
|
428
judge_client/1/judge_client
Normal file
428
judge_client/1/judge_client
Normal file
@ -0,0 +1,428 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import pipes
|
||||
import socket
|
||||
from threading import Thread
|
||||
import fcntl
|
||||
import shutil
|
||||
|
||||
import traceback
|
||||
import time
|
||||
from contextlib import closing
|
||||
|
||||
import requests
|
||||
|
||||
import Queue as queue
|
||||
from Queue import Queue, Empty
|
||||
|
||||
taskQ = Queue()
|
||||
submission = None
|
||||
|
||||
# path related function
|
||||
|
||||
def uoj_url(uri):
|
||||
return ("%s://%s%s" % (jconf['uoj_protocol'], jconf['uoj_host'], uri)).rstrip('/')
|
||||
def uoj_judger_path(path = ''):
|
||||
return "uoj_judger" + path
|
||||
|
||||
# os related funciton
|
||||
def clean_up_folder(path):
|
||||
for f in os.listdir(path):
|
||||
f_path = os.path.join(path, f)
|
||||
if os.path.isfile(f_path):
|
||||
os.unlink(f_path)
|
||||
else:
|
||||
shutil.rmtree(f_path)
|
||||
|
||||
def execute(cmd):
|
||||
if os.system(cmd):
|
||||
raise Exception('failed to execute: %s' % cmd)
|
||||
|
||||
def freopen(f, g):
|
||||
os.dup2(g.fileno(), f.fileno())
|
||||
g.close()
|
||||
|
||||
# init
|
||||
def init():
|
||||
global jconf
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
with open('.conf.json', 'r') as fp:
|
||||
jconf = json.load(fp)
|
||||
assert 'uoj_protocol' in jconf
|
||||
assert 'uoj_host' in jconf
|
||||
assert 'judger_name' in jconf
|
||||
assert 'judger_password' in jconf
|
||||
assert 'socket_port' in jconf
|
||||
assert 'socket_password' in jconf
|
||||
assert 'svn_username' in jconf
|
||||
assert 'svn_password' in jconf
|
||||
|
||||
# socket server
|
||||
def socket_server_loop():
|
||||
SOCK_CLOEXEC = 524288
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM | SOCK_CLOEXEC)) as s:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', jconf['socket_port']))
|
||||
s.listen(5)
|
||||
|
||||
while True:
|
||||
try:
|
||||
conn, addr = s.accept()
|
||||
with closing(conn) as conn:
|
||||
data = conn.recv(1024)
|
||||
assert data != None
|
||||
task = json.loads(data)
|
||||
assert task['password'] == jconf['socket_password']
|
||||
assert 'cmd' in task
|
||||
|
||||
taskQ.put(task)
|
||||
|
||||
if task['cmd'] == 'stop':
|
||||
print 'the judge client is closing...'
|
||||
taskQ.join()
|
||||
conn.sendall('ok')
|
||||
return 'stop'
|
||||
except Exception:
|
||||
print >>sys.stderr, '['+time.asctime()+']', 'connection rejected'
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print >>sys.stderr, '['+time.asctime()+']', 'a new task accomplished'
|
||||
|
||||
def start_judger_server():
|
||||
global socket_server_thread
|
||||
|
||||
print_judge_client_status()
|
||||
print >>sys.stderr, 'hello!'
|
||||
|
||||
socket_server_thread = Thread(target = socket_server_loop)
|
||||
socket_server_thread.setDaemon(True)
|
||||
socket_server_thread.start()
|
||||
|
||||
judger_loop()
|
||||
|
||||
# report thread
|
||||
def report_loop():
|
||||
if 'is_hack' in submission:
|
||||
return
|
||||
while not submission_judged:
|
||||
try:
|
||||
with open(uoj_judger_path('/result/cur_status.txt'), 'rb') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_SH)
|
||||
try:
|
||||
status = f.read(100)
|
||||
except Exception:
|
||||
status = None
|
||||
finally:
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
if status != None:
|
||||
data = {}
|
||||
data['update-status'] = True
|
||||
data['id'] = submission['id']
|
||||
if 'is_custom_test' in submission:
|
||||
data['is_custom_test'] = True
|
||||
data['status'] = status
|
||||
uoj_interact(data)
|
||||
time.sleep(0.2)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# handle task in main thread
|
||||
def handle_task():
|
||||
need_restart = False
|
||||
try:
|
||||
while True:
|
||||
task = taskQ.get_nowait()
|
||||
|
||||
if task['cmd'] == 'update':
|
||||
execute('make clean && svn update --username %s --password %s && make' % (jconf['svn_username'], jconf['svn_password']))
|
||||
if jconf['judger_name'] == 'main_judger':
|
||||
uoj_sync_judge_client()
|
||||
need_restart = True
|
||||
elif task['cmd'] == 'stop':
|
||||
taskQ.task_done()
|
||||
socket_server_thread.join()
|
||||
|
||||
print_judge_client_status()
|
||||
print sys.stderr, "goodbye!"
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
taskQ.task_done()
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
if need_restart:
|
||||
os.execl('./judge_client', './judge_client')
|
||||
|
||||
def print_judge_client_status():
|
||||
print >>sys.stderr, '[' + time.asctime() + ']',
|
||||
if submission != None:
|
||||
print >>sys.stderr, submission,
|
||||
print >>sys.stderr
|
||||
|
||||
# interact with uoj_judger
|
||||
def get_judger_result():
|
||||
res = {}
|
||||
with open(uoj_judger_path('/result/result.txt'), 'rb') as fres:
|
||||
res['score'] = 0
|
||||
res['time'] = 0
|
||||
res['memory'] = 0
|
||||
while True:
|
||||
line = fres.readline()
|
||||
if line == '':
|
||||
break
|
||||
line = line.strip()
|
||||
if line == 'details':
|
||||
res['details'] = fres.read()
|
||||
break
|
||||
|
||||
sp = line.split()
|
||||
assert len(sp) >= 1
|
||||
if sp[0] == 'error':
|
||||
res['error'] = line[len('error') + 1:]
|
||||
else:
|
||||
assert len(sp) == 2
|
||||
res[sp[0]] = sp[1]
|
||||
res['score'] = int(res['score'])
|
||||
res['time'] = int(res['time'])
|
||||
res['memory'] = int(res['memory'])
|
||||
res['status'] = 'Judged'
|
||||
return res
|
||||
|
||||
def update_problem_data(problem_id, problem_mtime):
|
||||
try:
|
||||
if jconf['judger_name'] == 'main_judger':
|
||||
return
|
||||
copy_name = uoj_judger_path('/data/%d' % problem_id)
|
||||
copy_zip_name = uoj_judger_path('/data/%d.zip' % problem_id)
|
||||
if os.path.isdir(copy_name):
|
||||
if os.path.getmtime(copy_name) >= problem_mtime:
|
||||
return
|
||||
else:
|
||||
execute('chmod 700 %s -R && rm -rf %s' % (pipes.quote(copy_name), pipes.quote(copy_name)))
|
||||
uoj_download('/problem/%d' % problem_id, copy_zip_name)
|
||||
execute('cd %s && unzip -q %d.zip && rm %d.zip && chmod -w %d -R' % (uoj_judger_path('/data'), problem_id, problem_id, problem_id))
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
raise Exception('failed to update problem data of #%d' % problem_id)
|
||||
else:
|
||||
print_judge_client_status()
|
||||
print >>sys.stderr, 'updated problem data of #%d successfully' % problem_id
|
||||
|
||||
def judge():
|
||||
global report_thread
|
||||
global submission_judged
|
||||
|
||||
clean_up_folder(uoj_judger_path('/work'))
|
||||
clean_up_folder(uoj_judger_path('/result'))
|
||||
update_problem_data(submission['problem_id'], submission['problem_mtime'])
|
||||
|
||||
with open(uoj_judger_path('/work/submission.conf'), 'wb') as fconf:
|
||||
uoj_download(submission['content']['file_name'], uoj_judger_path('/work/all.zip'))
|
||||
execute("cd %s && unzip -q all.zip && rm all.zip" % pipes.quote(uoj_judger_path('/work')))
|
||||
for k, v in submission['content']['config']:
|
||||
print >>fconf, k, v
|
||||
|
||||
if 'is_hack' in submission:
|
||||
if submission['hack']['input_type'] == 'USE_FORMATTER':
|
||||
uoj_download(submission['hack']['input'], uoj_judger_path('/work/hack_input_raw.txt'))
|
||||
execute('%s <%s >%s' % (
|
||||
pipes.quote(uoj_judger_path('/run/formatter')),
|
||||
pipes.quote(uoj_judger_path('/work/hack_input_raw.txt')),
|
||||
pipes.quote(uoj_judger_path('/work/hack_input.txt'))))
|
||||
else:
|
||||
uoj_download(submission['hack']['input'], uoj_judger_path('/work/hack_input.txt'))
|
||||
print >>fconf, 'test_new_hack_only on'
|
||||
elif 'is_custom_test' in submission:
|
||||
print >>fconf, 'custom_test on'
|
||||
|
||||
report_thread = Thread(target = report_loop)
|
||||
report_thread.setDaemon(True)
|
||||
|
||||
submission_judged = False
|
||||
report_thread.start()
|
||||
execute(pipes.quote(uoj_judger_path('/main_judger')))
|
||||
submission_judged = True
|
||||
report_thread.join()
|
||||
|
||||
return get_judger_result()
|
||||
|
||||
# interact with uoj web server
|
||||
def uoj_interact(data, files = {}):
|
||||
data = data.copy()
|
||||
data.update({
|
||||
'judger_name': jconf['judger_name'],
|
||||
'password': jconf['judger_password']
|
||||
})
|
||||
return requests.post(uoj_url('/judge/submit'), data=data, files=files).text
|
||||
def uoj_download(uri, filename):
|
||||
data = {
|
||||
'judger_name': jconf['judger_name'],
|
||||
'password': jconf['judger_password']
|
||||
}
|
||||
with open(filename, 'wb') as f:
|
||||
r = requests.post(uoj_url('/judge/download' + uri), data=data, stream=True)
|
||||
for chunk in r.iter_content(chunk_size=65536):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
def uoj_sync_judge_client():
|
||||
data = {
|
||||
'judger_name': jconf['judger_name'],
|
||||
'password': jconf['judger_password']
|
||||
}
|
||||
ret = requests.post(uoj_url('/judge/sync-judge-client'), data=data).text
|
||||
if ret != "ok":
|
||||
raise Exception('failed to sync judge clients: %s' % ret)
|
||||
|
||||
def send_and_fetch(result = None, fetch_new = True):
|
||||
global submission
|
||||
|
||||
"""send judgement result, and fetch new submission to judge"""
|
||||
|
||||
data = {}
|
||||
files = {}
|
||||
|
||||
if not fetch_new:
|
||||
data['fetch_new'] = False
|
||||
|
||||
if result != None:
|
||||
data['submit'] = True
|
||||
if 'is_hack' in submission:
|
||||
data['is_hack'] = True
|
||||
data['id'] = submission['hack']['id']
|
||||
if result != False and result['score']:
|
||||
try:
|
||||
print >>sys.stderr, "succ hack!"
|
||||
files = {
|
||||
('hack_input', open('uoj_judger/work/hack_input.txt', 'rb')),
|
||||
('std_output', open('uoj_judger/work/std_output.txt', 'rb'))
|
||||
}
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
result = False
|
||||
elif 'is_custom_test' in submission:
|
||||
data['is_custom_test'] = True
|
||||
data['id'] = submission['id']
|
||||
else:
|
||||
data['id'] = submission['id']
|
||||
|
||||
if result == False:
|
||||
result = {
|
||||
'score': 0,
|
||||
'error': 'Judgement Failed',
|
||||
'details': 'Unknown Error'
|
||||
}
|
||||
result['status'] = 'Judged'
|
||||
data['result'] = json.dumps(result, ensure_ascii=False)
|
||||
|
||||
while True:
|
||||
try:
|
||||
ret = uoj_interact(data, files)
|
||||
print ret
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
else:
|
||||
break
|
||||
time.sleep(2)
|
||||
|
||||
try:
|
||||
submission = json.loads(ret)
|
||||
except Exception as e:
|
||||
submission = None
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# judge client
|
||||
def judger_loop():
|
||||
ok = False
|
||||
while True:
|
||||
fetch_new = True
|
||||
|
||||
if ok and not (taskQ.empty() and socket_server_thread.isAlive()):
|
||||
fetch_new = False
|
||||
|
||||
if not ok:
|
||||
while True:
|
||||
if not taskQ.empty():
|
||||
handle_task()
|
||||
if not socket_server_thread.isAlive():
|
||||
raise Exception('socket server exited unexpectedly')
|
||||
|
||||
if send_and_fetch():
|
||||
break
|
||||
|
||||
print '['+time.asctime()+']', 'Nothing to judge...'
|
||||
time.sleep(2)
|
||||
|
||||
ok = True
|
||||
print_judge_client_status()
|
||||
print >>sys.stderr, 'judging'
|
||||
|
||||
try:
|
||||
res = judge()
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
res = False
|
||||
|
||||
ok = send_and_fetch(result=res,fetch_new=fetch_new)
|
||||
|
||||
# main function
|
||||
def main():
|
||||
init()
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
start_judger_server()
|
||||
if len(sys.argv) == 2:
|
||||
if sys.argv[1] == 'start':
|
||||
pid = os.fork()
|
||||
if pid == -1:
|
||||
raise Exception('fork failed')
|
||||
elif pid > 0:
|
||||
return
|
||||
else:
|
||||
freopen(sys.stdout, open(os.devnull, 'wb'))
|
||||
freopen(sys.stderr, open('log/judge.log', 'ab', buffering=0))
|
||||
start_judger_server()
|
||||
elif sys.argv[1] == 'update':
|
||||
try:
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
s.connect(('127.0.0.1', jconf['socket_port']))
|
||||
s.sendall(json.dumps({
|
||||
'password': jconf['socket_password'],
|
||||
'cmd': 'update'
|
||||
}))
|
||||
return
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
raise Exception('update failed')
|
||||
elif sys.argv[1] == 'stop':
|
||||
try:
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
s.connect(('127.0.0.1', jconf['socket_port']))
|
||||
s.sendall(json.dumps({
|
||||
'password': jconf['socket_password'],
|
||||
'cmd': 'stop'
|
||||
}))
|
||||
if s.recv(10) != 'ok':
|
||||
raise Exception('stop failed')
|
||||
return
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
raise Exception('stop failed')
|
||||
raise Exception('invalid argument')
|
||||
|
||||
try:
|
||||
main()
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
0
judge_client/1/log/judge.log
Normal file
0
judge_client/1/log/judge.log
Normal file
45
judge_client/1/uoj_judger/Makefile
Normal file
45
judge_client/1/uoj_judger/Makefile
Normal file
@ -0,0 +1,45 @@
|
||||
INCLUDE_PATH = include
|
||||
CXXFLAGS = -I./include -O2
|
||||
|
||||
EXE_CHECKER = \
|
||||
builtin/checker/bcmp \
|
||||
builtin/checker/acmp \
|
||||
builtin/checker/caseicmp \
|
||||
builtin/checker/casencmp \
|
||||
builtin/checker/casewcmp \
|
||||
builtin/checker/dcmp \
|
||||
builtin/checker/fcmp \
|
||||
builtin/checker/hcmp \
|
||||
builtin/checker/icmp \
|
||||
builtin/checker/lcmp \
|
||||
builtin/checker/ncmp \
|
||||
builtin/checker/rcmp \
|
||||
builtin/checker/rcmp4 \
|
||||
builtin/checker/rcmp6 \
|
||||
builtin/checker/rcmp9 \
|
||||
builtin/checker/rncmp \
|
||||
builtin/checker/uncmp \
|
||||
builtin/checker/wcmp \
|
||||
builtin/checker/yesno
|
||||
|
||||
EXE = main_judger \
|
||||
run/formatter \
|
||||
run/run_program \
|
||||
builtin/judger/judger \
|
||||
$(EXE_CHECKER)
|
||||
|
||||
all: $(EXE)
|
||||
|
||||
% : %.cpp
|
||||
$(CXX) $(CXXFLAGS) $< -o $@
|
||||
|
||||
run/run_program: include/uoj_env.h run/run_program_conf.h
|
||||
run/formatter : include/testlib.h
|
||||
|
||||
builtin/judger/judger: include
|
||||
main_judger: include
|
||||
|
||||
$(EXE_CHECKER): include/testlib.h
|
||||
|
||||
clean:
|
||||
rm -f $(EXE)
|
19
judge_client/1/uoj_judger/builtin/checker/acmp.cpp
Normal file
19
judge_client/1/uoj_judger/builtin/checker/acmp.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
const double EPS = 1.5E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two doubles, maximal absolute error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
double ja = ans.readDouble();
|
||||
double pa = ouf.readDouble();
|
||||
|
||||
if (fabs(ja - pa) > EPS + 1E-15)
|
||||
quitf(_wa, "expected %.10lf, found %.10lf", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %.10lf", ja);
|
||||
}
|
55
judge_client/1/uoj_judger/builtin/checker/bcmp.cpp
Normal file
55
judge_client/1/uoj_judger/builtin/checker/bcmp.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include <cstdio>
|
||||
|
||||
inline const char *englishEnding(int x)
|
||||
{
|
||||
x %= 100;
|
||||
if (x / 10 == 1)
|
||||
return "th";
|
||||
if (x % 10 == 1)
|
||||
return "st";
|
||||
if (x % 10 == 2)
|
||||
return "nd";
|
||||
if (x % 10 == 3)
|
||||
return "rd";
|
||||
return "th";
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
if (argc != 4)
|
||||
return 1;
|
||||
FILE *fout = fopen(argv[2], "r");
|
||||
FILE *fans = fopen(argv[3], "r");
|
||||
|
||||
if (fout == NULL || fans == NULL)
|
||||
return 1;
|
||||
|
||||
int n = 0;
|
||||
while (true)
|
||||
{
|
||||
n++;
|
||||
int c, d;
|
||||
c = fgetc(fout);
|
||||
d = fgetc(fans);
|
||||
if (c == EOF && d == EOF)
|
||||
break;
|
||||
if (c != d)
|
||||
{
|
||||
if (c == EOF)
|
||||
fprintf(stderr, "wrong answer %d%s byte differ - expected EOF found '%c'\n", n, englishEnding(n), (char)d);
|
||||
else if (d == EOF)
|
||||
fprintf(stderr, "wrong answer %d%s byte differ - expected '%c' found EOF\n", n, englishEnding(n), (char)c);
|
||||
else
|
||||
fprintf(stderr, "wrong answer %d%s byte differ - expected '%c' found '%c'\n", n, englishEnding(n), (char)c, (char)d);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
fprintf(stderr, "ok empty file\n");
|
||||
else if (n == 1)
|
||||
fprintf(stderr, "ok single byte\n");
|
||||
else
|
||||
fprintf(stderr, "ok %d byte\n", n);
|
||||
return 0;
|
||||
}
|
95
judge_client/1/uoj_judger/builtin/checker/caseicmp.cpp
Normal file
95
judge_client/1/uoj_judger/builtin/checker/caseicmp.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Checker to compare output and answer in the form:
|
||||
*
|
||||
* Case 1: <number>
|
||||
* Case 2: <number>
|
||||
* ...
|
||||
* Case n: <number>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define forn(i, n) for (int i = 0; i < int(n); i++)
|
||||
|
||||
vector<long long> readStream(InStream& in, TResult pe)
|
||||
{
|
||||
vector<long long> result;
|
||||
|
||||
for (int testCase = 1; !in.seekEof(); testCase++)
|
||||
{
|
||||
string caseStr = in.readToken();
|
||||
if (caseStr != "Case")
|
||||
quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
|
||||
|
||||
string numExpStr;
|
||||
stringstream ss;
|
||||
ss << testCase;
|
||||
ss >> numExpStr;
|
||||
numExpStr += ":";
|
||||
string numStr = in.readToken();
|
||||
if (numExpStr != numStr)
|
||||
quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
|
||||
|
||||
result.push_back(in.readLong());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setName("Single int64 checker with testcase-support");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
vector<long long> ja = readStream(ans, _fail);
|
||||
vector<long long> pa = readStream(ouf, _pe);
|
||||
|
||||
forn(i, min(ja.size(), pa.size()))
|
||||
if (ja[i] != pa[i])
|
||||
quitf(_wa, "Expected %s found %s [test case %d]", vtos(ja[i]).c_str(), vtos(pa[i]).c_str(), i + 1);
|
||||
|
||||
if (ja.size() != pa.size())
|
||||
quitf(_pe, "Expected %d test case(s) but found %d", (int)ja.size(), (int)pa.size());
|
||||
|
||||
string message = format("%d case(s):", (int)ja.size());
|
||||
if (ja.size() <= 5)
|
||||
{
|
||||
forn(i, ja.size())
|
||||
message += " " + vtos(ja[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
forn(i, 3)
|
||||
message += " " + vtos(ja[i]);
|
||||
message += " ...";
|
||||
forn(i, 2)
|
||||
message += " " + vtos(ja[ja.size() - 2 + i]);
|
||||
}
|
||||
|
||||
quitf(_ok, "%s", message.c_str());
|
||||
}
|
124
judge_client/1/uoj_judger/builtin/checker/casencmp.cpp
Normal file
124
judge_client/1/uoj_judger/builtin/checker/casencmp.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Checker to compare output and answer in the form:
|
||||
*
|
||||
* Case 1: <number> <number> <number> ... <number>
|
||||
* Case 2: <number> <number> <number> ... <number>
|
||||
* ...
|
||||
* Case n: <number> <number> <number> ... <number>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define forn(i, n) for (int i = 0; i < int(n); i++)
|
||||
|
||||
string token;
|
||||
|
||||
vector<long long> readStreamCase(InStream& in, TResult pe, int testCase, bool& prereadCase)
|
||||
{
|
||||
if (!prereadCase)
|
||||
{
|
||||
string caseStr = in.readToken();
|
||||
if (caseStr != "Case")
|
||||
quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
|
||||
}
|
||||
|
||||
string numExpStr;
|
||||
stringstream ss;
|
||||
ss << testCase;
|
||||
ss >> numExpStr;
|
||||
numExpStr += ":";
|
||||
string numStr = in.readToken();
|
||||
if (numExpStr != numStr)
|
||||
quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
|
||||
|
||||
vector<long long> result;
|
||||
while (!in.seekEof())
|
||||
{
|
||||
in.readTokenTo(token);
|
||||
if (token == "Case")
|
||||
{
|
||||
prereadCase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
result.push_back(stringToLongLong(in, token.c_str()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string longLongsToString(const vector<long long>& a)
|
||||
{
|
||||
if (a.empty())
|
||||
return "\"\" [size=0]";
|
||||
|
||||
string elems;
|
||||
if (a.size() <= 5)
|
||||
{
|
||||
forn(i, a.size())
|
||||
elems += vtos(a[i]) + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
forn(i, 3)
|
||||
elems += vtos(a[i]) + " ";
|
||||
elems += "... ";
|
||||
forn(i, 2)
|
||||
elems += vtos(a[a.size() - 2 + i]) + " ";
|
||||
}
|
||||
|
||||
return format("\"%s\" [size=%d]", trim(elems).c_str(), (int)a.size());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setName("Many int64s checker with testcase-support");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int testCase = 0;
|
||||
|
||||
bool ansPrereadCase = false;
|
||||
bool oufPrereadCase = false;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
testCase++;
|
||||
|
||||
vector<long long> ja = readStreamCase(ans, _fail, testCase, ansPrereadCase);
|
||||
vector<long long> pa = readStreamCase(ouf, _pe, testCase, oufPrereadCase);
|
||||
|
||||
if (ja != pa)
|
||||
{
|
||||
string js = longLongsToString(ja);
|
||||
string ps = longLongsToString(pa);
|
||||
quitf(_wa, "Sequences differ: jury has %s, but participant has %s [test case %d]", js.c_str(), ps.c_str(), testCase);
|
||||
}
|
||||
}
|
||||
|
||||
quitf(_ok, "%d test cases(s)", testCase);
|
||||
}
|
113
judge_client/1/uoj_judger/builtin/checker/casewcmp.cpp
Normal file
113
judge_client/1/uoj_judger/builtin/checker/casewcmp.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Checker to compare output and answer in the form:
|
||||
*
|
||||
* Case 1: <token> <token> ... <token>
|
||||
* Case 2: <token> <token> ... <token>
|
||||
* ...
|
||||
* Case n: <token> <token> ... <token>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define forn(i, n) for (int i = 0; i < int(n); i++)
|
||||
|
||||
string token;
|
||||
|
||||
vector<string> readStreamCase(InStream& in, TResult pe, int testCase, bool& prereadCase)
|
||||
{
|
||||
if (!prereadCase)
|
||||
{
|
||||
string caseStr = in.readToken();
|
||||
if (caseStr != "Case")
|
||||
quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
|
||||
}
|
||||
|
||||
string numExpStr;
|
||||
stringstream ss;
|
||||
ss << testCase;
|
||||
ss >> numExpStr;
|
||||
numExpStr += ":";
|
||||
string numStr = in.readToken();
|
||||
if (numExpStr != numStr)
|
||||
quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
|
||||
|
||||
vector<string> result;
|
||||
while (!in.seekEof())
|
||||
{
|
||||
in.readTokenTo(token);
|
||||
if (token == "Case")
|
||||
{
|
||||
prereadCase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
result.push_back(token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string stringsToString(const vector<string>& a)
|
||||
{
|
||||
if (a.empty())
|
||||
return "\"\" [size=0]";
|
||||
|
||||
string elems;
|
||||
forn(i, a.size())
|
||||
elems += a[i] + " ";
|
||||
|
||||
return format("\"%s\" [size=%d]", compress(trim(elems)).c_str(), (int)a.size());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setName("Tokens checker with testcase-support");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int testCase = 0;
|
||||
|
||||
bool ansPrereadCase = false;
|
||||
bool oufPrereadCase = false;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
testCase++;
|
||||
|
||||
vector<string> ja = readStreamCase(ans, _fail, testCase, ansPrereadCase);
|
||||
vector<string> pa = readStreamCase(ouf, _pe, testCase, oufPrereadCase);
|
||||
|
||||
if (ja != pa)
|
||||
{
|
||||
string js = stringsToString(ja);
|
||||
string ps = stringsToString(pa);
|
||||
quitf(_wa, "Sequences differ: jury has %s, but participant has %s [test case %d]", js.c_str(), ps.c_str(), testCase);
|
||||
}
|
||||
}
|
||||
|
||||
quitf(_ok, "%d test cases(s)", testCase);
|
||||
}
|
19
judge_client/1/uoj_judger/builtin/checker/dcmp.cpp
Normal file
19
judge_client/1/uoj_judger/builtin/checker/dcmp.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
const double EPS = 1E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two doubles, maximal absolute or relative error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
double ja = ans.readDouble();
|
||||
double pa = ouf.readDouble();
|
||||
|
||||
if (!doubleCompare(ja, pa, EPS))
|
||||
quitf(_wa, "expected %.10lf, found %.10lf", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %.10lf", ja);
|
||||
}
|
36
judge_client/1/uoj_judger/builtin/checker/fcmp.cpp
Normal file
36
judge_client/1/uoj_judger/builtin/checker/fcmp.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "testlib.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare files as sequence of lines");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
std::string strAnswer;
|
||||
|
||||
int n = 0;
|
||||
while (!ans.eof())
|
||||
{
|
||||
std::string j = ans.readString();
|
||||
|
||||
if (j == "" && ans.eof())
|
||||
break;
|
||||
|
||||
strAnswer = j;
|
||||
std::string p = ouf.readString();
|
||||
|
||||
n++;
|
||||
|
||||
if (j != p)
|
||||
quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "single line: '%s'", compress(strAnswer).c_str());
|
||||
|
||||
quitf(_ok, "%d lines", n);
|
||||
}
|
35
judge_client/1/uoj_judger/builtin/checker/hcmp.cpp
Normal file
35
judge_client/1/uoj_judger/builtin/checker/hcmp.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "testlib.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
pattern pnum("0|-?[1-9][0-9]*");
|
||||
|
||||
bool isNumeric(const string& p)
|
||||
{
|
||||
return pnum.matches(p);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two signed huge integers");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
string ja = ans.readWord();
|
||||
string pa = ouf.readWord();
|
||||
|
||||
if (!isNumeric(ja))
|
||||
quitf(_fail, "%s is not a valid integer", compress(ja).c_str());
|
||||
|
||||
if (!ans.seekEof())
|
||||
quitf(_fail, "expected exactly one token in the answer file");
|
||||
|
||||
if (!isNumeric(pa))
|
||||
quitf(_pe, "%s is not a valid integer", compress(pa).c_str());
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "expected '%s', found '%s'", compress(ja).c_str(), compress(pa).c_str());
|
||||
|
||||
quitf(_ok, "answer is '%s'", compress(ja).c_str());
|
||||
}
|
16
judge_client/1/uoj_judger/builtin/checker/icmp.cpp
Normal file
16
judge_client/1/uoj_judger/builtin/checker/icmp.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two signed int%d's", (int)(8 * sizeof(int)));
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int ja = ans.readInt();
|
||||
int pa = ouf.readInt();
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "expected %d, found %d", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %d", ja);
|
||||
}
|
54
judge_client/1/uoj_judger/builtin/checker/lcmp.cpp
Normal file
54
judge_client/1/uoj_judger/builtin/checker/lcmp.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "testlib.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool compareWords(string a, string b)
|
||||
{
|
||||
vector<string> va, vb;
|
||||
stringstream sa;
|
||||
|
||||
sa << a;
|
||||
string cur;
|
||||
while (sa >> cur)
|
||||
va.push_back(cur);
|
||||
|
||||
stringstream sb;
|
||||
sb << b;
|
||||
while (sb >> cur)
|
||||
vb.push_back(cur);
|
||||
|
||||
return (va == vb);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare files as sequence of tokens in lines");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
std::string strAnswer;
|
||||
|
||||
int n = 0;
|
||||
while (!ans.eof())
|
||||
{
|
||||
std::string j = ans.readString();
|
||||
|
||||
if (j == "" && ans.eof())
|
||||
break;
|
||||
|
||||
std::string p = ouf.readString();
|
||||
strAnswer = p;
|
||||
|
||||
n++;
|
||||
|
||||
if (!compareWords(j, p))
|
||||
quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "single line: '%s'", compress(strAnswer).c_str());
|
||||
|
||||
quitf(_ok, "%d lines", n);
|
||||
}
|
57
judge_client/1/uoj_judger/builtin/checker/ncmp.cpp
Normal file
57
judge_client/1/uoj_judger/builtin/checker/ncmp.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "testlib.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare ordered sequences of signed int%d numbers", (int)(8 * sizeof(long long)));
|
||||
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
string firstElems;
|
||||
|
||||
while (!ans.seekEof() && !ouf.seekEof())
|
||||
{
|
||||
n++;
|
||||
long long j = ans.readLong();
|
||||
long long p = ouf.readLong();
|
||||
if (j != p)
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), vtos(j).c_str(), vtos(p).c_str());
|
||||
else
|
||||
if (n <= 5)
|
||||
{
|
||||
if (firstElems.length() > 0)
|
||||
firstElems += " ";
|
||||
firstElems += vtos(j);
|
||||
}
|
||||
}
|
||||
|
||||
int extraInAnsCount = 0;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
ans.readLong();
|
||||
extraInAnsCount++;
|
||||
}
|
||||
|
||||
int extraInOufCount = 0;
|
||||
|
||||
while (!ouf.seekEof())
|
||||
{
|
||||
ouf.readLong();
|
||||
extraInOufCount++;
|
||||
}
|
||||
|
||||
if (extraInAnsCount > 0)
|
||||
quitf(_wa, "Answer contains longer sequence [length = %d], but output contains %d elements", n + extraInAnsCount, n);
|
||||
|
||||
if (extraInOufCount > 0)
|
||||
quitf(_wa, "Output contains longer sequence [length = %d], but answer contains %d elements", n + extraInOufCount, n);
|
||||
|
||||
if (n <= 5)
|
||||
quitf(_ok, "%d number(s): \"%s\"", n, compress(firstElems).c_str());
|
||||
else
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
19
judge_client/1/uoj_judger/builtin/checker/rcmp.cpp
Normal file
19
judge_client/1/uoj_judger/builtin/checker/rcmp.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
const double EPS = 1.5E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two doubles, maximal absolute error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
double ja = ans.readDouble();
|
||||
double pa = ouf.readDouble();
|
||||
|
||||
if (fabs(ja - pa) > EPS + 1E-15)
|
||||
quitf(_wa, "expected %.10lf, found %.10lf", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %.10lf", ja);
|
||||
}
|
32
judge_client/1/uoj_judger/builtin/checker/rcmp4.cpp
Normal file
32
judge_client/1/uoj_judger/builtin/checker/rcmp4.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1E-4;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, max absolute or relative error = %.5lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
double j, p;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
j = ans.readDouble();
|
||||
p = ouf.readDouble();
|
||||
if (!doubleCompare(j, p, EPS))
|
||||
{
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.5lf', found: '%.5lf', error = '%.5lf'",
|
||||
n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "found '%.5lf', expected '%.5lf', error '%.5lf'", p, j, doubleDelta(j, p));
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
32
judge_client/1/uoj_judger/builtin/checker/rcmp6.cpp
Normal file
32
judge_client/1/uoj_judger/builtin/checker/rcmp6.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, max absolute or relative error = %.7lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
double j, p;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
j = ans.readDouble();
|
||||
p = ouf.readDouble();
|
||||
if (!doubleCompare(j, p, EPS))
|
||||
{
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.7lf', found: '%.7lf', error = '%.7lf'",
|
||||
n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "found '%.7lf', expected '%.7lf', error '%.7lf'", p, j, doubleDelta(j, p));
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
32
judge_client/1/uoj_judger/builtin/checker/rcmp9.cpp
Normal file
32
judge_client/1/uoj_judger/builtin/checker/rcmp9.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1E-9;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, max absolute or relative error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
double j, p;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
j = ans.readDouble();
|
||||
p = ouf.readDouble();
|
||||
if (!doubleCompare(j, p, EPS))
|
||||
{
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.7lf', found: '%.7lf', error = '%.7lf'",
|
||||
n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "found '%.9lf', expected '%.9lf', error '%.9lf'", p, j, doubleDelta(j, p));
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
24
judge_client/1/uoj_judger/builtin/checker/rncmp.cpp
Normal file
24
judge_client/1/uoj_judger/builtin/checker/rncmp.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1.5E-5;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, maximal absolute error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
double j = ans.readDouble();
|
||||
double p = ouf.readDouble();
|
||||
if (fabs(j - p) > EPS + 1E-15)
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.10lf', found: '%.10lf'", n, englishEnding(n).c_str(), j, p);
|
||||
}
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
52
judge_client/1/uoj_judger/builtin/checker/uncmp.cpp
Normal file
52
judge_client/1/uoj_judger/builtin/checker/uncmp.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "testlib.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare unordered sequences of signed int%d numbers", (int)(8 * sizeof(long long)));
|
||||
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
vector<long long> ja, pa;
|
||||
|
||||
while (!ans.seekEof())
|
||||
ja.push_back(ans.readLong());
|
||||
|
||||
while (!ouf.seekEof())
|
||||
pa.push_back(ouf.readLong());
|
||||
|
||||
if (ja.size() != pa.size())
|
||||
quitf(_wa, "Expected %d elements, but %d found", (int)ja.size(), (int)pa.size());
|
||||
|
||||
sort(ja.begin(), ja.end());
|
||||
sort(pa.begin(), pa.end());
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "Expected sequence and output are different (as unordered sequences) [size=%d]", (int)ja.size());
|
||||
|
||||
string message;
|
||||
|
||||
if (ja.size() != 1)
|
||||
if (ja.empty())
|
||||
message = "empty sequence";
|
||||
else
|
||||
message = vtos(ja.size()) + " numbers (in increasing order):";
|
||||
else
|
||||
message = vtos(ja.size()) + " number:";
|
||||
|
||||
if (ja.size() <= 5)
|
||||
for (int i = 0; i < min(int(ja.size()), 5); i++)
|
||||
message += " " + vtos(ja[i]);
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
message += " " + vtos(ja[i]);
|
||||
message += " ...";
|
||||
for (int i = 0; i < 2; i++)
|
||||
message += " " + vtos(ja[ja.size() - 2 + i]);
|
||||
}
|
||||
|
||||
quitf(_ok, "%s", message.c_str());
|
||||
}
|
38
judge_client/1/uoj_judger/builtin/checker/wcmp.cpp
Normal file
38
judge_client/1/uoj_judger/builtin/checker/wcmp.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "testlib.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare sequences of tokens");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
string j, p;
|
||||
|
||||
while (!ans.seekEof() && !ouf.seekEof())
|
||||
{
|
||||
n++;
|
||||
|
||||
ans.readWordTo(j);
|
||||
ouf.readWordTo(p);
|
||||
|
||||
if (j != p)
|
||||
quitf(_wa, "%d%s words differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
|
||||
}
|
||||
|
||||
if (ans.seekEof() && ouf.seekEof())
|
||||
{
|
||||
if (n == 1)
|
||||
quitf(_ok, "\"%s\"", compress(j).c_str());
|
||||
else
|
||||
quitf(_ok, "%d tokens", n);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ans.seekEof())
|
||||
quitf(_wa, "Participant output contains extra tokens");
|
||||
else
|
||||
quitf(_wa, "Unexpected EOF in the participants output");
|
||||
}
|
||||
}
|
27
judge_client/1/uoj_judger/builtin/checker/yesno.cpp
Normal file
27
judge_client/1/uoj_judger/builtin/checker/yesno.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "testlib.h"
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const string YES = "YES";
|
||||
const string NO = "NO";
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("YES or NO (case insensetive)");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
std::string ja = upperCase(ans.readWord());
|
||||
std::string pa = upperCase(ouf.readWord());
|
||||
|
||||
if (ja != YES && ja != NO)
|
||||
quitf(_fail, "%s or %s expected in answer, but %s found", YES.c_str(), NO.c_str(), compress(ja).c_str());
|
||||
|
||||
if (pa != YES && pa != NO)
|
||||
quitf(_pe, "%s or %s expected, but %s found", YES.c_str(), NO.c_str(), compress(pa).c_str());
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "expected %s, found %s", compress(ja).c_str(), compress(pa).c_str());
|
||||
|
||||
quitf(_ok, "answer is %s", ja.c_str());
|
||||
}
|
480
judge_client/1/uoj_judger/builtin/judger/judger.cpp
Normal file
480
judge_client/1/uoj_judger/builtin/judger/judger.cpp
Normal file
@ -0,0 +1,480 @@
|
||||
#include "uoj_judger.h"
|
||||
|
||||
/*
|
||||
void hack_test() {
|
||||
put_status("Judging Hack ...");
|
||||
in = work_path "/hack_input.txt";
|
||||
out = work_path "/pro_output.txt";
|
||||
ans = work_path "/std_output.txt";
|
||||
|
||||
run_result rn;
|
||||
if (maker) {
|
||||
if (compile("maker") == false) {
|
||||
compile_info_update();
|
||||
put_info(1, 0, -1, -1, "Hack Failed : Maker Compile Error", "", "", read_file(result_path "/compile_result.txt", ""));
|
||||
end_info(0);
|
||||
}
|
||||
rn = run_maker(mtL, mmL, moL, info);
|
||||
if (rn.type != 0) {
|
||||
put_info(1, 0, -1, -1, "Hack Failed : " + info, "", "", "");
|
||||
end_info(0);
|
||||
}
|
||||
}
|
||||
if (getValid(in, info));
|
||||
else put_info(1, 0, -1, -1, "Hack Failed : Illegal Input", read_file(in, "", 100), "", info), end_info(0);
|
||||
rn = run_program(stp, in, ans, tL, mL, oL, info);
|
||||
if (rn.type != 0) put_info(1, 0, -1, -1, "Hack Failed : std " + info, read_file(in, "", 100), "", ""), end_info(0);
|
||||
rn = run_program(pro, in, out, tL, mL, oL, info);
|
||||
int ust = rn.ust, usm = rn.usm;
|
||||
if (rn.type != 0) put_info(1, 1, -1, -1, "Hack Successfully : " + info, read_file(in, "", 100), "", ""), end_info(0);
|
||||
rn = run_judge(stL, smL, soL, info);
|
||||
if (rn.type != 0) put_info(1, 1, -1, -1, "Hack Successfully : Checker " + info, read_file(in, "", 100), read_file(out, "", 100), ""), end_info(0);
|
||||
else if (abs(SCORE() - 1) < 1e-5) put_info(1, 0, ust, usm, "Hack failed : Answer is Correct", read_file(in, "", 100), read_file(out, "", 100), info), end_info(0);
|
||||
else put_info(1, 1, ust, usm, "Hack Successfully : Wrong Answer", read_file(in, "", 100), read_file(out, "", 100), read_file(res, "", 100)), end_info(0);
|
||||
exit(0);
|
||||
}
|
||||
void sample_test() {
|
||||
n = conf_int("n_sample_tests", n);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
put_status("Judging Sample Test ... " + toStr(i));
|
||||
in = mdata_path + "/" + input(-i);
|
||||
ans = mdata_path + "/" + output(-i);
|
||||
out = work_path "/" + output(-i);
|
||||
|
||||
int S = conf_int("sample_point_score", i, 100 / n);
|
||||
|
||||
if (valid) {
|
||||
if (getValid(in, info));
|
||||
else {
|
||||
put_info(i, 0, -1, -1, "Illegal Input", read_file(in, "", 100), "", info);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
run_result rn;
|
||||
if (submit) {
|
||||
if (file_exist(out)) put_info(i, 0, -1, -1, "File not exists", "", "", "");
|
||||
else {
|
||||
rn = run_judge(stL, smL, soL, info);
|
||||
if (rn.type != 0) put_info(i, 0, -1, -1, "Checker " + info, read_file(in, "", 100), read_file(out, "", 100), "");
|
||||
else {
|
||||
string ninfo;
|
||||
if (abs(SCORE() - 1) < 1e-5) ninfo = "Accepted";
|
||||
else if (abs(SCORE()) < 1e-5) ninfo = "Wrong Answer";
|
||||
else ninfo = "Acceptable Output";
|
||||
put_info(i, SCORE() * S, -1, -1, ninfo, read_file(in, "", 100), read_file(out, "", 100), read_file(res, ""));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
rn = run_program(pro, in, out, tL, mL, oL, info);
|
||||
int ust = rn.ust, usm = rn.usm;
|
||||
if (rn.type != 0) {
|
||||
put_info(i, 0, -1, -1, info, read_file(in, "", 100), "", "");
|
||||
continue;
|
||||
}
|
||||
rn = run_judge(stL, smL, soL, info);
|
||||
if (rn.type != 0) put_info(i, 0, -1, -1, "Checker " + info, read_file(in, "", 100), read_file(out, "", 100), "");
|
||||
else {
|
||||
string ninfo;
|
||||
if (abs(SCORE() - 1) < 1e-5) ninfo = "Accepted", ++cnt;
|
||||
else if (abs(SCORE()) < 1e-5) ninfo = "Wrong Answer";
|
||||
else ninfo = "Acceptable Output";
|
||||
put_info(i, SCORE() * S, ust, usm, ninfo, read_file(in, "", 100), read_file(out, "", 100), read_file(res, ""));
|
||||
}
|
||||
}
|
||||
if (cnt == n) totScore = 100;
|
||||
end_info(0);
|
||||
}
|
||||
void normal_test() {
|
||||
n = conf_int("n_tests", 10);
|
||||
m = conf_int("n_ex_tests", 0);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
put_status("Judging Test ... " + toStr(i));
|
||||
in = mdata_path + "/" + input(i);
|
||||
ans = mdata_path + "/" + output(i);
|
||||
out = work_path "/" + output(i);
|
||||
|
||||
int ntL = conf_int("time_limit", i, tL),
|
||||
nmL = conf_int("memory_limit", i, mL),
|
||||
noL = conf_int("output_limit", i, oL),
|
||||
nstL = conf_int("checker_time_limit", i, stL),
|
||||
nsmL = conf_int("checker_memory_limit", i, smL),
|
||||
nsoL = conf_int("checker_output_limit", i, soL);
|
||||
|
||||
int S = conf_int("point_score", i, 100 / n);
|
||||
|
||||
if (valid) {
|
||||
if (getValid(in, info));
|
||||
else {
|
||||
put_info(i, 0, -1, -1, "Illegal Input", read_file(in, "", 100), "", info);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
run_result rn;
|
||||
if (submit) {
|
||||
if (file_exist(out)) put_info(i, 0, -1, -1, "File not exists", "", "", "");
|
||||
else {
|
||||
rn = run_judge(nstL, nsmL, nsoL, info);
|
||||
if (rn.type != 0) put_info(i, 0, -1, -1, "Checker " + info, read_file(in, "", 100), read_file(out, "", 100), "");
|
||||
else {
|
||||
string ninfo;
|
||||
if (abs(SCORE() - 1) < 1e-5) ninfo = "Accepted";
|
||||
else if (abs(SCORE()) < 1e-5) ninfo = "Wrong Answer";
|
||||
else ninfo = "Acceptable Output";
|
||||
put_info(i, SCORE() * S, -1, -1, ninfo, read_file(in, "", 100), read_file(out, "", 100), read_file(res, ""));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
rn = run_program(pro, in, out, ntL, nmL, noL, info);
|
||||
int ust = rn.ust, usm = rn.usm;
|
||||
if (rn.type != 0) {
|
||||
put_info(i, 0, -1, -1, info, read_file(in, "", 100), "", "");
|
||||
continue;
|
||||
}
|
||||
rn = run_judge(nstL, nsmL, nsoL, info);
|
||||
if (rn.type != 0) put_info(i, 0, -1, -1, "Checker " + info, read_file(in, "", 100), read_file(out, "", 100), "");
|
||||
else {
|
||||
string ninfo;
|
||||
if (abs(SCORE() - 1) < 1e-5) ninfo = "Accepted", ++cnt;
|
||||
else if (abs(SCORE()) < 1e-5) ninfo = "Wrong Answer";
|
||||
else ninfo = "Acceptable Output";
|
||||
put_info(i, SCORE() * S, ust, usm, ninfo, read_file(in, "", 100), read_file(out, "", 100), read_file(res, ""));
|
||||
}
|
||||
}
|
||||
if (cnt != n) end_info(0);
|
||||
totScore = 100;
|
||||
bool pass = true;
|
||||
for (int i = 1; i <= m; ++i) {
|
||||
put_status("Judging Extra Test ... " + toStr(i));
|
||||
in = mdata_path + "/" + input(-i);
|
||||
ans = mdata_path + "/" + output(-i);
|
||||
out = work_path "/" + output(-i);
|
||||
run_result rn;
|
||||
|
||||
if (valid) {
|
||||
if (getValid(in, info));
|
||||
else {
|
||||
put_info(-1, -3, -1, -1, "Extra Test Failed : Illegal Input on " + toStr(i), read_file(in, "", 100), "", info);
|
||||
pass = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
rn = run_program(pro, in, out, tL, mL, oL, info);
|
||||
int ust = rn.ust, usm = rn.usm;
|
||||
if (rn.type != 0) {
|
||||
put_info(-1, -3, -1, -1, "Extra Test Failed : " + info + " on " + toStr(i), read_file(in, "", 100), "", "");
|
||||
pass = false; break;
|
||||
}
|
||||
rn = run_judge(stL, smL, soL, info);
|
||||
if (rn.type != 0) {
|
||||
put_info(-1, -3, -1, -1, "Extra Test Failed : Checker " + info + " on " + toStr(i), read_file(in, "", 100), read_file(out, "", 100), "");
|
||||
pass = false; break;
|
||||
}
|
||||
else if (abs(SCORE() - 1) < 1e-5);
|
||||
else {
|
||||
put_info(-1, -3, ust, usm, "Extra Test Failed : Wrong Answer on " + toStr(i), read_file(in, "", 100), read_file(out, "", 100), read_file(res, "", 100));
|
||||
pass = false; break;
|
||||
}
|
||||
}
|
||||
if (pass && m) put_info(-1, 0, -1, -1, "Pass Extra Test", "", "", "");
|
||||
end_info(0);
|
||||
}
|
||||
void new_ex_test() {
|
||||
bool pass = true;
|
||||
put_status("Judging New Extra Test ... ");
|
||||
int R = conf_int("n_ex_tests", 0);
|
||||
for (int i = R; i <= R; ++i) {
|
||||
in = mdata_path + "/" + input(-i);
|
||||
ans = mdata_path + "/" + output(-i);
|
||||
if (file_exist(in)) in = mdata_path + "/" + input(i);
|
||||
if (file_exist(ans)) ans = mdata_path + "/" + input(i);
|
||||
|
||||
out = work_path "/" + output(-i);
|
||||
run_result rn;
|
||||
|
||||
if (valid) {
|
||||
if (getValid(in, info));
|
||||
else {
|
||||
put_info(1, 0, -1, -1, "Extra Test Failed : Illegal Input on " + toStr(i), read_file(in, "", 100), "", info);
|
||||
pass = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
rn = run_program(pro, in, out, tL, mL, oL, info);
|
||||
int ust = rn.ust, usm = rn.usm;
|
||||
if (rn.type != 0) {
|
||||
put_info(1, 0, -1, -1, "Extra Test Failed : " + info + " on " + toStr(i), read_file(in, "", 100), "", "");
|
||||
pass = false; break;
|
||||
}
|
||||
rn = run_judge(stL, smL, soL, info);
|
||||
if (rn.type != 0) {
|
||||
put_info(1, 0, -1, -1, "Extra Test Failed : Checker " + info + " on " + toStr(i), read_file(in, "", 100), read_file(out, "", 100), "");
|
||||
pass = false; break;
|
||||
}
|
||||
else if (abs(SCORE() - 1) < 1e-5);
|
||||
else {
|
||||
put_info(1, 0, ust, usm, "Extra Test Failed : Wrong Answer on " + toStr(i), read_file(in, "", 100), read_file(out, "", 100), read_file(res, "", 100));
|
||||
pass = false; break;
|
||||
}
|
||||
}
|
||||
if (pass) put_info(1, 1, -1, -1, "Pass Extra Test", "", "", "");
|
||||
end_info(0);
|
||||
}
|
||||
*/
|
||||
|
||||
void ordinary_test() {
|
||||
int n = conf_int("n_tests", 10);
|
||||
int m = conf_int("n_ex_tests", 0);
|
||||
int nT = conf_int("n_subtasks", 0);
|
||||
|
||||
if (!conf_is("submit_answer", "on")) {
|
||||
report_judge_status_f("Compiling");
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
}
|
||||
|
||||
bool passed = true;
|
||||
if (nT == 0) {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Test #%d", i);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
}
|
||||
po.scr = scale_score(po.scr, conf_int("point_score", i, 100 / n));
|
||||
add_point_info(po);
|
||||
}
|
||||
} else if (nT == 1) {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Test #%d", i);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
po.scr = i == 1 ? 0 : -100;
|
||||
add_point_info(po);
|
||||
break;
|
||||
} else {
|
||||
po.scr = i == 1 ? 100 : 0;
|
||||
add_point_info(po);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set<int> passedSubtasks;
|
||||
for (int t = 1; t <= nT; t++) {
|
||||
int startI = conf_int("subtask_end", t - 1, 0) + 1;
|
||||
int endI = conf_int("subtask_end", t, 0);
|
||||
|
||||
vector<PointInfo> points;
|
||||
|
||||
vector<int> dependences;
|
||||
if (conf_str("subtask_dependence", t, "none") == "many") {
|
||||
string cur = "subtask_dependence_" + vtos(t);
|
||||
int p = 1;
|
||||
while (conf_int(cur, p, 0) != 0) {
|
||||
dependences.push_back(conf_int(cur, p, 0));
|
||||
p++;
|
||||
}
|
||||
} else if (conf_int("subtask_dependence", t, 0) != 0) {
|
||||
dependences.push_back(conf_int("subtask_dependence", t, 0));
|
||||
}
|
||||
bool skipped = false;
|
||||
for (vector<int>::iterator it = dependences.begin(); it != dependences.end(); it++) {
|
||||
if (!passedSubtasks.count(*it)) {
|
||||
skipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skipped) {
|
||||
add_subtask_info(t, 0, "Skipped", points);
|
||||
continue;
|
||||
}
|
||||
|
||||
int tfull = conf_int("subtask_score", t, 100 / nT);
|
||||
int tscore = tfull;
|
||||
string info = "Accepted";
|
||||
for (int i = startI; i <= endI; i++) {
|
||||
report_judge_status_f("Judging Test #%d of Subtask #%d", i, t);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
po.scr = i == startI ? 0 : -tfull;
|
||||
tscore = 0;
|
||||
points.push_back(po);
|
||||
info = po.info;
|
||||
break;
|
||||
} else {
|
||||
po.scr = i == startI ? tfull : 0;
|
||||
tscore = tfull;
|
||||
points.push_back(po);
|
||||
}
|
||||
}
|
||||
|
||||
if (info == "Accepted") {
|
||||
passedSubtasks.insert(t);
|
||||
}
|
||||
|
||||
add_subtask_info(t, tscore, info, points);
|
||||
}
|
||||
}
|
||||
if (conf_is("submit_answer", "on") || !passed) {
|
||||
end_judge_ok();
|
||||
}
|
||||
|
||||
tot_score = 100;
|
||||
for (int i = 1; i <= m; i++) {
|
||||
report_judge_status_f("Judging Extra Test #%d", i);
|
||||
PointInfo po = test_point("answer", -i);
|
||||
if (po.scr != 100) {
|
||||
po.num = -1;
|
||||
po.info = "Extra Test Failed : " + po.info + " on " + vtos(i);
|
||||
po.scr = -3;
|
||||
add_point_info(po);
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
if (m != 0) {
|
||||
PointInfo po(-1, 0, -1, -1, "Extra Test Passed", "", "", "");
|
||||
add_point_info(po);
|
||||
}
|
||||
end_judge_ok();
|
||||
}
|
||||
|
||||
void hack_test() {
|
||||
if (conf_is("submit_answer", "on")) {
|
||||
end_judge_judgement_failed("Hack is not supported in this problem.");
|
||||
} else {
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
TestPointConfig tpc;
|
||||
tpc.input_file_name = work_path + "/hack_input.txt";
|
||||
tpc.output_file_name = work_path + "/pro_output.txt";
|
||||
tpc.answer_file_name = work_path + "/std_output.txt";
|
||||
|
||||
prepare_run_standard_program();
|
||||
PointInfo po = test_hack_point("answer", tpc);
|
||||
add_point_info(po);
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void sample_test() {
|
||||
if (conf_is("submit_answer", "on")) {
|
||||
int n = conf_int("n_tests", 10);
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Test #%d", i);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 0) {
|
||||
po.info = "Accepted";
|
||||
po.scr = 100;
|
||||
}
|
||||
po.scr = scale_score(po.scr, conf_int("point_score", i, 100 / n));
|
||||
po.res = "no comment";
|
||||
add_point_info(po);
|
||||
}
|
||||
end_judge_ok();
|
||||
} else {
|
||||
report_judge_status_f("Compiling");
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
|
||||
int n = conf_int("n_sample_tests", 0);
|
||||
bool passed = true;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Sample Test #%d", i);
|
||||
PointInfo po = test_point("answer", -i);
|
||||
po.num = i;
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
}
|
||||
po.scr = scale_score(po.scr, 100 / n);
|
||||
add_point_info(po);
|
||||
}
|
||||
if (passed) {
|
||||
tot_score = 100;
|
||||
}
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void custom_test() {
|
||||
if (conf_is("submit_answer", "on")) {
|
||||
end_judge_judgement_failed("Custom test is not supported in this problem.");
|
||||
} else {
|
||||
report_judge_status_f("Compiling");
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
|
||||
report_judge_status_f("Judging");
|
||||
add_custom_test_info(ordinary_custom_test("answer"));
|
||||
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
judger_init(argc, argv);
|
||||
|
||||
if (conf_is("test_new_hack_only", "on")) {
|
||||
hack_test();
|
||||
} else if (conf_is("test_sample_only", "on")) {
|
||||
sample_test();
|
||||
} else if (conf_is("custom_test", "on")) {
|
||||
custom_test();
|
||||
} else {
|
||||
ordinary_test();
|
||||
}
|
||||
|
||||
/*
|
||||
submit = conf_is("submit_answer", "on");
|
||||
hack = conf_is("test_new_hack_only", "on");
|
||||
sample = conf_is("test_sample_only", "on");
|
||||
maker = conf_is("make_hack_test", "on");
|
||||
valid = conf_is("validate_input_before_test", "on");
|
||||
newt = conf_is("test_new_extra", "on");
|
||||
|
||||
if (submit == 0) {
|
||||
if (compile("answer") == false) end_info(-1);
|
||||
}
|
||||
|
||||
jud = config["use_checker"];
|
||||
pro = work_path "/answer";
|
||||
mak = work_path "/maker";
|
||||
stp = mdata_path + "/std";
|
||||
chk = mdata_path + "/val";
|
||||
vre = result_path"/valid_result.txt";
|
||||
sco = result_path"/checker_score.txt";
|
||||
res = result_path"/checker_result.txt";
|
||||
|
||||
|
||||
tL = conf_int("time_limit", 1);
|
||||
mL = conf_int("memory_limit", 256);
|
||||
oL = conf_int("output_limit", 64);
|
||||
stL = conf_int("checker_time_limit", 10);
|
||||
smL = conf_int("checker_memory_limit", 256);
|
||||
soL = conf_int("checker_output_limit", 64);
|
||||
vtL = conf_int("validator_time_limit", 10);
|
||||
vmL = conf_int("validator_memory_limit", 256);
|
||||
voL = conf_int("validator_output_limit", 64);
|
||||
mtL = conf_int("maker_time_limit", 1);
|
||||
mmL = conf_int("maker_memory_limit", 256);
|
||||
moL = conf_int("maker_output_limit", 64);
|
||||
|
||||
if (hack) hack_test();
|
||||
if (sample) sample_test();
|
||||
if (newt) new_ex_test();
|
||||
normal_test();
|
||||
*/
|
||||
}
|
3490
judge_client/1/uoj_judger/include/testlib.h
Normal file
3490
judge_client/1/uoj_judger/include/testlib.h
Normal file
File diff suppressed because it is too large
Load Diff
38
judge_client/1/uoj_judger/include/uoj_env.h
Normal file
38
judge_client/1/uoj_judger/include/uoj_env.h
Normal file
@ -0,0 +1,38 @@
|
||||
#include "uoj_work_path.h"
|
||||
|
||||
#define UOJ_DATA_PATH UOJ_WORK_PATH "/data"
|
||||
#define UOJ_RESULT_PATH UOJ_WORK_PATH "/result"
|
||||
|
||||
#define RS_SPJ_BASE 1000
|
||||
#define failed_spj RS_SPJ_BASE
|
||||
#define successed_hack 100000
|
||||
#define RS_SPJ RS_SPJ_BASE
|
||||
#define RS_HACK successed_hack
|
||||
#define RS_AC 0
|
||||
#define RS_WA 1
|
||||
#define RS_RE 2
|
||||
#define RS_MLE 3
|
||||
#define RS_TLE 4
|
||||
#define RS_OLE 5
|
||||
#define RS_DGS 6
|
||||
#define RS_JGF 7
|
||||
#define RS_SPJ_AC (RS_SPJ + RS_AC)
|
||||
#define RS_SPJ_RE (RS_SPJ_BASE + RS_RE)
|
||||
#define RS_SPJ_MLE (RS_SPJ_BASE + RS_MLE)
|
||||
#define RS_SPJ_TLE (RS_SPJ_BASE + RS_TLE)
|
||||
#define RS_SPJ_OLE (RS_SPJ_BASE + RS_OLE)
|
||||
#define RS_SPJ_DGS (RS_SPJ_BASE + RS_DGS)
|
||||
#define RS_SPJ_JGF (RS_SPJ_BASE + RS_JGF)
|
||||
#define RS_HK_RE (successed_hack + RS_RE)
|
||||
#define RS_HK_MLE (successed_hack + RS_MLE)
|
||||
#define RS_HK_TLE (successed_hack + RS_TLE)
|
||||
#define RS_HK_OLE (successed_hack + RS_OLE)
|
||||
#define RS_HK_DGS (successed_hack + RS_DGS)
|
||||
#define RS_HK_JGF (successed_hack + RS_JGF)
|
||||
#define RS_HK_SPJ_RE (successed_hack + RS_SPJ_RE)
|
||||
#define RS_HK_SPJ_MLE (successed_hack + RS_SPJ_MLE)
|
||||
#define RS_HK_SPJ_TLE (successed_hack + RS_SPJ_TLE)
|
||||
#define RS_HK_SPJ_OLE (successed_hack + RS_SPJ_OLE)
|
||||
#define RS_HK_SPJ_DGS (successed_hack + RS_SPJ_DGS)
|
||||
#define RS_HK_SPJ_JGF (successed_hack + RS_SPJ_JGF)
|
||||
|
1444
judge_client/1/uoj_judger/include/uoj_judger.h
Normal file
1444
judge_client/1/uoj_judger/include/uoj_judger.h
Normal file
File diff suppressed because it is too large
Load Diff
22
judge_client/1/uoj_judger/main_judger.cpp
Normal file
22
judge_client/1/uoj_judger/main_judger.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "uoj_judger.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
main_judger_init(argc, argv);
|
||||
RunResult res = run_program(
|
||||
(result_path + "/run_judger_result.txt").c_str(),
|
||||
"/dev/null",
|
||||
"/dev/null",
|
||||
"stderr",
|
||||
conf_run_limit("judger", 0, RL_JUDGER_DEFAULT),
|
||||
"--unsafe",
|
||||
conf_str("judger").c_str(),
|
||||
main_path.c_str(),
|
||||
work_path.c_str(),
|
||||
result_path.c_str(),
|
||||
data_path.c_str(),
|
||||
NULL);
|
||||
if (res.type != RS_AC) {
|
||||
end_judge_judgement_failed("Judgement Failed : Judger " + info_str(res));
|
||||
}
|
||||
return 0;
|
||||
}
|
0
judge_client/1/uoj_judger/result/.gitkeep
Normal file
0
judge_client/1/uoj_judger/result/.gitkeep
Normal file
55
judge_client/1/uoj_judger/run/formatter.cpp
Normal file
55
judge_client/1/uoj_judger/run/formatter.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include<cstdio>
|
||||
#include<cstdlib>
|
||||
#include<cstring>
|
||||
#include<cmath>
|
||||
#include<algorithm>
|
||||
using namespace std;
|
||||
typedef long long LL;
|
||||
int main()
|
||||
{
|
||||
//freopen("1.in","r",stdin);
|
||||
char c,last;
|
||||
int nSpace=0,nR=0,first;
|
||||
while(1)
|
||||
{
|
||||
last=c,c=getchar();
|
||||
if(c==EOF)
|
||||
{
|
||||
if(last!='\n')
|
||||
putchar('\n');
|
||||
break;
|
||||
}
|
||||
else if(c!='\r'&&c!=' ')
|
||||
{
|
||||
if(c!='\n'&&first==0)
|
||||
{
|
||||
for(int j=1;j<=nSpace;++j)
|
||||
putchar(' ');
|
||||
for(int j=1;j<=nR;++j)
|
||||
putchar('\r');
|
||||
}
|
||||
else if(c!='\n')
|
||||
{
|
||||
for(int j=1;j<=nR;++j)
|
||||
putchar('\r');
|
||||
for(int j=1;j<=nSpace;++j)
|
||||
putchar(' ');
|
||||
}
|
||||
nSpace=nR=0;
|
||||
putchar(c);
|
||||
}
|
||||
else if(c==' ')
|
||||
{
|
||||
++nSpace;
|
||||
if(nR==0)
|
||||
first=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++nR;
|
||||
if(nSpace==0)
|
||||
first=1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
546
judge_client/1/uoj_judger/run/run_program.cpp
Normal file
546
judge_client/1/uoj_judger/run/run_program.cpp
Normal file
@ -0,0 +1,546 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/user.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <argp.h>
|
||||
#include "uoj_env.h"
|
||||
using namespace std;
|
||||
|
||||
struct RunResult {
|
||||
int result;
|
||||
int ust;
|
||||
int usm;
|
||||
int exit_code;
|
||||
|
||||
RunResult(int _result, int _ust = -1, int _usm = -1, int _exit_code = -1)
|
||||
: result(_result), ust(_ust), usm(_usm), exit_code(_exit_code) {
|
||||
if (result != RS_AC) {
|
||||
ust = -1, usm = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int put_result(RunResult res) {
|
||||
printf("%d %d %d %d\n", res.result, res.ust, res.usm, res.exit_code);
|
||||
if (res.result == RS_JGF) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct RunProgramConfig
|
||||
{
|
||||
int time_limit;
|
||||
int memory_limit;
|
||||
int output_limit;
|
||||
int stack_limit;
|
||||
string input_file_name;
|
||||
string output_file_name;
|
||||
string error_file_name;
|
||||
string work_path;
|
||||
string type;
|
||||
vector<string> extra_readable_files;
|
||||
bool allow_proc;
|
||||
bool safe_mode;
|
||||
bool need_show_trace_details;
|
||||
|
||||
string program_name;
|
||||
string program_basename;
|
||||
vector<string> argv;
|
||||
};
|
||||
|
||||
char self_path[PATH_MAX + 1] = {};
|
||||
|
||||
#include "run_program_conf.h"
|
||||
|
||||
argp_option run_program_argp_options[] =
|
||||
{
|
||||
{"tl" , 'T', "TIME_LIMIT" , 0, "Set time limit (in second)" , 1},
|
||||
{"ml" , 'M', "MEMORY_LIMIT", 0, "Set memory limit (in mb)" , 2},
|
||||
{"ol" , 'O', "OUTPUT_LIMIT", 0, "Set output limit (in mb)" , 3},
|
||||
{"sl" , 'S', "STACK_LIMIT" , 0, "Set stack limit (in mb)" , 4},
|
||||
{"in" , 'i', "IN" , 0, "Set input file name" , 5},
|
||||
{"out" , 'o', "OUT" , 0, "Set output file name" , 6},
|
||||
{"err" , 'e', "ERR" , 0, "Set error file name" , 7},
|
||||
{"work-path" , 'w', "WORK_PATH" , 0, "Set the work path of the program" , 8},
|
||||
{"type" , 't', "TYPE" , 0, "Set the program type (for some program such as python)", 9},
|
||||
{"add-readable" , 500, "FILE" , 0, "Add a readable file" , 10},
|
||||
{"unsafe" , 501, 0 , 0, "Don't check dangerous syscalls" , 11},
|
||||
{"show-trace-details" , 502, 0 , 0, "Show trace details" , 12},
|
||||
{"allow-proc" , 503, 0 , 0, "Allow fork, exec... etc." , 13},
|
||||
{"add-readable-raw" , 504, "FOLDER" , 0, "Add a readable (don't transform to its real path)" , 14},
|
||||
{0}
|
||||
};
|
||||
error_t run_program_argp_parse_opt (int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
RunProgramConfig *config = (RunProgramConfig*)state->input;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case 'T':
|
||||
config->time_limit = atoi(arg);
|
||||
break;
|
||||
case 'M':
|
||||
config->memory_limit = atoi(arg);
|
||||
break;
|
||||
case 'O':
|
||||
config->output_limit = atoi(arg);
|
||||
break;
|
||||
case 'S':
|
||||
config->stack_limit = atoi(arg);
|
||||
break;
|
||||
case 'i':
|
||||
config->input_file_name = arg;
|
||||
break;
|
||||
case 'o':
|
||||
config->output_file_name = arg;
|
||||
break;
|
||||
case 'e':
|
||||
config->error_file_name = arg;
|
||||
break;
|
||||
case 'w':
|
||||
config->work_path = realpath(arg);
|
||||
if (config->work_path.empty()) {
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
config->type = arg;
|
||||
break;
|
||||
case 500:
|
||||
config->extra_readable_files.push_back(realpath(arg));
|
||||
break;
|
||||
case 501:
|
||||
config->safe_mode = false;
|
||||
break;
|
||||
case 502:
|
||||
config->need_show_trace_details = true;
|
||||
break;
|
||||
case 503:
|
||||
config->allow_proc = true;
|
||||
break;
|
||||
case 504:
|
||||
config->extra_readable_files.push_back(arg);
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
config->argv.push_back(arg);
|
||||
for (int i = state->next; i < state->argc; i++) {
|
||||
config->argv.push_back(state->argv[i]);
|
||||
}
|
||||
state->next = state->argc;
|
||||
break;
|
||||
case ARGP_KEY_END:
|
||||
if (state->arg_num == 0) {
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char run_program_argp_args_doc[] = "program arg1 arg2 ...";
|
||||
char run_program_argp_doc[] = "run_program: a tool to run program safely";
|
||||
|
||||
argp run_program_argp = {
|
||||
run_program_argp_options,
|
||||
run_program_argp_parse_opt,
|
||||
run_program_argp_args_doc,
|
||||
run_program_argp_doc
|
||||
};
|
||||
|
||||
RunProgramConfig run_program_config;
|
||||
|
||||
void parse_args(int argc, char **argv) {
|
||||
run_program_config.time_limit = 1;
|
||||
run_program_config.memory_limit = 256;
|
||||
run_program_config.output_limit = 64;
|
||||
run_program_config.stack_limit = 1024;
|
||||
run_program_config.input_file_name = "stdin";
|
||||
run_program_config.output_file_name = "stdout";
|
||||
run_program_config.error_file_name = "stderr";
|
||||
run_program_config.work_path = "";
|
||||
run_program_config.type = "default";
|
||||
run_program_config.safe_mode = true;
|
||||
run_program_config.need_show_trace_details = false;
|
||||
run_program_config.allow_proc = false;
|
||||
|
||||
argp_parse(&run_program_argp, argc, argv, ARGP_NO_ARGS | ARGP_IN_ORDER, 0, &run_program_config);
|
||||
|
||||
run_program_config.stack_limit = min(run_program_config.stack_limit, run_program_config.memory_limit);
|
||||
|
||||
if (!run_program_config.work_path.empty()) {
|
||||
if (chdir(run_program_config.work_path.c_str()) == -1) {
|
||||
exit(put_result(RS_JGF));
|
||||
}
|
||||
}
|
||||
|
||||
if (run_program_config.type == "java7u76" || run_program_config.type == "java8u31") {
|
||||
run_program_config.program_name = run_program_config.argv[0];
|
||||
} else {
|
||||
run_program_config.program_name = realpath(run_program_config.argv[0]);
|
||||
}
|
||||
if (run_program_config.work_path.empty()) {
|
||||
run_program_config.work_path = dirname(run_program_config.program_name);
|
||||
run_program_config.program_basename = basename(run_program_config.program_name);
|
||||
run_program_config.argv[0] = "./" + run_program_config.program_basename;
|
||||
|
||||
if (chdir(run_program_config.work_path.c_str()) == -1) {
|
||||
exit(put_result(RS_JGF));
|
||||
}
|
||||
}
|
||||
|
||||
if (run_program_config.type == "python2.7") {
|
||||
string pre[4] = {"/usr/bin/python2.7", "-E", "-s", "-B"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 4);
|
||||
} else if (run_program_config.type == "python3.4") {
|
||||
string pre[3] = {"/usr/bin/python3.4", "-I", "-B"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
|
||||
} else if (run_program_config.type == "java7u76") {
|
||||
string pre[3] = {abspath(0, string(self_path) + "/../runtime/jdk1.7.0_76/bin/java"), "-Xmx1024m", "-Xss1024m"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
|
||||
} else if (run_program_config.type == "java8u31") {
|
||||
string pre[3] = {abspath(0, string(self_path) + "/../runtime/jdk1.8.0_31/bin/java"), "-Xmx1024m", "-Xss1024m"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
|
||||
}
|
||||
}
|
||||
|
||||
void set_limit(int r, int rcur, int rmax = -1) {
|
||||
if (rmax == -1)
|
||||
rmax = rcur;
|
||||
struct rlimit l;
|
||||
if (getrlimit(r, &l) == -1) {
|
||||
exit(55);
|
||||
}
|
||||
l.rlim_cur = rcur;
|
||||
l.rlim_max = rmax;
|
||||
if (setrlimit(r, &l) == -1) {
|
||||
exit(55);
|
||||
}
|
||||
}
|
||||
void run_child() {
|
||||
set_limit(RLIMIT_CPU, run_program_config.time_limit, run_program_config.time_limit + 2);
|
||||
set_limit(RLIMIT_FSIZE, run_program_config.output_limit << 20);
|
||||
set_limit(RLIMIT_STACK, run_program_config.stack_limit << 20);
|
||||
|
||||
if (run_program_config.input_file_name != "stdin") {
|
||||
if (freopen(run_program_config.input_file_name.c_str(), "r", stdin) == NULL) {
|
||||
exit(11);
|
||||
}
|
||||
}
|
||||
if (run_program_config.output_file_name != "stdout" && run_program_config.output_file_name != "stderr") {
|
||||
if (freopen(run_program_config.output_file_name.c_str(), "w", stdout) == NULL) {
|
||||
exit(12);
|
||||
}
|
||||
}
|
||||
if (run_program_config.error_file_name != "stderr") {
|
||||
if (run_program_config.error_file_name == "stdout") {
|
||||
if (dup2(1, 2) == -1) {
|
||||
exit(13);
|
||||
}
|
||||
} else {
|
||||
if (freopen(run_program_config.error_file_name.c_str(), "w", stderr) == NULL) {
|
||||
exit(14);
|
||||
}
|
||||
}
|
||||
|
||||
if (run_program_config.output_file_name == "stderr") {
|
||||
if (dup2(2, 1) == -1) {
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *env_path_str = getenv("PATH");
|
||||
char *env_lang_str = getenv("LANG");
|
||||
char *env_shell_str = getenv("SHELL");
|
||||
string env_path = env_path_str ? env_path_str : "";
|
||||
string env_lang = env_lang_str ? env_lang_str : "";
|
||||
string env_shell = env_shell_str ? env_shell_str : "";
|
||||
|
||||
clearenv();
|
||||
setenv("USER", "poor_program", 1);
|
||||
setenv("LOGNAME", "poor_program", 1);
|
||||
setenv("HOME", run_program_config.work_path.c_str(), 1);
|
||||
if (env_lang_str) {
|
||||
setenv("LANG", env_lang.c_str(), 1);
|
||||
}
|
||||
if (env_path_str) {
|
||||
setenv("PATH", env_path.c_str(), 1);
|
||||
}
|
||||
setenv("PWD", run_program_config.work_path.c_str(), 1);
|
||||
if (env_shell_str) {
|
||||
setenv("SHELL", env_shell.c_str(), 1);
|
||||
}
|
||||
|
||||
char **program_c_argv = new char*[run_program_config.argv.size() + 1];
|
||||
for (size_t i = 0; i < run_program_config.argv.size(); i++) {
|
||||
program_c_argv[i] = new char[run_program_config.argv[i].size() + 1];
|
||||
strcpy(program_c_argv[i], run_program_config.argv[i].c_str());
|
||||
}
|
||||
program_c_argv[run_program_config.argv.size()] = NULL;
|
||||
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
|
||||
exit(16);
|
||||
}
|
||||
if (execv(program_c_argv[0], program_c_argv) == -1) {
|
||||
exit(17);
|
||||
}
|
||||
}
|
||||
|
||||
const int MaxNRPChildren = 50;
|
||||
struct rp_child_proc {
|
||||
pid_t pid;
|
||||
int mode;
|
||||
};
|
||||
int n_rp_children;
|
||||
pid_t rp_timer_pid;
|
||||
rp_child_proc rp_children[MaxNRPChildren];
|
||||
|
||||
int rp_children_pos(pid_t pid) {
|
||||
for (int i = 0; i < n_rp_children; i++) {
|
||||
if (rp_children[i].pid == pid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int rp_children_add(pid_t pid) {
|
||||
if (n_rp_children == MaxNRPChildren) {
|
||||
return -1;
|
||||
}
|
||||
rp_children[n_rp_children].pid = pid;
|
||||
rp_children[n_rp_children].mode = -1;
|
||||
n_rp_children++;
|
||||
return 0;
|
||||
}
|
||||
void rp_children_del(pid_t pid) {
|
||||
int new_n = 0;
|
||||
for (int i = 0; i < n_rp_children; i++) {
|
||||
if (rp_children[i].pid != pid) {
|
||||
rp_children[new_n++] = rp_children[i];
|
||||
}
|
||||
}
|
||||
n_rp_children = new_n;
|
||||
}
|
||||
|
||||
void stop_child(pid_t pid) {
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
void stop_all() {
|
||||
kill(rp_timer_pid, SIGKILL);
|
||||
for (int i = 0; i < n_rp_children; i++) {
|
||||
kill(rp_children[i].pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
RunResult trace_children() {
|
||||
rp_timer_pid = fork();
|
||||
if (rp_timer_pid == -1) {
|
||||
stop_all();
|
||||
return RunResult(RS_JGF);
|
||||
} else if (rp_timer_pid == 0) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = run_program_config.time_limit + 2;
|
||||
ts.tv_nsec = 0;
|
||||
nanosleep(&ts, NULL);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
cerr << "timerpid " << rp_timer_pid << endl;
|
||||
}
|
||||
|
||||
pid_t prev_pid = -1;
|
||||
while (true) {
|
||||
int stat = 0;
|
||||
int sig = 0;
|
||||
struct rusage ruse;
|
||||
|
||||
pid_t pid = wait4(-1, &stat, __WALL, &ruse);
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
if (prev_pid != pid) {
|
||||
cerr << "----------" << pid << "----------" << endl;
|
||||
}
|
||||
prev_pid = pid;
|
||||
}
|
||||
if (pid == rp_timer_pid) {
|
||||
if (WIFEXITED(stat) || WIFSIGNALED(stat)) {
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int p = rp_children_pos(pid);
|
||||
if (p == -1) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "new_proc %lld\n", (long long int)pid);
|
||||
}
|
||||
if (rp_children_add(pid) == -1) {
|
||||
stop_child(pid);
|
||||
stop_all();
|
||||
return RunResult(RS_DGS);
|
||||
}
|
||||
p = n_rp_children - 1;
|
||||
}
|
||||
|
||||
int usertim = ruse.ru_utime.tv_sec * 1000 + ruse.ru_utime.tv_usec / 1000;
|
||||
int usermem = ruse.ru_maxrss;
|
||||
if (usertim > run_program_config.time_limit * 1000) {
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
}
|
||||
if (usermem > run_program_config.memory_limit * 1024) {
|
||||
stop_all();
|
||||
return RunResult(RS_MLE);
|
||||
}
|
||||
|
||||
if (WIFEXITED(stat)) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "exit : %d\n", WEXITSTATUS(stat));
|
||||
}
|
||||
if (rp_children[0].mode == -1) {
|
||||
stop_all();
|
||||
return RunResult(RS_JGF, -1, -1, WEXITSTATUS(stat));
|
||||
} else {
|
||||
if (pid == rp_children[0].pid) {
|
||||
stop_all();
|
||||
return RunResult(RS_AC, usertim, usermem, WEXITSTATUS(stat));
|
||||
} else {
|
||||
rp_children_del(pid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(stat)) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "sig exit : %d\n", WTERMSIG(stat));
|
||||
}
|
||||
if (pid == rp_children[0].pid) {
|
||||
switch(WTERMSIG(stat)) {
|
||||
case SIGXCPU: // nearly impossible
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
case SIGXFSZ:
|
||||
stop_all();
|
||||
return RunResult(RS_OLE);
|
||||
default:
|
||||
stop_all();
|
||||
return RunResult(RS_RE);
|
||||
}
|
||||
} else {
|
||||
rp_children_del(pid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (WIFSTOPPED(stat)) {
|
||||
sig = WSTOPSIG(stat);
|
||||
|
||||
if (rp_children[p].mode == -1) {
|
||||
if ((p == 0 && sig == SIGTRAP) || (p != 0 && sig == SIGSTOP)) {
|
||||
if (p == 0) {
|
||||
int ptrace_opt = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD;
|
||||
if (run_program_config.safe_mode) {
|
||||
ptrace_opt |= PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK;
|
||||
ptrace_opt |= PTRACE_O_TRACEEXEC;
|
||||
}
|
||||
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, ptrace_opt) == -1) {
|
||||
stop_all();
|
||||
return RunResult(RS_JGF);
|
||||
}
|
||||
}
|
||||
sig = 0;
|
||||
}
|
||||
rp_children[p].mode = 0;
|
||||
} else if (sig == (SIGTRAP | 0x80)) {
|
||||
if (rp_children[p].mode == 0) {
|
||||
if (run_program_config.safe_mode) {
|
||||
if (!check_safe_syscall(pid, run_program_config.need_show_trace_details)) {
|
||||
stop_all();
|
||||
return RunResult(RS_DGS);
|
||||
}
|
||||
}
|
||||
rp_children[p].mode = 1;
|
||||
} else {
|
||||
if (run_program_config.safe_mode) {
|
||||
on_syscall_exit(pid, run_program_config.need_show_trace_details);
|
||||
}
|
||||
rp_children[p].mode = 0;
|
||||
}
|
||||
|
||||
sig = 0;
|
||||
} else if (sig == SIGTRAP) {
|
||||
switch ((stat >> 16) & 0xffff) {
|
||||
case PTRACE_EVENT_CLONE:
|
||||
case PTRACE_EVENT_FORK:
|
||||
case PTRACE_EVENT_VFORK:
|
||||
sig = 0;
|
||||
break;
|
||||
case PTRACE_EVENT_EXEC:
|
||||
rp_children[p].mode = 1;
|
||||
sig = 0;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
stop_all();
|
||||
return RunResult(RS_JGF);
|
||||
}
|
||||
}
|
||||
|
||||
if (sig != 0) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "sig : %d\n", sig);
|
||||
}
|
||||
}
|
||||
|
||||
switch(sig) {
|
||||
case SIGXCPU:
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
case SIGXFSZ:
|
||||
stop_all();
|
||||
return RunResult(RS_OLE);
|
||||
}
|
||||
}
|
||||
|
||||
ptrace(PTRACE_SYSCALL, pid, NULL, sig);
|
||||
}
|
||||
}
|
||||
|
||||
RunResult run_parent(pid_t pid) {
|
||||
init_conf(run_program_config);
|
||||
|
||||
n_rp_children = 0;
|
||||
|
||||
rp_children_add(pid);
|
||||
return trace_children();
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
self_path[readlink("/proc/self/exe", self_path, PATH_MAX)] = '\0';
|
||||
parse_args(argc, argv);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
return put_result(RS_JGF);
|
||||
} else if (pid == 0) {
|
||||
run_child();
|
||||
} else {
|
||||
return put_result(run_parent(pid));
|
||||
}
|
||||
return put_result(RS_JGF);
|
||||
}
|
689
judge_client/1/uoj_judger/run/run_program_conf.h
Normal file
689
judge_client/1/uoj_judger/run/run_program_conf.h
Normal file
@ -0,0 +1,689 @@
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long int reg_val_t;
|
||||
#define REG_SYSCALL orig_rax
|
||||
#define REG_RET rax
|
||||
#define REG_ARG0 rdi
|
||||
#define REG_ARG1 rsi
|
||||
#define REG_ARG2 rdx
|
||||
#define REG_ARG3 rcx
|
||||
#else
|
||||
typedef long int reg_val_t;
|
||||
#define REG_SYSCALL orig_eax
|
||||
#define REG_RET eax
|
||||
#define REG_ARG0 ebx
|
||||
#define REG_ARG1 ecx
|
||||
#define REG_ARG2 edx
|
||||
#define REG_ARG3 esx
|
||||
#endif
|
||||
|
||||
const size_t MaxPathLen = 200;
|
||||
|
||||
set<string> writable_file_name_set;
|
||||
set<string> readable_file_name_set;
|
||||
set<string> statable_file_name_set;
|
||||
set<string> soft_ban_file_name_set;
|
||||
int syscall_max_cnt[1000];
|
||||
bool syscall_should_soft_ban[1000];
|
||||
|
||||
string basename(const string &path) {
|
||||
size_t p = path.rfind('/');
|
||||
if (p == string::npos) {
|
||||
return path;
|
||||
} else {
|
||||
return path.substr(p + 1);
|
||||
}
|
||||
}
|
||||
string dirname(const string &path) {
|
||||
size_t p = path.rfind('/');
|
||||
if (p == string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
return path.substr(0, p);
|
||||
}
|
||||
}
|
||||
string getcwdp(pid_t pid) {
|
||||
char s[20];
|
||||
char cwd[MaxPathLen + 1];
|
||||
if (pid != 0) {
|
||||
sprintf(s, "/proc/%lld/cwd", (long long int)pid);
|
||||
} else {
|
||||
sprintf(s, "/proc/self/cwd");
|
||||
}
|
||||
int l = readlink(s, cwd, MaxPathLen);
|
||||
if (l == -1) {
|
||||
return "";
|
||||
}
|
||||
cwd[l] = '\0';
|
||||
return cwd;
|
||||
}
|
||||
string abspath(pid_t pid, const string &path) {
|
||||
if (path.size() > MaxPathLen) {
|
||||
return "";
|
||||
}
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
string s;
|
||||
string b;
|
||||
size_t st;
|
||||
if (path[0] == '/') {
|
||||
s = "/";
|
||||
st = 1;
|
||||
} else {
|
||||
s = getcwdp(pid) + "/";
|
||||
st = 0;
|
||||
}
|
||||
for (size_t i = st; i < path.size(); i++) {
|
||||
b += path[i];
|
||||
if (path[i] == '/') {
|
||||
if (b == "../" && !s.empty()) {
|
||||
if (s == "./") {
|
||||
s = "../";
|
||||
} else if (s != "/") {
|
||||
size_t p = s.size() - 1;
|
||||
while (p > 0 && s[p - 1] != '/') {
|
||||
p--;
|
||||
}
|
||||
if (s.size() - p == 3 && s[p] == '.' && s[p + 1] == '.' && s[p + 2] == '/') {
|
||||
s += b;
|
||||
} else {
|
||||
s.resize(p);
|
||||
}
|
||||
}
|
||||
} else if (b != "./" && b != "/") {
|
||||
s += b;
|
||||
}
|
||||
b.clear();
|
||||
}
|
||||
}
|
||||
if (b == ".." && !s.empty()) {
|
||||
if (s == "./") {
|
||||
s = "..";
|
||||
} else if (s != "/") {
|
||||
size_t p = s.size() - 1;
|
||||
while (p > 0 && s[p - 1] != '/') {
|
||||
p--;
|
||||
}
|
||||
if (s.size() - p == 3 && s[p] == '.' && s[p + 1] == '.' && s[p + 2] == '/') {
|
||||
s += b;
|
||||
} else {
|
||||
s.resize(p);
|
||||
}
|
||||
}
|
||||
} else if (b != ".") {
|
||||
s += b;
|
||||
}
|
||||
if (s.size() >= 2 && s[s.size() - 1] == '/') {
|
||||
s.resize(s.size() - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
string realpath(const string &path) {
|
||||
char real[PATH_MAX + 1] = {};
|
||||
if (realpath(path.c_str(), real) == NULL) {
|
||||
return "";
|
||||
}
|
||||
return real;
|
||||
}
|
||||
|
||||
inline bool is_in_set_smart(string name, const set<string> &s) {
|
||||
if (name.size() > MaxPathLen) {
|
||||
return false;
|
||||
}
|
||||
if (s.count(name)) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i + 1 < name.size(); i++) {
|
||||
if ((i == 0 || name[i - 1] == '/') && name[i] == '.' && name[i + 1] == '.' && (i + 2 == name.size() || name[i + 2] == '.')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int level;
|
||||
for (level = 0; !name.empty(); name = dirname(name), level++) {
|
||||
if (level == 1 && s.count(name + "/*")) {
|
||||
return true;
|
||||
}
|
||||
if (s.count(name + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (level == 1 && s.count("/*")) {
|
||||
return true;
|
||||
}
|
||||
if (s.count("/")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_writable_file(string name) {
|
||||
if (name == "/") {
|
||||
return writable_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, writable_file_name_set) || is_in_set_smart(realpath(name), readable_file_name_set);
|
||||
}
|
||||
inline bool is_readable_file(const string &name) {
|
||||
if (is_writable_file(name)) {
|
||||
return true;
|
||||
}
|
||||
if (name == "/") {
|
||||
return readable_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, readable_file_name_set) || is_in_set_smart(realpath(name), readable_file_name_set);
|
||||
}
|
||||
inline bool is_statable_file(const string &name) {
|
||||
if (is_readable_file(name)) {
|
||||
return true;
|
||||
}
|
||||
if (name == "/") {
|
||||
return statable_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, statable_file_name_set) || is_in_set_smart(realpath(name), statable_file_name_set);
|
||||
}
|
||||
inline bool is_soft_ban_file(const string &name) {
|
||||
if (name == "/") {
|
||||
return soft_ban_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, soft_ban_file_name_set) || is_in_set_smart(realpath(name), soft_ban_file_name_set);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
int syscall_max_cnt_list_default[][2] = {
|
||||
{__NR_read , -1},
|
||||
{__NR_write , -1},
|
||||
{__NR_readv , -1},
|
||||
{__NR_writev , -1},
|
||||
{__NR_open , -1},
|
||||
{__NR_unlink , -1},
|
||||
{__NR_close , -1},
|
||||
{__NR_readlink , -1},
|
||||
{__NR_openat , -1},
|
||||
{__NR_unlinkat , -1},
|
||||
{__NR_readlinkat , -1},
|
||||
{__NR_stat , -1},
|
||||
{__NR_fstat , -1},
|
||||
{__NR_lstat , -1},
|
||||
{__NR_lseek , -1},
|
||||
{__NR_access , -1},
|
||||
{__NR_dup , -1},
|
||||
{__NR_dup2 , -1},
|
||||
{__NR_dup3 , -1},
|
||||
{__NR_ioctl , -1},
|
||||
{__NR_fcntl , -1},
|
||||
|
||||
{__NR_mmap , -1},
|
||||
{__NR_mprotect , -1},
|
||||
{__NR_munmap , -1},
|
||||
{__NR_brk , -1},
|
||||
{__NR_mremap , -1},
|
||||
{__NR_msync , -1},
|
||||
{__NR_mincore , -1},
|
||||
{__NR_madvise , -1},
|
||||
|
||||
{__NR_rt_sigaction , -1},
|
||||
{__NR_rt_sigprocmask, -1},
|
||||
{__NR_rt_sigreturn , -1},
|
||||
{__NR_rt_sigpending , -1},
|
||||
{__NR_sigaltstack , -1},
|
||||
|
||||
{__NR_getcwd , -1},
|
||||
|
||||
{__NR_exit , -1},
|
||||
{__NR_exit_group , -1},
|
||||
|
||||
{__NR_arch_prctl , -1},
|
||||
|
||||
{__NR_gettimeofday , -1},
|
||||
{__NR_getrlimit , -1},
|
||||
{__NR_getrusage , -1},
|
||||
{__NR_times , -1},
|
||||
{__NR_time , -1},
|
||||
{__NR_clock_gettime , -1},
|
||||
|
||||
{__NR_restart_syscall, -1},
|
||||
|
||||
{-1 , -1}
|
||||
};
|
||||
|
||||
int syscall_soft_ban_list_default[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
const char *readable_file_name_list_default[] = {
|
||||
"/etc/ld.so.nohwcap",
|
||||
"/etc/ld.so.preload",
|
||||
"/etc/ld.so.cache",
|
||||
"/lib/x86_64-linux-gnu/",
|
||||
"/usr/lib/x86_64-linux-gnu/",
|
||||
"/usr/lib/locale/locale-archive",
|
||||
"/proc/self/exe",
|
||||
"/etc/timezone",
|
||||
"/usr/share/zoneinfo/",
|
||||
"/dev/random",
|
||||
"/dev/urandom",
|
||||
"/proc/meminfo",
|
||||
"/etc/localtime",
|
||||
NULL
|
||||
};
|
||||
|
||||
#else
|
||||
#error T_T
|
||||
#endif
|
||||
|
||||
void add_file_permission(const string &file_name, char mode) {
|
||||
if (mode == 'w') {
|
||||
writable_file_name_set.insert(file_name);
|
||||
} else if (mode == 'r') {
|
||||
readable_file_name_set.insert(file_name);
|
||||
} else if (mode == 's') {
|
||||
statable_file_name_set.insert(file_name);
|
||||
}
|
||||
for (string name = dirname(file_name); !name.empty(); name = dirname(name)) {
|
||||
statable_file_name_set.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
void init_conf(const RunProgramConfig &config) {
|
||||
for (int i = 0; syscall_max_cnt_list_default[i][0] != -1; i++) {
|
||||
syscall_max_cnt[syscall_max_cnt_list_default[i][0]] = syscall_max_cnt_list_default[i][1];
|
||||
}
|
||||
for (int i = 0; syscall_soft_ban_list_default[i] != -1; i++) {
|
||||
syscall_should_soft_ban[syscall_soft_ban_list_default[i]] = true;
|
||||
}
|
||||
|
||||
for (int i = 0; readable_file_name_list_default[i]; i++) {
|
||||
readable_file_name_set.insert(readable_file_name_list_default[i]);
|
||||
}
|
||||
statable_file_name_set.insert(config.work_path + "/");
|
||||
|
||||
if (config.type != "java7u76" && config.type != "java8u31") {
|
||||
add_file_permission(config.program_name, 'r');
|
||||
} else {
|
||||
int p = config.program_name.find('.');
|
||||
if (p == string::npos) {
|
||||
readable_file_name_set.insert(config.work_path + "/");
|
||||
} else {
|
||||
readable_file_name_set.insert(config.work_path + "/" + config.program_name.substr(0, p) + "/");
|
||||
}
|
||||
}
|
||||
add_file_permission(config.work_path, 'r');
|
||||
|
||||
for (vector<string>::const_iterator it = config.extra_readable_files.begin(); it != config.extra_readable_files.end(); it++) {
|
||||
add_file_permission(*it, 'r');
|
||||
}
|
||||
|
||||
writable_file_name_set.insert("/dev/null");
|
||||
|
||||
if (config.allow_proc) {
|
||||
syscall_max_cnt[__NR_clone ] = -1;
|
||||
syscall_max_cnt[__NR_fork ] = -1;
|
||||
syscall_max_cnt[__NR_vfork ] = -1;
|
||||
syscall_max_cnt[__NR_nanosleep ] = -1;
|
||||
syscall_max_cnt[__NR_execve ] = -1;
|
||||
}
|
||||
|
||||
if (config.type == "python2.7") {
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 1;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = -1;
|
||||
syscall_max_cnt[__NR_getdents64 ] = -1;
|
||||
|
||||
readable_file_name_set.insert("/usr/bin/python2.7");
|
||||
readable_file_name_set.insert("/usr/lib/python2.7/");
|
||||
readable_file_name_set.insert("/usr/bin/lib/python2.7/");
|
||||
readable_file_name_set.insert("/usr/local/lib/python2.7/");
|
||||
readable_file_name_set.insert("/usr/lib/pymodules/python2.7/");
|
||||
readable_file_name_set.insert("/usr/bin/Modules/");
|
||||
readable_file_name_set.insert("/usr/bin/pybuilddir.txt");
|
||||
|
||||
statable_file_name_set.insert("/usr");
|
||||
statable_file_name_set.insert("/usr/bin");
|
||||
} else if (config.type == "python3.4") {
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 1;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = -1;
|
||||
syscall_max_cnt[__NR_getdents64 ] = -1;
|
||||
|
||||
readable_file_name_set.insert("/usr/bin/python3.4");
|
||||
readable_file_name_set.insert("/usr/lib/python3.4/");
|
||||
readable_file_name_set.insert("/usr/lib/python3/");
|
||||
readable_file_name_set.insert("/usr/bin/lib/python3.4/");
|
||||
readable_file_name_set.insert("/usr/local/lib/python3.4/");
|
||||
readable_file_name_set.insert("/usr/bin/pyvenv.cfg");
|
||||
readable_file_name_set.insert("/usr/pyvenv.cfg");
|
||||
readable_file_name_set.insert("/usr/bin/Modules/");
|
||||
readable_file_name_set.insert("/usr/bin/pybuilddir.txt");
|
||||
readable_file_name_set.insert("/usr/lib/dist-python");
|
||||
|
||||
statable_file_name_set.insert("/usr");
|
||||
statable_file_name_set.insert("/usr/bin");
|
||||
statable_file_name_set.insert("/usr/lib");
|
||||
} else if (config.type == "java7u76") {
|
||||
syscall_max_cnt[__NR_gettid ] = -1;
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 14;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_uname ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_clone ] = 13;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = 4;
|
||||
|
||||
syscall_max_cnt[__NR_clock_getres ] = 2;
|
||||
|
||||
syscall_max_cnt[__NR_setrlimit ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_sched_getaffinity] = -1;
|
||||
syscall_max_cnt[__NR_sched_yield ] = -1;
|
||||
|
||||
syscall_should_soft_ban[__NR_socket ] = true;
|
||||
syscall_should_soft_ban[__NR_connect ] = true;
|
||||
syscall_should_soft_ban[__NR_geteuid ] = true;
|
||||
syscall_should_soft_ban[__NR_getuid ] = true;
|
||||
|
||||
soft_ban_file_name_set.insert("/etc/nsswitch.conf");
|
||||
soft_ban_file_name_set.insert("/etc/passwd");
|
||||
|
||||
add_file_permission(abspath(0, string(self_path) + "/../runtime/jdk1.7.0_76") + "/", 'r');
|
||||
readable_file_name_set.insert("/sys/devices/system/cpu/");
|
||||
readable_file_name_set.insert("/proc/");
|
||||
statable_file_name_set.insert("/usr/java/");
|
||||
statable_file_name_set.insert("/tmp/");
|
||||
} else if (config.type == "java8u31") {
|
||||
syscall_max_cnt[__NR_gettid ] = -1;
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 15;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_uname ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_clone ] = 14;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = 4;
|
||||
|
||||
syscall_max_cnt[__NR_clock_getres ] = 2;
|
||||
|
||||
syscall_max_cnt[__NR_setrlimit ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_sched_getaffinity] = -1;
|
||||
syscall_max_cnt[__NR_sched_yield ] = -1;
|
||||
|
||||
syscall_should_soft_ban[__NR_socket ] = true;
|
||||
syscall_should_soft_ban[__NR_connect ] = true;
|
||||
syscall_should_soft_ban[__NR_geteuid ] = true;
|
||||
syscall_should_soft_ban[__NR_getuid ] = true;
|
||||
|
||||
soft_ban_file_name_set.insert("/etc/nsswitch.conf");
|
||||
soft_ban_file_name_set.insert("/etc/passwd");
|
||||
|
||||
add_file_permission(abspath(0, string(self_path) + "/../runtime/jdk1.8.0_31") + "/", 'r');
|
||||
readable_file_name_set.insert("/sys/devices/system/cpu/");
|
||||
readable_file_name_set.insert("/proc/");
|
||||
statable_file_name_set.insert("/usr/java/");
|
||||
statable_file_name_set.insert("/tmp/");
|
||||
} else if (config.type == "compiler") {
|
||||
syscall_max_cnt[__NR_gettid ] = -1;
|
||||
syscall_max_cnt[__NR_set_tid_address] = -1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = -1;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getpid ] = -1;
|
||||
syscall_max_cnt[__NR_vfork ] = -1;
|
||||
syscall_max_cnt[__NR_fork ] = -1;
|
||||
syscall_max_cnt[__NR_clone ] = -1;
|
||||
syscall_max_cnt[__NR_execve ] = -1;
|
||||
syscall_max_cnt[__NR_wait4 ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_clock_gettime ] = -1;
|
||||
syscall_max_cnt[__NR_clock_getres ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_setrlimit ] = -1;
|
||||
syscall_max_cnt[__NR_pipe ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getdents64 ] = -1;
|
||||
syscall_max_cnt[__NR_getdents ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_umask ] = -1;
|
||||
syscall_max_cnt[__NR_rename ] = -1;
|
||||
syscall_max_cnt[__NR_chmod ] = -1;
|
||||
syscall_max_cnt[__NR_mkdir ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_chdir ] = -1;
|
||||
syscall_max_cnt[__NR_fchdir ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_ftruncate ] = -1; // for javac = =
|
||||
|
||||
syscall_max_cnt[__NR_sched_getaffinity] = -1; // for javac = =
|
||||
syscall_max_cnt[__NR_sched_yield ] = -1; // for javac = =
|
||||
|
||||
syscall_max_cnt[__NR_uname ] = -1; // for javac = =
|
||||
syscall_max_cnt[__NR_sysinfo ] = -1; // for javac = =
|
||||
|
||||
syscall_should_soft_ban[__NR_socket ] = true; // for javac
|
||||
syscall_should_soft_ban[__NR_connect ] = true; // for javac
|
||||
syscall_should_soft_ban[__NR_geteuid ] = true; // for javac
|
||||
syscall_should_soft_ban[__NR_getuid ] = true; // for javac
|
||||
|
||||
writable_file_name_set.insert("/tmp/");
|
||||
|
||||
readable_file_name_set.insert(config.work_path);
|
||||
writable_file_name_set.insert(config.work_path + "/");
|
||||
|
||||
readable_file_name_set.insert(abspath(0, string(self_path) + "/../runtime") + "/");
|
||||
|
||||
readable_file_name_set.insert("system_root");
|
||||
readable_file_name_set.insert("/usr/");
|
||||
readable_file_name_set.insert("/lib/");
|
||||
readable_file_name_set.insert("/lib64/");
|
||||
readable_file_name_set.insert("/bin/");
|
||||
readable_file_name_set.insert("/sbin/");
|
||||
// readable_file_name_set.insert("/proc/meminfo");
|
||||
// readable_file_name_set.insert("/proc/self/");
|
||||
|
||||
readable_file_name_set.insert("/sys/devices/system/cpu/");
|
||||
readable_file_name_set.insert("/proc/");
|
||||
soft_ban_file_name_set.insert("/etc/nsswitch.conf"); // for javac = =
|
||||
soft_ban_file_name_set.insert("/etc/passwd"); // for javac = =
|
||||
|
||||
readable_file_name_set.insert("/etc/timezone");
|
||||
readable_file_name_set.insert("/etc/fpc-2.6.2.cfg.d/");
|
||||
readable_file_name_set.insert("/etc/fpc.cfg");
|
||||
|
||||
statable_file_name_set.insert("/*");
|
||||
}
|
||||
}
|
||||
|
||||
string read_string_from_regs(reg_val_t addr, pid_t pid) {
|
||||
char res[MaxPathLen + 1], *ptr = res;
|
||||
while (ptr != res + MaxPathLen) {
|
||||
*(reg_val_t*)ptr = ptrace(PTRACE_PEEKDATA, pid, addr, NULL);
|
||||
for (int i = 0; i < sizeof(reg_val_t); i++, ptr++, addr++) {
|
||||
if (*ptr == 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
res[MaxPathLen] = 0;
|
||||
return res;
|
||||
}
|
||||
string read_abspath_from_regs(reg_val_t addr, pid_t pid) {
|
||||
return abspath(pid, read_string_from_regs(addr, pid));
|
||||
}
|
||||
|
||||
inline void soft_ban_syscall(pid_t pid, user_regs_struct reg) {
|
||||
reg.REG_SYSCALL += 1024;
|
||||
ptrace(PTRACE_SETREGS, pid, NULL, ®);
|
||||
}
|
||||
|
||||
inline bool on_dgs_file_detect(pid_t pid, user_regs_struct reg, const string &fn) {
|
||||
if (is_soft_ban_file(fn)) {
|
||||
soft_ban_syscall(pid, reg);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool check_safe_syscall(pid_t pid, bool need_show_trace_details) {
|
||||
struct user_regs_struct reg;
|
||||
ptrace(PTRACE_GETREGS, pid, NULL, ®);
|
||||
|
||||
int cur_instruction = ptrace(PTRACE_PEEKTEXT, pid, reg.rip - 2, NULL) & 0xffff;
|
||||
if (cur_instruction != 0x050f) {
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "informal syscall %d\n", cur_instruction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int syscall = (int)reg.REG_SYSCALL;
|
||||
if (0 > syscall || syscall >= 1000) {
|
||||
return false;
|
||||
}
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "syscall %d\n", (int)syscall);
|
||||
}
|
||||
|
||||
if (syscall_should_soft_ban[syscall]) {
|
||||
soft_ban_syscall(pid, reg);
|
||||
} else if (syscall_max_cnt[syscall]-- == 0) {
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "dgs %d\n", (int)syscall);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (syscall == __NR_open || syscall == __NR_openat) {
|
||||
reg_val_t fn_addr;
|
||||
reg_val_t flags;
|
||||
if (syscall == __NR_open) {
|
||||
fn_addr = reg.REG_ARG0;
|
||||
flags = reg.REG_ARG1;
|
||||
} else {
|
||||
fn_addr = reg.REG_ARG1;
|
||||
flags = reg.REG_ARG2;
|
||||
}
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "open ");
|
||||
|
||||
switch (flags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
fprintf(stderr, "r ");
|
||||
break;
|
||||
case O_WRONLY:
|
||||
fprintf(stderr, "w ");
|
||||
break;
|
||||
case O_RDWR:
|
||||
fprintf(stderr, "rw");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "??");
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, " %s\n", fn.c_str());
|
||||
}
|
||||
|
||||
bool is_read_only = (flags & O_ACCMODE) == O_RDONLY &&
|
||||
(flags & O_CREAT) == 0 &&
|
||||
(flags & O_EXCL) == 0 &&
|
||||
(flags & O_TRUNC) == 0;
|
||||
if (is_read_only) {
|
||||
if (realpath(fn) != "" && !is_readable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else {
|
||||
if (!is_writable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
}
|
||||
} else if (syscall == __NR_readlink || syscall == __NR_readlinkat) {
|
||||
reg_val_t fn_addr;
|
||||
if (syscall == __NR_readlink) {
|
||||
fn_addr = reg.REG_ARG0;
|
||||
} else {
|
||||
fn_addr = reg.REG_ARG1;
|
||||
}
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "readlink %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_readable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_unlink || syscall == __NR_unlinkat) {
|
||||
reg_val_t fn_addr;
|
||||
if (syscall == __NR_unlink) {
|
||||
fn_addr = reg.REG_ARG0;
|
||||
} else {
|
||||
fn_addr = reg.REG_ARG1;
|
||||
}
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "unlink %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_writable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_access) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "access %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_statable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_stat || syscall == __NR_lstat) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "stat %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_statable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_execve) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "execve %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_readable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_chmod || syscall == __NR_rename) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "change %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_writable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void on_syscall_exit(pid_t pid, bool need_show_trace_details) {
|
||||
struct user_regs_struct reg;
|
||||
ptrace(PTRACE_GETREGS, pid, NULL, ®);
|
||||
if (need_show_trace_details) {
|
||||
if ((long long int)reg.REG_SYSCALL >= 1024) {
|
||||
fprintf(stderr, "ban sys %lld\n", (long long int)reg.REG_SYSCALL - 1024);
|
||||
} else {
|
||||
fprintf(stderr, "exitsys %lld (ret %d)\n", (long long int)reg.REG_SYSCALL, (int)reg.REG_RET);
|
||||
}
|
||||
}
|
||||
|
||||
if ((long long int)reg.REG_SYSCALL >= 1024) {
|
||||
reg.REG_SYSCALL -= 1024;
|
||||
reg.REG_RET = -EACCES;
|
||||
ptrace(PTRACE_SETREGS, pid, NULL, ®);
|
||||
}
|
||||
}
|
0
judge_client/1/uoj_judger/work/.gitkeep
Normal file
0
judge_client/1/uoj_judger/work/.gitkeep
Normal file
BIN
problem/1.zip
Normal file
BIN
problem/1.zip
Normal file
Binary file not shown.
BIN
problem/116.zip
Normal file
BIN
problem/116.zip
Normal file
Binary file not shown.
BIN
problem/225.zip
Normal file
BIN
problem/225.zip
Normal file
Binary file not shown.
BIN
problem/52.zip
Normal file
BIN
problem/52.zip
Normal file
Binary file not shown.
BIN
problem/79.zip
Normal file
BIN
problem/79.zip
Normal file
Binary file not shown.
BIN
problem/8.zip
Normal file
BIN
problem/8.zip
Normal file
Binary file not shown.
23
uoj/1/.htaccess
Normal file
23
uoj/1/.htaccess
Normal file
@ -0,0 +1,23 @@
|
||||
Options -Indexes
|
||||
|
||||
php_value session.save_path /var/lib/php5/uoj
|
||||
php_value session.gc_maxlifetime 172800
|
||||
php_value session.cookie_lifetime 31536000
|
||||
|
||||
php_value post_max_size 10M
|
||||
php_value upload_max_filesize 10M
|
||||
|
||||
php_value session.gc_probability 1
|
||||
php_value session.gc_divisor 1000
|
||||
|
||||
DirectorySlash Off
|
||||
|
||||
DirectoryIndex
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{QUERY_STRING} ^$
|
||||
RewriteRule ^(.*)/$ /$1 [L,R=301]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
55
uoj/1/app/.default-config.php
Normal file
55
uoj/1/app/.default-config.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
return [
|
||||
'database' => [
|
||||
'database' => 'app_uoj233',
|
||||
'username' => 'root',
|
||||
'password' => '',
|
||||
'host' => '127.0.0.1'
|
||||
],
|
||||
'web' => [
|
||||
'domain' => null,
|
||||
'main' => [
|
||||
'protocol' => 'http',
|
||||
'host' => 'local_uoj.ac',
|
||||
'port' => 80
|
||||
],
|
||||
'blog' => [
|
||||
'protocol' => 'http',
|
||||
'host' => 'blog.local_uoj.ac',
|
||||
'port' => 80
|
||||
]
|
||||
],
|
||||
'security' => [
|
||||
'user' => [
|
||||
'client_salt' => 'salt0'
|
||||
],
|
||||
'cookie' => [
|
||||
'checksum_salt' => ['salt1', 'salt2', 'salt3']
|
||||
],
|
||||
],
|
||||
'mail' => [
|
||||
'noreply' => [
|
||||
'username' => 'noreply@none',
|
||||
'password' => 'noreply',
|
||||
'host' => 'smtp.sina.com',
|
||||
'secure' => '',
|
||||
'port' => 25
|
||||
]
|
||||
],
|
||||
'judger' => [
|
||||
'socket' => [
|
||||
'port' => '233',
|
||||
'password' => 'password233'
|
||||
]
|
||||
],
|
||||
'svn' => [
|
||||
'our-root' => [
|
||||
'username' => 'our-root',
|
||||
'password' => 'our-root'
|
||||
]
|
||||
],
|
||||
'switch' => [
|
||||
'ICP-license' => false,
|
||||
'web-analytics' => false
|
||||
]
|
||||
];
|
1
uoj/1/app/.htaccess
Normal file
1
uoj/1/app/.htaccess
Normal file
@ -0,0 +1 @@
|
||||
deny from all
|
89
uoj/1/app/cli.php
Normal file
89
uoj/1/app/cli.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
$_SERVER['DOCUMENT_ROOT'] = dirname(__DIR__);
|
||||
|
||||
require $_SERVER['DOCUMENT_ROOT'] . '/app/uoj-lib.php';
|
||||
|
||||
// TODO: more beautiful argv parser
|
||||
|
||||
$handlers = [
|
||||
'upgrade:up' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:up <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use($name) {
|
||||
Upgrader::up($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:down' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:down <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use($name) {
|
||||
Upgrader::down($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:refresh' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:refresh <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use($name) {
|
||||
Upgrader::refresh($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:remove' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:remove <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use($name) {
|
||||
Upgrader::remove($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:latest' => function () {
|
||||
if (func_num_args() != 0) {
|
||||
die("php cli.php upgrade:latest\n");
|
||||
}
|
||||
Upgrader::transaction(function() {
|
||||
Upgrader::upgradeToLatest();
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:remove-all' => function () {
|
||||
if (func_num_args() != 0) {
|
||||
die("php cli.php upgrade:remove-all\n");
|
||||
}
|
||||
Upgrader::transaction(function() {
|
||||
Upgrader::removeAll();
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'help' => 'showHelp'
|
||||
];
|
||||
|
||||
function showHelp() {
|
||||
global $handlers;
|
||||
echo "UOJ Command-Line Interface\n";
|
||||
echo "php cli.php <task-name> params1 params2 ...\n";
|
||||
echo "\n";
|
||||
echo "The following tasks are available:\n";
|
||||
foreach ($handlers as $cmd => $handler) {
|
||||
echo "\t$cmd\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($argv) <= 1) {
|
||||
showHelp();
|
||||
die();
|
||||
}
|
||||
|
||||
if (!isset($handlers[$argv[1]])) {
|
||||
echo "Invalid parameters.\n";
|
||||
showHelp();
|
||||
die();
|
||||
}
|
||||
|
||||
call_user_func_array($handlers[$argv[1]], array_slice($argv, 2));
|
56
uoj/1/app/controllers/add_contest.php
Normal file
56
uoj/1/app/controllers/add_contest.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!isSuperUser($myUser))
|
||||
{
|
||||
become403Page();
|
||||
}
|
||||
$time_form = new UOJForm('time');
|
||||
$time_form->addInput(
|
||||
'name', 'text', '比赛标题', 'New Contest',
|
||||
function($str) {
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->addInput(
|
||||
'start_time', 'text', '开始时间', date("Y-m-d H:i:s"),
|
||||
function($str, &$vdata) {
|
||||
try {
|
||||
$vdata['start_time'] = new DateTime($str);
|
||||
} catch (Exception $e) {
|
||||
return '无效时间格式';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->addInput(
|
||||
'last_min', 'text', '时长(单位:分钟)', 180,
|
||||
function($str) {
|
||||
return !validateUInt($str) ? '必须为一个整数' : '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->handle = function(&$vdata) {
|
||||
$start_time_str = $vdata['start_time']->format('Y-m-d H:i:s');
|
||||
|
||||
$purifier = HTML::pruifier();
|
||||
|
||||
$esc_name = $_POST['name'];
|
||||
$esc_name = $purifier->purify($esc_name);
|
||||
$esc_name = mysql_real_escape_string($esc_name);
|
||||
|
||||
mysql_query("insert into contests (name, start_time, last_min, status) values ('$esc_name', '$start_time_str', ${_POST['last_min']}, 'unfinished')");
|
||||
};
|
||||
$time_form->succ_href="/contests";
|
||||
$time_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('添加比赛') ?>
|
||||
<h1 class="page-header">添加比赛</h1>
|
||||
<div class="tab-pane active" id="tab-time">
|
||||
<?php
|
||||
$time_form->printHTML();
|
||||
?>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
43
uoj/1/app/controllers/announcements.php
Normal file
43
uoj/1/app/controllers/announcements.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
function echoBlogCell($blog) {
|
||||
$level = $blog['level'];
|
||||
|
||||
switch ($level) {
|
||||
case 0:
|
||||
$level_str = '';
|
||||
break;
|
||||
case 1:
|
||||
$level_str = '<span style="color:red">[三级置顶]</span> ';
|
||||
break;
|
||||
case 2:
|
||||
$level_str = '<span style="color:red">[二级置顶]</span> ';
|
||||
break;
|
||||
case 3:
|
||||
$level_str = '<span style="color:red">[一级置顶]</span> ';
|
||||
break;
|
||||
}
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td>' . $level_str . getBlogLink($blog['id']) . '</td>';
|
||||
echo '<td>' . getUserLink($blog['poster']) . '</td>';
|
||||
echo '<td>' . $blog['post_time'] . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
$header = <<<EOD
|
||||
<tr>
|
||||
<th width="60%">标题</th>
|
||||
<th width="20%">发表者</th>
|
||||
<th width="20%">发表日期</th>
|
||||
</tr>
|
||||
EOD;
|
||||
$config = [
|
||||
'table_classes' => ['table', 'table-hover'],
|
||||
'page_len' => 100
|
||||
];
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('announcements')) ?>
|
||||
<h3>公告</h3>
|
||||
<?php echoLongTable(array('blogs.id', 'poster', 'title', 'post_time', 'zan', 'level'), 'important_blogs, blogs', 'is_hidden = 0 and important_blogs.blog_id = blogs.id', 'order by level desc, important_blogs.blog_id desc', $header, 'echoBlogCell', $config); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
7
uoj/1/app/controllers/blog_show.php
Normal file
7
uoj/1/app/controllers/blog_show.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
redirectTo(HTML::blog_url($blog['poster'], $_SERVER["REQUEST_URI"]));
|
||||
?>
|
29
uoj/1/app/controllers/blogs.php
Normal file
29
uoj/1/app/controllers/blogs.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
function echoBlogCell($blog) {
|
||||
echo '<tr>';
|
||||
echo '<td>' . getBlogLink($blog['id']) . '</td>';
|
||||
echo '<td>' . getUserLink($blog['poster']) . '</td>';
|
||||
echo '<td>' . $blog['post_time'] . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
$header = <<<EOD
|
||||
<tr>
|
||||
<th width="60%">标题</th>
|
||||
<th width="20%">发表者</th>
|
||||
<th width="20%">发表日期</th>
|
||||
</tr>
|
||||
EOD;
|
||||
$config = array();
|
||||
$config['table_classes'] = array('table', 'table-hover');
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('blogs')) ?>
|
||||
<?php if (Auth::check()): ?>
|
||||
<div class="pull-right">
|
||||
<a href="<?= HTML::blog_url(Auth::id(), '/') ?>" class="btn btn-default btn-sm">我的博客首页</a>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<h3>博客总览</h3>
|
||||
<?php echoLongTable(array('id', 'poster', 'title', 'post_time', 'zan'), 'blogs', 'is_hidden = 0', 'order by post_time desc', $header, 'echoBlogCell', $config); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
194
uoj/1/app/controllers/change_user_info.php
Normal file
194
uoj/1/app/controllers/change_user_info.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
if (!Auth::check()) {
|
||||
redirectToLogin();
|
||||
}
|
||||
function handlePost() {
|
||||
global $myUser;
|
||||
if (!isset($_POST['old_password']))
|
||||
{
|
||||
return '无效表单';
|
||||
}
|
||||
$old_password = $_POST['old_password'];
|
||||
if (!validatePassword($old_password) || !checkPassword($myUser, $old_password))
|
||||
{
|
||||
return "失败:密码错误。";
|
||||
}
|
||||
if ($_POST['ptag'])
|
||||
{
|
||||
$password = $_POST['password'];
|
||||
if (!validatePassword($password))
|
||||
{
|
||||
return "失败:无效密码。";
|
||||
}
|
||||
$password = getPasswordToStore($password, $myUser['username']);
|
||||
DB::update("update user_info set password = '$password' where username = '{$myUser['username']}'");
|
||||
}
|
||||
|
||||
$email = $_POST['email'];
|
||||
if (!validateEmail($email))
|
||||
{
|
||||
return "失败:无效电子邮箱。";
|
||||
}
|
||||
$esc_email = mysql_real_escape_string($email);
|
||||
DB::update("update user_info set email = '$esc_email' where username = '{$myUser['username']}'");
|
||||
|
||||
if ($_POST['Qtag'])
|
||||
{
|
||||
$qq = $_POST['qq'];
|
||||
if (!validateQQ($qq))
|
||||
{
|
||||
return "失败:无效QQ。";
|
||||
}
|
||||
$esc_qq = mysql_real_escape_string($qq);
|
||||
DB::update("update user_info set qq = '$esc_qq' where username = '{$myUser['username']}'");
|
||||
}
|
||||
else
|
||||
DB::update("update user_info set QQ = NULL where username = '{$myUser['username']}'");
|
||||
if ($_POST['sex'] == "U" || $_POST['sex'] == 'M' || $_POST['sex'] == 'F')
|
||||
{
|
||||
$sex = $_POST['sex'];
|
||||
$esc_sex = mysql_real_escape_string($sex);
|
||||
DB::update("update user_info set sex = '$esc_sex' where username = '{$myUser['username']}'");
|
||||
}
|
||||
|
||||
if (validateMotto($_POST['motto'])) {
|
||||
$esc_motto = DB::escape($_POST['motto']);
|
||||
DB::update("update user_info set motto = '$esc_motto' where username = '{$myUser['username']}'");
|
||||
}
|
||||
|
||||
return "ok";
|
||||
}
|
||||
if (isset($_POST['change'])) {
|
||||
die(handlePost());
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['dialog'] = '';
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('modify my profile')) ?>
|
||||
<h2 class="page-header"><?= UOJLocale::get('modify my profile') ?></h2>
|
||||
<form id="form-update" class="form-horizontal">
|
||||
<h4><?= UOJLocale::get('please enter your password for authorization') ?></h4>
|
||||
<div id="div-old_password" class="form-group">
|
||||
<label for="input-old_password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" name="old_password" id="input-old_password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-old_password"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h4><?= UOJLocale::get('please enter your new profile') ?></h4>
|
||||
<div id="div-password" class="form-group">
|
||||
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('new password') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your new password') ?>" maxlength="20" />
|
||||
<input type="password" class="form-control top-buffer-sm" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your new password') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-password"><?= UOJLocale::get('leave it blank if you do not want to change the password') ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-email" class="form-group">
|
||||
<label for="input-email" class="col-sm-2 control-label"><?= UOJLocale::get('email') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="email" class="form-control" name="email" id="input-email" value="<?=$myUser['email']?>" placeholder="<?= UOJLocale::get('enter your email') ?>" maxlength="50" />
|
||||
<span class="help-block" id="help-email"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-qq" class="form-group">
|
||||
<label for="input-qq" class="col-sm-2 control-label"><?= UOJLocale::get('QQ') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" name="qq" id="input-qq" value="<?= $myUser['qq'] != 0 ? $myUser['qq'] : '' ?>" placeholder="<?= UOJLocale::get('enter your QQ') ?>" maxlength="50" />
|
||||
<span class="help-block" id="help-qq"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-sex" class="form-group">
|
||||
<label for="input-sex" class="col-sm-2 control-label"><?= UOJLocale::get('sex') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<select class="form-control" id="input-sex" name="sex">
|
||||
<option value="U"<?= Auth::user()['sex'] == 'U' ? ' selected="selected"' : ''?>><?= UOJLocale::get('refuse to answer') ?></option>
|
||||
<option value="M"<?= Auth::user()['sex'] == 'M' ? ' selected="selected"' : ''?>><?= UOJLocale::get('male') ?></option>
|
||||
<option value="F"<?= Auth::user()['sex'] == 'F' ? ' selected="selected"' : ''?>><?= UOJLocale::get('female') ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-motto" class="form-group">
|
||||
<label for="input-motto" class="col-sm-2 control-label"><?= UOJLocale::get('motto') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<textarea class="form-control" id="input-motto" name="motto"><?=HTML::escape($myUser['motto'])?></textarea>
|
||||
<span class="help-block" id="help-motto"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-3">
|
||||
<p class="form-control-static"><strong><?= UOJLocale::get('change avatar help') ?></strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-3">
|
||||
<button type="submit" id="button-submit" class="btn btn-default"><?= UOJLocale::get('submit') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateUpdatePost() {
|
||||
var ok = true;
|
||||
ok &= getFormErrorAndShowHelp('email', validateEmail);
|
||||
ok &= getFormErrorAndShowHelp('old_password', validatePassword);
|
||||
|
||||
if ($('#input-password').val().length > 0)
|
||||
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
|
||||
if ($('#input-qq').val().length > 0)
|
||||
ok &= getFormErrorAndShowHelp('qq', validateQQ);
|
||||
ok &= getFormErrorAndShowHelp('motto', validateMotto);
|
||||
return ok;
|
||||
}
|
||||
function submitUpdatePost() {
|
||||
if (!validateUpdatePost())
|
||||
return;
|
||||
$.post('/user/modify-profile', {
|
||||
change : '',
|
||||
etag : $('#input-email').val().length,
|
||||
ptag : $('#input-password').val().length,
|
||||
Qtag : $('#input-qq').val().length,
|
||||
email : $('#input-email').val(),
|
||||
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>"),
|
||||
old_password : md5($('#input-old_password').val(), "<?= getPasswordClientSalt() ?>"),
|
||||
qq : $('#input-qq').val(),
|
||||
sex : $('#input-sex').val(),
|
||||
motto : $('#input-motto').val()
|
||||
}, function(msg) {
|
||||
if (msg == 'ok') {
|
||||
BootstrapDialog.show({
|
||||
title : '修改成功',
|
||||
message : '用户信息修改成功',
|
||||
type : BootstrapDialog.TYPE_SUCCESS,
|
||||
buttons : [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
onhidden : function(dialog) {
|
||||
window.location.href = '/user/profile/<?=$myUser['username']?>';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
BootstrapDialog.show({
|
||||
title : '修改失败',
|
||||
message : msg,
|
||||
type : BootstrapDialog.TYPE_DANGER,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
$(document).ready(function(){$('#form-update').submit(function(e) {submitUpdatePost();e.preventDefault();});
|
||||
});
|
||||
</script>
|
||||
<?php echoUOJPageFooter() ?>
|
||||
|
64
uoj/1/app/controllers/click_zan.php
Normal file
64
uoj/1/app/controllers/click_zan.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
function validateZan() {
|
||||
if (!validateUInt($_POST['id']))
|
||||
return false;
|
||||
if (!validateInt($_POST['delta']))
|
||||
return false;
|
||||
if ($_POST['delta'] != 1 && $_POST['delta'] != -1)
|
||||
return false;
|
||||
if ($_POST['type'] != 'B' && $_POST['type'] != 'BC' && $_POST['type'] != 'P' && $_POST['type'] != 'C')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if (!validateZan()) {
|
||||
die('<div class="text-danger">failed</div>');
|
||||
}
|
||||
if ($myUser == null) {
|
||||
die('<div class="text-danger">please <a href="'.HTML::url('/login').'">log in</a></div>');
|
||||
}
|
||||
|
||||
$id = $_POST['id'];
|
||||
$delta = $_POST['delta'];
|
||||
$type = $_POST['type'];
|
||||
|
||||
switch ($type) {
|
||||
case 'B':
|
||||
$table_name = 'blogs';
|
||||
break;
|
||||
case 'BC':
|
||||
$table_name = 'blogs_comments';
|
||||
break;
|
||||
case 'P':
|
||||
$table_name = 'problems';
|
||||
break;
|
||||
case 'C':
|
||||
$table_name = 'contests';
|
||||
break;
|
||||
}
|
||||
|
||||
$cur = queryZanVal($id, $type, $myUser);
|
||||
|
||||
if ($cur != $delta) {
|
||||
$row = mysql_fetch_array(mysql_query("select zan from $table_name where id = $id"));
|
||||
if ($row == null) {
|
||||
die('<div class="text-danger">failed</div>');
|
||||
}
|
||||
$cur += $delta;
|
||||
if ($cur == 0) {
|
||||
mysql_query("delete from click_zans where username = '{$myUser['username']}' and type = '$type' and target_id = $id");
|
||||
} else if ($cur != $delta) {
|
||||
mysql_query("update click_zans set val = '$cur' where username = '{$myUser['username']}' and type = '$type' and target_id = $id");
|
||||
} else {
|
||||
mysql_query("insert into click_zans (username, type, target_id, val) values ('{$myUser['username']}', '$type', $id, $cur)");
|
||||
}
|
||||
$cnt = $row['zan'] + $delta;
|
||||
mysql_query("update $table_name set zan = $cnt where id = $id");
|
||||
} else {
|
||||
$row = mysql_fetch_array(mysql_query("select zan from $table_name where id = $id"));
|
||||
if ($row == null) {
|
||||
die('<div class="text-danger">failed</div>');
|
||||
}
|
||||
$cnt = $row['zan'];
|
||||
}
|
||||
?>
|
||||
<?= getClickZanBlock($type, $id, $cnt, $cur) ?>
|
523
uoj/1/app/controllers/contest_inside.php
Normal file
523
uoj/1/app/controllers/contest_inside.php
Normal file
@ -0,0 +1,523 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
genMoreContestInfo($contest);
|
||||
|
||||
if (!hasContestPermission($myUser, $contest)) {
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
header("Location: /contest/{$contest['id']}/register");
|
||||
die();
|
||||
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
|
||||
if ($myUser == null || !hasRegistered($myUser, $contest)) {
|
||||
becomeMsgPage("<h1>比赛正在进行中</h1><p>很遗憾,您尚未报名。比赛结束后再来看吧~</p>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['check_notice'])) {
|
||||
$result = mysql_query("select * from contests_notice where contest_id = '${contest['id']}' order by time desc limit 1");
|
||||
try {
|
||||
while ($row = mysql_fetch_array($result)) {
|
||||
if (new DateTime($row['time']) > new DateTime($_POST['last_time'])) {
|
||||
die(json_encode(array('msg' => $row['title'] . ' : ' . $row['content'], 'time' => UOJTime::$time_now_str)));
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
die(json_encode(array('time' => UOJTime::$time_now_str)));
|
||||
}
|
||||
|
||||
if (isset($_GET['tab'])) {
|
||||
$cur_tab = $_GET['tab'];
|
||||
} else {
|
||||
$cur_tab = 'dashboard';
|
||||
}
|
||||
|
||||
// problems: pos => id
|
||||
// data : id, submit_time, submitter, problem_pos, score
|
||||
// people : username, user_rating
|
||||
function queryContestData() {
|
||||
global $contest;
|
||||
$problems = array();
|
||||
$prob_pos = array();
|
||||
$n_problems = 0;
|
||||
$result = mysql_query("select problem_id from contests_problems where contest_id = ${contest['id']} order by problem_id");
|
||||
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
|
||||
$prob_pos[$problems[] = (int)$row[0]] = $n_problems++;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
if ($contest['cur_progress'] < CONTEST_FINISHED) {
|
||||
$result = mysql_query("select id, submit_time, submitter, problem_id, score from submissions where contest_id = {$contest['id']} and score is not null order by id");
|
||||
} else {
|
||||
$result = mysql_query("select submission_id, date_add('{$contest['start_time_str']}', interval penalty second), submitter, problem_id, score from contests_submissions where contest_id = {$contest['id']}");
|
||||
}
|
||||
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
|
||||
$row[0] = (int)$row[0];
|
||||
$row[3] = $prob_pos[$row[3]];
|
||||
$row[4] = (int)$row[4];
|
||||
$data[] = $row;
|
||||
}
|
||||
|
||||
$people = array();
|
||||
$result = mysql_query("select username, user_rating from contests_registrants where contest_id = {$contest['id']} and has_participated = 1");
|
||||
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
|
||||
$row[1] = (int)$row[1];
|
||||
$people[] = $row;
|
||||
}
|
||||
|
||||
return array('problems' => $problems, 'data' => $data, 'people' => $people);
|
||||
}
|
||||
|
||||
function calcStandings($contest_data, &$score, &$standings, $update_contests_submissions = false) {
|
||||
global $contest;
|
||||
|
||||
// score: username, problem_pos => score, penalty, id
|
||||
$score = array();
|
||||
$n_people = count($contest_data['people']);
|
||||
$n_problems = count($contest_data['problems']);
|
||||
foreach ($contest_data['people'] as $person) {
|
||||
$score[$person[0]] = array();
|
||||
}
|
||||
foreach ($contest_data['data'] as $submission) {
|
||||
$penalty = (new DateTime($submission[1]))->getTimestamp() - $contest['start_time']->getTimestamp();
|
||||
if ($contest['extra_config']['standings_version'] >= 2) {
|
||||
if ($submission[4] == 0) {
|
||||
$penalty = 0;
|
||||
}
|
||||
}
|
||||
$score[$submission[2]][$submission[3]] = array($submission[4], $penalty, $submission[0]);
|
||||
}
|
||||
|
||||
// standings: rank => score, penalty, [username, user_rating], virtual_rank
|
||||
$standings = array();
|
||||
foreach ($contest_data['people'] as $person) {
|
||||
$cur = array(0, 0, $person);
|
||||
for ($i = 0; $i < $n_problems; $i++) {
|
||||
if (isset($score[$person[0]][$i])) {
|
||||
$cur_row = $score[$person[0]][$i];
|
||||
$cur[0] += $cur_row[0];
|
||||
$cur[1] += $cur_row[1];
|
||||
if ($update_contests_submissions) {
|
||||
DB::insert("insert into contests_submissions (contest_id, submitter, problem_id, submission_id, score, penalty) values ({$contest['id']}, '{$person[0]}', {$contest_data['problems'][$i]}, {$cur_row[2]}, {$cur_row[0]}, {$cur_row[1]})");
|
||||
}
|
||||
}
|
||||
}
|
||||
$standings[] = $cur;
|
||||
}
|
||||
|
||||
usort($standings, function($lhs, $rhs) {
|
||||
if ($lhs[0] != $rhs[0]) {
|
||||
return $rhs[0] - $lhs[0];
|
||||
} else if ($lhs[1] != $rhs[1]) {
|
||||
return $lhs[1] - $rhs[1];
|
||||
} else {
|
||||
return strcmp($lhs[2][0], $rhs[2][0]);
|
||||
}
|
||||
});
|
||||
|
||||
$is_same_rank = function($lhs, $rhs) {
|
||||
return $lhs[0] == $rhs[0] && $lhs[1] == $rhs[1];
|
||||
};
|
||||
|
||||
for ($i = 0; $i < $n_people; $i++) {
|
||||
if ($i == 0 || !$is_same_rank($standings[$i - 1], $standings[$i])) {
|
||||
$standings[$i][] = $i + 1;
|
||||
} else {
|
||||
$standings[$i][] = $standings[$i - 1][3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
if (CONTEST_PENDING_FINAL_TEST <= $contest['cur_progress'] && $contest['cur_progress'] <= CONTEST_TESTING) {
|
||||
$start_test_form = new UOJForm('start_test');
|
||||
$start_test_form->handle = function() {
|
||||
global $contest;
|
||||
$result = mysql_query("select id, problem_id, content from submissions where contest_id = {$contest['id']}");
|
||||
while ($submission = mysql_fetch_array($result, MYSQL_ASSOC)) {
|
||||
if (!isset($contest['extra_config']["problem_{$submission['problem_id']}"])) {
|
||||
$content = json_decode($submission['content'], true);
|
||||
if (isset($content['final_test_config'])) {
|
||||
$content['config'] = $content['final_test_config'];
|
||||
unset($content['final_test_config']);
|
||||
}
|
||||
if (isset($content['first_test_config'])) {
|
||||
unset($content['first_test_config']);
|
||||
}
|
||||
$esc_content = mysql_real_escape_string(json_encode($content));
|
||||
DB::update("update submissions set judge_time = NULL, result = '', score = NULL, status = 'Waiting Rejudge', content = '$esc_content' where id = {$submission['id']}");
|
||||
}
|
||||
}
|
||||
mysql_query("update contests set status = 'testing' where id = {$contest['id']}");
|
||||
};
|
||||
$start_test_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
|
||||
$start_test_form->submit_button_config['smart_confirm'] = '';
|
||||
if ($contest['cur_progress'] < CONTEST_TESTING) {
|
||||
$start_test_form->submit_button_config['text'] = '开始最终测试';
|
||||
} else {
|
||||
$start_test_form->submit_button_config['text'] = '重新开始最终测试';
|
||||
}
|
||||
|
||||
$start_test_form->runAtServer();
|
||||
}
|
||||
if ($contest['cur_progress'] >= CONTEST_TESTING) {
|
||||
$publish_result_form = new UOJForm('publish_result');
|
||||
$publish_result_form->handle = function() {
|
||||
// time config
|
||||
set_time_limit(0);
|
||||
ignore_user_abort(true);
|
||||
|
||||
global $contest;
|
||||
$contest_data = queryContestData();
|
||||
calcStandings($contest_data, $score, $standings, true);
|
||||
if (!isset($contest['extra_config']['unrated'])) {
|
||||
$rating_k = isset($contest['extra_config']['rating_k']) ? $contest['extra_config']['rating_k'] : 400;
|
||||
$ratings = calcRating($standings, $rating_k);
|
||||
} else {
|
||||
$ratings = array();
|
||||
for ($i = 0; $i < count($standings); $i++) {
|
||||
$ratings[$i] = $standings[$i][2][1];
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($standings); $i++) {
|
||||
$user = queryUser($standings[$i][2][0]);
|
||||
$change = $ratings[$i] - $user['rating'];
|
||||
$user_link = getUserLink($user['username']);
|
||||
|
||||
if ($change != 0) {
|
||||
$tail = '<strong style="color:red">' . ($change > 0 ? '+' : '') . $change . '</strong>';
|
||||
$content = <<<EOD
|
||||
<p>${user_link} 您好:</p>
|
||||
<p class="indent2">您在 <a href="/contest/{$contest['id']}">{$contest['name']}</a> 这场比赛后的Rating变化为${tail},当前Rating为 <strong style="color:red">{$ratings[$i]}</strong>。</p>
|
||||
EOD;
|
||||
} else {
|
||||
$content = <<<EOD
|
||||
<p>${user_link} 您好:</p>
|
||||
<p class="indent2">您在 <a href="/contest/{$contest['id']}">{$contest['name']}</a> 这场比赛后Rating没有变化。当前Rating为 <strong style="color:red">{$ratings[$i]}</strong>。</p>
|
||||
EOD;
|
||||
}
|
||||
sendSystemMsg($user['username'], 'Rating变化通知', $content);
|
||||
mysql_query("update user_info set rating = {$ratings[$i]} where username = '{$standings[$i][2][0]}'");
|
||||
mysql_query("update contests_registrants set rank = {$standings[$i][3]} where contest_id = {$contest['id']} and username = '{$standings[$i][2][0]}'");
|
||||
}
|
||||
mysql_query("update contests set status = 'finished' where id = {$contest['id']}");
|
||||
};
|
||||
$publish_result_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
|
||||
$publish_result_form->submit_button_config['smart_confirm'] = '';
|
||||
$publish_result_form->submit_button_config['text'] = '公布成绩';
|
||||
|
||||
$publish_result_form->runAtServer();
|
||||
}
|
||||
}
|
||||
|
||||
function echoDashboard() {
|
||||
global $myUser, $contest, $post_notice;
|
||||
|
||||
echo '<div class="table-responsive">';
|
||||
echo '<table class="table table-bordered table-hover table-striped table-text-center">';
|
||||
echo '<thead>';
|
||||
echo '<th style="width:5em">#</th>';
|
||||
echo '<th>', UOJLocale::get('problems::problem'), '</th>';
|
||||
echo '</thead>';
|
||||
echo '<tbody>';
|
||||
$contest_problems = DB::selectAll("select contests_problems.problem_id, best_ac_submissions.submission_id from contests_problems left join best_ac_submissions on contests_problems.problem_id = best_ac_submissions.problem_id and submitter = '{$myUser['username']}' where contest_id = {$contest['id']} order by contests_problems.problem_id asc");
|
||||
for ($i = 0; $i < count($contest_problems); $i++) {
|
||||
$problem = queryProblemBrief($contest_problems[$i]['problem_id']);
|
||||
echo '<tr>';
|
||||
if ($contest_problems[$i]['submission_id']) {
|
||||
echo '<td class="success">';
|
||||
} else {
|
||||
echo '<td>';
|
||||
}
|
||||
echo chr(ord('A') + $i), '</td>';
|
||||
echo '<td>', getContestProblemLink($problem, $contest['id']), '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<h3>', UOJLocale::get('contests::contest notice'), '</h3>';
|
||||
$header = '';
|
||||
$header .= '<tr>';
|
||||
$header .= '<th style="width:10em">'.UOJLocale::get('title').'</th>';
|
||||
$header .= '<th>'.UOJLocale::get('content').'</th>';
|
||||
$header .= '<th style="width:12em">'.UOJLocale::get('time').'</th>';
|
||||
$header .= '</tr>';
|
||||
echoLongTable(array('*'), 'contests_notice', "contest_id = '{$contest['id']}'", "order by time desc", $header,
|
||||
function($notice) {
|
||||
echo '<tr>';
|
||||
echo '<td>', HTML::escape($notice['title']), '</td>';
|
||||
echo '<td style="white-space:pre-wrap; text-align: left">', $notice['content'], '</td>';
|
||||
echo '<td>', $notice['time'], '</td>';
|
||||
echo '</tr>';
|
||||
},
|
||||
array(
|
||||
'table_classes' => array('table', 'table-bordered', 'table-hover', 'table-striped', 'table-vertical-middle', 'table-text-center'),
|
||||
'echo_full' => true
|
||||
)
|
||||
);
|
||||
|
||||
if (isSuperUser(Auth::user())) {
|
||||
echo '<div class="text-center">';
|
||||
echo '<button id="button-display-post-notice" type="button" class="btn btn-danger btn-xs">发布比赛公告</button>';
|
||||
echo '</div>';
|
||||
echo '<div id="div-form-post-notice" style="display:none" class="bot-buffer-md">';
|
||||
$post_notice->printHTML();
|
||||
echo '</div>';
|
||||
echo <<<EOD
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#button-display-post-notice').click(function() {
|
||||
$('#div-form-post-notice').toggle('fast');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOD;
|
||||
}
|
||||
}
|
||||
|
||||
function echoMySubmissions() {
|
||||
global $contest, $myUser;
|
||||
|
||||
$show_all_submissions_status = Cookie::get('show_all_submissions') !== null ? 'checked="checked" ' : '';
|
||||
$show_all_submissions = UOJLocale::get('contests::show all submissions');
|
||||
echo <<<EOD
|
||||
<div class="checkbox text-right">
|
||||
<label for="input-show_all_submissions"><input type="checkbox" id="input-show_all_submissions" $show_all_submissions_status/> $show_all_submissions</label>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$('#input-show_all_submissions').click(function() {
|
||||
if (this.checked) {
|
||||
$.cookie('show_all_submissions', '');
|
||||
} else {
|
||||
$.removeCookie('show_all_submissions');
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
EOD;
|
||||
if (Cookie::get('show_all_submissions') !== null) {
|
||||
echoSubmissionsList("contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
|
||||
} else {
|
||||
echoSubmissionsList("submitter = '{$myUser['username']}' and contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
|
||||
}
|
||||
}
|
||||
|
||||
function echoStandings() {
|
||||
global $contest;
|
||||
|
||||
$contest_data = queryContestData();
|
||||
calcStandings($contest_data, $score, $standings);
|
||||
|
||||
echo '<div id="standings">';
|
||||
echo '</div>';
|
||||
|
||||
/*
|
||||
echo '<div class="table-responsive">';
|
||||
echo '<table id="standings-table" class="table table-bordered table-striped table-text-center table-vertical-middle">';
|
||||
echo '</table>';
|
||||
echo '</div>';
|
||||
*/
|
||||
|
||||
echo '<script type="text/javascript">';
|
||||
echo 'standings_version=', $contest['extra_config']['standings_version'], ';';
|
||||
echo 'contest_id=', $contest['id'], ';';
|
||||
echo 'standings=', json_encode($standings), ';';
|
||||
echo 'score=', json_encode($score), ';';
|
||||
echo 'problems=', json_encode($contest_data['problems']), ';';
|
||||
echo '$(document).ready(showStandings());';
|
||||
echo '</script>';
|
||||
}
|
||||
|
||||
function echoContestCountdown() {
|
||||
global $contest;
|
||||
$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
||||
$time_str = UOJTime::$time_now_str;
|
||||
$contest_ends_in = UOJLocale::get('contests::contest ends in');
|
||||
echo <<<EOD
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">$contest_ends_in</h3>
|
||||
</div>
|
||||
<div class="panel-body text-center countdown" data-rest="$rest_second"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
checkContestNotice({$contest['id']}, '$time_str');
|
||||
</script>
|
||||
EOD;
|
||||
}
|
||||
|
||||
function echoContestJudgeProgress() {
|
||||
global $contest;
|
||||
if ($contest['cur_progress'] < CONTEST_TESTING) {
|
||||
$rop = 0;
|
||||
$title = UOJLocale::get('contests::contest pending final test');
|
||||
} else {
|
||||
$total = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']}");
|
||||
$n_judged = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']} and status = 'Judged'");
|
||||
$rop = $total == 0 ? 100 : (int)($n_judged / $total * 100);
|
||||
$title = UOJLocale::get('contests::contest final testing');
|
||||
}
|
||||
echo <<<EOD
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">$title</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="progress bot-buffer-no">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$rop" aria-valuemin="0" aria-valuemax="100" style="width: {$rop}%; min-width: 20px;">{$rop}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
EOD;
|
||||
}
|
||||
|
||||
function echoContestFinished() {
|
||||
$title = UOJLocale::get('contests::contest ended');
|
||||
echo <<<EOD
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">$title</h3>
|
||||
</div>
|
||||
</div>
|
||||
EOD;
|
||||
}
|
||||
|
||||
$post_notice = new UOJForm('post_notice');
|
||||
$post_notice->addInput('title', 'text', '标题', '',
|
||||
function($title) {
|
||||
if (!$title) {
|
||||
return '标题不能为空';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$post_notice->addTextArea('content', '正文', '',
|
||||
function($content) {
|
||||
if (!$content) {
|
||||
return '公告不能为空';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$post_notice->handle = function() {
|
||||
global $contest;
|
||||
$title = DB::escape($_POST['title']);
|
||||
$content = DB::escape($_POST['content']);
|
||||
mysql_query("insert into contests_notice (contest_id, title, content, time) values ('{$contest['id']}', '$title', '$content', now())");
|
||||
};
|
||||
$post_notice->runAtServer();
|
||||
|
||||
$tabs_info = array(
|
||||
'dashboard' => array(
|
||||
'name' => UOJLocale::get('contests::contest dashboard'),
|
||||
'url' => "/contest/{$contest['id']}"
|
||||
),
|
||||
'submissions' => array(
|
||||
'name' => UOJLocale::get('contests::contest submissions'),
|
||||
'url' => "/contest/{$contest['id']}/submissions"
|
||||
),
|
||||
'standings' => array(
|
||||
'name' => UOJLocale::get('contests::contest standings'),
|
||||
'url' => "/contest/{$contest['id']}/standings"
|
||||
)
|
||||
);
|
||||
|
||||
if (!isset($tabs_info[$cur_tab])) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$page_header = HTML::stripTags($contest['name']) . ' - ';
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . $tabs_info[$cur_tab]['name'] . ' - ' . UOJLocale::get('contests::contest')) ?>
|
||||
<div class="text-center">
|
||||
<h1><?= $contest['name'] ?></h1>
|
||||
<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
|
||||
</div>
|
||||
<div class="row">
|
||||
<?php if ($cur_tab == 'standings'): ?>
|
||||
<div class="col-sm-12">
|
||||
<?php else: ?>
|
||||
<div class="col-sm-9">
|
||||
<?php endif ?>
|
||||
<?= HTML::tablist($tabs_info, $cur_tab) ?>
|
||||
<div class="top-buffer-md">
|
||||
<?php
|
||||
if ($cur_tab == 'dashboard') {
|
||||
echoDashboard();
|
||||
} elseif ($cur_tab == 'submissions') {
|
||||
echoMySubmissions();
|
||||
} elseif ($cur_tab == 'standings') {
|
||||
echoStandings();
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($cur_tab == 'standings'): ?>
|
||||
<div class="col-sm-12">
|
||||
<hr />
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<?php
|
||||
if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS) {
|
||||
echoContestCountdown();
|
||||
} else if ($contest['cur_progress'] <= CONTEST_TESTING) {
|
||||
echoContestJudgeProgress();
|
||||
} else {
|
||||
echoContestFinished();
|
||||
}
|
||||
?>
|
||||
<?php if ($cur_tab == 'standings'): ?>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<?php endif ?>
|
||||
<p>此次比赛为OI赛制。</p>
|
||||
<p><strong>注意:比赛时只显示测样例的结果。</strong></p>
|
||||
|
||||
<a href="/contest/<?=$contest['id']?>/registrants" class="btn btn-info btn-block"><?= UOJLocale::get('contests::contest registrants') ?></a>
|
||||
<?php if (isSuperUser($myUser)): ?>
|
||||
<a href="/contest/<?=$contest['id']?>/manage" class="btn btn-primary btn-block">管理</a>
|
||||
<?php if (isset($start_test_form)): ?>
|
||||
<div class="top-buffer-sm">
|
||||
<?php $start_test_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php if (isset($publish_result_form)): ?>
|
||||
<div class="top-buffer-sm">
|
||||
<?php $publish_result_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($contest['extra_config']['links']) { ?>
|
||||
<?php if ($cur_tab == 'standings'): ?>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="panel panel-info">
|
||||
<?php else: ?>
|
||||
<div class="panel panel-info top-buffer-lg">
|
||||
<?php endif ?>
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">比赛资料</h3>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<?php foreach ($contest['extra_config']['links'] as $link) { ?>
|
||||
<a href="/blog/<?=$link[1]?>" class="list-group-item"><?=$link[0]?></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
262
uoj/1/app/controllers/contest_manage.php
Normal file
262
uoj/1/app/controllers/contest_manage.php
Normal file
@ -0,0 +1,262 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
genMoreContestInfo($contest);
|
||||
|
||||
if (!isSuperUser($myUser)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
$time_form = new UOJForm('time');
|
||||
$time_form->addInput(
|
||||
'name', 'text', '比赛标题', $contest['name'],
|
||||
function($str) {
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->addInput(
|
||||
'start_time', 'text', '开始时间', $contest['start_time_str'],
|
||||
function($str, &$vdata) {
|
||||
try {
|
||||
$vdata['start_time'] = new DateTime($str);
|
||||
} catch (Exception $e) {
|
||||
return '无效时间格式';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->addInput(
|
||||
'last_min', 'text', '时长(单位:分钟)', $contest['last_min'],
|
||||
function($str) {
|
||||
return !validateUInt($str) ? '必须为一个整数' : '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->handle = function(&$vdata) {
|
||||
global $contest;
|
||||
$start_time_str = $vdata['start_time']->format('Y-m-d H:i:s');
|
||||
|
||||
$purifier = HTML::pruifier();
|
||||
|
||||
$esc_name = $_POST['name'];
|
||||
$esc_name = $purifier->purify($esc_name);
|
||||
$esc_name = DB::escape($esc_name);
|
||||
|
||||
DB::update("update contests set start_time = '$start_time_str', last_min = {$_POST['last_min']}, name = '$esc_name' where id = {$contest['id']}");
|
||||
};
|
||||
|
||||
$managers_form = newAddDelCmdForm('managers',
|
||||
function($username) {
|
||||
if (!validateUsername($username) || !queryUser($username)) {
|
||||
return "不存在名为{$username}的用户";
|
||||
}
|
||||
return '';
|
||||
},
|
||||
function($type, $username) {
|
||||
global $contest;
|
||||
if ($type == '+') {
|
||||
mysql_query("insert into contests_permissions (contest_id, username) values (${contest['id']}, '$username')");
|
||||
} else if ($type == '-') {
|
||||
mysql_query("delete from contests_permissions where contest_id = ${contest['id']} and username = '$username'");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$problems_form = newAddDelCmdForm('problems',
|
||||
function($cmd) {
|
||||
if (!preg_match('/^(\d+)\s*(\[\S+\])?$/', $cmd, $matches)) {
|
||||
return "无效题号";
|
||||
}
|
||||
$problem_id = $matches[1];
|
||||
if (!validateUInt($problem_id) || !($problem = queryProblemBrief($problem_id))) {
|
||||
return "不存在题号为{$problem_id}的题";
|
||||
}
|
||||
if (!hasProblemPermission(Auth::user(), $problem)) {
|
||||
return "无权添加题号为{$problem_id}的题";
|
||||
}
|
||||
return '';
|
||||
},
|
||||
function($type, $cmd) {
|
||||
global $contest;
|
||||
|
||||
if (!preg_match('/^(\d+)\s*(\[\S+\])?$/', $cmd, $matches)) {
|
||||
return "无效题号";
|
||||
}
|
||||
|
||||
$problem_id = $matches[1];
|
||||
|
||||
if ($type == '+') {
|
||||
DB::insert("insert into contests_problems (contest_id, problem_id) values ({$contest['id']}, '$problem_id')");
|
||||
} else if ($type == '-') {
|
||||
DB::delete("delete from contests_problems where contest_id = {$contest['id']} and problem_id = '$problem_id'");
|
||||
}
|
||||
|
||||
if (isset($matches[2])) {
|
||||
switch ($matches[2]) {
|
||||
case '[sample]':
|
||||
unset($contest['extra_config']["problem_$problem_id"]);
|
||||
break;
|
||||
case '[full]':
|
||||
$contest['extra_config']["problem_$problem_id"] = 'full';
|
||||
break;
|
||||
case '[no-details]':
|
||||
$contest['extra_config']["problem_$problem_id"] = 'no-details';
|
||||
break;
|
||||
}
|
||||
$esc_extra_config = json_encode($contest['extra_config']);
|
||||
$esc_extra_config = DB::escape($esc_extra_config);
|
||||
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$rating_k_form = new UOJForm('rating_k');
|
||||
$rating_k_form->addInput('rating_k', 'text', 'rating 变化上限', isset($contest['extra_config']['rating_k']) ? $contest['extra_config']['rating_k'] : 400,
|
||||
function ($x) {
|
||||
if (!validateUInt($x) || $x < 1 || $x > 1000) {
|
||||
return '不合法的上限';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$rating_k_form->handle = function() {
|
||||
global $contest;
|
||||
$contest['extra_config']['rating_k'] = $_POST['rating_k'];
|
||||
$esc_extra_config = json_encode($contest['extra_config']);
|
||||
$esc_extra_config = DB::escape($esc_extra_config);
|
||||
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
|
||||
};
|
||||
$rating_k_form->runAtServer();
|
||||
|
||||
$rated_form = new UOJForm('rated');
|
||||
$rated_form->handle = function() {
|
||||
global $contest;
|
||||
if (isset($contest['extra_config']['unrated'])) {
|
||||
unset($contest['extra_config']['unrated']);
|
||||
} else {
|
||||
$contest['extra_config']['unrated'] = '';
|
||||
}
|
||||
$esc_extra_config = json_encode($contest['extra_config']);
|
||||
$esc_extra_config = DB::escape($esc_extra_config);
|
||||
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
|
||||
};
|
||||
$rated_form->submit_button_config['class_str'] = 'btn btn-warning btn-block';
|
||||
$rated_form->submit_button_config['text'] = isset($contest['extra_config']['unrated']) ? '设置比赛为rated' : '设置比赛为unrated';
|
||||
$rated_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$rated_form->runAtServer();
|
||||
|
||||
$version_form = new UOJForm('version');
|
||||
$version_form->addInput('standings_version', 'text', '排名版本', $contest['extra_config']['standings_version'],
|
||||
function ($x) {
|
||||
if (!validateUInt($x) || $x < 1 || $x > 2) {
|
||||
return '不是合法的版本号';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$version_form->handle = function() {
|
||||
global $contest;
|
||||
$contest['extra_config']['standings_version'] = $_POST['standings_version'];
|
||||
$esc_extra_config = json_encode($contest['extra_config']);
|
||||
$esc_extra_config = DB::escape($esc_extra_config);
|
||||
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
|
||||
};
|
||||
$version_form->runAtServer();
|
||||
}
|
||||
|
||||
$time_form->runAtServer();
|
||||
$managers_form->runAtServer();
|
||||
$problems_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - 比赛管理') ?>
|
||||
<h1 class="page-header" align="center"><?=$contest['name']?> 管理</h1>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="active"><a href="#tab-time" role="tab" data-toggle="tab">比赛时间</a></li>
|
||||
<li><a href="#tab-managers" role="tab" data-toggle="tab">管理者</a></li>
|
||||
<li><a href="#tab-problems" role="tab" data-toggle="tab">试题</a></li>
|
||||
<?php if (isSuperUser($myUser)): ?>
|
||||
<li><a href="#tab-others" role="tab" data-toggle="tab">其它</a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="/contest/<?=$contest['id']?>" role="tab">返回</a></li>
|
||||
</ul>
|
||||
<div class="tab-content top-buffer-sm">
|
||||
<div class="tab-pane active" id="tab-time">
|
||||
<?php $time_form->printHTML(); ?>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab-managers">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>用户名</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$row_id = 0;
|
||||
$result = DB::query("select username from contests_permissions where contest_id = {$contest['id']}");
|
||||
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
|
||||
$row_id++;
|
||||
echo '<tr>', '<td>', $row_id, '</td>', '<td>', getUserLink($row['username']), '</td>', '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-center">命令格式:命令一行一个,+mike表示把mike加入管理者,-mike表示把mike从管理者中移除</p>
|
||||
<?php $managers_form->printHTML(); ?>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab-problems">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>试题名</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$result = DB::query("select problem_id from contests_problems where contest_id = ${contest['id']} order by problem_id asc");
|
||||
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
|
||||
$problem = queryProblemBrief($row['problem_id']);
|
||||
$problem_config_str = isset($contest['extra_config']["problem_{$problem['id']}"]) ? $contest['extra_config']["problem_{$problem['id']}"] : 'sample';
|
||||
echo '<tr>', '<td>', $problem['id'], '</td>', '<td>', getProblemLink($problem), ' ', "[$problem_config_str]", '</td>', '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-center">命令格式:命令一行一个,+233表示把题号为233的试题加入比赛,-233表示把题号为233的试题从比赛中移除</p>
|
||||
<?php $problems_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php if (isSuperUser($myUser)): ?>
|
||||
<div class="tab-pane" id="tab-others">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3>Rating控制</h3>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<?php $rated_form->printHTML(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php $rating_k_form->printHTML(); ?>
|
||||
</div>
|
||||
<div class="col-sm-12 top-buffer-sm">
|
||||
<h3>版本控制</h3>
|
||||
<?php $version_form->printHTML(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
111
uoj/1/app/controllers/contest_members.php
Normal file
111
uoj/1/app/controllers/contest_members.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
genMoreContestInfo($contest);
|
||||
|
||||
$has_contest_permission = hasContestPermission($myUser, $contest);
|
||||
$show_ip = $has_contest_permission;
|
||||
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
$iHasRegistered = $myUser != null && hasRegistered($myUser, $contest);
|
||||
|
||||
if ($iHasRegistered) {
|
||||
$unregister_form = new UOJForm('unregister');
|
||||
$unregister_form->handle = function() {
|
||||
global $myUser, $contest;
|
||||
mysql_query("delete from contests_registrants where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
|
||||
updateContestPlayerNum($contest);
|
||||
};
|
||||
$unregister_form->submit_button_config['class_str'] = 'btn btn-danger btn-xs';
|
||||
$unregister_form->submit_button_config['text'] = '取消报名';
|
||||
$unregister_form->succ_href = "/contests";
|
||||
|
||||
$unregister_form->runAtServer();
|
||||
}
|
||||
|
||||
if ($has_contest_permission) {
|
||||
$pre_rating_form = new UOJForm('pre_rating');
|
||||
$pre_rating_form->handle = function() {
|
||||
global $contest;
|
||||
foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']}") as $reg) {
|
||||
$user = queryUser($reg['username']);
|
||||
DB::update("update contests_registrants set user_rating = {$user['rating']} where contest_id = {$contest['id']} and username = '{$user['username']}'");
|
||||
}
|
||||
};
|
||||
$pre_rating_form->submit_button_config['align'] = 'right';
|
||||
$pre_rating_form->submit_button_config['class_str'] = 'btn btn-warning';
|
||||
$pre_rating_form->submit_button_config['text'] = '重新计算参赛前的 rating';
|
||||
$pre_rating_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$pre_rating_form->runAtServer();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . UOJLocale::get('contests::contest registrants')) ?>
|
||||
|
||||
<h1 class="text-center"><?= $contest['name'] ?></h1>
|
||||
<?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED): ?>
|
||||
<?php if ($iHasRegistered): ?>
|
||||
<div class="pull-right">
|
||||
<?php $unregister_form->printHTML(); ?>
|
||||
</div>
|
||||
<div><a style="color:green">已报名</a></div>
|
||||
<?php else: ?>
|
||||
<div>当前尚未报名,您可以<a style="color:red" href="/contest/<?= $contest['id'] ?>/register">报名</a>。</div>
|
||||
<?php endif ?>
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php
|
||||
if ($show_ip) {
|
||||
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>remote_addr</th><th>rating</th></tr>';
|
||||
|
||||
$ip_owner = array();
|
||||
foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']} order by user_rating asc") as $reg) {
|
||||
$user = queryUser($reg['username']);
|
||||
$ip_owner[$user['remote_addr']] = $reg['username'];
|
||||
}
|
||||
} else {
|
||||
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>rating</th></tr>';
|
||||
}
|
||||
|
||||
echoLongTable(array('*'), 'contests_registrants', "contest_id = {$contest['id']}", 'order by user_rating desc',
|
||||
$header_row,
|
||||
function($contest, $num) {
|
||||
global $myUser;
|
||||
global $show_ip, $ip_owner;
|
||||
|
||||
$user = queryUser($contest['username']);
|
||||
$user_link = getUserLink($contest['username'], $contest['user_rating']);
|
||||
if (!$show_ip) {
|
||||
echo '<tr>';
|
||||
} else {
|
||||
if ($ip_owner[$user['remote_addr']] != $user['username']) {
|
||||
echo '<tr class="danger">';
|
||||
} else {
|
||||
echo '<tr>';
|
||||
}
|
||||
}
|
||||
echo '<td>'.$num.'</td>';
|
||||
echo '<td>'.$user_link.'</td>';
|
||||
if ($show_ip) {
|
||||
echo '<td>'.$user['remote_addr'].'</td>';
|
||||
}
|
||||
echo '<td>'.$contest['user_rating'].'</td>';
|
||||
echo '</tr>';
|
||||
},
|
||||
array('page_len' => 100,
|
||||
'get_row_index' => '',
|
||||
'print_after_table' => function() {
|
||||
global $pre_rating_form;
|
||||
if (isset($pre_rating_form)) {
|
||||
$pre_rating_form->printHTML();
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
37
uoj/1/app/controllers/contest_registration.php
Normal file
37
uoj/1/app/controllers/contest_registration.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
genMoreContestInfo($contest);
|
||||
|
||||
if ($myUser == null) {
|
||||
redirectToLogin();
|
||||
} elseif (hasContestPermission($myUser, $contest) || hasRegistered($myUser, $contest) || $contest['cur_progress'] != CONTEST_NOT_STARTED) {
|
||||
redirectTo('/contests');
|
||||
}
|
||||
|
||||
$register_form = new UOJForm('register');
|
||||
$register_form->handle = function() {
|
||||
global $myUser, $contest;
|
||||
mysql_query("insert into contests_registrants (username, user_rating, contest_id, has_participated) values ('{$myUser['username']}', {$myUser['rating']}, {$contest['id']}, 0)");
|
||||
updateContestPlayerNum($contest);
|
||||
};
|
||||
$register_form->submit_button_config['class_str'] = 'btn btn-primary';
|
||||
$register_form->submit_button_config['text'] = '报名比赛';
|
||||
$register_form->succ_href = "/contests";
|
||||
|
||||
$register_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - 报名') ?>
|
||||
<h1 class="page-header">比赛规则</h1>
|
||||
<ul>
|
||||
<li>比赛报名后不算正式参赛,报名后进了比赛页面也不算参赛,<strong>看了题目才算正式参赛</strong>。如果未正式参赛则不算rating。</li>
|
||||
<li>比赛中途可以提交,若同一题有多次提交按<strong>最后一次不是Compile Error的提交</strong>算成绩。(其实UOJ会自动无视你所有Compile Error的提交当作没看见)</li>
|
||||
<li>比赛中途提交后,可以看到<strong>测样例</strong>的结果。(若为提交答案题则对于每个测试点,该测试点有分则该测试点为满分)</li>
|
||||
<li>比赛结束后会进行最终测试,最终测试后的排名为最终排名。</li>
|
||||
<li>比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。</li>
|
||||
<li>请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以0分处理或者封禁。</li>
|
||||
</ul>
|
||||
<?php $register_form->printHTML(); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
97
uoj/1/app/controllers/contests.php
Normal file
97
uoj/1/app/controllers/contests.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
$upcoming_contest_name = null;
|
||||
$upcoming_contest_href = null;
|
||||
$rest_second = 1000000;
|
||||
function echoContest($contest) {
|
||||
global $myUser, $upcoming_contest_name, $upcoming_contest_href, $rest_second;
|
||||
|
||||
$contest_name_link = <<<EOD
|
||||
<a href="/contest/{$contest['id']}">{$contest['name']}</a>
|
||||
EOD;
|
||||
genMoreContestInfo($contest);
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
$cur_rest_second = $contest['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
||||
if ($cur_rest_second < $rest_second) {
|
||||
$upcoming_contest_name = $contest['name'];
|
||||
$upcoming_contest_href = "/contest/{$contest['id']}";
|
||||
$rest_second = $cur_rest_second;
|
||||
}
|
||||
if ($myUser != null && hasRegistered($myUser, $contest)) {
|
||||
$contest_name_link .= '<sup><a style="color:green">'.UOJLocale::get('contests::registered').'</a></sup>';
|
||||
} else {
|
||||
$contest_name_link .= '<sup><a style="color:red" href="/contest/'.$contest['id'].'/register">'.UOJLocale::get('contests::register').'</a></sup>';
|
||||
}
|
||||
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
|
||||
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::in progress').'</a></sup>';
|
||||
} elseif ($contest['cur_progress'] == CONTEST_PENDING_FINAL_TEST) {
|
||||
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::pending final test').'</a></sup>';
|
||||
} elseif ($contest['cur_progress'] == CONTEST_TESTING) {
|
||||
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::final testing').'</a></sup>';
|
||||
} elseif ($contest['cur_progress'] == CONTEST_FINISHED) {
|
||||
$contest_name_link .= '<sup><a style="color:grey" href="/contest/'.$contest['id'].'/standings">'.UOJLocale::get('contests::ended').'</a></sup>';
|
||||
}
|
||||
|
||||
$last_hour = round($contest['last_min'] / 60, 2);
|
||||
|
||||
$click_zan_block = getClickZanBlock('C', $contest['id'], $contest['zan']);
|
||||
echo '<tr>';
|
||||
echo '<td>', $contest_name_link, '</td>';
|
||||
echo '<td>', '<a href="'.HTML::timeanddate_url($contest['start_time'], array('duration' => $contest['last_min'])).'">'.$contest['start_time_str'].'</a>', '</td>';
|
||||
echo '<td>', UOJLocale::get('hours', $last_hour), '</td>';
|
||||
echo '<td>', '<a href="/contest/'.$contest['id'].'/registrants"><span class="glyphicon glyphicon-user"></span> ×'.$contest['player_num'].'</a>', '</td>';
|
||||
echo '<td>', '<div class="text-left">'.$click_zan_block.'</div>', '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('contests')) ?>
|
||||
<h4><?= UOJLocale::get('contests::current or upcoming contests') ?></h4>
|
||||
<?php
|
||||
$table_header = '';
|
||||
$table_header .= '<tr>';
|
||||
$table_header .= '<th>'.UOJLocale::get('contests::contest name').'</th>';
|
||||
$table_header .= '<th style="width:15em;">'.UOJLocale::get('contests::start time').'</th>';
|
||||
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::duration').'</th>';
|
||||
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::the number of registrants').'</th>';
|
||||
$table_header .= '<th style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
|
||||
$table_header .= '</tr>';
|
||||
echoLongTable(array('*'), 'contests', "status != 'finished'", 'order by id desc', $table_header,
|
||||
echoContest,
|
||||
array('page_len' => 100)
|
||||
);
|
||||
|
||||
if ($rest_second <= 86400) {
|
||||
echo <<<EOD
|
||||
<div class="text-center bot-buffer-lg">
|
||||
<div class="text-warning">$upcoming_contest_name 倒计时</div>
|
||||
<div id="contest-countdown"></div>
|
||||
<script type="text/javascript">
|
||||
$('#contest-countdown').countdown($rest_second, function() {
|
||||
if (confirm('$upcoming_contest_name 已经开始了。是否要跳转到比赛页面?')) {
|
||||
window.location.href = "$upcoming_contest_href";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
EOD;
|
||||
}
|
||||
?>
|
||||
|
||||
<h4><?= UOJLocale::get('contests::ended contests') ?></h4>
|
||||
<?php
|
||||
echoLongTable(array('*'), 'contests', "status = 'finished'", 'order by id desc', $table_header,
|
||||
echoContest,
|
||||
array('page_len' => 100,
|
||||
'print_after_table' => function() {
|
||||
global $myUser;
|
||||
if (isSuperUser($myUser)) {
|
||||
echo '<div class="text-right">';
|
||||
echo '<a href="/contest/new" class="btn btn-primary">'.UOJLocale::get('contests::add new contest').'</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
47
uoj/1/app/controllers/download.php
Normal file
47
uoj/1/app/controllers/download.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
switch ($_GET['type']) {
|
||||
case 'problem':
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$visible = isProblemVisibleToUser($problem, $myUser);
|
||||
if (!$visible && $myUser != null) {
|
||||
$result = mysql_query("select contest_id from contests_problems where problem_id = {$_GET['id']}");
|
||||
while (list($contest_id) = mysql_fetch_array($result, MYSQL_NUM)) {
|
||||
$contest = queryContest($contest_id);
|
||||
genMoreContestInfo($contest);
|
||||
if ($contest['cur_progress'] != CONTEST_NOT_STARTED && hasRegistered($myUser, $contest) && queryContestProblemRank($contest, $problem)) {
|
||||
$visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$visible) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$id = $_GET['id'];
|
||||
|
||||
$file_name = "/var/uoj_data/$id/download.zip";
|
||||
$download_name = "problem_$id.zip";
|
||||
break;
|
||||
case 'testlib.h':
|
||||
$file_name = "/home/local_main_judger/judge_client/uoj_judger/include/testlib.h";
|
||||
$download_name = "testlib.h";
|
||||
break;
|
||||
default:
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
$mimetype = finfo_file($finfo, $file_name);
|
||||
if ($mimetype === false) {
|
||||
become404Page();
|
||||
}
|
||||
finfo_close($finfo);
|
||||
|
||||
header("X-Sendfile: $file_name");
|
||||
header("Content-type: $mimetype");
|
||||
header("Content-Disposition: attachment; filename=$download_name");
|
||||
?>
|
68
uoj/1/app/controllers/faq.php
Normal file
68
uoj/1/app/controllers/faq.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php echoUOJPageHeader(UOJLocale::get('help')) ?>
|
||||
<article>
|
||||
<header>
|
||||
<h2 class="page-header">常见问题及其解答</h2>
|
||||
</header>
|
||||
<section>
|
||||
<header>
|
||||
<h4>1. 什么是UOJ</h4>
|
||||
</header>
|
||||
<p>见 <a href="http://uoj.ac/blog/4">http://uoj.ac/blog/4</a>。 </p>
|
||||
</section>
|
||||
<section>
|
||||
<header>
|
||||
<h4>2. 注册后怎么上传头像</h4>
|
||||
</header>
|
||||
<p>UOJ不提供头像存储服务。每到一个网站都要上传一个头像挺烦的对不对?UOJ支持Gravatar,请使用Gravatar吧!Gravatar是一个全球的头像存储服务,你的头像将会与你的电子邮箱绑定。在各大网站比如各种Wordpress还有各种OJ比如Vijos、Contest Hunter上,只要你电子邮箱填对了,那么你的头像也就立即能显示了!</p>
|
||||
<p>快使用Gravatar吧! Gravatar地址:<a href="https://cn.gravatar.com/">https://cn.gravatar.com/</a>。进去后注册个帐号然后与邮箱绑定并上传头像,就ok啦!</p>
|
||||
</section>
|
||||
<section>
|
||||
<header>
|
||||
<h4>3. UOJ的测评环境</h4>
|
||||
</header>
|
||||
<p>测评环境是Linux,Ubuntu 14.04,64位系统。</p>
|
||||
<p>C++的编译器是 g++ 4.8.4,编译命令:<code>g++ code.cpp -o code -lm -O2 -DONLINE_JUDGE</code>。如果选择C++11会在编译命令后面添加<code>-std=c++11</code>。</p>
|
||||
<p>C的编译器是 gcc 4.8.4,编译命令:<code>gcc code.c -o code -lm -O2 -DONLINE_JUDGE</code>。</p>
|
||||
<p>Pascal的编译器是 fpc 2.6.2,编译命令:<code>fpc code.pas -O2</code>。</p>
|
||||
<p>Java7的JDK版本是 jdk-7u76,编译命令:<code>javac code.java</code>。</p>
|
||||
<p>Java8的JDK版本是 jdk-8u31,编译命令:<code>javac code.java</code>。</p>
|
||||
<p>Python会先编译为优化过的字节码<samp>.pyo</samp>文件。支持的Python版本分别为Python 2.7和3.4。</p>
|
||||
</section>
|
||||
<section>
|
||||
<header>
|
||||
<h4>4. 递归 10<sup>7</sup> 层怎么没爆栈啊</h4>
|
||||
</header>
|
||||
<p>没错就是这样!除非是特殊情况,UOJ测评程序时的栈大小与该题的空间限制是相等的!</p>
|
||||
</section>
|
||||
<section>
|
||||
<header>
|
||||
<h4>5. 博客使用指南</h4>
|
||||
</header>
|
||||
<p>见 <a href="http://uoj.ac/blog/7">http://uoj.ac/blog/7</a>。 </p>
|
||||
</section>
|
||||
<section>
|
||||
<header>
|
||||
<h4>6. 交互式类型的题怎么本地测试</h4>
|
||||
</header>
|
||||
<p>唔……好问题。交互式的题一般给了一个头文件要你include进来,以及一个实现接口的源文件grader。好像大家对多个源文件一起编译还不太熟悉。</p>
|
||||
<p>对于C++:<code>g++ -o code grader.cpp code.cpp</code></p>
|
||||
<p>对于C语言:<code>gcc -o code grader.c code.c</code></p>
|
||||
<p>如果你是悲催的电脑盲,实在不会折腾没关系!你可以把grader的文件内容完整地粘贴到你的code的include语句之后,就可以了!</p>
|
||||
<p>什么你是萌萌哒Pascal选手?一般来说都会给个grader,你需要写一个Pascal单元。这个grader会使用你的单元。所以你只需要把源文件取名为单元名 + <code>.pas</code>,然后:</p>
|
||||
<p>对于Pascal语言:<code>fpc grader.pas</code></p>
|
||||
<p>就可以啦!</p>
|
||||
</section>
|
||||
<section>
|
||||
<header>
|
||||
<h4>7. 联系方式</h4>
|
||||
</header>
|
||||
<p>如果你想出题、想办比赛,或者发现了BUG,或者对网站有什么建议,可以通过下面的方式联系我们:</p>
|
||||
<ul>
|
||||
<li>UOJ私信联系vfleaking。</li>
|
||||
<li>邮件联系vfleaking@163.com。</li>
|
||||
<li>你也可以进QQ群水水,群号是197293072,Universal OJ用户群。</li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
57
uoj/1/app/controllers/forgot_pw.php
Normal file
57
uoj/1/app/controllers/forgot_pw.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
$forgot_form = new UOJForm('forgot');
|
||||
$forgot_form->addInput('username', 'text', '用户名', '',
|
||||
function($username, &$vdata) {
|
||||
if (!validateUsername($username)) {
|
||||
return '用户名不合法';
|
||||
}
|
||||
$vdata['user'] = queryUser($username);
|
||||
if (!$vdata['user']) {
|
||||
return '该用户不存在';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$forgot_form->handle = function(&$vdata) {
|
||||
$user = $vdata['user'];
|
||||
$password = $user["password"];
|
||||
|
||||
$sufs = base64url_encode($user['username'] . "." . md5($user['username'] . "+" . $password));
|
||||
$url = HTML::url("/reset-password", array('params' => array('p' => $sufs)));
|
||||
$html = <<<EOD
|
||||
<base target="_blank" />
|
||||
|
||||
<p>{$user['username']}您好,</p>
|
||||
<p>您刚刚启用了UOJ密码找回功能,请进入下面的链接重设您的密码:</p>
|
||||
<p><a href="$url">$url</a></p>
|
||||
<p>Universal Online Judge</p>
|
||||
|
||||
<style type="text/css">
|
||||
body{font-size:14px;font-family:arial,verdana,sans-serif;line-height:1.666;padding:0;margin:0;overflow:auto;white-space:normal;word-wrap:break-word;min-height:100px}
|
||||
pre {white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}
|
||||
</style>
|
||||
EOD;
|
||||
|
||||
$mailer = UOJMail::noreply();
|
||||
$mailer->addAddress($user['email'], $user['username']);
|
||||
$mailer->Subject = "UOJ密码找回";
|
||||
$mailer->msgHTML($html);
|
||||
if (!$mailer->send()) {
|
||||
error_log($mailer->ErrorInfo);
|
||||
becomeMsgPage('<div class="text-center"><h2>邮件发送失败,请重试 <span class="glyphicon glyphicon-remove"></span></h2></div>');
|
||||
} else {
|
||||
becomeMsgPage('<div class="text-center"><h2>邮件发送成功 <span class="glyphicon glyphicon-ok"></span></h2></div>');
|
||||
}
|
||||
};
|
||||
$forgot_form->submit_button_config['align'] = 'offset';
|
||||
|
||||
$forgot_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('找回密码') ?>
|
||||
<h2 class="page-header">找回密码</h2>
|
||||
<h4>请输入需要找回密码的用户名:</h4>
|
||||
<?php $forgot_form->printHTML(); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
86
uoj/1/app/controllers/hack.php
Normal file
86
uoj/1/app/controllers/hack.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($hack = queryHack($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
$submission = querySubmission($hack['submission_id']);
|
||||
$problem = queryProblemBrief($submission['problem_id']);
|
||||
$problem_extra_config = getProblemExtraConfig($problem);
|
||||
|
||||
if ($submission['contest_id']) {
|
||||
$contest = queryContest($submission['contest_id']);
|
||||
genMoreContestInfo($contest);
|
||||
} else {
|
||||
$contest = null;
|
||||
}
|
||||
|
||||
if (!isHackVisibleToUser($hack, $problem, $myUser)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$delete_form = new UOJForm('delete');
|
||||
$delete_form->handle = function() {
|
||||
global $hack;
|
||||
mysql_query("delete from hacks where id = {$hack['id']}");
|
||||
};
|
||||
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
|
||||
$delete_form->submit_button_config['text'] = '删除此Hack';
|
||||
$delete_form->submit_button_config['align'] = 'right';
|
||||
$delete_form->submit_button_config['smart_confirm'] = '';
|
||||
$delete_form->succ_href = "/hacks";
|
||||
$delete_form->runAtServer();
|
||||
}
|
||||
|
||||
$should_show_content = hasViewPermission($problem_extra_config['view_content_type'], $myUser, $problem, $submission);
|
||||
$should_show_all_details = hasViewPermission($problem_extra_config['view_all_details_type'], $myUser, $problem, $submission);
|
||||
$should_show_details = hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission);
|
||||
$should_show_details_to_me = isSuperUser($myUser);
|
||||
if ($hack['success'] === null) {
|
||||
$should_show_all_details = false;
|
||||
}
|
||||
if (!isSubmissionFullVisibleToUser($submission, $contest, $problem, $myUser)
|
||||
|| !isHackFullVisibleToUser($hack, $contest, $problem, $myUser)) {
|
||||
$should_show_content = $should_show_all_details = false;
|
||||
}
|
||||
|
||||
if ($should_show_all_details) {
|
||||
$styler = new HackDetailsStyler();
|
||||
if (!$should_show_details) {
|
||||
$styler->fade_all_details = true;
|
||||
$styler->show_small_tip = false;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['shjs'] = "";
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('problems::hack').' #'.$hack['id']) ?>
|
||||
|
||||
<?php echoHackListOnlyOne($hack, array(), $myUser) ?>
|
||||
<?php if ($should_show_all_details): ?>
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title"><?= UOJLocale::get('details') ?></h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<?php echoJudgementDetails($hack['details'], $styler, 'details') ?>
|
||||
<?php if ($should_show_details_to_me): ?>
|
||||
<?php if ($styler->fade_all_details): ?>
|
||||
<hr />
|
||||
<?php echoHackDetails($hack['details'], 'final_details') ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php echoSubmissionsListOnlyOne($submission, array(), $myUser) ?>
|
||||
<?php if ($should_show_content): ?>
|
||||
<?php echoSubmissionContent($submission, getProblemSubmissionRequirement($problem)) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (isset($delete_form)): ?>
|
||||
<?php $delete_form->printHTML() ?>
|
||||
<?php endif ?>
|
||||
<?php echoUOJPageFooter() ?>
|
101
uoj/1/app/controllers/hack_list.php
Normal file
101
uoj/1/app/controllers/hack_list.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
$conds = array();
|
||||
|
||||
$q_problem_id = isset($_GET['problem_id']) && validateUInt($_GET['problem_id']) ? $_GET['problem_id'] : null;
|
||||
$q_submission_id = isset($_GET['submission_id']) && validateUInt($_GET['submission_id']) ? $_GET['submission_id'] : null;
|
||||
$q_hacker = isset($_GET['hacker']) && validateUsername($_GET['hacker']) ? $_GET['hacker'] : null;
|
||||
$q_owner = isset($_GET['owner']) && validateUsername($_GET['owner']) ? $_GET['owner'] : null;
|
||||
if($q_problem_id != null) {
|
||||
$conds[] = "problem_id = $q_problem_id";
|
||||
}
|
||||
if($q_submission_id != null) {
|
||||
$conds[] = "submission_id = $q_submission_id";
|
||||
}
|
||||
if($q_hacker != null) {
|
||||
$conds[] = "hacker = '$q_hacker'";
|
||||
}
|
||||
if($q_owner != null) {
|
||||
$conds[] = "owner = '$q_owner'";
|
||||
}
|
||||
|
||||
$selected_all = ' selected="selected"';
|
||||
$selected_succ ='';
|
||||
$selected_fail ='';
|
||||
if(isset($_GET['status']) && validateUInt($_GET['status'])) {
|
||||
if($_GET['status'] == 1) {
|
||||
$selected_all = '';
|
||||
$selected_succ =' selected="selected"';
|
||||
$conds[] = 'success = 1';
|
||||
}
|
||||
if($_GET['status'] == 2) {
|
||||
$selected_all = '';
|
||||
$selected_fail = ' selected="selected"';
|
||||
$conds[] = 'success = 0';
|
||||
}
|
||||
}
|
||||
|
||||
if ($conds) {
|
||||
$cond = join($conds, ' and ');
|
||||
} else {
|
||||
$cond = '1';
|
||||
}
|
||||
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('hacks')) ?>
|
||||
<div class="hidden-xs">
|
||||
<?php if ($myUser != null): ?>
|
||||
<div class="pull-right">
|
||||
<a href="/hacks?hacker=<?= $myUser['username'] ?>" class="btn btn-success btn-sm"><?= UOJLocale::get('problems::hacks by me') ?></a>
|
||||
<a href="/hacks?owner=<?= $myUser['username'] ?>" class="btn btn-danger btn-sm"><?= UOJLocale::get('problems::hacks to me') ?></a>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<form id="form-search" class="form-inline" role="form">
|
||||
<div id="form-group-submission_id" class="form-group">
|
||||
<label for="input-submission_id" class="control-label"><?= UOJLocale::get('problems::submission id') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="submission_id" id="input-submission_id" value="<?= $q_submission_id ?>" maxlength="6" style="width:5em" />
|
||||
</div>
|
||||
<div id="form-group-problem_id" class="form-group">
|
||||
<label for="input-problem_id" class="control-label"><?= UOJLocale::get('problems::problem id') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="problem_id" id="input-problem_id" value="<?= $q_problem_id ?>" maxlength="4" style="width:4em" />
|
||||
</div>
|
||||
<div id="form-group-hacker" class="form-group">
|
||||
<label for="input-hacker" class="control-label"><?= UOJLocale::get('problems::hacker') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="hacker" id="input-hacker" value="<?= $q_hacker ?>" maxlength="100" style="width:10em" />
|
||||
</div>
|
||||
<div id="form-group-owner" class="form-group">
|
||||
<label for="input-owner" class="control-label"><?= UOJLocale::get('problems::owner') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="owner" id="input-owner" value="<?= $q_owner ?>" maxlength="100" style="width:10em" />
|
||||
</div>
|
||||
<div id="form-group-status" class="form-group">
|
||||
<label for="input-status" class="control-label"><?= UOJLocale::get('problems::result') ?>:</label>
|
||||
<select class="form-control input-sm" id="input-status" name="status">
|
||||
<option value=""<?= $selected_all?>>All</option>
|
||||
<option value="1"<?= $selected_succ ?>>Success!</option>
|
||||
<option value="2"<?= $selected_fail ?>>Failed.</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" id="submit-search" class="btn btn-default btn-sm"><?= UOJLocale::get('search') ?></button>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
$('#form-search').submit(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
url = '/hacks';
|
||||
qs = [];
|
||||
$(['submission_id', 'problem_id', 'hacker', 'owner', 'status']).each(function () {
|
||||
if ($('#input-' + this).val()) {
|
||||
qs.push(this + '=' + encodeURIComponent($('#input-' + this).val()));
|
||||
}
|
||||
});
|
||||
if (qs.length > 0) {
|
||||
url += '?' + qs.join('&');
|
||||
}
|
||||
location.href = url;
|
||||
});
|
||||
</script>
|
||||
<div class="top-buffer-sm"></div>
|
||||
</div>
|
||||
<?php
|
||||
echoHacksList($cond, 'order by id desc', array('judge_time_hidden' => ''), $myUser);
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
55
uoj/1/app/controllers/index.php
Normal file
55
uoj/1/app/controllers/index.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
$blogs = DB::selectAll("select blogs.id, title, poster, post_time from important_blogs, blogs where is_hidden = 0 and important_blogs.blog_id = blogs.id order by level desc, important_blogs.blog_id desc limit 5");
|
||||
?>
|
||||
<?php echoUOJPageHeader('UOJ') ?>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-9">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60%"><?= UOJLocale::get('announcements') ?></th>
|
||||
<th style="width:20%"></th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $now_cnt = 0; ?>
|
||||
<?php foreach ($blogs as $blog): ?>
|
||||
<?php
|
||||
$now_cnt++;
|
||||
$new_tag = '';
|
||||
if ((time() - strtotime($blog['post_time'])) / 3600 / 24 <= 7) {
|
||||
$new_tag = '<sup style="color:red"> new</sup>';
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><a href="/blog/<?= $blog['id'] ?>"><?= $blog['title'] ?></a><?= $new_tag ?></td>
|
||||
<td>by <?= getUserLink($blog['poster']) ?></td>
|
||||
<td><small><?= $blog['post_time'] ?></small></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<?php for ($i = $now_cnt + 1; $i <= 5; $i++): ?>
|
||||
<tr><td colspan="233"> </td></tr>
|
||||
<?php endfor ?>
|
||||
<tr><td class="text-right" colspan="233"><a href="/announcements"><?= UOJLocale::get('all the announcements') ?></a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-3">
|
||||
<img class="media-object img-thumbnail" src="/pictures/UOJ.png" alt="UOJ logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3><?= UOJLocale::get('top rated') ?></h3>
|
||||
<?php echoRanklist(array('echo_full' => '', 'top10' => '')) ?>
|
||||
<div class="text-center">
|
||||
<a href="/ranklist"><?= UOJLocale::get('view all') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
39
uoj/1/app/controllers/judge/download.php
Normal file
39
uoj/1/app/controllers/judge/download.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
|
||||
if (!authenticateJudger()) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
switch ($_GET['type']) {
|
||||
case 'submission':
|
||||
$file_name = UOJContext::storagePath()."/submission/{$_GET['id']}/{$_GET['rand_str_id']}";
|
||||
$download_name = "submission.zip";
|
||||
break;
|
||||
case 'tmp':
|
||||
$file_name = UOJContext::storagePath()."/tmp/{$_GET['rand_str_id']}";
|
||||
$download_name = "tmp";
|
||||
break;
|
||||
case 'problem':
|
||||
$id = $_GET['id'];
|
||||
if (!validateUInt($id) || !($problem = queryProblemBrief($id))) {
|
||||
become404Page();
|
||||
}
|
||||
$file_name = "/var/uoj_data/$id.zip";
|
||||
$download_name = "$id.zip";
|
||||
break;
|
||||
default:
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
$mimetype = finfo_file($finfo, $file_name);
|
||||
if ($mimetype === false) {
|
||||
become404Page();
|
||||
}
|
||||
finfo_close($finfo);
|
||||
|
||||
header("X-Sendfile: $file_name");
|
||||
header("Content-type: $mimetype");
|
||||
header("Content-Disposition: attachment; filename=$download_name");
|
||||
?>
|
212
uoj/1/app/controllers/judge/submit.php
Normal file
212
uoj/1/app/controllers/judge/submit.php
Normal file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('svn');
|
||||
|
||||
if (!authenticateJudger()) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
function submissionJudged() {
|
||||
$submission = DB::selectFirst("select submitter, status, content, result, problem_id from submissions where id = {$_POST['id']}");
|
||||
if ($submission == null) {
|
||||
return;
|
||||
}
|
||||
if ($submission['status'] != 'Judging' && $submission['status'] != 'Judged, Judging') {
|
||||
return;
|
||||
}
|
||||
$content = json_decode($submission['content'], true);
|
||||
|
||||
if (isset($content['first_test_config'])) {
|
||||
$result = json_decode($submission['result'], true);
|
||||
$result['final_result'] = json_decode($_POST['result'], true);
|
||||
$result['final_result']['details'] = uojTextEncode($result['final_result']['details']);
|
||||
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$content['final_test_config'] = $content['config'];
|
||||
$content['config'] = $content['first_test_config'];
|
||||
unset($content['first_test_config']);
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
DB::update("update submissions set status = 'Judged', result = '$esc_result', content = '$esc_content' where id = {$_POST['id']}");
|
||||
} else {
|
||||
$result = json_decode($_POST['result'], true);
|
||||
$result['details'] = uojTextEncode($result['details']);
|
||||
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
if (isset($result["error"])) {
|
||||
DB::update("update submissions set status = '{$result['status']}', result_error = '{$result['error']}', result = '$esc_result', score = null, used_time = null, used_memory = null where id = {$_POST['id']}");
|
||||
} else {
|
||||
DB::update("update submissions set status = '{$result['status']}', result_error = null, result = '$esc_result', score = {$result['score']}, used_time = {$result['time']}, used_memory = {$result['memory']} where id = {$_POST['id']}");
|
||||
}
|
||||
|
||||
if (isset($content['final_test_config'])) {
|
||||
$content['first_test_config'] = $content['config'];
|
||||
$content['config'] = $content['final_test_config'];
|
||||
unset($content['final_test_config']);
|
||||
$esc_content = mysql_real_escape_string(json_encode($content));
|
||||
|
||||
DB::update("update submissions set status = 'Judged, Waiting', content = '$esc_content' where id = ${_POST['id']}");
|
||||
}
|
||||
}
|
||||
DB::update("update submissions set status_details = '' where id = {$_POST['id']}");
|
||||
updateBestACSubmissions($submission['submitter'], $submission['problem_id']);
|
||||
}
|
||||
|
||||
function customTestSubmissionJudged() {
|
||||
$submission = DB::selectFirst("select submitter, status, content, result, problem_id from custom_test_submissions where id = {$_POST['id']}");
|
||||
if ($submission == null) {
|
||||
return;
|
||||
}
|
||||
if ($submission['status'] != 'Judging') {
|
||||
return;
|
||||
}
|
||||
$content = json_decode($submission['content'], true);
|
||||
$result = json_decode($_POST['result'], true);
|
||||
$result['details'] = uojTextEncode($result['details']);
|
||||
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
if (isset($result["error"])) {
|
||||
DB::update("update custom_test_submissions set status = '{$result['status']}', result = '$esc_result' where id = {$_POST['id']}");
|
||||
} else {
|
||||
DB::update("update custom_test_submissions set status = '{$result['status']}', result = '$esc_result' where id = {$_POST['id']}");
|
||||
}
|
||||
DB::update("update custom_test_submissions set status_details = '' where id = {$_POST['id']}");
|
||||
}
|
||||
|
||||
function hackJudged() {
|
||||
$result = json_decode($_POST['result'], true);
|
||||
$esc_details = DB::escape(uojTextEncode($result['details']));
|
||||
$ok = DB::update("update hacks set success = {$result['score']}, details = '$esc_details' where id = {$_POST['id']}");
|
||||
|
||||
if ($ok) {
|
||||
list($hack_input) = mysql_fetch_array(mysql_query("select input from hacks where id = {$_POST['id']}"), MYSQL_NUM);
|
||||
unlink(UOJContext::storagePath().$hack_input);
|
||||
|
||||
if ($result['score']) {
|
||||
list($problem_id) = mysql_fetch_array(mysql_query("select problem_id from hacks where id = ${_POST['id']}"), MYSQL_NUM);
|
||||
if (validateUploadedFile('hack_input') && validateUploadedFile('std_output')) {
|
||||
svnAddExtraTest(queryProblemBrief($problem_id), $_FILES["hack_input"]["tmp_name"], $_FILES["std_output"]["tmp_name"]);
|
||||
} else {
|
||||
error_log("hack successfully but received no data. id: ${_POST['id']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['submit'])) {
|
||||
if (!validateUInt($_POST['id'])) {
|
||||
die("Wow! hacker! T_T....");
|
||||
}
|
||||
if (isset($_POST['is_hack'])) {
|
||||
hackJudged();
|
||||
} elseif (isset($_POST['is_custom_test'])) {
|
||||
customTestSubmissionJudged();
|
||||
} else {
|
||||
submissionJudged();
|
||||
}
|
||||
}
|
||||
if (isset($_POST['update-status'])) {
|
||||
if (!validateUInt($_POST['id'])) {
|
||||
die("Wow! hacker! T_T....");
|
||||
}
|
||||
$esc_status_details = DB::escape($_POST['status']);
|
||||
if (isset($_POST['is_custom_test'])) {
|
||||
DB::update("update custom_test_submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
|
||||
} else {
|
||||
DB::update("update submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
$submission = null;
|
||||
$hack = null;
|
||||
function querySubmissionToJudge($status, $set_q) {
|
||||
global $submission;
|
||||
$submission = DB::selectFirst("select id, problem_id, content from submissions where status = '$status' order by id limit 1");
|
||||
if ($submission) {
|
||||
DB::update("update submissions set $set_q where id = {$submission['id']} and status = '$status'");
|
||||
if (DB::affected_rows() != 1) {
|
||||
$submission = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function queryCustomTestSubmissionToJudge() {
|
||||
global $submission;
|
||||
$submission = DB::selectFirst("select id, problem_id, content from custom_test_submissions where judge_time is null order by id limit 1");
|
||||
if ($submission) {
|
||||
DB::update("update custom_test_submissions set judge_time = now(), status = 'Judging' where id = {$submission['id']} and judge_time is null");
|
||||
if (DB::affected_rows() != 1) {
|
||||
$submission = null;
|
||||
}
|
||||
}
|
||||
if ($submission) {
|
||||
$submission['is_custom_test'] = '';
|
||||
}
|
||||
}
|
||||
function queryHackToJudge() {
|
||||
global $hack;
|
||||
$hack = DB::selectFirst("select id, submission_id, input, input_type from hacks where judge_time is null order by id limit 1");
|
||||
if ($hack) {
|
||||
DB::update("update hacks set judge_time = now() where id = {$hack['id']} and judge_time is null");
|
||||
if (DB::affected_rows() != 1) {
|
||||
$hack = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function findSubmissionToJudge() {
|
||||
global $submission, $hack;
|
||||
querySubmissionToJudge('Waiting', "judge_time = now(), status = 'Judging'");
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
queryCustomTestSubmissionToJudge();
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
querySubmissionToJudge('Waiting Rejudge', "judge_time = now(), status = 'Judging'");
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
querySubmissionToJudge('Judged, Waiting', "status = 'Judged, Judging'");
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
queryHackToJudge();
|
||||
if ($hack) {
|
||||
$submission = DB::selectFirst("select id, problem_id, content from submissions where id = {$hack['submission_id']} and score = 100");
|
||||
if (!$submission) {
|
||||
$details = "<error>the score gained by the hacked submission is not 100.\n</error>";
|
||||
$esc_details = DB::escape(uojTextEncode($details));
|
||||
DB::update("update hacks set success = 0, details = '$esc_details' where id = {$hack['id']}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (isset($_POST['fetch_new']) && !$_POST['fetch_new']) {
|
||||
die("Nothing to judge");
|
||||
}
|
||||
if (!findSubmissionToJudge()) {
|
||||
die("Nothing to judge");
|
||||
}
|
||||
|
||||
$submission['id'] = (int)$submission['id'];
|
||||
$submission['problem_id'] = (int)$submission['problem_id'];
|
||||
$submission['problem_mtime'] = filemtime("/var/uoj_data/{$submission['problem_id']}.zip");
|
||||
$submission['content'] = json_decode($submission['content']);
|
||||
|
||||
if ($hack) {
|
||||
$submission['is_hack'] = "";
|
||||
$submission['hack']['id'] = (int)$hack['id'];
|
||||
$submission['hack']['input'] = $hack['input'];
|
||||
$submission['hack']['input_type'] = $hack['input_type'];
|
||||
}
|
||||
|
||||
echo json_encode($submission);
|
||||
?>
|
22
uoj/1/app/controllers/judge/sync_judge_client.php
Normal file
22
uoj/1/app/controllers/judge/sync_judge_client.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('svn');
|
||||
|
||||
if (!authenticateJudger()) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
foreach (DB::selectAll("select * from judger_info where ip != ''") as $judger) {
|
||||
$socket = fsockopen($judger['ip'], UOJConfig::$data['judger']['socket']['port']);
|
||||
if ($socket === false) {
|
||||
die("judge client {$judger['ip']} lost.");
|
||||
}
|
||||
fwrite($socket, json_encode([
|
||||
'password' => UOJConfig::$data['judger']['socket']['password'],
|
||||
'cmd' => 'update'
|
||||
]));
|
||||
fclose($socket);
|
||||
}
|
||||
|
||||
die("ok");
|
||||
?>
|
120
uoj/1/app/controllers/login.php
Normal file
120
uoj/1/app/controllers/login.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
if (Auth::check()) {
|
||||
redirectTo('/');
|
||||
}
|
||||
|
||||
function handleLoginPost() {
|
||||
if (!crsf_check()) {
|
||||
return 'expired';
|
||||
}
|
||||
if (!isset($_POST['username'])) {
|
||||
return "failed";
|
||||
}
|
||||
if (!isset($_POST['password'])) {
|
||||
return "failed";
|
||||
}
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
if (!validateUsername($username)) {
|
||||
return "failed";
|
||||
}
|
||||
if (!validatePassword($password)) {
|
||||
return "failed";
|
||||
}
|
||||
|
||||
$user = queryUser($username);
|
||||
if (!$user || !checkPassword($user, $password)) {
|
||||
return "failed";
|
||||
}
|
||||
|
||||
if ($user['usergroup'] == 'B') {
|
||||
return "banned";
|
||||
}
|
||||
|
||||
Auth::login($user['username']);
|
||||
return "ok";
|
||||
}
|
||||
|
||||
if (isset($_POST['login'])) {
|
||||
echo handleLoginPost();
|
||||
die();
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('login')) ?>
|
||||
<h2 class="page-header"><?= UOJLocale::get('login') ?></h2>
|
||||
<form id="form-login" class="form-horizontal" method="post">
|
||||
<div id="div-username" class="form-group">
|
||||
<label for="input-username" class="col-sm-2 control-label"><?= UOJLocale::get('username') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-username"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-password" class="form-group">
|
||||
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-password"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-3">
|
||||
<button type="submit" id="button-submit" class="btn btn-default"><?= UOJLocale::get('submit') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateLoginPost() {
|
||||
var ok = true;
|
||||
ok &= getFormErrorAndShowHelp('username', validateUsername);
|
||||
ok &= getFormErrorAndShowHelp('password', validatePassword);
|
||||
return ok;
|
||||
}
|
||||
|
||||
function submitLoginPost() {
|
||||
if (!validateLoginPost()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$.post('/login', {
|
||||
_token : "<?= crsf_token() ?>",
|
||||
login : '',
|
||||
username : $('#input-username').val(),
|
||||
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||
}, function(msg) {
|
||||
if (msg == 'ok') {
|
||||
var prevUrl = document.referrer;
|
||||
if (prevUrl == '' || /.*\/login.*/.test(prevUrl) || /.*\/register.*/.test(prevUrl) || /.*\/reset-password.*/.test(prevUrl)) {
|
||||
prevUrl = '/';
|
||||
};
|
||||
window.location.href = prevUrl;
|
||||
} else if (msg == 'banned') {
|
||||
$('#div-username').addClass('has-error');
|
||||
$('#help-username').html('用户已被禁用。');
|
||||
} else if (msg == 'expired') {
|
||||
$('#div-username').addClass('has-error');
|
||||
$('#help-username').html('页面已过期。');
|
||||
} else {
|
||||
$('#div-username').addClass('has-error');
|
||||
$('#help-username').html('用户名或密码错误。');
|
||||
$('#div-password').addClass('has-error');
|
||||
$('#help-password').html('用户名或密码错误。<a href="/forgot-password">忘记密码?</a>');
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#form-login').submit(function(e) {
|
||||
e.preventDefault();
|
||||
submitLoginPost();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<?php echoUOJPageFooter() ?>
|
12
uoj/1/app/controllers/logout.php
Normal file
12
uoj/1/app/controllers/logout.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
crsf_defend();
|
||||
Auth::logout();
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
var prevUrl = document.referrer;
|
||||
if (!prevUrl) {
|
||||
prevUrl = '/';
|
||||
};
|
||||
window.location.href = prevUrl;
|
||||
</script>
|
265
uoj/1/app/controllers/problem.php
Normal file
265
uoj/1/app/controllers/problem.php
Normal file
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$problem_content = queryProblemContent($problem['id']);
|
||||
|
||||
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null;
|
||||
if ($contest != null) {
|
||||
genMoreContestInfo($contest);
|
||||
$problem_rank = queryContestProblemRank($contest, $problem);
|
||||
if ($problem_rank == null) {
|
||||
become404Page();
|
||||
} else {
|
||||
$problem_letter = chr(ord('A') + $problem_rank - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$is_in_contest = false;
|
||||
$ban_in_contest = false;
|
||||
if ($contest != null) {
|
||||
if (!hasContestPermission($myUser, $contest)) {
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
become404Page();
|
||||
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
|
||||
if ($myUser == null || !hasRegistered($myUser, $contest)) {
|
||||
becomeMsgPage("<h1>比赛正在进行中</h1><p>很遗憾,您尚未报名。比赛结束后再来看吧~</p>");
|
||||
} else {
|
||||
$is_in_contest = true;
|
||||
DB::update("update contests_registrants set has_participated = 1 where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
|
||||
}
|
||||
} else {
|
||||
$ban_in_contest = !isProblemVisibleToUser($problem, $myUser);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isProblemVisibleToUser($problem, $myUser)) {
|
||||
become404Page();
|
||||
}
|
||||
}
|
||||
|
||||
$submission_requirement = json_decode($problem['submission_requirement'], true);
|
||||
$problem_extra_config = getProblemExtraConfig($problem);
|
||||
$custom_test_requirement = getProblemCustomTestRequirement($problem);
|
||||
|
||||
if ($custom_test_requirement && Auth::check()) {
|
||||
$custom_test_submission = DB::selectFirst("select * from custom_test_submissions where submitter = '".Auth::id()."' and problem_id = {$problem['id']} order by id desc limit 1");
|
||||
$custom_test_submission_result = json_decode($custom_test_submission['result'], true);
|
||||
}
|
||||
if ($custom_test_requirement && $_GET['get'] == 'custom-test-status-details' && Auth::check()) {
|
||||
if ($custom_test_submission == null) {
|
||||
echo json_encode(null);
|
||||
} else if ($custom_test_submission['status'] != 'Judged') {
|
||||
echo json_encode(array(
|
||||
'judged' => false,
|
||||
'html' => getSubmissionStatusDetails($custom_test_submission)
|
||||
));
|
||||
} else {
|
||||
ob_start();
|
||||
$styler = new CustomTestSubmissionDetailsStyler();
|
||||
if (!hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission)) {
|
||||
$styler->fade_all_details = true;
|
||||
}
|
||||
echoJudgementDetails($custom_test_submission_result['details'], $styler, 'custom_test_details');
|
||||
$result = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo json_encode(array(
|
||||
'judged' => true,
|
||||
'html' => getSubmissionStatusDetails($custom_test_submission),
|
||||
'result' => $result
|
||||
));
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
$can_use_zip_upload = true;
|
||||
foreach ($submission_requirement as $req) {
|
||||
if ($req['type'] == 'source code') {
|
||||
$can_use_zip_upload = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleUpload($zip_file_name, $content, $tot_size) {
|
||||
global $problem, $contest, $myUser, $is_in_contest;
|
||||
|
||||
$content['config'][] = array('problem_id', $problem['id']);
|
||||
if ($is_in_contest && !isset($contest['extra_config']["problem_{$problem['id']}"])) {
|
||||
$content['final_test_config'] = $content['config'];
|
||||
$content['config'][] = array('test_sample_only', 'on');
|
||||
}
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
$language = '/';
|
||||
foreach ($content['config'] as $row) {
|
||||
if (strEndWith($row[0], '_language')) {
|
||||
$language = $row[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($language != '/') {
|
||||
Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');
|
||||
}
|
||||
$esc_language = DB::escape($language);
|
||||
|
||||
$result = array();
|
||||
$result['status'] = "Waiting";
|
||||
$result_json = json_encode($result);
|
||||
|
||||
if ($is_in_contest) {
|
||||
mysql_query("insert into submissions (problem_id, contest_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, ${contest['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', 0)");
|
||||
} else {
|
||||
mysql_query("insert into submissions (problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', {$problem['is_hidden']})");
|
||||
}
|
||||
}
|
||||
function handleCustomTestUpload($zip_file_name, $content, $tot_size) {
|
||||
global $problem, $contest, $myUser;
|
||||
|
||||
$content['config'][] = array('problem_id', $problem['id']);
|
||||
$content['config'][] = array('custom_test', 'on');
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
$language = '/';
|
||||
foreach ($content['config'] as $row) {
|
||||
if (strEndWith($row[0], '_language')) {
|
||||
$language = $row[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($language != '/') {
|
||||
Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');
|
||||
}
|
||||
$esc_language = DB::escape($language);
|
||||
|
||||
$result = array();
|
||||
$result['status'] = "Waiting";
|
||||
$result_json = json_encode($result);
|
||||
|
||||
DB::insert("insert into custom_test_submissions (problem_id, submit_time, submitter, content, status, result) values ({$problem['id']}, now(), '{$myUser['username']}', '$esc_content', '{$result['status']}', '$result_json')");
|
||||
}
|
||||
|
||||
if ($can_use_zip_upload) {
|
||||
$zip_answer_form = newZipSubmissionForm('zip_answer',
|
||||
$submission_requirement,
|
||||
'uojRandAvaiableSubmissionFileName',
|
||||
'handleUpload');
|
||||
$zip_answer_form->extra_validator = function() {
|
||||
global $ban_in_contest;
|
||||
if ($ban_in_contest) {
|
||||
return '请耐心等待比赛结束后题目对所有人可见了再提交';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
$zip_answer_form->succ_href = $is_in_contest ? "/contest/{$contest['id']}/submissions" : '/submissions';
|
||||
$zip_answer_form->runAtServer();
|
||||
}
|
||||
|
||||
$answer_form = newSubmissionForm('answer',
|
||||
$submission_requirement,
|
||||
'uojRandAvaiableSubmissionFileName',
|
||||
'handleUpload');
|
||||
$answer_form->extra_validator = function() {
|
||||
global $ban_in_contest;
|
||||
if ($ban_in_contest) {
|
||||
return '请耐心等待比赛结束后题目对所有人可见了再提交';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
$answer_form->succ_href = $is_in_contest ? "/contest/{$contest['id']}/submissions" : '/submissions';
|
||||
$answer_form->runAtServer();
|
||||
|
||||
if ($custom_test_requirement) {
|
||||
$custom_test_form = newSubmissionForm('custom_test',
|
||||
$custom_test_requirement,
|
||||
function() {
|
||||
return uojRandAvaiableFileName('/tmp/');
|
||||
},
|
||||
'handleCustomTestUpload');
|
||||
$custom_test_form->appendHTML(<<<EOD
|
||||
<div id="div-custom_test_result"></div>
|
||||
EOD
|
||||
);
|
||||
$custom_test_form->succ_href = 'none';
|
||||
$custom_test_form->extra_validator = function() {
|
||||
global $ban_in_contest, $custom_test_submission;
|
||||
if ($ban_in_contest) {
|
||||
return '请耐心等待比赛结束后题目对所有人可见了再提交';
|
||||
}
|
||||
if ($custom_test_submission && $custom_test_submission['status'] != 'Judged') {
|
||||
return '上一个测评尚未结束';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
$custom_test_form->ctrl_enter_submit = true;
|
||||
$custom_test_form->setAjaxSubmit(<<<EOD
|
||||
function(response_text) {custom_test_onsubmit(response_text, $('#div-custom_test_result')[0], '{$_SERVER['REQUEST_URI']}?get=custom-test-status-details')}
|
||||
EOD
|
||||
);
|
||||
$custom_test_form->submit_button_config['text'] = UOJLocale::get('problems::run');
|
||||
$custom_test_form->runAtServer();
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['mathjax'] = '';
|
||||
$REQUIRE_LIB['shjs'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::problem')) ?>
|
||||
<div class="pull-right">
|
||||
<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
|
||||
</div>
|
||||
|
||||
<?php if ($contest): ?>
|
||||
<div class="page-header row">
|
||||
<h1 class="col-md-3 text-left"><small><?= $contest['name'] ?></small></h1>
|
||||
<h1 class="col-md-7 text-center"><?= $problem_letter ?>. <?= $problem['title'] ?></h1>
|
||||
<div class="col-md-2 text-right" id="contest-countdown"></div>
|
||||
</div>
|
||||
<a role="button" class="btn btn-info pull-right" href="/contest/<?= $contest['id'] ?>/problem/<?= $problem['id'] ?>/statistics"><span class="glyphicon glyphicon-stats"></span> <?= UOJLocale::get('problems::statistics') ?></a>
|
||||
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
|
||||
<script type="text/javascript">
|
||||
checkContestNotice(<?= $contest['id'] ?>, '<?= UOJTime::$time_now_str ?>');
|
||||
$('#contest-countdown').countdown(<?= $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp() ?>);
|
||||
</script>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<h1 class="page-header text-center">#<?= $problem['id']?>. <?= $problem['title'] ?></h1>
|
||||
<a role="button" class="btn btn-info pull-right" href="/problem/<?= $problem['id'] ?>/statistics"><span class="glyphicon glyphicon-stats"></span> <?= UOJLocale::get('problems::statistics') ?></a>
|
||||
<?php endif ?>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="active"><a href="#tab-statement" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-book"></span> <?= UOJLocale::get('problems::statement') ?></a></li>
|
||||
<li><a href="#tab-submit-answer" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-upload"></span> <?= UOJLocale::get('problems::submit') ?></a></li>
|
||||
<?php if ($custom_test_requirement): ?>
|
||||
<li><a href="#tab-custom-test" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-console"></span> <?= UOJLocale::get('problems::custom test') ?></a></li>
|
||||
<?php endif ?>
|
||||
<?php if (hasProblemPermission($myUser, $problem)): ?>
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab"><?= UOJLocale::get('problems::manage') ?></a></li>
|
||||
<?php endif ?>
|
||||
<?php if ($contest): ?>
|
||||
<li><a href="/contest/<?= $contest['id'] ?>" role="tab"><?= UOJLocale::get('contests::back to the contest') ?></a></li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tab-statement">
|
||||
<article class="top-buffer-md"><?= $problem_content['statement'] ?></article>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab-submit-answer">
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php if ($can_use_zip_upload): ?>
|
||||
<?php $zip_answer_form->printHTML(); ?>
|
||||
<hr />
|
||||
<strong><?= UOJLocale::get('problems::or upload files one by one') ?><br /></strong>
|
||||
<?php endif ?>
|
||||
<?php $answer_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php if ($custom_test_requirement): ?>
|
||||
<div class="tab-pane" id="tab-custom-test">
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php $custom_test_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
653
uoj/1/app/controllers/problem_data_manage.php
Normal file
653
uoj/1/app/controllers/problem_data_manage.php
Normal file
@ -0,0 +1,653 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('svn');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
if (!hasProblemPermission($myUser, $problem)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
$problem_extra_config = getProblemExtraConfig($problem);
|
||||
|
||||
if (isset($_POST['getsvn'])) {
|
||||
if (Auth::check()) {
|
||||
$html = <<<EOD
|
||||
<base target="_blank" />
|
||||
|
||||
<p>{$myUser['username']}您好,</p>
|
||||
<p>您的svn密码是:{$myUser['svn_password']}</p>
|
||||
<p>Universal Online Judge</p>
|
||||
|
||||
<style type="text/css">
|
||||
body{font-size:14px;font-family:arial,verdana,sans-serif;line-height:1.666;padding:0;margin:0;overflow:auto;white-space:normal;word-wrap:break-word;min-height:100px}
|
||||
pre {white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}
|
||||
</style>
|
||||
EOD;
|
||||
|
||||
$mailer = UOJMail::noreply();
|
||||
$mailer->addAddress($myUser['email'], $myUser['username']);
|
||||
$mailer->Subject = "svn密码";
|
||||
$mailer->msgHTML($html);
|
||||
if ($mailer->send()) {
|
||||
echo 'good';
|
||||
}
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
$data_dir = "/var/uoj_data/${problem['id']}";
|
||||
|
||||
function echoFileNotFound($file_name) {
|
||||
echo '<h4>', htmlspecialchars($file_name), '<sub class="text-danger"> ', 'file not found', '</sub></h4>';
|
||||
}
|
||||
function echoFilePre($file_name) {
|
||||
global $data_dir;
|
||||
$file_full_name = $data_dir . '/' . $file_name;
|
||||
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
$mimetype = finfo_file($finfo, $file_full_name);
|
||||
if ($mimetype === false) {
|
||||
echoFileNotFound($file_name);
|
||||
return;
|
||||
}
|
||||
finfo_close($finfo);
|
||||
|
||||
echo '<h4>', htmlspecialchars($file_name), '<sub> ', $mimetype, '</sub></h4>';
|
||||
echo "<pre>\n";
|
||||
|
||||
$output_limit = 1000;
|
||||
if (strStartWith($mimetype, 'text/')) {
|
||||
echo htmlspecialchars(uojFilePreview($file_full_name, $output_limit));
|
||||
} else {
|
||||
echo htmlspecialchars(strOmit(shell_exec('xxd -g 4 -l 5000 ' . escapeshellarg($file_full_name) . ' | head -c ' . ($output_limit + 4)), $output_limit));
|
||||
}
|
||||
echo "\n</pre>";
|
||||
}
|
||||
|
||||
$info_form = new UOJForm('info');
|
||||
$http_host = HTML::escape(UOJContext::httpHost());
|
||||
$download_url = HTML::escape(HTML::url("/download.php?type=problem&id={$problem['id']}"));
|
||||
$info_form->appendHTML(<<<EOD
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">SVN地址</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control-static">
|
||||
<button id="button-getsvn" type="button" class="btn btn-info btn-xs pull-right">把我的svn密码发到我的邮箱</button>
|
||||
<a>svn://{$http_host}/problem/{$problem['id']}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$('#button-getsvn').click(function(){
|
||||
if (!confirm("确定要发送你的svn密码到${myUser['email']}吗")) {
|
||||
return;
|
||||
}
|
||||
$.post('${_SERVER['REQUEST_URI']}', {
|
||||
getsvn : ''
|
||||
}, function(res) {
|
||||
if (res == "good") {
|
||||
BootstrapDialog.show({
|
||||
title : "操作成功",
|
||||
message : "svn密码已经发送至您的邮箱,请查收。",
|
||||
type : BootstrapDialog.TYPE_SUCCESS,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
} else {
|
||||
BootstrapDialog.show({
|
||||
title : "操作失败",
|
||||
message : "邮件未发送成功",
|
||||
type : BootstrapDialog.TYPE_DANGER,
|
||||
buttons: [{
|
||||
label: '好吧',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOD
|
||||
);
|
||||
$info_form->appendHTML(<<<EOD
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">problem_{$problem['id']}.zip</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control-static">
|
||||
<a href="$download_url">$download_url</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
EOD
|
||||
);
|
||||
$info_form->appendHTML(<<<EOD
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">testlib.h</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control-static">
|
||||
<a href="/download.php?type=testlib.h">下载</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
EOD
|
||||
);
|
||||
|
||||
$esc_submission_requirement = HTML::escape(json_encode(json_decode($problem['submission_requirement']), JSON_PRETTY_PRINT));
|
||||
$info_form->appendHTML(<<<EOD
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">提交文件配置</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control-static"><pre>
|
||||
$esc_submission_requirement
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
EOD
|
||||
);
|
||||
$esc_extra_config = HTML::escape(json_encode(json_decode($problem['extra_config']), JSON_PRETTY_PRINT));
|
||||
$info_form->appendHTML(<<<EOD
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">其它配置</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control-static"><pre>
|
||||
$esc_extra_config
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
EOD
|
||||
);
|
||||
if (isSuperUser($myUser)) {
|
||||
$info_form->addVInput('submission_requirement', 'text', '提交文件配置', $problem['submission_requirement'],
|
||||
function ($submission_requirement, &$vdata) {
|
||||
$submission_requirement = json_decode($submission_requirement, true);
|
||||
if ($submission_requirement === null) {
|
||||
return '不是合法的JSON';
|
||||
}
|
||||
$vdata['submission_requirement'] = json_encode($submission_requirement);
|
||||
},
|
||||
null);
|
||||
$info_form->addVInput('extra_config', 'text', '其它配置', $problem['extra_config'],
|
||||
function ($extra_config, &$vdata) {
|
||||
$extra_config = json_decode($extra_config, true);
|
||||
if ($extra_config === null) {
|
||||
return '不是合法的JSON';
|
||||
}
|
||||
$vdata['extra_config'] = json_encode($extra_config);
|
||||
},
|
||||
null);
|
||||
$info_form->handle = function(&$vdata) {
|
||||
global $problem;
|
||||
$esc_submission_requirement = DB::escape($vdata['submission_requirement']);
|
||||
$esc_extra_config = DB::escape($vdata['extra_config']);
|
||||
DB::update("update problems set submission_requirement = '$esc_submission_requirement', extra_config = '$esc_extra_config' where id = {$problem['id']}");
|
||||
};
|
||||
} else {
|
||||
$info_form->no_submit = true;
|
||||
}
|
||||
|
||||
class DataDisplayer {
|
||||
public $problem_conf = array();
|
||||
public $data_files = array();
|
||||
public $displayers = array();
|
||||
|
||||
public function __construct($problem_conf = null, $data_files = null) {
|
||||
global $data_dir;
|
||||
|
||||
if (isset($problem_conf)) {
|
||||
foreach ($problem_conf as $key => $val) {
|
||||
$this->problem_conf[$key] = array('val' => $val);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($data_files)) {
|
||||
$this->data_files = array_filter(scandir($data_dir), function($x){return $x !== '.' && $x !== '..' && $x !== 'problem.conf';});
|
||||
natsort($this->data_files);
|
||||
array_unshift($this->data_files, 'problem.conf');
|
||||
} else {
|
||||
$this->data_files = $data_files;
|
||||
}
|
||||
|
||||
$this->setDisplayer('problem.conf', function($self) {
|
||||
global $info_form;
|
||||
$info_form->printHTML();
|
||||
echo '<div class="top-buffer-md"></div>';
|
||||
|
||||
echo '<table class="table table-bordered table-hover table-striped table-text-center">';
|
||||
echo '<thead>';
|
||||
echo '<tr>';
|
||||
echo '<th>key</th>';
|
||||
echo '<th>value</th>';
|
||||
echo '</tr>';
|
||||
echo '</thead>';
|
||||
echo '<tbody>';
|
||||
foreach ($self->problem_conf as $key => $info) {
|
||||
if (!isset($info['status'])) {
|
||||
echo '<tr>';
|
||||
echo '<td>', htmlspecialchars($key), '</td>';
|
||||
echo '<td>', htmlspecialchars($info['val']), '</td>';
|
||||
echo '</tr>';
|
||||
} elseif ($info['status'] == 'danger') {
|
||||
echo '<tr class="text-danger">';
|
||||
echo '<td>', htmlspecialchars($key), '</td>';
|
||||
echo '<td>', htmlspecialchars($info['val']), ' <span class="glyphicon glyphicon-remove"></span>', '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
|
||||
echoFilePre('problem.conf');
|
||||
});
|
||||
}
|
||||
|
||||
public function setProblemConfRowStatus($key, $status) {
|
||||
$this->problem_conf[$key]['status'] = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDisplayer($file_name, $fun) {
|
||||
$this->displayers[$file_name] = $fun;
|
||||
return $this;
|
||||
}
|
||||
public function addDisplayer($file_name, $fun) {
|
||||
$this->data_files[] = $file_name;
|
||||
$this->displayers[$file_name] = $fun;
|
||||
return $this;
|
||||
}
|
||||
public function echoDataFilesList($active_file) {
|
||||
foreach ($this->data_files as $file_name) {
|
||||
if ($file_name != $active_file) {
|
||||
echo '<li>';
|
||||
} else {
|
||||
echo '<li class="active">';
|
||||
}
|
||||
echo '<a href="#">', htmlspecialchars($file_name), '</a>', '</li>';
|
||||
}
|
||||
}
|
||||
public function displayFile($file_name) {
|
||||
global $data_dir;
|
||||
|
||||
if (isset($this->displayers[$file_name])) {
|
||||
$fun = $this->displayers[$file_name];
|
||||
$fun($this);
|
||||
} elseif (in_array($file_name, $this->data_files)) {
|
||||
echoFilePre($file_name);
|
||||
} else {
|
||||
echoFileNotFound($file_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDataDisplayer() {
|
||||
global $data_dir;
|
||||
global $problem;
|
||||
|
||||
$allow_files = array_flip(array_filter(scandir($data_dir), function($x){return $x !== '.' && $x !== '..';}));
|
||||
|
||||
$getDisplaySrcFunc = function($name) use($allow_files) {
|
||||
return function() use($name, $allow_files) {
|
||||
$src_name = $name . '.cpp';
|
||||
if (isset($allow_files[$src_name])) {
|
||||
echoFilePre($src_name);
|
||||
} else {
|
||||
echoFileNotFound($src_name);
|
||||
}
|
||||
if (isset($allow_files[$name])) {
|
||||
echoFilePre($name);
|
||||
} else {
|
||||
echoFileNotFound($name);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
$problem_conf = getUOJConf("$data_dir/problem.conf");
|
||||
if ($problem_conf === -1) {
|
||||
return (new DataDisplayer())->setDisplayer('problem.conf', function() {
|
||||
global $info_form;
|
||||
$info_form->printHTML();
|
||||
echoFileNotFound('problem.conf');
|
||||
});
|
||||
}
|
||||
if ($problem_conf === -2) {
|
||||
return (new DataDisplayer())->setDisplayer('problem.conf', function() {
|
||||
global $info_form;
|
||||
$info_form->printHTML();
|
||||
echo '<h4 class="text-danger">problem.conf 格式有误</h4>';
|
||||
echoFilePre('problem.conf');
|
||||
});
|
||||
}
|
||||
|
||||
$judger_name = getUOJConfVal($problem_conf, 'use_builtin_judger', null);
|
||||
if (!isset($problem_conf['use_builtin_judger'])) {
|
||||
return new DataDisplayer($problem_conf);
|
||||
}
|
||||
if ($problem_conf['use_builtin_judger'] == 'on') {
|
||||
$n_tests = getUOJConfVal($problem_conf, 'n_tests', 10);
|
||||
if (!validateUInt($n_tests)) {
|
||||
return (new DataDisplayer($problem_conf))->setProblemConfRowStatus('n_tests', 'danger');
|
||||
}
|
||||
|
||||
$has_extra_tests = !(isset($problem_conf['submit_answer']) && $problem_conf['submit_answer'] == 'on');
|
||||
|
||||
$data_disp = new DataDisplayer($problem_conf, array('problem.conf'));
|
||||
$data_disp->addDisplayer('tests',
|
||||
function($self) use($problem_conf, $allow_files, $n_tests, $n_ex_tests) {
|
||||
for ($num = 1; $num <= $n_tests; $num++) {
|
||||
$input_file_name = getUOJProblemInputFileName($problem_conf, $num);
|
||||
$output_file_name = getUOJProblemOutputFileName($problem_conf, $num);
|
||||
echo '<div class="row">';
|
||||
echo '<div class="col-md-6">';
|
||||
if (isset($allow_files[$input_file_name])) {
|
||||
echoFilePre($input_file_name);
|
||||
} else {
|
||||
echoFileNotFound($input_file_name);
|
||||
}
|
||||
echo '</div>';
|
||||
echo '<div class="col-md-6">';
|
||||
if (isset($allow_files[$output_file_name])) {
|
||||
echoFilePre($output_file_name);
|
||||
} else {
|
||||
echoFileNotFound($output_file_name);
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
);
|
||||
if ($has_extra_tests) {
|
||||
$n_ex_tests = getUOJConfVal($problem_conf, 'n_ex_tests', 0);
|
||||
if (!validateUInt($n_ex_tests)) {
|
||||
return (new DataDisplayer($problem_conf))->setProblemConfRowStatus('n_ex_tests', 'danger');
|
||||
}
|
||||
|
||||
$data_disp->addDisplayer('extra tests',
|
||||
function($self) use($problem_conf, $allow_files, $n_tests, $n_ex_tests) {
|
||||
for ($num = 1; $num <= $n_ex_tests; $num++) {
|
||||
$input_file_name = getUOJProblemExtraInputFileName($problem_conf, $num);
|
||||
$output_file_name = getUOJProblemExtraOutputFileName($problem_conf, $num);
|
||||
echo '<div class="row">';
|
||||
echo '<div class="col-md-6">';
|
||||
if (isset($allow_files[$input_file_name])) {
|
||||
echoFilePre($input_file_name);
|
||||
} else {
|
||||
echoFileNotFound($input_file_name);
|
||||
}
|
||||
echo '</div>';
|
||||
echo '<div class="col-md-6">';
|
||||
if (isset($allow_files[$output_file_name])) {
|
||||
echoFilePre($output_file_name);
|
||||
} else {
|
||||
echoFileNotFound($output_file_name);
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($problem_conf['use_builtin_checker'])) {
|
||||
$data_disp->addDisplayer('checker', function($self) {
|
||||
echo '<h4>use builtin checker : ', $self->problem_conf['use_builtin_checker']['val'], '</h4>';
|
||||
});
|
||||
} else {
|
||||
$data_disp->addDisplayer('checker', $getDisplaySrcFunc('chk'));
|
||||
}
|
||||
if ($problem['hackable']) {
|
||||
$data_disp->addDisplayer('standard', $getDisplaySrcFunc('std'));
|
||||
$data_disp->addDisplayer('validator', $getDisplaySrcFunc('val'));
|
||||
}
|
||||
return $data_disp;
|
||||
} else {
|
||||
return (new DataDisplayer($problem_conf))->setProblemConfRowStatus('use_builtin_judger', 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
$data_disp = getDataDisplayer();
|
||||
|
||||
if (isset($_GET['display_file'])) {
|
||||
if (!isset($_GET['file_name'])) {
|
||||
echoFileNotFound('');
|
||||
} else {
|
||||
$data_disp->displayFile($_GET['file_name']);
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
$hackable_form = new UOJForm('hackable');
|
||||
$hackable_form->handle = function() {
|
||||
global $problem;
|
||||
$problem['hackable'] = !$problem['hackable'];
|
||||
$ret = svnSyncProblemData($problem);
|
||||
if ($ret) {
|
||||
becomeMsgPage('<div>' . $ret . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
|
||||
}
|
||||
|
||||
$hackable = $problem['hackable'] ? 1 : 0;
|
||||
mysql_query("update problems set hackable = $hackable where id = ${problem['id']}");
|
||||
};
|
||||
$hackable_form->submit_button_config['class_str'] = 'btn btn-warning btn-block';
|
||||
$hackable_form->submit_button_config['text'] = $problem['hackable'] ? '禁止使用hack' : '允许使用hack';
|
||||
$hackable_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$data_form = new UOJForm('data');
|
||||
$data_form->handle = function() {
|
||||
global $problem, $myUser;
|
||||
$ret = svnSyncProblemData($problem, $myUser);
|
||||
if ($ret) {
|
||||
becomeMsgPage('<div>' . $ret . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
|
||||
}
|
||||
};
|
||||
$data_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
|
||||
$data_form->submit_button_config['text'] = '与svn仓库同步';
|
||||
$data_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$clear_data_form = new UOJForm('clear_data');
|
||||
$clear_data_form->handle = function() {
|
||||
global $problem;
|
||||
svnClearProblemData($problem);
|
||||
};
|
||||
$clear_data_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
|
||||
$clear_data_form->submit_button_config['text'] = '清空题目数据';
|
||||
$clear_data_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$rejudge_form = new UOJForm('rejudge');
|
||||
$rejudge_form->handle = function() {
|
||||
global $problem;
|
||||
rejudgeProblem($problem);
|
||||
};
|
||||
$rejudge_form->succ_href = "/submissions?problem_id={$problem['id']}";
|
||||
$rejudge_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
|
||||
$rejudge_form->submit_button_config['text'] = '重测该题';
|
||||
$rejudge_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$view_type_form = new UOJForm('view_type');
|
||||
$view_type_form->addVSelect('view_content_type',
|
||||
array('NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看提交文件:',
|
||||
$problem_extra_config['view_content_type']
|
||||
);
|
||||
$view_type_form->addVSelect('view_all_details_type',
|
||||
array('NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看全部详细信息:',
|
||||
$problem_extra_config['view_all_details_type']
|
||||
);
|
||||
$view_type_form->addVSelect('view_details_type',
|
||||
array('NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看测试点详细信息:',
|
||||
$problem_extra_config['view_details_type']
|
||||
);
|
||||
$view_type_form->handle = function() {
|
||||
global $problem, $problem_extra_config;
|
||||
$config = $problem_extra_config;
|
||||
$config['view_content_type'] = $_POST['view_content_type'];
|
||||
$config['view_all_details_type'] = $_POST['view_all_details_type'];
|
||||
$config['view_details_type'] = $_POST['view_details_type'];
|
||||
$esc_config = DB::escape(json_encode($config));
|
||||
mysql_query("update problems set extra_config = '$esc_config' where id = '{$problem['id']}'");
|
||||
};
|
||||
$view_type_form->submit_button_config['class_str'] = 'btn btn-warning btn-block top-buffer-sm';
|
||||
|
||||
if ($problem['hackable']) {
|
||||
$test_std_form = new UOJForm('test_std');
|
||||
$test_std_form->handle = function() {
|
||||
global $myUser, $problem;
|
||||
|
||||
$user_std = queryUser('std');
|
||||
if (!$user_std) {
|
||||
becomeMsgPage('Please create an user named "std"');
|
||||
}
|
||||
|
||||
$requirement = json_decode($problem['submission_requirement'], true);
|
||||
|
||||
$zip_file_name = uojRandAvaiableSubmissionFileName();
|
||||
$zip_file = new ZipArchive();
|
||||
if ($zip_file->open(UOJContext::storagePath().$zip_file_name, ZipArchive::CREATE) !== true) {
|
||||
becomeMsgPage('提交失败');
|
||||
}
|
||||
|
||||
$content = array();
|
||||
$content['file_name'] = $zip_file_name;
|
||||
$content['config'] = array();
|
||||
foreach ($requirement as $req) {
|
||||
if ($req['type'] == "source code") {
|
||||
$content['config'][] = array("{$req['name']}_language", "C++");
|
||||
}
|
||||
}
|
||||
|
||||
$tot_size = 0;
|
||||
foreach ($requirement as $req) {
|
||||
$zip_file->addFile("/var/uoj_data/{$problem['id']}/std.cpp", $req['file_name']);
|
||||
$tot_size += $zip_file->statName($req['file_name'])['size'];
|
||||
}
|
||||
|
||||
$zip_file->close();
|
||||
|
||||
$content['config'][] = array('validate_input_before_test', 'on');
|
||||
$content['config'][] = array('problem_id', $problem['id']);
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
$esc_language = DB::escape('C++');
|
||||
|
||||
$result = array();
|
||||
$result['status'] = "Waiting";
|
||||
$result_json = json_encode($result);
|
||||
$is_hidden = $problem['is_hidden'] ? 1 : 0;
|
||||
|
||||
DB::insert("insert into submissions (problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values ({$problem['id']}, now(), '{$user_std['username']}', '$esc_content', '$esc_language', $tot_size, '{$result['status']}', '$result_json', $is_hidden)");
|
||||
};
|
||||
$test_std_form->succ_href = "/submissions?problem_id={$problem['id']}";
|
||||
$test_std_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
|
||||
$test_std_form->submit_button_config['text'] = '检验数据正确性';
|
||||
$test_std_form->runAtServer();
|
||||
}
|
||||
|
||||
$hackable_form->runAtServer();
|
||||
$view_type_form->runAtServer();
|
||||
$data_form->runAtServer();
|
||||
$clear_data_form->runAtServer();
|
||||
$rejudge_form->runAtServer();
|
||||
$info_form->runAtServer();
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['dialog'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 数据 - 题目管理') ?>
|
||||
<h1 class="page-header" align="center">#<?=$problem['id']?> : <?=$problem['title']?> 管理</h1>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">编辑</a></li>
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">管理者</a></li>
|
||||
<li class="active"><a href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">数据</a></li>
|
||||
<li><a href="/problem/<?=$problem['id']?>" role="tab">返回</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10 top-buffer-sm">
|
||||
<div class="row">
|
||||
<div class="col-md-3 top-buffer-sm" id="div-file_list">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<?php $data_disp->echoDataFilesList('problem.conf'); ?>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-9 top-buffer-sm" id="div-file_content">
|
||||
<?php $data_disp->displayFile('problem.conf'); ?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
curFileName = '';
|
||||
$('#div-file_list a').click(function(e) {
|
||||
$('#div-file_content').html('<h3>loading...</h3>');
|
||||
$(this).tab('show');
|
||||
|
||||
var fileName = $(this).text();
|
||||
curFileName = fileName;
|
||||
$.get('/problem/<?= $problem['id'] ?>/manage/data', {
|
||||
display_file: '',
|
||||
file_name: fileName
|
||||
},
|
||||
function(data) {
|
||||
if (curFileName != fileName) {
|
||||
return;
|
||||
}
|
||||
$('#div-file_content').html(data);
|
||||
},
|
||||
'html'
|
||||
);
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 top-buffer-sm">
|
||||
<div class="top-buffer-md">
|
||||
<?php if ($problem['hackable']): ?>
|
||||
<span class="glyphicon glyphicon-ok"></span> hack功能已启用
|
||||
<?php else: ?>
|
||||
<span class="glyphicon glyphicon-remove"></span> hack功能已禁止
|
||||
<?php endif ?>
|
||||
<?php $hackable_form->printHTML() ?>
|
||||
</div>
|
||||
<div class="top-buffer-md">
|
||||
<?php if ($problem['hackable']): ?>
|
||||
<?php $test_std_form->printHTML() ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="top-buffer-md">
|
||||
<button id="button-display_view_type" type="button" class="btn btn-primary btn-block" onclick="$('#div-view_type').toggle('fast');">修改提交记录可视权限</button>
|
||||
<div class="top-buffer-sm" id="div-view_type" style="display:none; padding-left:5px; padding-right:5px;">
|
||||
<?php $view_type_form->printHTML(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-buffer-md">
|
||||
<?php $data_form->printHTML(); ?>
|
||||
</div>
|
||||
<div class="top-buffer-md">
|
||||
<?php $clear_data_form->printHTML(); ?>
|
||||
</div>
|
||||
<div class="top-buffer-md">
|
||||
<?php $rejudge_form->printHTML(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
64
uoj/1/app/controllers/problem_managers_manage.php
Normal file
64
uoj/1/app/controllers/problem_managers_manage.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('svn');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
if (!hasProblemPermission($myUser, $problem)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
$managers_form = newAddDelCmdForm('managers',
|
||||
function($username) {
|
||||
if (!validateUsername($username) || !queryUser($username)) {
|
||||
return "不存在名为{$username}的用户";
|
||||
}
|
||||
return '';
|
||||
},
|
||||
function($type, $username) {
|
||||
global $problem;
|
||||
if ($type == '+') {
|
||||
mysql_query("insert into problems_permissions (problem_id, username) values (${problem['id']}, '$username')");
|
||||
} else if ($type == '-') {
|
||||
mysql_query("delete from problems_permissions where problem_id = ${problem['id']} and username = '$username'");
|
||||
}
|
||||
},
|
||||
function() {
|
||||
global $problem;
|
||||
svnRefreshPasswordOfProblem($problem['id']);
|
||||
}
|
||||
);
|
||||
|
||||
$managers_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 管理者 - 题目管理') ?>
|
||||
<h1 class="page-header" align="center">#<?=$problem['id']?> : <?=$problem['title']?> 管理</h1>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">编辑</a></li>
|
||||
<li class="active"><a href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">管理者</a></li>
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">数据</a></li>
|
||||
<li><a href="/problem/<?=$problem['id']?>" role="tab">返回</a></li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>用户名</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$row_id = 0;
|
||||
$result = mysql_query("select username from problems_permissions where problem_id = ${problem['id']}");
|
||||
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
|
||||
$row_id++;
|
||||
echo '<tr>', '<td>', $row_id, '</td>', '<td>', getUserLink($row['username']), '</td>', '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-center">命令格式:命令一行一个,+mike表示把mike加入管理者,-mike表示把mike从管理者中移除</p>
|
||||
<?php $managers_form->printHTML(); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
183
uoj/1/app/controllers/problem_set.php
Normal file
183
uoj/1/app/controllers/problem_set.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('svn');
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$new_problem_form = new UOJForm('new_problem');
|
||||
$new_problem_form->handle = function() {
|
||||
mysql_query("insert into problems (title, is_hidden, submission_requirement) values ('New Problem', 1, '{}')");
|
||||
$id = mysql_insert_id();
|
||||
mysql_query("insert into problems_contents (id, statement, statement_md) values ($id, '', '')");
|
||||
svnNewProblem($id);
|
||||
};
|
||||
$new_problem_form->submit_button_config['align'] = 'right';
|
||||
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
|
||||
$new_problem_form->submit_button_config['text'] = UOJLocale::get('problems::add new');
|
||||
$new_problem_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$new_problem_form->runAtServer();
|
||||
}
|
||||
|
||||
function echoProblem($problem) {
|
||||
global $myUser;
|
||||
if (isProblemVisibleToUser($problem, $myUser)) {
|
||||
echo '<tr class="text-center">';
|
||||
if ($problem['submission_id']) {
|
||||
echo '<td class="success">';
|
||||
} else {
|
||||
echo '<td>';
|
||||
}
|
||||
echo '#', $problem['id'], '</td>';
|
||||
echo '<td class="text-left">', '<a href="/problem/', $problem['id'], '">', $problem['title'], '</a>';
|
||||
if (isset($_COOKIE['show_tags_mode'])) {
|
||||
foreach (queryProblemTags($problem['id']) as $tag) {
|
||||
echo '<a class="uoj-problem-tag">', '<span class="badge">', HTML::escape($tag), '</span>', '</a>';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
if (isset($_COOKIE['show_submit_mode'])) {
|
||||
$perc = $problem['submit_num'] > 0 ? round(100 * $problem['ac_num'] / $problem['submit_num']) : 0;
|
||||
echo <<<EOD
|
||||
<td><a href="/submissions?problem_id={$problem['id']}&min_score=100&max_score=100">×{$problem['ac_num']}</a></td>
|
||||
<td><a href="/submissions?problem_id={$problem['id']}">×{$problem['submit_num']}</a></td>
|
||||
<td>
|
||||
<div class="progress bot-buffer-no">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$perc" aria-valuemin="0" aria-valuemax="100" style="width: $perc%; min-width: 20px;">{$perc}%</div>
|
||||
</div>
|
||||
</td>
|
||||
EOD;
|
||||
}
|
||||
echo '<td class="text-left">', getClickZanBlock('P', $problem['id'], $problem['zan']), '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
$cond = array();
|
||||
|
||||
$search_tag = null;
|
||||
|
||||
$cur_tab = isset($_GET['tab']) ? $_GET['tab'] : 'all';
|
||||
if ($cur_tab == 'template') {
|
||||
$search_tag = "模板题";
|
||||
}
|
||||
if (isset($_GET['tag'])) {
|
||||
$search_tag = $_GET['tag'];
|
||||
}
|
||||
if ($search_tag) {
|
||||
$cond[] = "'".DB::escape($search_tag)."' in (select tag from problems_tags where problems_tags.problem_id = problems.id)";
|
||||
}
|
||||
|
||||
if ($cond) {
|
||||
$cond = join($cond, ' and ');
|
||||
} else {
|
||||
$cond = '1';
|
||||
}
|
||||
|
||||
$header = '<tr>';
|
||||
$header .= '<th class="text-center" style="width:5em;">ID</th>';
|
||||
$header .= '<th>'.UOJLocale::get('problems::problem').'</th>';
|
||||
if (isset($_COOKIE['show_submit_mode'])) {
|
||||
$header .= '<th class="text-center" style="width:5em;">'.UOJLocale::get('problems::ac').'</th>';
|
||||
$header .= '<th class="text-center" style="width:5em;">'.UOJLocale::get('problems::submit').'</th>';
|
||||
$header .= '<th class="text-center" style="width:150px;">'.UOJLocale::get('problems::ac ratio').'</th>';
|
||||
}
|
||||
$header .= '<th class="text-center" style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
|
||||
$header .= '</tr>';
|
||||
|
||||
$tabs_info = array(
|
||||
'all' => array(
|
||||
'name' => UOJLocale::get('problems::all problems'),
|
||||
'url' => "/problems"
|
||||
),
|
||||
'template' => array(
|
||||
'name' => UOJLocale::get('problems::template problems'),
|
||||
'url' => "/problems/template"
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
<?php
|
||||
echoLongTable(array('*'),
|
||||
"problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id", $cond, 'order by id asc',
|
||||
$header,
|
||||
'echoProblem',
|
||||
array('page_len' => 3,
|
||||
'table_classes' => array('table', 'table-bordered', 'table-hover', 'table-striped'),
|
||||
'print_after_table' => function() {
|
||||
global $myUser;
|
||||
if (isSuperUser($myUser)) {
|
||||
global $new_problem_form;
|
||||
$new_problem_form->printHTML();
|
||||
}
|
||||
},
|
||||
'head_pagination' => true
|
||||
)
|
||||
);
|
||||
?>*/
|
||||
|
||||
$pag_config = array('page_len' => 100);
|
||||
$pag_config['col_names'] = array('*');
|
||||
$pag_config['table_name'] = "problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id";
|
||||
$pag_config['cond'] = $cond;
|
||||
$pag_config['tail'] = "order by id asc";
|
||||
$pag = new Paginator($pag_config);
|
||||
|
||||
$div_classes = array('table-responsive');
|
||||
$table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped');
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('problems')) ?>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?>
|
||||
</div>
|
||||
<div class="col-sm-4 col-sm-push-4 checkbox text-right">
|
||||
<label class="checkbox-inline" for="input-show_tags_mode"><input type="checkbox" id="input-show_tags_mode" <?= isset($_COOKIE['show_tags_mode']) ? 'checked="checked" ': ''?>/> <?= UOJLocale::get('problems::show tags') ?></label>
|
||||
<label class="checkbox-inline" for="input-show_submit_mode"><input type="checkbox" id="input-show_submit_mode" <?= isset($_COOKIE['show_submit_mode']) ? 'checked="checked" ': ''?>/> <?= UOJLocale::get('problems::show statistics') ?></label>
|
||||
</div>
|
||||
<div class="col-sm-4 col-sm-pull-4">
|
||||
<?php echo $pag->pagination(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-buffer-sm"></div>
|
||||
<script type="text/javascript">
|
||||
$('#input-show_tags_mode').click(function() {
|
||||
if (this.checked) {
|
||||
$.cookie('show_tags_mode', '', {path: '/problems'});
|
||||
} else {
|
||||
$.removeCookie('show_tags_mode', {path: '/problems'});
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
$('#input-show_submit_mode').click(function() {
|
||||
if (this.checked) {
|
||||
$.cookie('show_submit_mode', '', {path: '/problems'});
|
||||
} else {
|
||||
$.removeCookie('show_submit_mode', {path: '/problems'});
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
echo '<div class="', join($div_classes, ' '), '">';
|
||||
echo '<table class="', join($table_classes, ' '), '">';
|
||||
echo '<thead>';
|
||||
echo $header;
|
||||
echo '</thead>';
|
||||
echo '<tbody>';
|
||||
|
||||
foreach ($pag->get() as $idx => $row) {
|
||||
echoProblem($row);
|
||||
}
|
||||
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
echo '</div>';
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$new_problem_form->printHTML();
|
||||
}
|
||||
|
||||
echo $pag->pagination();
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
58
uoj/1/app/controllers/problem_statement_manage.php
Normal file
58
uoj/1/app/controllers/problem_statement_manage.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
if (!hasProblemPermission($myUser, $problem)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
$problem_content = queryProblemContent($problem['id']);
|
||||
$problem_tags = queryProblemTags($problem['id']);
|
||||
|
||||
$problem_editor = new UOJBlogEditor();
|
||||
$problem_editor->name = 'problem';
|
||||
$problem_editor->blog_url = "/problem/{$problem['id']}";
|
||||
$problem_editor->cur_data = array(
|
||||
'title' => $problem['title'],
|
||||
'content_md' => $problem_content['statement_md'],
|
||||
'content' => $problem_content['statement'],
|
||||
'tags' => $problem_tags,
|
||||
'is_hidden' => $problem['is_hidden']
|
||||
);
|
||||
$problem_editor->label_text = array_merge($problem_editor->label_text, array(
|
||||
'view blog' => '查看题目',
|
||||
'blog visibility' => '题目可见性'
|
||||
));
|
||||
|
||||
$problem_editor->save = function($data) {
|
||||
global $problem, $problem_tags;
|
||||
DB::update("update problems set title = '".DB::escape($data['title'])."' where id = {$problem['id']}");
|
||||
DB::update("update problems_contents set statement = '".DB::escape($data['content'])."', statement_md = '".DB::escape($data['content_md'])."' where id = {$problem['id']}");
|
||||
|
||||
if ($data['tags'] !== $problem_tags) {
|
||||
DB::delete("delete from problems_tags where problem_id = {$problem['id']}");
|
||||
foreach ($data['tags'] as $tag) {
|
||||
DB::insert("insert into problems_tags (problem_id, tag) values ({$problem['id']}, '".DB::escape($tag)."')");
|
||||
}
|
||||
}
|
||||
if ($data['is_hidden'] != $problem['is_hidden'] ) {
|
||||
DB::update("update problems set is_hidden = {$data['is_hidden']} where id = {$problem['id']}");
|
||||
DB::update("update submissions set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");
|
||||
DB::update("update hacks set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");
|
||||
}
|
||||
};
|
||||
|
||||
$problem_editor->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 编辑 - 题目管理') ?>
|
||||
<h1 class="page-header" align="center">#<?=$problem['id']?> : <?=$problem['title']?> 管理</h1>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="active"><a href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">编辑</a></li>
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">管理者</a></li>
|
||||
<li><a href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">数据</a></li>
|
||||
<li><a href="/problem/<?=$problem['id']?>" role="tab">返回</a></li>
|
||||
</ul>
|
||||
<?php $problem_editor->printHTML() ?>
|
||||
<?php echoUOJPageFooter() ?>
|
169
uoj/1/app/controllers/problem_statistics.php
Normal file
169
uoj/1/app/controllers/problem_statistics.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null;
|
||||
if ($contest != null) {
|
||||
genMoreContestInfo($contest);
|
||||
if (!isContestProblemVisibleToUser($problem, $contest, $myUser)) {
|
||||
become404Page();
|
||||
}
|
||||
} else {
|
||||
if (!isProblemVisibleToUser($problem, $myUser)) {
|
||||
become404Page();
|
||||
}
|
||||
}
|
||||
|
||||
function scoreDistributionData() {
|
||||
$data = array();
|
||||
$result = DB::select("select score, count(*) from submissions where problem_id = {$_GET['id']} and score is not null group by score");
|
||||
$is_res_empty = true;
|
||||
$has_score_0 = false;
|
||||
$has_score_100 = false;
|
||||
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
|
||||
if ($row[0] == 0) {
|
||||
$has_score_0 = true;
|
||||
} else if ($row[0] == 100) {
|
||||
$has_score_100 = true;
|
||||
}
|
||||
$score = $row[0] * 100;
|
||||
$data[] = array('score' => $score, 'count' => $row[1]);
|
||||
}
|
||||
if (!$has_score_0) {
|
||||
array_unshift($data, array('score' => 0, 'count' => 0));
|
||||
}
|
||||
if (!$has_score_100) {
|
||||
$data[] = array('score' => 10000, 'count' => 0);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = scoreDistributionData();
|
||||
$pre_data = $data;
|
||||
$suf_data = $data;
|
||||
for ($i = 0; $i < count($data); $i++) {
|
||||
$data[$i]['score'] /= 100;
|
||||
}
|
||||
for ($i = 1; $i < count($data); $i++) {
|
||||
$pre_data[$i]['count'] += $pre_data[$i - 1]['count'];
|
||||
}
|
||||
for ($i = count($data) - 1; $i > 0; $i--) {
|
||||
$suf_data[$i - 1]['count'] += $suf_data[$i]['count'];
|
||||
}
|
||||
|
||||
$submissions_sort_by_choice = !isset($_COOKIE['submissions-sort-by-code-length']) ? 'time' : 'tot_size';
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['morris'] = "";
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::statistics')) ?>
|
||||
|
||||
<h1 class="page-header text-center"><?= $problem['title'] ?> <?= UOJLocale::get('problems::statistics') ?></h1>
|
||||
|
||||
<?php if ($contest && !hasContestPermission($myUser, $contest) && $contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
|
||||
<h2 class="text-center text-muted">比赛尚未结束</h2>
|
||||
<?php else: ?>
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::accepted submissions') ?></h2>
|
||||
<div class="text-right bot-buffer-sm">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a href="<?=$SERVER['REQUEST_URI']?>" class="<?=$submissions_sort_by_choice == 'time' ? 'btn btn-info btn-xs active' : 'btn btn-info btn-xs'?>" id="submissions-sort-by-run-time"><?= UOJLocale::get('problems::fastest') ?></a>
|
||||
<a href="<?=$SERVER['REQUEST_URI']?>" class="<?=$submissions_sort_by_choice == 'tot_size' ? 'btn btn-info btn-xs active' : 'btn btn-info btn-xs'?>" id="submissions-sort-by-code-length"><?= UOJLocale::get('problems::shortest') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#submissions-sort-by-run-time').click(function() {
|
||||
$.cookie('submissions-sort-by-run-time', '');
|
||||
$.removeCookie('submissions-sort-by-code-length');
|
||||
});
|
||||
$('#submissions-sort-by-code-length').click(function() {
|
||||
$.cookie('submissions-sort-by-code-length', '');
|
||||
$.removeCookie('submissions-sort-by-run-time');
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php if ($submissions_sort_by_choice == 'time'): ?>
|
||||
<?php echoSubmissionsList("best_ac_submissions.submission_id = submissions.id and best_ac_submissions.problem_id = {$problem['id']}", 'order by best_ac_submissions.used_time, best_ac_submissions.used_memory, best_ac_submissions.tot_size', array('judge_time_hidden' => '', 'table_name' => 'best_ac_submissions, submissions'), $myUser); ?>
|
||||
<?php else: ?>
|
||||
<?php echoSubmissionsList("best_ac_submissions.shortest_id = submissions.id and best_ac_submissions.problem_id = {$problem['id']}", 'order by best_ac_submissions.shortest_tot_size, best_ac_submissions.shortest_used_time, best_ac_submissions.shortest_used_memory', array('judge_time_hidden' => '', 'table_name' => 'best_ac_submissions, submissions'), $myUser); ?>
|
||||
<?php endif ?>
|
||||
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::score distribution') ?></h2>
|
||||
<div id="score-distribution-chart" style="height: 250px;"></div>
|
||||
<script type="text/javascript">
|
||||
new Morris.Bar({
|
||||
element: 'score-distribution-chart',
|
||||
data: <?= json_encode($data) ?>,
|
||||
barColors: function(r, s, type) {
|
||||
return getColOfScore(r.label);
|
||||
},
|
||||
xkey: 'score',
|
||||
ykeys: ['count'],
|
||||
labels: ['number'],
|
||||
hoverCallback: function(index, options, content, row) {
|
||||
var scr = row.score;
|
||||
return '<div class="morris-hover-row-label">' + 'score: ' + scr + '</div>' +
|
||||
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&min_score=' + scr + '&max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
|
||||
},
|
||||
resize: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::prefix sum of score distribution') ?></h2>
|
||||
<div id="score-distribution-chart-pre" style="height: 250px;"></div>
|
||||
<script type="text/javascript">
|
||||
new Morris.Line({
|
||||
element: 'score-distribution-chart-pre',
|
||||
data: <?= json_encode($pre_data) ?>,
|
||||
xkey: 'score',
|
||||
ykeys: ['count'],
|
||||
labels: ['number'],
|
||||
lineColors: function(row, sidx, type) {
|
||||
if (type == 'line') {
|
||||
return '#0b62a4';
|
||||
}
|
||||
return getColOfScore(row.src.score / 100);
|
||||
},
|
||||
xLabelFormat: function(x) {
|
||||
return (x.getTime() / 100).toString();
|
||||
},
|
||||
hoverCallback: function(index, options, content, row) {
|
||||
var scr = row.score / 100;
|
||||
return '<div class="morris-hover-row-label">' + 'score: ≤' + scr + '</div>' +
|
||||
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
|
||||
},
|
||||
resize: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::suffix sum of score distribution') ?></h2>
|
||||
<div id="score-distribution-chart-suf" style="height: 250px;"></div>
|
||||
<script type="text/javascript">
|
||||
new Morris.Line({
|
||||
element: 'score-distribution-chart-suf',
|
||||
data: <?= json_encode($suf_data) ?>,
|
||||
xkey: 'score',
|
||||
ykeys: ['count'],
|
||||
labels: ['number'],
|
||||
lineColors: function(row, sidx, type) {
|
||||
if (type == 'line') {
|
||||
return '#0b62a4';
|
||||
}
|
||||
return getColOfScore(row.src.score / 100);
|
||||
},
|
||||
xLabelFormat: function(x) {
|
||||
return (x.getTime() / 100).toString();
|
||||
},
|
||||
hoverCallback: function(index, options, content, row) {
|
||||
var scr = row.score / 100;
|
||||
return '<div class="morris-hover-row-label">' + 'score: ≥' + scr + '</div>' +
|
||||
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&min_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
|
||||
},
|
||||
resize: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user