contestify 1.2.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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