at_coder_friends 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a7432316e843545fb879309e242139dc2c0405c
4
- data.tar.gz: 9e22a2dcde9e98a171ed393365de8fd308957996
3
+ metadata.gz: ac22f93d26dc92de38a01ed67b2fa1a8465380c2
4
+ data.tar.gz: 28e89a872bcc0825b5a4f53019fd43a7dea07511
5
5
  SHA512:
6
- metadata.gz: 70e350831761899ee4178e65f8563cbd328a0b8e70971e4620015d623484c0297fa60a6a5ce447bb349d3b2fa06799b336fb270913a34b8efb857dc97e9dfaa3
7
- data.tar.gz: ff84c2c7fb80dda6105561d82d94c697c7e5133e91219c9f6530f8e7db083192a5ac8a5148a204fd5740c3cf470d53fd56ac5026b908738f12ccb671d824dd39
6
+ metadata.gz: dee4b060fbd70915fa8e59c170a9d02e9d2ae8af925251d89e4fb3f5b1a9d7d33e20f71ffb39de4ea54190ff50011ac0ad8795149c576134a12f876448831cf8
7
+ data.tar.gz: 02a32cd3a62047c90cd8dd30269a8d5d6c82d443bc1f7e7adebfddc4c78742a6d4bd3ff6f53a24ec71f10da5bdd74a34ce9107cd7463d6b1b575adc317714bf5
data/.travis.yml CHANGED
@@ -7,7 +7,7 @@ env:
7
7
  - CC_TEST_REPORTER_ID=cb942bcc168feb78c43c506364d5344c5ec9a46a0b68dc66acb581e77c981ff1
8
8
  - COVERAGE=true
9
9
  before_install:
10
- - gem install bundler -v '2.0.2'
10
+ - gem install bundler -v '2.1.4'
11
11
  before_script:
12
12
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
13
13
  - chmod +x ./cc-test-reporter
data/CHANGELOG.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Change log
2
2
 
3
3
  ## master (unreleased)
4
+ ### Changed
5
+ - Modulo RegExp pattern.
6
+ - Minor changes in ERBs.
4
7
 
5
8
  ## 0.6.3 (2020-01-05)
6
9
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- at_coder_friends (0.6.3)
4
+ at_coder_friends (0.6.4)
5
5
  colorize (~> 0.8.1)
6
6
  launchy (~> 2.4.3)
7
7
  mechanize (~> 2.0)
@@ -19,10 +19,10 @@ GEM
19
19
  docile (1.3.2)
20
20
  domain_name (0.5.20190701)
21
21
  unf (>= 0.0.5, < 1.0.0)
22
- hashdiff (1.0.0)
22
+ hashdiff (1.0.1)
23
23
  http-cookie (1.0.3)
24
24
  domain_name (~> 0.5)
25
- json (2.2.0)
25
+ json (2.3.0)
26
26
  launchy (2.4.3)
27
27
  addressable (~> 2.3)
28
28
  mechanize (2.7.6)
@@ -34,31 +34,31 @@ GEM
34
34
  nokogiri (~> 1.6)
35
35
  ntlm-http (~> 0.1, >= 0.1.1)
36
36
  webrobots (>= 0.0.9, < 0.2)
37
- mime-types (3.3)
37
+ mime-types (3.3.1)
38
38
  mime-types-data (~> 3.2015)
39
39
  mime-types-data (3.2019.1009)
40
40
  mini_portile2 (2.4.0)
41
41
  net-http-digest_auth (1.4.1)
42
42
  net-http-persistent (3.1.0)
43
43
  connection_pool (~> 2.2)
44
- nokogiri (1.10.5)
44
+ nokogiri (1.10.9)
45
45
  mini_portile2 (~> 2.4.0)
46
46
  ntlm-http (0.1.1)
47
- public_suffix (4.0.1)
47
+ public_suffix (4.0.3)
48
48
  rake (13.0.1)
49
49
  rspec (3.9.0)
50
50
  rspec-core (~> 3.9.0)
51
51
  rspec-expectations (~> 3.9.0)
52
52
  rspec-mocks (~> 3.9.0)
53
- rspec-core (3.9.0)
54
- rspec-support (~> 3.9.0)
53
+ rspec-core (3.9.1)
54
+ rspec-support (~> 3.9.1)
55
55
  rspec-expectations (3.9.0)
56
56
  diff-lcs (>= 1.2.0, < 2.0)
57
57
  rspec-support (~> 3.9.0)
58
- rspec-mocks (3.9.0)
58
+ rspec-mocks (3.9.1)
59
59
  diff-lcs (>= 1.2.0, < 2.0)
60
60
  rspec-support (~> 3.9.0)
61
- rspec-support (3.9.0)
61
+ rspec-support (3.9.2)
62
62
  safe_yaml (1.0.5)
63
63
  simplecov (0.17.1)
64
64
  docile (~> 1.1)
@@ -68,7 +68,7 @@ GEM
68
68
  unf (0.1.4)
69
69
  unf_ext
