contestify 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,28 +1,46 @@
1
1
  Contestify
2
- ====
2
+ =====
3
3
 
4
- This is a simple ruby gem to automate the process of setting up a programming contest from a [COCI](http://www.hsin.hr/coci/) problem set using [DOMJudge](http://domjudge.sourceforge.net/).
4
+ This is a simple ruby gem to automate the process of setting up a programming contest with [DOMJudge](http://domjudge.sourceforge.net/). You can choose from several judges and the problems will be uploaded without too much work, because, let's be honest, DOMJudge is a great online judge, but it sucks at UI.
5
5
 
6
- Usage
6
+ Install
7
7
  ---
8
8
 
9
+ Installing Contestify couldn't be easier. Just type
10
+
11
+ gem install contestify
12
+
13
+ Then, you can run `contestify check` to check the OS dependencies. Most Unix systems _should_ have the dependencies installed.
14
+
15
+ Usage & Help
16
+ ---
17
+
18
+ You can run the `contestify` command with no arguments to see the available judges. Each available judge will ask for different arguments. If you need help with a specific judge, you can type `contestify help <judge>` and it will give you some help about it.
19
+
9
20
  ```
10
- contestify coci_problems_url judge_upload_url judge_password
21
+ contestify help coci
11
22
  ```
12
23
 
13
- This will get the .zip file from the `coci_problems_url` and add the problems to the DOM Judge on the server. This **will not** create a new contest. Just add problems to the current one.
24
+ Provide the requested data for each judge and it should run smoothly. It is important to notice that Contestify **DOES NOT** store **ANY** data. It asks for passwords just to be able to upload (and sometimes fetch) the problems. If you still doubt it, you can check the code for yourself.
25
+
26
+
27
+ Notes
28
+ ---
29
+
30
+ * Contestify **will not** create a new contest. Just add problems to the current one. So you'll need to have an active contest to run this.
14
31
 
15
- It is assumed that the admin username is `admin`.
32
+ * It is assumed that the **Admin username** is `admin`.
16
33
 
17
- ### Example
34
+ Fast Example
35
+ ----
18
36
 
19
37
  ```
20
- contestify http://hsin.hr/coci/contest1_testdata.zip http://juez.factorcomun.org/jury/problem.php p4ssw0rd
38
+ contestify coci http://hsin.hr/coci/contest1_testdata.zip http://juez.factorcomun.org/jury/problem.php p4ssw0rd
21
39
  ```
22
40
 
23
41
  Contributions
24
42
  ---
25
43
 
26
- If you find any bug or have any addition, please let us know. You can use the [issue tracker](https://github.com/nhocki/contestify/issues) for that. We would like to expand this script and be able to use any other problem (not just the ones from COCI).
44
+ If you find any bug or have any addition, please let us know. You can use the [issue tracker](https://github.com/nhocki/contestify/issues) for that.
27
45
 
28
- This script was started by [Andrés Mejía](https://github.com/andmej). I added the config file option and gemified it.
46
+ We would also love to have more and more judges, so if you want to write one, just submit a pull request with it.
data/bin/contestify CHANGED
@@ -2,12 +2,8 @@
2
2
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
3
  require 'contestify'
4
4
 
5
- problems_url = ARGV[0]
6
- judge_url = ARGV[1]
7
- judge_password = ARGV[2]
8
-
9
5
  begin
10
- Contestify::Contest.new(problems_url, judge_url, judge_password).setup!
6
+ Contestify::Contest.start
11
7
  rescue Exception => e
12
8
  puts e.message
13
9
  end
data/contestify.gemspec CHANGED
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "contestify"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Contestify::VERSION
17
-
17
+
18
+ gem.add_dependency "thor", "~> 0.14.6"
18
19
  gem.add_development_dependency "rake", "~> 0.9.2"
19
20
  end
@@ -1,53 +1,18 @@
1
1
  module Contestify
2
- class Contest
2
+ class Contest < Thor
3
3
  include Contestify::Util
4
4
 
5
- attr_accessor :problems_url, :judge_url, :judge_password, :base_dir
6
-
7
- def initialize(problems_url, judge_url, judge_password)
8
- @problems_url, @judge_url, @judge_password = problems_url, judge_url, judge_password
9
-
10
- if [@problems_url, @judge_url, @judge_password].any? {|attribute| attribute.nil? or attribute.empty?}
11
- raise Exception.new(Contestify::HELP_MESSAGE)
12
- end
5
+ desc "coci PROBLEMS_URL JUDGE_UPLOAD_URL JUDGE_PASSWORD", "Setups a contest based on a COCI problemset"
6
+ def coci(problems_url, judge_url, judge_password)
7
+ contest = Contestify::Judge::Coci.new problems_url
8
+ Contestify::Uploader.upload!(judge_url, judge_password, contest.problems_paths)
9
+ clean_dir! contest.working_root_path
13
10
  end
14
11
 
15
- def setup!
12
+ desc "check", "Checks that the user has the required OS software installed"
13
+ def check
16
14
  check_dependencies
17
- get_problems
18
- unzip_problems
19
- @base_dir = Dir.pwd
20
- problems_path = Contestify::Configuration.configure!(@base_dir)
21
- Contestify::Uploader.upload!(@judge_url, @judge_password, problems_path)
22
- clean_dir!
23
- end
24
-
25
- private ######################################################################## PRIVATE
26
-
27
- def clean_dir!
28
- puts red "Deleting created files & folders (#{@base_dir})"
29
- FileUtils.rm_rf @base_dir
30
- end
31
-
32
- def check_dependencies
33
- system?(:zip) and
34
- system?(:curl) and
35
- system?(:unzip) and
36
- system?(:dos2unix)
37
- end
38
-
39
- def get_problems
40
- FileUtils.mkdir_p File.join(Dir.pwd, "contestify")
41
- Dir.chdir File.join(Dir.pwd, "contestify")
42
- puts green "=> Fetching problems from #{@problems_url}"
43
- curl_output = `curl #{@problems_url} > #{File.join(Dir.pwd, "problems.zip")}`
44
- raise Exception.new(red Contestify::CURL_PROBLEM) unless $?.success?
45
- end
46
-
47
- def unzip_problems
48
- puts green "=> Unzipping problems"
49
- unzip_output = `unzip #{File.join(Dir.pwd, "problems.zip")}`
50
- raise Exception.new(red Contestify::UNZIP_PROBLEM) unless $?.success?
15
+ puts green "Everything looks ok. You may now use Contestify."
51
16
  end
52
17
  end
53
18
  end
@@ -0,0 +1,14 @@
1
+ Contestify::Coci
2
+ ===
3
+
4
+ This strategy will upload the problems from a given URL to the server. Usage:
5
+
6
+ contestify coci problem_url judge_upload_url judge_admin_password
7
+
8
+ Note that the Judge URL is the actual **upload** URL, which is normally `http://you-dom-judge.com/jury/problem.php`.
9
+
10
+ Here's an example of usage with _real_ data:
11
+
12
+ contestify http://hsin.hr/coci/contest1_testdata.zip http://domjudge.factorcomun.org/jury/problem.php p4ssw0rd
13
+
14
+ This will get the .zip file from the `http://hsin.hr/coci/contest1_testdata.zip` and add the problems to the DOM Judge on the server.
@@ -0,0 +1,35 @@
1
+ require 'contestify/judges/coci/configuration'
2
+
3
+ module Contestify
4
+ module Judge
5
+ class Coci
6
+ attr_accessor :problems_url, :working_root_path, :problems_paths
7
+
8
+ def initialize(problems_url)
9
+ @problems_url = problems_url
10
+ get_problems
11
+ unzip_problems
12
+ @working_root_path = Dir.pwd
13
+ @problems_paths = Contestify::Coci::Configuration.configure!(@working_root_path)
14
+ end
15
+
16
+ private ################################################ PRIVATE
17
+
18
+ def get_problems
19
+ working_path = File.join(Dir.pwd, "contestify")
20
+ FileUtils.mkdir_p working_path
21
+ Dir.chdir working_path
22
+ puts green "=> Fetching problems from #{@problems_url}"
23
+ curl_output = `curl #{@problems_url} > #{File.join(Dir.pwd, "problems.zip")}`
24
+ raise Exception.new(red Contestify::CURL_PROBLEM) unless $?.success?
25
+ end
26
+
27
+ def unzip_problems
28
+ puts green "=> Unzipping problems"
29
+ unzip_output = `unzip #{File.join(Dir.pwd, "problems.zip")}`
30
+ raise Exception.new(red Contestify::UNZIP_PROBLEM) unless $?.success?
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,97 @@
1
+ module Contestify
2
+ module Coci
3
+ # <tt>Coci::Configuration</tt> holds all the logic to configure a COCI based
4
+ # contest. This class **must** implement the `self.configure!` method.
5
+ class Configuration
6
+
7
+ # <tt>Contestify::Configuration.configure!</tt> is *the only* method you
8
+ # should call from the contest interface. All the other methods should be
9
+ # called from here. This is the only method needed for future strategies
10
+ # for other judges.
11
+ #
12
+ # This should be called in a directory structure such that you have a
13
+ # `base_folder` with one folder for each problem you want to upload to the
14
+ # server. Each of these problems folders should have all the input/output
15
+ # files you want to use as test cases.
16
+ #
17
+ # This method **must** return the absolute paths of the problem folders in
18
+ # an array. This return value will be used in Contestify::Uploader.upload!
19
+ #
20
+ # ==== Parameters
21
+ # base_folder<String>:: The base folder where all the problems folders are
22
+ # stored.
23
+ #
24
+ def self.configure!(base_folder)
25
+ puts green "=> Configuring problems (#{base_folder})"
26
+ problem_index = 0
27
+ Dir.glob("*").select { |f| File.directory?(f) }.map do |dir|
28
+ Dir.chdir File.join(base_folder, dir)
29
+ dirid = Dir.pwd.split('/').last[0...8]
30
+ puts green "==> #{dirid.upcase}"
31
+ rename_data_files
32
+ add_problem_config(dirid, problem_index)
33
+ problem_index += 1
34
+ puts green "==> All the work for #{dirid.upcase} is done"
35
+ # Return the absolute path for this problem
36
+ Dir.pwd
37
+ end
38
+ end
39
+
40
+ #########################################################################
41
+ private #################################################################
42
+
43
+ # <tt>Coci::Configuration.rename_data_files</tt> is in charge of
44
+ # giving each input/output file a standard name that DOMJudge understands.
45
+ #
46
+ # This method should be overriden in future strategies.
47
+ #
48
+ # After this method is called, every different input/output file **must**
49
+ # have the following structure:
50
+ #
51
+ # `problemid`.`number`.in
52
+ # `problemid`.`number`.out
53
+ #
54
+ # where:
55
+ #
56
+ # `problemid` is at most 8 letters long and it's the id for the judge and
57
+ # `number` is the test case number. The .in and .out will determine the
58
+ # input and compare file the judge will run/diff.
59
+ def self.rename_data_files
60
+ puts blue "===> Renaming input/output files"
61
+ Dir.glob("*").select { |f| f =~ /\.in.[0-9]+/ }.each do |f|
62
+ parts = f.split(".")
63
+ new_name = [parts[0], parts[2], parts[1]].join(".")
64
+ File.rename f, new_name
65
+
66
+ f.gsub!(".in", ".out")
67
+ parts = f.split(".")
68
+ new_name = [parts[0], parts[2], parts[1]].join(".")
69
+ File.rename f, new_name
70
+ end
71
+ end
72
+
73
+
74
+ # <tt>Coci::Configuration.add_problem_config</tt> will add the
75
+ # configuration file needed for DOMJudge to understand the problem.
76
+ #
77
+ # ==== Parameters
78
+ # probid<String>:: The problem id for DOMJudge. This **must** be at most
79
+ # 8 letters long.
80
+ #
81
+ # problem_index<Integer>:: This is the problem number. This is only used
82
+ # to choose the problem ballon color.
83
+ #
84
+ def self.add_problem_config(probid, problem_index)
85
+ puts blue "===> Adding DOM Judge configuration #{probid}"
86
+ file_content = <<-TEXT
87
+ probid = #{probid}
88
+ name = #{probid}
89
+ allow_submit = true
90
+ color = #{Contestify::PROBLEM_COLORS[problem_index]}
91
+ timelimit = 1
92
+ TEXT
93
+ File.open(File.join(Dir.pwd, "domjudge-problem.ini"), 'w') {|f| f.write(file_content) }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1 @@
1
+ require 'contestify/judges/coci/coci'
@@ -4,5 +4,17 @@ module Contestify
4
4
  `which #{command.to_s}`
5
5
  raise Exception.new(red "`#{command}` is not installed. Please install it first") unless $?.success?
6
6
  end
7
+
8
+ def clean_dir!(directory)
9
+ puts red "Deleting created files & folders (#{directory})"
10
+ FileUtils.rm_rf directory
11
+ end
12
+
13
+ def check_dependencies
14
+ system?(:zip) and
15
+ system?(:curl) and
16
+ system?(:unzip) and
17
+ system?(:dos2unix)
18
+ end
7
19
  end
8
20
  end
@@ -1,3 +1,3 @@
1
1
  module Contestify
2
- VERSION = "1.2.1"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/contestify.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'thor'
2
3
  require "contestify/version"
3
4
  require "contestify/messages"
4
5
  require "contestify/colorize"
@@ -6,5 +7,6 @@ require "contestify/util"
6
7
  require "contestify/contest"
7
8
  require "contestify/configuration"
8
9
  require "contestify/uploader"
10
+ require "contestify/judges"
9
11
 
10
12
  include Contestify::Colorize
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contestify
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-08 00:00:00.000000000 Z
12
+ date: 2011-12-09 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: &70364193982720 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.14.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70364193982720
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rake
16
- requirement: &70289636072180 !ruby/object:Gem::Requirement
27
+ requirement: &70364193981160 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ~>
@@ -21,7 +32,7 @@ dependencies:
21
32
  version: 0.9.2
22
33
  type: :development
23
34
  prerelease: false
24
- version_requirements: *70289636072180
35
+ version_requirements: *70364193981160
25
36
  description: Gem to prepare internal programming contests taking problems from the
26
37
  COCI contests.
27
38
  email:
@@ -40,8 +51,11 @@ files:
40
51
  - contestify.gemspec
41
52
  - lib/contestify.rb
42
53
  - lib/contestify/colorize.rb
43
- - lib/contestify/configuration.rb
44
54
  - lib/contestify/contest.rb
55
+ - lib/contestify/judges.rb
56
+ - lib/contestify/judges/coci/README.md
57
+ - lib/contestify/judges/coci/coci.rb
58
+ - lib/contestify/judges/coci/configuration.rb
45
59
  - lib/contestify/messages.rb
46
60
  - lib/contestify/uploader.rb
47
61
  - lib/contestify/util.rb
@@ -60,7 +74,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
74
  version: '0'
61
75
  segments:
62
76
  - 0
63
- hash: 3991357628571573628
77
+ hash: -3999630116395939116
64
78
  required_rubygems_version: !ruby/object:Gem::Requirement
65
79
  none: false
66
80
  requirements:
@@ -69,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
83
  version: '0'
70
84
  segments:
71
85
  - 0
72
- hash: 3991357628571573628
86
+ hash: -3999630116395939116
73
87
  requirements: []
74
88
  rubyforge_project:
75
89
  rubygems_version: 1.8.10
@@ -1,94 +0,0 @@
1
- module Contestify
2
- # <tt>Contestify::Configuration</tt> is in charge of renaming files and
3
- # adding DOMJudge specific configuration files to each problem.
4
- class Configuration
5
-
6
- # <tt>Contestify::Configuration.configure!</tt> is *the only* method you
7
- # should call from the contest interface. All the other methods should be
8
- # called from here. This is the only method needed for future strategies
9
- # for other judges.
10
- #
11
- # This should be called in a directory structure such that you have a
12
- # `base_folder` with one folder for each problem you want to upload to the
13
- # server. Each of these problems folders should have all the input/output
14
- # files you want to use as test cases.
15
- #
16
- # This method **must** return the absolute paths of the problem folders in
17
- # an array. This return value will be used in Contestify::Uploader.upload!
18
- #
19
- # ==== Parameters
20
- # base_folder<String>:: The base folder where all the problems folders are
21
- # stored.
22
- #
23
- def self.configure!(base_folder)
24
- puts green "=> Configuring problems"
25
- problem_index = 0
26
- Dir.glob("*").select { |f| File.directory?(f) }.map do |dir|
27
- Dir.chdir File.join(base_folder, dir)
28
- dirid = Dir.pwd.split('/').last[0...8]
29
- puts green "==> #{dirid.upcase}"
30
- rename_data_files
31
- add_problem_config(dirid, problem_index)
32
- problem_index += 1
33
- puts green "==> All the work for #{dirid.upcase} is done"
34
- Dir.pwd # Return the absolute path for this problem
35
- end
36
- end
37
-
38
- # <tt>Contestify::Configuration.rename_data_files</tt> is in charge of
39
- # giving each input/output file a standard name that DOMJudge understands.
40
- #
41
- # This method should be overriden in future strategies.
42
- #
43
- # After this method is called, every different input/output file **must**
44
- # have the following structure:
45
- #
46
- # `problemid`.`number`.in
47
- # `problemid`.`number`.out
48
- #
49
- # where:
50
- #
51
- # `problemid` is at most 8 letters long and it's the id for the judge and
52
- # `number` is the test case number. The .in and .out will determine the
53
- # input and compare file the judge will run/diff.
54
- def self.rename_data_files
55
- puts blue "===> Renaming input/output files"
56
- Dir.glob("*").select { |f| f =~ /\.in.[0-9]+/ }.each do |f|
57
- # puts red " Renaming #{f}..."
58
- parts = f.split(".")
59
- new_name = [parts[0], parts[2], parts[1]].join(".")
60
- # puts blue " " + new_name
61
- File.rename f, new_name
62
-
63
- f.gsub!(".in", ".out")
64
- # puts red " Renaming #{f}..."
65
- parts = f.split(".")
66
- new_name = [parts[0], parts[2], parts[1]].join(".")
67
- # puts blue " " + new_name
68
- File.rename f, new_name
69
- end
70
- end
71
-
72
- # <tt>Contestify::Configuration.add_problem_config</tt> will add the
73
- # configuration file needed for DOMJudge to understand the problem.
74
- #
75
- # ==== Parameters
76
- # probid<String>:: The problem id for DOMJudge. This **must** be at most
77
- # 8 letters long.
78
- #
79
- # problem_index<Integer>:: This is the problem number. This is only used
80
- # to choose the problem ballon color.
81
-
82
- def self.add_problem_config(probid, problem_index)
83
- puts blue "===> Adding DOM Judge configuration #{probid}"
84
- file_content = <<-TEXT
85
- probid = #{probid}
86
- name = #{probid}
87
- allow_submit = true
88
- color = #{Contestify::PROBLEM_COLORS[problem_index]}
89
- timelimit = 1
90
- TEXT
91
- File.open(File.join(Dir.pwd, "domjudge-problem.ini"), 'w') {|f| f.write(file_content) }
92
- end
93
- end
94
- end