70
70
  unf_ext (0.0.7.6)
71
- webmock (3.7.6)
71
+ webmock (3.8.2)
72
72
  addressable (>= 2.3.6)
73
73
  crack (>= 0.3.2)
74
74
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -87,4 +87,4 @@ DEPENDENCIES
87
87
  webmock (~> 3.0)
88
88
 
89
89
  BUNDLED WITH
90
- 2.0.2
90
+ 2.1.4
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
25
  `git ls-files -z`.split("\x0").reject do |f|
26
- f.match(%r{^(test|spec|features)/})
26
+ f.match(%r{^(test|spec|features|tasks)/})
27
27
  end
28
28
  end
29
29
  spec.bindir = 'exe'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'optparse'
4
- require 'launchy'
5
4
 
6
5
  module AtCoderFriends
7
6
  # command line interface
@@ -140,6 +139,7 @@ module AtCoderFriends
140
139
  end
141
140
 
142
141
  def open_contest
142
+ require 'launchy'
143
143
  Launchy.open(ctx.scraping_agent.contest_url)
144
144
  end
145
145
  end
@@ -11,20 +11,28 @@ module AtCoderFriends
11
11
  DEFAULT_FILE = File.join(ACF_HOME, 'config', 'default.yml')
12
12
 
13
13
  class << self
14
+ def dotfile
15
+ DOTFILE
16
+ end
17
+
18
+ def default_file
19
+ DEFAULT_FILE
20
+ end
21
+
14
22
  def load_config(ctx)
15
23
  path = config_file_for(ctx.path)
16
24
  config = load_yaml(path)
17
- return config if path == DEFAULT_FILE
25
+ return config if path == default_file
18
26
 
19
27
  merge_with_default(config)
20
28
  end
21
29
 
22
30
  def config_file_for(target_dir)
23
- find_project_dotfile(target_dir) || DEFAULT_FILE
31
+ find_project_dotfile(target_dir) || default_file
24
32
  end
25
33
 
26
34
  def find_project_dotfile(target_dir)
27
- find_file_upwards(DOTFILE, target_dir)
35
+ find_file_upwards(dotfile, target_dir)
28
36
  end
29
37
 
30
38
  def find_file_upwards(filename, start_dir)
@@ -7,11 +7,15 @@ module AtCoderFriends
7
7
  # - configuration
8
8
  # - application modules
9
9
  class Context
10
- attr_reader :options, :path
10
+ attr_reader :options, :path_info
11
11
 
12
12
  def initialize(options, path)
13
13
  @options = options
14
- @path = File.expand_path(path)
14
+ @path_info = PathInfo.new(File.expand_path(path))
15
+ end
16
+
17
+ def path
18
+ path_info.path
15
19
  end
16
20
 
17
21
  def config
@@ -26,6 +30,10 @@ module AtCoderFriends
26
30
  @generator ||= Generator::Main.new(self)
27
31
  end
28
32
 
33
+ def emitter
34
+ @emitter ||= Emitter.new(self)
35
+ end
36
+
29
37
  def sample_test_runner
30
38
  @sample_test_runner ||= TestRunner::Sample.new(self)
31
39
  end
@@ -38,10 +46,6 @@ module AtCoderFriends
38
46
  @verifier ||= Verifier.new(self)
39
47
  end
40
48
 
41
- def emitter
42
- @emitter ||= Emitter.new(self)
43
- end
44
-
45
49
  def post_process
46
50
  @scraping_agent&.save_session
47
51
  end
@@ -9,8 +9,8 @@ module AtCoderFriends
9
9
  include PathUtil
10
10
 
11
11
  def initialize(ctx)
12
- @src_dir = ctx.path
13
- @smp_dir = smp_dir(@src_dir)
12
+ @src_dir = ctx.path_info.src_dir
13
+ @smp_dir = ctx.path_info.smp_dir
14
14
  end
15
15
 
16
16
  def emit(pbm)
@@ -22,7 +22,8 @@ module AtCoderFriends
22
22
  @pbm = pbm
23
23
  src = File.read(select_template)
24
24
  src = ERB.new(src).result(binding)
25
- render(src)
25
+ src = render(src) if respond_to?(:render)
26
+ src
26
27
  end
27
28
 
28
29
  def select_template
@@ -21,7 +21,7 @@ module AtCoderFriends
21
21
  |\\\(([^()]+)\\\)
22
22
  |\$([^$]+)\$
23
23
  |\{([^{}]+)\}
24
- |([\d,]+)
24
+ |([\d,^+]+)
25
25
  |([一二三四五六七八九十百千万億]+)
26
26
  )
27
27
  }x.freeze
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ # holds target path information
5
+ class PathInfo
6
+ SMP_DIR = 'data'
7
+ CASES_DIR = 'cases'
8
+ TMP_DIR = '.tmp'
9
+
10
+ attr_reader :path, :dir
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ # in setup command, path is directory name (existent/non-existent)
15
+ # in other commands(test, submit, verify), path is existent file name
16
+ @dir = File.file?(path) ? File.dirname(path) : path
17
+ end
18
+
19
+ def contest_name
20
+ File.basename(dir).delete('#').downcase
21
+ end
22
+
23
+ def components
24
+ # overwrites @dir here for non-existent files (test purpose)
25
+ @dir, prg = File.split(path)
26
+ base, ext = prg.split('.')
27
+ q = base.gsub(/_[^#_]+\z/, '')
28
+ [path, dir, prg, base, ext, q]
29
+ end
30
+
31
+ def src_dir
32
+ dir
33
+ end
34
+
35
+ def smp_dir
36
+ File.join(dir, SMP_DIR)
37
+ end
38
+
39
+ def cases_dir
40
+ File.join(dir, CASES_DIR)
41
+ end
42
+
43
+ def cases_out_dir
44
+ File.join(dir, TMP_DIR, CASES_DIR)
45
+ end
46
+
47
+ def tmp_dir
48
+ File.join(dir, TMP_DIR)
49
+ end
50
+ end
51
+ end
@@ -3,37 +3,6 @@
3
3
  module AtCoderFriends
4
4
  # Common methods and behaviors for dealing with paths.
5
5
  module PathUtil
6
- module_function
7
-
8
- SMP_DIR = 'data'
9
- CASES_DIR = 'cases'
10
- TMP_DIR = '.tmp'
11
-
12
- def contest_name(path)
13
- dir = File.file?(path) ? File.dirname(path) : path
14
- File.basename(dir).delete('#').downcase
15
- end
16
-
17
- def split_prg_path(path)
18
- dir, prg = File.split(path)
19
- base, ext = prg.split('.')
20
- q = base.split('_')[0]
21
- [path, dir, prg, base, ext, q]
22
- end
23
-
24
- def smp_dir(dir)
25
- File.join(dir, SMP_DIR)
26
- end
27
-
28
- def cases_dir(dir)
29
- File.join(dir, CASES_DIR)
30
- end
31
-
32
- def tmp_dir(path)
33
- dir = File.dirname(path)
34
- File.join(dir, '.tmp')
35
- end
36
-
37
6
  def makedirs_unless(dir)
38
7
  FileUtils.makedirs(dir) unless Dir.exist?(dir)
39
8
  end
@@ -7,7 +7,6 @@ module AtCoderFriends
7
7
  module Scraping
8
8
  # common functions for scraping
9
9
  class Agent
10
- include AtCoderFriends::PathUtil
11
10
  include Session
12
11
  include Authentication
13
12
  include Tasks
@@ -29,7 +28,7 @@ module AtCoderFriends
29
28
  end
30
29
 
31
30
  def contest
32
- @contest ||= contest_name(ctx.path)
31
+ @contest ||= ctx.path_info.contest_name
33
32
  end
34
33
 
35
34
  def common_url(path)
@@ -6,10 +6,8 @@ module AtCoderFriends
6
6
  module Scraping
7
7
  # run tests on custom_test page
8
8
  module CustomTest
9
- include AtCoderFriends::PathUtil
10
-
11
9
  def code_test(infile)
12
- path, _dir, _prg, _base, ext, _q = split_prg_path(ctx.path)
10
+ path, _dir, _prg, _base, ext, _q = ctx.path_info.components
13
11
  lang = lang_id(ext)
14
12
  src = File.read(path, encoding: Encoding::UTF_8)
15
13
  data = File.read(infile)
@@ -4,10 +4,8 @@ module AtCoderFriends
4
4
  module Scraping
5
5
  # submit sources on submit page
6
6
  module Submission
7
- include AtCoderFriends::PathUtil
8
-
9
7
  def submit
10
- path, _dir, prg, _base, ext, q = split_prg_path(ctx.path)
8
+ path, _dir, prg, _base, ext, q = ctx.path_info.components
11
9
  puts "***** submit #{prg} *****"
12
10
  lang = lang_id(ext)
13
11
  src = File.read(path, encoding: Encoding::UTF_8)
@@ -8,6 +8,7 @@ module AtCoderFriends
8
8
  # run tests for the specified program.
9
9
  class Base
10
10
  include PathUtil
11
+
11
12
  STATUS_STR = {
12
13
  OK: '<< OK >>'.green,
13
14
  WA: '!!!!! WA !!!!!'.red,
@@ -19,7 +20,7 @@ module AtCoderFriends
19
20
 
20
21
  def initialize(ctx)
21
22
  @ctx = ctx
22
- @path, @dir, @prg, @base, @ext, @q = split_prg_path(ctx.path)
23
+ @path, @dir, @prg, @base, @ext, @q = ctx.path_info.components
23
24
  @detail = true
24
25
  end
25
26
 
@@ -32,11 +33,23 @@ module AtCoderFriends
32
33
  end
33
34
 
34
35
  def test_loc
35
- test_cmd ? 'local' : 'remote'
36
+ if test_cmd
37
+ 'local'
38
+ elsif ctx.scraping_agent.respond_to?(:code_test)
39
+ 'remote'
40
+ else
41
+ raise AppError, "test_cmd for .#{ext} is not specified."
42
+ end
36
43
  end
37
44
 
38
45
  def test_mtd
39
- test_cmd ? :local_test : :remote_test
46
+ if test_cmd
47
+ :local_test
48
+ elsif ctx.scraping_agent.respond_to?(:code_test)
49
+ :remote_test
50
+ else
51
+ raise AppError, "test_cmd for .#{ext} is not specified."
52
+ end
40
53
  end
41
54
 
42
55
  def run_test(id, infile, outfile, expfile)
@@ -63,7 +76,7 @@ module AtCoderFriends
63
76
  end
64
77
 
65
78
  def local_test(infile, outfile)
66
- system("#{test_cmd} < #{infile} > #{outfile}")
79
+ system("#{test_cmd} < \"#{infile}\" > \"#{outfile}\"")
67
80
  end
68
81
 
69
82
  def remote_test(infile, outfile)
@@ -4,20 +4,18 @@ module AtCoderFriends
4
4
  module TestRunner
5
5
  # run test cases for the specified program with judge input/output.
6
6
  class Judge < Base
7
- include PathUtil
8
-
9
7
  attr_reader :data_dir, :result_dir
10
8
 
11
9
  def initialize(ctx)
12
10
  super(ctx)
13
- @data_dir = cases_dir(dir)
14
- @result_dir = cases_dir(tmp_dir(path))
11
+ @data_dir = ctx.path_info.cases_dir
12
+ @result_dir = ctx.path_info.cases_out_dir
15
13
  end
16
14
 
17
15
  def judge_all
18
16
  puts "***** judge_all #{prg} (#{test_loc}) *****"
19
- results = Dir["#{data_dir}/#{q}/in/*.txt"].sort.map do |infile|
20
- id = File.basename(infile, '.txt')
17
+ results = Dir["#{data_dir}/#{q}/in/*"].sort.map do |infile|
18
+ id = File.basename(infile)
21
19
  judge(id, false)
22
20
  end
23
21
  !results.empty? && results.all?
@@ -30,9 +28,9 @@ module AtCoderFriends
30
28
 
31
29
  def judge(id, detail = true)
32
30
  @detail = detail
33
- infile = "#{data_dir}/#{q}/in/#{id}.txt"
34
- outfile = "#{result_dir}/#{q}/result/#{id}.txt"
35
- expfile = "#{data_dir}/#{q}/out/#{id}.txt"
31
+ infile = "#{data_dir}/#{q}/in/#{id}"
32
+ outfile = "#{result_dir}/#{q}/result/#{id}"
33
+ expfile = "#{data_dir}/#{q}/out/#{id}"
36
34
  run_test(id, infile, outfile, expfile)
37
35
  end
38
36
  end
@@ -4,19 +4,17 @@ module AtCoderFriends
4
4
  module TestRunner
5
5
  # run test cases for the specified program with sample input/output.
6
6
  class Sample < Base
7
- include PathUtil
8
-
9
7
  attr_reader :data_dir
10
8
 
11
9
  def initialize(ctx)
12
10
  super(ctx)
13
- @data_dir = smp_dir(dir)
11
+ @data_dir = ctx.path_info.smp_dir
14
12
  end
15
13
 
16
14
  def test_all
17
15
  puts "***** test_all #{prg} (#{test_loc}) *****"
18
16
  results = Dir["#{data_dir}/#{q}_*.in"].sort.map do |infile|
19
- id = File.basename(infile, '.in').sub(/[^_]+_/, '')
17
+ id = File.basename(infile, '.in').sub(/\A#{q}_/, '')
20
18
  test(id)
21
19
  end
22
20
  !results.empty? && results.all?
@@ -10,9 +10,8 @@ module AtCoderFriends
10
10
  attr_reader :path, :file, :vdir, :vpath
11
11
 
12
12
  def initialize(ctx)
13
- @path = ctx.path
14
- @file = File.basename(path)
15
- @vdir = tmp_dir(path)
13
+ @path, _dir, @file = ctx.path_info.components
14
+ @vdir = ctx.path_info.tmp_dir
16
15
  @vpath = File.join(vdir, "#{file}.verified")
17
16
  end
18
17
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtCoderFriends
4
- VERSION = '0.6.3'
4
+ VERSION = '0.6.4'
5
5
  end
@@ -4,6 +4,7 @@ require 'at_coder_friends/version'
4
4
  require 'at_coder_friends/errors'
5
5
  require 'at_coder_friends/path_util'
6
6
  require 'at_coder_friends/config_loader'
7
+ require 'at_coder_friends/path_info'
7
8
  require 'at_coder_friends/verifier'
8
9
  require 'at_coder_friends/test_runner/base'
9
10
  require 'at_coder_friends/test_runner/sample'
@@ -1,8 +1,8 @@
1
1
  // <%= pbm.url %>
2
-
3
2
  <%
4
3
  if pbm.options.interactive
5
- %>#include <cstdio>
4
+ %>
5
+ #include <cstdio>
6
6
  #include <vector>
7
7
  #include <string>
8
8
 
@@ -33,10 +33,10 @@ void query() {
33
33
  }
34
34
 
35
35
  //------------------------------------------------------------------------------
36
- /*** CONSTS ***/
37
-
38
- /*** DCLS ***/
39
-
36
+ <% gen_consts.each do |const| %><%= const %>
37
+ <% end %>
38
+ <% gen_decls.each do |dcl| %><%= dcl %>
39
+ <% end %>
40
40
  void solve() {
41
41
  printf("! %s\n", ans);
42
42
  fflush(stdout);
@@ -50,23 +50,26 @@ void solve() {
50
50
  }
51
51
 
52
52
  void input() {
53
- /*** INPUTS ***/
54
- #ifdef DEBUG
53
+ <% gen_inputs.each do |input| %> <%= input %>
54
+ <% end
55
+ %>#ifdef DEBUG
55
56
  scanf("%s", source);
56
57
  #endif
57
- }<%
58
+ }
59
+ <%
58
60
  else
59
- %>#include <cstdio>
61
+ %>
62
+ #include <cstdio>
60
63
 
61
64
  using namespace std;
62
65
 
63
66
  #define REP(i,n) for(int i=0; i<(int)(n); i++)
64
67
  #define FOR(i,b,e) for(int i=(b); i<=(int)(e); i++)
65
68
 
66
- /*** CONSTS ***/
67
-
68
- /*** DCLS ***/
69
-
69
+ <% gen_consts.each do |const| %><%= const %>
70
+ <% end %>
71
+ <% gen_decls.each do |dcl| %><%= dcl %>
72
+ <% end %>
70
73
  void solve() {
71
74
  <%
72
75
  if (vs = pbm.options.binary_values)
@@ -80,11 +83,12 @@ end
80
83
  }
81
84
 
82
85
  void input() {
83
- /*** INPUTS ***/
84
- }<%
86
+ <% gen_inputs.each do |input| %> <%= input %>
87
+ <% end
88
+ %>}
89
+ <%
85
90
  end
86
91
  %>
87
-
88
92
  int main() {
89
93
  input();
90
94
  solve();
@@ -1,8 +1,8 @@
1
1
  # <%= pbm.url %>
2
-
3
2
  <%
4
3
  if pbm.options.interactive
5
- %>def query(*args)
4
+ %>
5
+ def query(*args)
6
6
  puts "? #{args.join(' ')}"
7
7
  STDOUT.flush
8
8
  if $DEBUG
@@ -15,10 +15,10 @@ end
15
15
 
16
16
  $DEBUG = true
17
17
 
18
- ### CONSTS ###
19
-
20
- ### DCLS ###
21
-
18
+ <% gen_consts.each do |const| %><%= const %>
19
+ <% end %>
20
+ <% gen_decls.each do |dcl| %><%= dcl %>
21
+ <% end %>
22
22
  if $DEBUG
23
23
  @responses = []
24
24
  @source = gets.chomp
@@ -35,10 +35,11 @@ if $DEBUG
35
35
  puts "----------------------------------------"
36
36
  end<%
37
37
  else
38
- %>### CONSTS ###
39
-
40
- ### DCLS ###
41
-
38
+ %>
39
+ <% gen_consts.each do |const| %><%= const %>
40
+ <% end %>
41
+ <% gen_decls.each do |dcl| %><%= dcl %>
42
+ <% end %>
42
43
  <%
43
44
  if (vs = pbm.options.binary_values)
44
45
  %>puts cond ? '<%= vs[0] %>' : '<%= vs[1] %>'<%
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: at_coder_friends
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - nejiko96
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-04 00:00:00.000000000 Z
11
+ date: 2020-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -174,6 +174,7 @@ files:
174
174
  - lib/at_coder_friends/parser/sample_data.rb
175
175
  - lib/at_coder_friends/parser/section_wrapper.rb
176
176
  - lib/at_coder_friends/parser/sections.rb
177
+ - lib/at_coder_friends/path_info.rb
177
178
  - lib/at_coder_friends/path_util.rb
178
179
  - lib/at_coder_friends/problem.rb
179
180
  - lib/at_coder_friends/scraping/agent.rb
@@ -187,15 +188,6 @@ files:
187
188
  - lib/at_coder_friends/test_runner/sample.rb
188
189
  - lib/at_coder_friends/verifier.rb
189
190
  - lib/at_coder_friends/version.rb
190
- - tasks/regression/check_const.rake
191
- - tasks/regression/check_diff.rake
192
- - tasks/regression/check_fmt.rake
193
- - tasks/regression/check_parse.rake
194
- - tasks/regression/list_handler.rb
195
- - tasks/regression/regression.rb
196
- - tasks/regression/report_handler.rb
197
- - tasks/regression/section_list.rake
198
- - tasks/regression/setup.rake
199
191
  - templates/cxx_builtin.cxx.erb
200
192
  - templates/ruby_builtin.rb.erb
201
193
  homepage: https://github.com/nejiko96/at_coder_friends
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'regression'
4
-
5
- module AtCoderFriends
6
- # tasks for regression
7
- module Regression
8
- module_function
9
-
10
- CONST_PAT = {
11
- mod: /
12
- (.{,30}(?:
13
- で割った|modulo|mod\b|divided\s+by|dividing\s+by
14
- ).{,30})
15
- /xmi,
16
- max: /
17
- (.{,30}(?:
18
- ≦|≤|\\le|&leq?;
19
- |<|\\lt|&lt;
20
- |以上.{1,25}以下の整数
21
- ).{,30})
22
- /xmi
23
- }.freeze
24
-
25
- def collect(tgt)
26
- open_const_report('collect', tgt) do |f|
27
- local_pbm_list.each do |contest, q, url|
28
- page = agent.get(url)
29
- body = page.body.force_encoding('utf-8')
30
- ms = body.scan(CONST_PAT[tgt.to_sym])
31
- ms.each do |m|
32
- s = m[0].delete("\r\n")
33
- f.puts [contest, q, tsv_escape(s)].join("\t")
34
- end
35
- end
36
- end
37
- end
38
-
39
- def check_mod
40
- open_const_report('check', 'mod') do |f|
41
- local_pbm_list.each do |contest, q, url|
42
- pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
43
- Parser::Sections.process(pbm)
44
- Parser::Modulo.process(pbm)
45
- pbm.constants.each do |cnst|
46
- f.puts [contest, q, cnst.value].join("\t")
47
- end
48
- end
49
- end
50
- end
51
-
52
- def check_max
53
- open_const_report('check', 'max') do |f|
54
- local_pbm_list.each do |contest, q, url|
55
- pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
56
- Parser::Sections.process(pbm)
57
- Parser::Constraints.process(pbm)
58
- pbm.constants.each do |cns|
59
- f.puts [contest, q, "#{cns.name}:#{cns.value}"].join("\t")
60
- end
61
- end
62
- end
63
- end
64
-
65
- def merge_list(tgt)
66
- tbl = load_merge_list(tgt)
67
- save_merge_list(tgt, tbl)
68
- end
69
-
70
- def load_merge_list(tgt)
71
- tbl = {}
72
- %w[collect check]
73
- .map { |act| load_const_report(act, tgt) }
74
- .each
75
- .with_index(1) do |data, n|
76
- data
77
- .group_by { |contest, q, _| "#{contest}\t#{q}" }
78
- .map { |key, grp| [key, grp.map { |row| row[2] }.join("\n")] }
79
- .each do |key, txt|
80
- tbl[key] ||= { 'v1' => '', 'v2' => '' }
81
- tbl[key]["v#{n}"] = '"' + txt + '"'
82
- end
83
- end
84
- tbl
85
- end
86
-
87
- def save_merge_list(tgt, tbl)
88
- open_const_report('merge', tgt) do |f|
89
- tbl.sort.each do |k, h|
90
- f.puts [k, h['v1'], h['v2']].join("\t")
91
- end
92
- end
93
- end
94
-
95
- def open_const_report(act, tgt)
96
- open_report("#{act}_#{tgt}.txt") { |f| yield f }
97
- end
98
-
99
- def load_const_report(act, tgt)
100
- file = report_path("#{act}_#{tgt}.txt")
101
- Encoding.default_external = 'utf-8'
102
- CSV.read(file, col_sep: "\t", headers: false)
103
- end
104
- end
105
- end
106
-
107
- namespace :regression do
108
- desc 'list all mod values'
109
- task :collect_mod do
110
- AtCoderFriends::Regression.collect('mod')
111
- end
112
-
113
- desc 'check extracted mod values'
114
- task :check_mod do
115
- AtCoderFriends::Regression.check_mod
116
- end
117
-
118
- desc 'merge mod values list'
119
- task :merge_mod do
120
- AtCoderFriends::Regression.merge_list('mod')
121
- end
122
-
123
- desc 'list all max values'
124
- task :collect_max do
125
- AtCoderFriends::Regression.collect('max')
126
- end
127
-
128
- desc 'check extracted max values'
129
- task :check_max do
130
- AtCoderFriends::Regression.check_max
131
- end
132
-
133
- desc 'merge max values list'
134
- task :merge_max do
135
- AtCoderFriends::Regression.merge_list('max')
136
- end
137
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'regression'
4
-
5
- module AtCoderFriends
6
- # tasks for regression
7
- module Regression
8
- module_function
9
-
10
- def check_diff
11
- emit_dir = format(EMIT_DIR_FMT, now: Time.now.strftime('%Y%m%d%H%M%S'))
12
- rmdir_force(emit_dir)
13
-
14
- local_pbm_list.each do |contest, q, url|
15
- pbm = local_scraping_agent(emit_dir, contest).fetch_problem(q, url)
16
- pipeline(pbm)
17
- end
18
-
19
- diff_log = report_path('check_diff.txt')
20
- system("diff -r --exclude=.git #{EMIT_ORG_DIR} #{emit_dir} > #{diff_log}")
21
- end
22
- end
23
- end
24
-
25
- namespace :regression do
26
- desc 'run regression check'
27
- task :check_diff do
28
- AtCoderFriends::Regression.check_diff
29
- end
30
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'regression'
4
-
5
- module AtCoderFriends
6
- # tasks for regression
7
- module Regression
8
- module_function
9
-
10
- def check_fmt
11
- open_report('check_fmt.txt') do |f|
12
- local_pbm_list.each do |contest, q, url|
13
- next unless (res = process_fmt(contest, q, url))
14
-
15
- f.puts [
16
- contest, q, *res.map { |s| tsv_escape(s) }
17
- ].join("\t")
18
- end
19
- end
20
- end
21
-
22
- def process_fmt(contest, q, url)
23
- pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
24
- Parser::Sections.process(pbm)
25
- Parser::SampleData.process(pbm)
26
- fmt1 = Parser::InputFormat.find_fmt(pbm)
27
- return unless fmt1 && !fmt1.empty?
28
-
29
- fmt2 = Parser::InputFormat.normalize_fmt(fmt1).join("\n")
30
- Parser::InputFormat.process(pbm)
31
- Parser::InputType.process(pbm)
32
- inpdefs = pbm.formats_src
33
- fmt3 = inpdefs.map(&:to_s).join("\n")
34
- fmt4 = inpdefs.any? { |inpdef| inpdef.cols.empty? } ? '○' : ''
35
- [fmt1, fmt2, fmt3, fmt4]
36
- end
37
- end
38
- end
39
-
40
- namespace :regression do
41
- desc 'checks input format patterns'
42
- task :check_fmt do
43
- AtCoderFriends::Regression.check_fmt
44
- end
45
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'regression'
4
- require 'at_coder_friends'
5
-
6
- module AtCoderFriends
7
- # tasks for regression
8
- module Regression
9
- module_function
10
-
11
- def check_parse
12
- list = local_pbm_list.map do |contest, q, url|
13
- pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
14
- Parser::Main.process(pbm)
15
- flags = [
16
- !fmt?(pbm),
17
- pbm.samples.all? { |smp| smp.txt.empty? },
18
- pbm.options.interactive
19
- ]
20
- [contest, q, flags]
21
- end
22
- report(list, 'check_parse.txt')
23
- end
24
-
25
- def check_bin
26
- list = local_pbm_list.map do |contest, q, url|
27
- pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
28
- Parser::Main.process(pbm)
29
- flags = [pbm.options.binary_values]
30
- [contest, q, flags]
31
- end
32
- report(list, 'check_bin.txt')
33
- end
34
-
35
- def fmt?(pbm)
36
- fmt = Parser::InputFormat.find_fmt(pbm)
37
- fmt && !fmt.empty?
38
- end
39
-
40
- def report(list, file)
41
- open_report(file) do |f|
42
- list
43
- .select { |_, _, flags| flags.any? }
44
- .map { |c, q, flags| [c, q, flags.map { |flg| f_to_s(flg) }] }
45
- .each { |args| f.puts args.flatten.join("\t") }
46
- end
47
- end
48
-
49
- def f_to_s(f)
50
- if f.is_a?(Array)
51
- f
52
- else
53
- f ? '◯' : '-'
54
- end
55
- end
56
- end
57
- end
58
-
59
- namespace :regression do
60
- desc 'checks page parse result'
61
- task :check_parse do
62
- AtCoderFriends::Regression.check_parse
63
- end
64
-
65
- desc 'checks binary problem parse result'
66
- task :check_bin do
67
- AtCoderFriends::Regression.check_bin
68
- end
69
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'net/http'
4
- require 'uri'
5
- require 'json'
6
- require 'csv'
7
-
8
- module AtCoderFriends
9
- # tasks for regression
10
- module Regression
11
- module_function
12
-
13
- CONTEST_LIST_URL = 'https://kenkoooo.com/atcoder/resources/contests.json'
14
- ACF_HOME = File.expand_path(File.join(__dir__, '..', '..'))
15
- REGRESSION_HOME = File.join(ACF_HOME, 'regression')
16
- PAGES_DIR = File.join(REGRESSION_HOME, 'pages')
17
- EMIT_ORG_DIR = File.join(REGRESSION_HOME, 'emit_org')
18
- EMIT_DIR_FMT = File.join(REGRESSION_HOME, 'emit_%<now>s')
19
-
20
- def contest_id_list
21
- uri = URI.parse(CONTEST_LIST_URL)
22
- json = Net::HTTP.get(uri)
23
- contests = JSON.parse(json)
24
- puts "Total #{contests.size} contests"
25
- contests.map { |h| h['id'] }
26
- end
27
-
28
- def local_pbm_list
29
- Dir.glob(PAGES_DIR + '/**/*.html').map do |pbm_path|
30
- contest = File.basename(File.dirname(pbm_path))
31
- q = File.basename(pbm_path, '.html')
32
- url = "file://#{pbm_path}"
33
- [contest, q, url]
34
- end.sort # .take(10)
35
- end
36
-
37
- def pbm_list_from_file(file)
38
- dat = File.join(REGRESSION_HOME, file)
39
- CSV.read(dat, col_sep: "\t", headers: false).map do |contest, q|
40
- pbm_path = File.join(PAGES_DIR, contest, "#{q}.html")
41
- url = "file://#{pbm_path}"
42
- [contest, q, url]
43
- end
44
- end
45
- end
46
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'mechanize'
4
- require 'at_coder_friends'
5
- require_relative 'list_handler'
6
- require_relative 'report_handler'
7
-
8
- module AtCoderFriends
9
- # tasks for regression
10
- module Regression
11
- module_function
12
-
13
- def scraping_agent(root, contest)
14
- root ||= REGRESSION_HOME
15
- @ctx = Context.new({}, File.join(root, contest))
16
- @ctx.scraping_agent
17
- end
18
-
19
- def local_scraping_agent(root, contest)
20
- scraping_agent(root, contest)
21
- .tap { |sa| sa.agent.pre_connect_hooks.clear }
22
- end
23
-
24
- def agent
25
- @agent ||= Mechanize.new
26
- end
27
-
28
- def pipeline(pbm)
29
- Parser::Main.process(pbm)
30
- @ctx.generator.process(pbm)
31
- @ctx.emitter.emit(pbm)
32
- end
33
-
34
- def rmdir_force(dir)
35
- FileUtils.rm_r(dir) if Dir.exist?(dir)
36
- end
37
- end
38
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AtCoderFriends
4
- # tasks for regression
5
- module Regression
6
- module_function
7
-
8
- def open_report(file)
9
- File.open(report_path(file), 'wb') { |f| yield f }
10
- end
11
-
12
- def report_path(file)
13
- File.join(REGRESSION_HOME, file)
14
- end
15
-
16
- def tsv_escape(str)
17
- '"' + str.gsub('"', '""').gsub("\t", ' ') + '"'
18
- end
19
- end
20
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'regression'
4
-
5
- module AtCoderFriends
6
- # tasks for regression
7
- module Regression
8
- module_function
9
-
10
- SectionInfo = Struct.new(:contest, :q, :title)
11
-
12
- def section_list
13
- list = load_section_list
14
- save_section_list(list)
15
- end
16
-
17
- def load_section_list
18
- local_pbm_list.flat_map do |contest, q, url|
19
- page = agent.get(url)
20
- %w[h2 h3].flat_map do |tag|
21
- page.search(tag).map do |h|
22
- SectionInfo.new(contest, q, normalize(h.content))
23
- end
24
- end
25
- end
26
- end
27
-
28
- def save_section_list(list)
29
- open_report('section_list.txt') do |f|
30
- list.group_by(&:title).each do |k, vs|
31
- f.puts [k, vs.size, vs[0].contest, vs[0].q].join("\t")
32
- end
33
- end
34
- end
35
-
36
- def normalize(s)
37
- s
38
- .tr('0-9A-Za-z', '0-9A-Za-z')
39
- .gsub(/[[:space:]]/, '')
40
- .gsub(/[^一-龠_ぁ-ん_ァ-ヶーa-zA-Z0-9 ]/, '')
41
- .downcase
42
- .gsub(/\d+/, '{N}')
43
- .strip
44
- end
45
- end
46
- end
47
-
48
- namespace :regression do
49
- desc 'list all section titles'
50
- task :section_list do
51
- AtCoderFriends::Regression.section_list
52
- end
53
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'regression'
4
-
5
- module AtCoderFriends
6
- # tasks for regression
7
- module Regression
8
- module_function
9
-
10
- def setup
11
- rmdir_force(PAGES_DIR)
12
- rmdir_force(EMIT_ORG_DIR)
13
- contest_id_list.each do |contest|
14
- setup_by_contest(contest)
15
- sleep 3
16
- end
17
- end
18
-
19
- def setup_by_contest(contest)
20
- scraping_agent(EMIT_ORG_DIR, contest).fetch_all do |pbm|
21
- setup_by_pbm(contest, pbm)
22
- end
23
- rescue StandardError => e
24
- p e
25
- end
26
-
27
- def setup_by_pbm(contest, pbm)
28
- html_path = File.join(PAGES_DIR, contest, "#{pbm.q}.html")
29
- save_file(html_path, pbm.page.body)
30
- pipeline(pbm)
31
- rescue StandardError => e
32
- p e
33
- end
34
-
35
- def save_file(path, content)
36
- dir = File.dirname(path)
37
- FileUtils.makedirs(dir) unless Dir.exist?(dir)
38
- File.binwrite(path, content)
39
- end
40
- end
41
- end
42
-
43
- namespace :regression do
44
- desc 'setup regression environment'
45
- task :setup do
46
- AtCoderFriends::Regression.setup
47
- end
48
- end