gotasku 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3a96fbb30bae291ebd35b1b6b7afb2ef18b1588a
4
+ data.tar.gz: 0ac6145dd789670da679de4e8e3b8f8d4ef6ac14
5
+ SHA512:
6
+ metadata.gz: 87b2eab023c391a130c2839167b7380e3e0a9371220f8a0e4e454c1d1b0d6b9e54b2e2330b9bf34b89736db79542041f1cc6480377bc5ec297bc40b3c7869fe5
7
+ data.tar.gz: 7c10a24044927d1dd28f6b70e7ed43a6a10160568e14c965eff37af71f783b252256250a945690ebf615b98026d50b7e29cdb55981d65846574ad3874ebe05e6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 John Hager
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,116 @@
1
+ gotasku
2
+ =======
3
+
4
+ Saving and opening tsumego
5
+
6
+ _Use_
7
+
8
+ *Problem*
9
+
10
+ New problem. You can provide "id", "sgf", or "data" options.
11
+ "id" => Integer (id of goproblem.com problem)
12
+ "sgf" => String or File of SGF
13
+ "data" => Hash with any of the following keys
14
+ ("id", "sgf", "difficulty",
15
+ "diff_num", "type", "rating")
16
+
17
+ Gotasku::Problem.new(options)
18
+
19
+ Gotasku::Problem.new("id" => 1000)
20
+
21
+ Gotasku::Problem.new("sgf" => "problem.sgf")
22
+ Gotasku::Problem.new("sgf" => "(;AB[aa]AW[ab]")
23
+
24
+ Gotasku::Problem.new("data" => {"id" => 1000,
25
+ "sgf" => "(;AB[aa]AW[ab])",
26
+ "difficulty" => "6 dan",
27
+ "diff_num" => -6,
28
+ "type" => "life and death",
29
+ "rating" => 3})
30
+
31
+ You can access Problem data with #sgf, #id, #difficulty, #type,
32
+ #rating, and #data instance methods
33
+
34
+ prob = Gotasku::Problem.new("id" => 10000)
35
+ prob.data
36
+ prob.difficulty
37
+
38
+ Gotasku use SgfParser to parse the SGF and save it.
39
+
40
+ You can get the tree from SGF with the #tree instance method
41
+
42
+ prob.tree
43
+
44
+ And you can save the file with the #save instance method
45
+
46
+ prob.save
47
+
48
+ The file will be saved as "#{id}.sgf" or using a time stamp if no id.
49
+
50
+ \*see MongoDB for more
51
+
52
+
53
+ *User*
54
+
55
+ You can create a user object.
56
+
57
+ user = Gotasku::User.new('username', 'password')
58
+
59
+ *Session*
60
+
61
+ You can login to goproblems.com by creating a new session object.
62
+ The current session is saved in the @@current\_session class variable
63
+
64
+ Gotasku::Session.new(user)
65
+
66
+
67
+ _*For MongoDB*_
68
+
69
+ If you have MongoDB installed, you can run the rake tasks from the
70
+ root directory of the gem:
71
+
72
+ rake db:create
73
+ rake problems
74
+
75
+ The first will create the database, the second will populate a
76
+ collection with all the go problems from goproblems.com.
77
+
78
+ _Querying_
79
+
80
+ You can build a query with Gotasku::Query.create (you will be
81
+ prompted on how you want to filter the problems collection.
82
+
83
+ Gotasku::Query.create
84
+
85
+ Or you can query the collection directly
86
+ Gotasku::Query.new(query\_hash)
87
+
88
+ Gotasku::Query.new({"id" => 1000})
89
+
90
+ This returns a Gotasku::Query object, with an instance variable
91
+ (@found) that holds the Mongo::Cursor object from the query.
92
+
93
+ query = Gotasku::Query.new({"type" => "life and death"})
94
+ query.found
95
+
96
+ Gotasku::Query has a #to\_a instance method to get an array from the
97
+ Mongo::Cursor object.
98
+
99
+ query.to\_a
100
+
101
+ *Problem*
102
+
103
+ index = 0
104
+ Gotasku::Problem.new("data", query.to\_a[index])
105
+
106
+ _Executable_
107
+
108
+ There is an executable in the bin folder, which you can add to your
109
+ PATH. It provides commands for getting data from or saving single
110
+ problems.
111
+
112
+ Use the help option to get more info:
113
+
114
+ $ path/to/gem/bin/gotasku --help
115
+
116
+
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ require_relative "../lib/gotasku"
6
+
7
+ def get_help
8
+ puts "\"gotasku #{ARGV.join(" ")}\" isn't a command I know."
9
+ puts "Try `gotasku --help`"
10
+ end
11
+
12
+ def problem(options)
13
+ Gotasku::Problem.new(options)
14
+ end
15
+
16
+ def prompt(text)
17
+ print(text)
18
+ $stdin.gets.chomp.strip
19
+ end
20
+
21
+ problem_commands = %w[ display sgf id save data difficulty
22
+ rating type ]
23
+ session_commands = %w[ login ]
24
+ rake_commands = %w[ test ]
25
+
26
+ options = {}
27
+ optparse = OptionParser.new do |opts|
28
+
29
+ opts.banner = "Gotasku"
30
+ opts.separator ""
31
+ opts.separator "Problem Commands"
32
+ problem_commands.each do |c|
33
+ opts.separator " #{c}"
34
+ end
35
+
36
+ opts.separator ""
37
+ opts.separator "Options"
38
+
39
+ options["id"] = nil
40
+ opts.on( '-i', '--id Num', Integer, "Provide the problem id" ) do |id|
41
+ options["id"] = id
42
+ end
43
+
44
+ options["sgf"] = nil
45
+ opts.on( '-s', '--sgf String|FILE',
46
+ "Provide the problem SGF" ) do |sgf|
47
+ options["sgf"] = sgf
48
+ end
49
+
50
+ opts.separator "Session Commands"
51
+ session_commands.each do |c|
52
+ opts.separator " #{c}"
53
+ end
54
+
55
+ opts.separator "Options"
56
+ options[:username] = nil
57
+ opts.on( '-u', '--username Username',
58
+ "Provide your Username" ) do |username|
59
+ options[:username] = username
60
+ end
61
+
62
+ options[:password] = nil
63
+ opts.on( '-p', '--password Password',
64
+ "Provide your Password" ) do |password|
65
+ options[:password] = password
66
+ end
67
+
68
+ opts.separator "Other Commands"
69
+ rake_commands.each do |c|
70
+ opts.separator " #{c}"
71
+ end
72
+
73
+ opts.separator "Help"
74
+ opts.on("-h","--help","help") do
75
+ puts optparse
76
+ end
77
+ end
78
+
79
+ optparse.parse!
80
+
81
+ # if multiple commands are given
82
+ if ARGV.length > 1
83
+ get_help
84
+ exit
85
+ end
86
+
87
+ if session_commands.include?(ARGV[0])
88
+ options[:username] ||= prompt("Username: ")
89
+ options[:password] ||= prompt("Password: ")
90
+
91
+ user = Gotasku::User.new(options[:username], options[:password])
92
+ p Gotasku::Session.new(user)
93
+ elsif problem_commands.include?(ARGV[0])
94
+ p problem(options).send(ARGV[0].to_sym)
95
+ elsif rake_commands.include?(ARGV[0])
96
+ puts `rake`
97
+ elsif ARGV[0].nil?
98
+ p problem(options)
99
+ else
100
+ get_help
101
+ end
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+ require 'mongo'
3
+
4
+ require_relative 'populate_database'
5
+
6
+ namespace :db do
7
+ desc "Create the database"
8
+ task :create do
9
+ client = Mongo::MongoClient.new
10
+ db = client.db('sgf')
11
+ collection = db.collection('owner')
12
+ collection.insert({name: "John"})
13
+ end
14
+
15
+ desc "Drop the database"
16
+ task :drop do
17
+ Mongo::MongoClient.new.drop_database('sgf')
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'mongo'
3
+ require_relative '../../lib/gotasku'
4
+
5
+ namespace :problems do
6
+ desc "Populate the problems collection"
7
+ task :populate do
8
+ client = Mongo::MongoClient.new
9
+ db = client.db('sgf')
10
+ collection = db.collection('problems')
11
+
12
+ (0..Gotasku::Problem.last_problem_id).each do |id|
13
+ if collection.find(id: id).first
14
+ print '_'
15
+ next
16
+ end
17
+
18
+ prob = Gotasku::Problem.new(id: id)
19
+
20
+ begin
21
+ if prob.blank? == false
22
+ collection.insert(prob.data)
23
+ print '.'
24
+ else
25
+ print '*'
26
+ end
27
+ rescue
28
+ print 'E'
29
+ end
30
+ end
31
+ end
32
+
33
+ desc "Structure the problems collection"
34
+ task :structure do
35
+ collection = Mongo::MongoClient.new.db('sgf').collection('problems')
36
+ collection.ensure_index(
37
+ {id: Mongo::ASCENDING},
38
+ {unique: true, dropDups: true})
39
+ collection.create_index(diff_num: Mongo::DESCENDING)
40
+ collection.create_index(rating: Mongo::DESCENDING)
41
+ collection.create_index("type")
42
+ end
43
+
44
+ desc "Populate and structure problems collection"
45
+ task default: [:populate, :structure]
46
+ end
@@ -0,0 +1,17 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'sgf'
4
+ require 'mechanize'
5
+ require 'mongo'
6
+
7
+ require_relative 'gotasku/gotasku'
8
+
9
+ require_relative 'gotasku/exceptions'
10
+ require_relative 'gotasku/problem'
11
+ require_relative 'gotasku/difficulty_string'
12
+ require_relative 'gotasku/rating_string'
13
+ require_relative 'gotasku/display'
14
+ require_relative 'gotasku/parser'
15
+ require_relative 'gotasku/user'
16
+ require_relative 'gotasku/session'
17
+ require_relative 'gotasku/query'
@@ -0,0 +1,22 @@
1
+ class Gotasku::DifficultyString < String
2
+
3
+ # convert difficulty string to number
4
+ def convert
5
+ case self
6
+ when /p/ #pro
7
+ # grab number, mulitply by -1 and subtract 10
8
+ (self.slice(/\d+/).to_i * -1) - 10
9
+ when /d/ #dan
10
+ # grab number, multiply by -1
11
+ (self.slice(/\d+/).to_i * -1)
12
+ when /k/ #kyu
13
+ # grab number
14
+ self.slice(/\d+/).to_i
15
+ else
16
+ nil
17
+ end
18
+ end
19
+ end
20
+
21
+
22
+
@@ -0,0 +1,26 @@
1
+ class Gotasku::Display
2
+ def self.show_problem(problem)
3
+ puts "=" * 40
4
+ puts "id: #{problem.id}"
5
+ puts "type: #{problem.type}"
6
+ puts "rating: #{problem.rating}"
7
+ puts "difficulty: #{problem.difficulty}"
8
+ puts "=" * 40
9
+ end
10
+
11
+ def self.show_list(list)
12
+ list.each_with_index {|item, i| puts "#{i}...#{item}"}
13
+ end
14
+
15
+ def self.show(object)
16
+ case object
17
+ when ::Gotasku::Problem
18
+ self.show_problem(object)
19
+ when ::Array
20
+ self.show_list(object)
21
+ else
22
+ puts "Sorry, I can't show that.."
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,5 @@
1
+ class Gotasku::NotFound < Exception
2
+ end
3
+
4
+ class Gotasku::InvalidEntry < Exception
5
+ end
@@ -0,0 +1,4 @@
1
+ class Gotasku
2
+
3
+ end
4
+
@@ -0,0 +1,28 @@
1
+ class Gotasku::Parser < SGF::Parser
2
+
3
+ # validates and parses sgf string
4
+ def parse(sgf)
5
+ super(validate(sgf))
6
+ end
7
+
8
+ # validates sgf string for common goproblems.com issues
9
+ def validate(sgf)
10
+ validate_for(validate_for(sgf, :AB), :AW)
11
+ end
12
+
13
+ # validates for a property (AB or AW)
14
+ def validate_for(sgf, property)
15
+ regs = {AB: /AB/, AW: /AW/}
16
+
17
+ # find the first instance, all will be replaced and the first
18
+ # instance will be readded
19
+ start = sgf =~ regs[property]
20
+
21
+ if start
22
+ sgf.gsub!(regs[property], '')
23
+ sgf = sgf[0...start] + property.to_s + sgf[start..-1]
24
+ end
25
+
26
+ sgf
27
+ end
28
+ end
@@ -0,0 +1,151 @@
1
+ class Gotasku::Problem
2
+
3
+ @@uri = "http://www.goproblems.com/"
4
+
5
+ attr_reader :id
6
+
7
+ # initializes with a hash of options
8
+ def initialize(options = {})
9
+ @id = options["id"]
10
+ @sgf = options["sgf"]
11
+
12
+ if @sgf
13
+ # checks if sgf is a sgf file, and reads it if it is
14
+ if @sgf =~ /\.sgf/ && File.exists?(@sgf)
15
+ @sgf = File.open(@sgf, 'r').read
16
+ end
17
+
18
+ @data ||= {}
19
+ end
20
+
21
+ # overrides sgf and id if they are provided in the option data
22
+ if options["data"]
23
+ @data = options["data"]
24
+ @id = @data["id"] || @id
25
+ @sgf = @data["sgf"] || @sgf
26
+ end
27
+ end
28
+
29
+ # get the id of the last problem on goproblems.com
30
+ def self.last_problem_id
31
+ @@last_problem_id ||= begin
32
+ doc = Nokogiri::HTML(open('http://www.goproblems.com/').read)
33
+ link = doc.css('td a').select {|a| a[:href] =~ /^\/\d+\z/}[0]
34
+ link[:href].slice(/\d+/).to_i
35
+ end
36
+ end
37
+
38
+ # print out information on problem on screen
39
+ def display
40
+ Gotasku::Display.show(self)
41
+ end
42
+
43
+ # access sgf or strip problem sgf from goproblems.com
44
+ def sgf
45
+ @sgf ||= data["sgf"]
46
+ end
47
+
48
+ # strip problem difficulty from goproblems.com
49
+ def difficulty
50
+ data["difficulty"]
51
+ end
52
+
53
+ # strip problem type from goproblems.com
54
+ def type
55
+ data["type"]
56
+ end
57
+
58
+ # strip problem rating from goproblems.com
59
+ def rating
60
+ data["rating"]
61
+ end
62
+
63
+ # access or get the tree from the Parser
64
+ def tree
65
+ @tree ||= Gotasku::Parser.new.parse(sgf)
66
+ end
67
+
68
+ # saves the sgf
69
+ def save
70
+ tree.save("#{id || Time.now.to_i}.sgf")
71
+ end
72
+
73
+ # this method is not finished
74
+ # uploads sgf to goproblems.com
75
+ def upload
76
+ # don't upload a problem that came from goproblems
77
+ unless @id
78
+ sess = Gotasku::Session.current
79
+
80
+ add_info_page = sess.link_with(href: /add/).click
81
+ add_page = add_info_page.link_with(text: /direct/).click
82
+
83
+ add_page.form_with(
84
+ action: 'addtest.php3') do |f|
85
+ source_field = f.field_with(name: "source")
86
+ source_field.value = ''
87
+
88
+ name_field = f.field_with(name: "name")
89
+ name_field.value = ''
90
+
91
+ sgf_field = f.field_with(name: "sgf")
92
+ sgf_field.value = @sgf
93
+
94
+ intro_field = f.field_with(name: "intro")
95
+ intro_field.value = ''
96
+
97
+ type_select = f.field_with(name: "genre")
98
+ type_select.value = 'elementary'
99
+
100
+ group_select = f.field_with(name: "usergroup[]")
101
+ group_select.value = ['none']
102
+
103
+ @submit = f.submit
104
+ end
105
+
106
+ # after submit, goes to flash app to confirm and complete
107
+ # the problem
108
+ end
109
+ end
110
+
111
+ def blank?
112
+ sgf == '(;)'
113
+ end
114
+
115
+ def data
116
+ @data ||= begin
117
+ # get sgf from go problems
118
+ doc = Nokogiri::HTML(open("#{@@uri}#{@id}").read)
119
+ sgf_text = doc.css("div#player-container").text.gsub(/\r?\n/, '')
120
+
121
+ raise Gotasku::NotFound if sgf_text.empty?
122
+
123
+ difficulty = Gotasku::DifficultyString.new(
124
+ doc.css("div.difficulty a").text)
125
+ type = doc.css("div.prob_genre").text
126
+ rating = Gotasku::RatingString.new(
127
+ doc.css("div.probstars")[0][:class]).convert
128
+
129
+ {
130
+ "id" => @id,
131
+ "sgf" => sgf_text,
132
+ "difficulty" => difficulty,
133
+ "diff_num" => difficulty.convert,
134
+ "type" => type,
135
+ "rating" => rating
136
+ }
137
+
138
+ rescue Gotasku::NotFound
139
+ # puts "This problem cannot be found"
140
+
141
+ # at the moment, this seems the best solution, not ideal though
142
+ # because it allows for problems to be saved even if they are not
143
+ # found
144
+ {sgf: '(;)'}
145
+
146
+ rescue SocketError
147
+ puts "Poor internet connection"
148
+ {sgf: '(;)'}
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,106 @@
1
+ class Gotasku::Query
2
+ @@problems = Mongo::MongoClient.new.db('sgf').collection('problems')
3
+
4
+ attr_reader :found
5
+
6
+ # gets a Mongo::Cursor object
7
+ def initialize(query)
8
+ @found = @@problems.find(query)
9
+ end
10
+
11
+ # returns an array of items in @found
12
+ def to_a
13
+ @found.rewind!
14
+ @found.to_a
15
+ end
16
+
17
+ # returns a Mongo::Cursor object
18
+ def self.create
19
+ self.new(self.build_query)
20
+ end
21
+
22
+ =begin
23
+ # key is a string or symbol
24
+ def self.sort_by(key)
25
+ end
26
+
27
+ # query is a hash
28
+ def self.query_by(query)
29
+ end
30
+ =end
31
+
32
+ private
33
+ # prompts user for input
34
+ def self.prompt(text, tru = nil, fals = nil)
35
+ print text
36
+
37
+ response = gets.chomp.strip.downcase
38
+
39
+ # if values are given to verrifying input as true/false
40
+ if tru and fals
41
+ if response =~ /#{tru}/
42
+ true
43
+ elsif response =~ /#{fals}/
44
+ false
45
+ else
46
+ nil
47
+ end
48
+ else # else return an Array
49
+ response = response.split(',')
50
+
51
+ # throw an exception if the input is invalid
52
+ unless response.class == Array and
53
+ not(response.find {|item| item =~ /[a-z]/})
54
+ raise Gotasku::InvalidEntry
55
+ end
56
+
57
+ response.collect {|item| item.to_i}
58
+ end
59
+ end
60
+
61
+ #gets unique set of types from collection
62
+ def self.get_types
63
+ @@problems.distinct(:type).sort
64
+ end
65
+
66
+ #gets unique set of difficulties from collection
67
+ def self.get_difficulties
68
+ @@problems.distinct(:difficulty).sort do |x, y|
69
+ x = Gotasku::DifficultyString.new(x).convert
70
+ y = Gotasku::DifficultyString.new(y).convert
71
+ x <=> y
72
+ end
73
+ end
74
+
75
+ #gets unique set of ratings from collection
76
+ def self.get_ratings
77
+ @@problems.distinct(:rating).sort
78
+ end
79
+
80
+ # runs a prompt to build a query for the database
81
+ def self.build_query
82
+ query_build = {}
83
+ q = {
84
+ type: -> {self.get_types},
85
+ difficulty: -> {self.get_difficulties},
86
+ rating: -> {self.get_ratings}
87
+ }
88
+
89
+ q.keys.each do |key|
90
+ if self.prompt("Do you want to filter by #{key} (y/n)? ",
91
+ 'y', 'n')
92
+ list = q[key].call
93
+ Gotasku::Display.show(list)
94
+ puts '=' * 40
95
+ values = self.prompt(
96
+ "Please type the index/number for the values " +
97
+ "you would like to filter for (e.g. 0, 3, 4): ")
98
+
99
+ values.collect! {|value| list[value]}
100
+ query_build[key] = { "$in" => values }
101
+ end
102
+ end
103
+
104
+ query_build
105
+ end
106
+ end
@@ -0,0 +1,11 @@
1
+ class Gotasku::RatingString < String
2
+
3
+ # convert string to number
4
+ def convert
5
+ sum = self.slice(/sitesum:\d+/).slice(/\d+/).to_i
6
+ num = self.slice(/sitenum:\d+/).slice(/\d+/).to_i
7
+
8
+ # find the average or return the sum (if num is 0)
9
+ num == 0 ? 0 : sum / num
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ class Gotasku::Session
2
+
3
+ @@current = nil
4
+
5
+ attr_reader :account
6
+
7
+ # uses mechanize to login to goproblems.com and makes makes this the
8
+ # current session
9
+ def initialize(user)
10
+ a = Mechanize.new
11
+ a.get('http://www.goproblems.com/') do |page|
12
+ # Click the login link
13
+ login_page = a.click(page.link_with(href: /login/))
14
+
15
+ # Submit the login form
16
+ login_page.form_with(
17
+ action: 'login.php') do |f|
18
+ username_field = f.field_with(name: "name")
19
+ username_field.value = user.username
20
+
21
+ password_field = f.field_with(name: "password")
22
+ password_field.value = user.password
23
+
24
+ @account = f.submit
25
+ end
26
+ end
27
+
28
+ @@current = @account
29
+ end
30
+
31
+ # accesses current session
32
+ def self.current
33
+ @@current
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ class Gotasku::User
2
+
3
+ attr_reader :username, :password
4
+
5
+ # initializes a new user with all the info needed to create a new
6
+ # session
7
+ def initialize(username, password)
8
+ @username = username
9
+ @password = password
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'db/tasks/db-tasks.rb'
2
+
3
+ task :run_all_tests do
4
+ Dir.glob('test/*.rb').each {|file| load file}
5
+ end
6
+
7
+ task :delete_sgfs do
8
+ Dir.glob('*.sgf').each {|file| File.delete(file)}
9
+ end
10
+
11
+ task default: [:run_all_tests]
@@ -0,0 +1,77 @@
1
+ require_relative '../lib/gotasku'
2
+
3
+ require 'minitest/autorun'
4
+ require 'stringio'
5
+
6
+
7
+ load 'variables.rb' # username and password for testing
8
+
9
+ class GotaskuCLITest < MiniTest::Unit::TestCase
10
+
11
+ def test_get_problem
12
+ io = StringIO.new
13
+ io.puts(`./bin/gotasku`)
14
+ io.rewind
15
+ assert_match /Gotasku::Problem/, io.read
16
+ end
17
+
18
+ def test_get_problem_with_id
19
+ io = StringIO.new
20
+ io.puts(`./bin/gotasku --id 1000`)
21
+ io.rewind
22
+ assert_match /@id=1000/, io.read
23
+ end
24
+
25
+ def test_get_problem_with_sgf
26
+ io = StringIO.new
27
+ io.puts(`./bin/gotasku --sgf '(;AB[aa][bb][cc]AW[dd][ee])'`)
28
+ io.rewind
29
+ assert_includes io.read, "AB[aa][bb][cc]AW[dd][ee]"
30
+ end
31
+
32
+ def test_get_problem_with_sgf_from_file
33
+ File.open('./test/john.sgf', 'w') {|f| f.write('(;AW[cc][cd][ad]AB[aa])')}
34
+ io = StringIO.new
35
+ io.puts(`./bin/gotasku --sgf ./test/john.sgf`)
36
+ io.rewind
37
+ assert_includes io.read, "AW[cc]"
38
+ end
39
+
40
+ def test_save_problem
41
+ name = '1000.sgf'
42
+
43
+ File.delete(name) if File.exists?(name)
44
+ `./bin/gotasku save --id 1000 --sgf '(;AB[aa][bb][cc]AW[ab][bc][cd])'`
45
+ assert_includes Dir.glob('*.sgf'), name
46
+ end
47
+
48
+ def test_display_problem_data
49
+ io = StringIO.new
50
+ io.puts(`./bin/gotasku display --id 1000`)
51
+ io.rewind
52
+ output = io.read
53
+ assert_match /id: 1000/, output
54
+ assert_match /type: life and death/, output
55
+ assert_match /rating: 3/, output
56
+ assert_match /difficulty: 6 dan/, output
57
+ end
58
+
59
+ def test_login
60
+ io = StringIO.new
61
+ io.puts(`./bin/gotasku login --username #{::USERNAME} --password #{::PASSWORD}`)
62
+ io.rewind
63
+ assert_match /Gotasku::Session/, io.read
64
+ end
65
+
66
+ =begin
67
+ def test_get_problems_list_by_difficulty
68
+ end
69
+
70
+ def test_get_problems_list_by_rating
71
+ end
72
+
73
+ def test_get_problems_list_by_type
74
+ end
75
+ =end
76
+ end
77
+
@@ -0,0 +1,81 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/gotasku'
3
+
4
+ class GotaskuParserTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @parser = SGF::Parser.new
7
+ @gparser = Gotasku::Parser.new
8
+ end
9
+
10
+ def test_initialize_parser
11
+ assert_kind_of Gotasku::Parser, Gotasku::Parser.new
12
+ end
13
+
14
+ def test_inherits_for_sgf_parser
15
+ assert_includes Gotasku::Parser.ancestors, SGF::Parser
16
+ end
17
+
18
+ def test_parses_correct_sgf
19
+ sgf = '(;AB[lb][lc][ld][kc][jb][ka][ck][dg][ei])'
20
+
21
+ assert_equal @parser.parse(sgf), @gparser.parse(sgf)
22
+ end
23
+
24
+ def test_validates_sgf
25
+ correct_sgf = '(;AB[lb][lc][ld])'
26
+ incorrect_sgf = '(;AB[lb]AB[lc]AB[ld])'
27
+
28
+ assert_equal correct_sgf, @gparser.validate(incorrect_sgf)
29
+ end
30
+
31
+ def test_validates_sgf_with_AB_and_AW
32
+ correct_sgf = '(;AB[lb][lc][ld]AW[bb][cc])'
33
+ incorrect_sgf = '(;AB[lb]AB[lc]AB[ld]AW[bb]AW[cc])'
34
+
35
+ assert_equal correct_sgf, @gparser.validate(incorrect_sgf)
36
+ end
37
+
38
+ def test_parses_incorrect_sgf
39
+ correct_sgf = '(;AB[bb][bb][bb])'
40
+ incorrect_sgf = '(;AB[bb]AB[bb]AB[bb])'
41
+
42
+ assert_equal @parser.parse(correct_sgf),
43
+ @gparser.parse(incorrect_sgf)
44
+ end
45
+
46
+ def test_parses_incorrect_sgf_with_AB_and_AW
47
+ correct_sgf = '(;AB[lb][lc][ld]AW[bb][cc])'
48
+ incorrect_sgf = '(;AB[lb]AB[lc]AB[ld]AW[bb]AW[cc])'
49
+
50
+ assert_equal @parser.parse(correct_sgf),
51
+ @gparser.parse(incorrect_sgf)
52
+ end
53
+
54
+ def test_full_incorrect_sgf_AB
55
+ incorrect_sgf = '(;AW[pe]AW[pf]AW[qg]AW[qh]AW[oi]AW[ri]AW[oj]AW[pj]AW[rj]AW[sk]AW[rl]AW[sl]AW[rm]AB[pg]AB[ph]AB[rh]AB[pi]AB[qi]AB[qj]AB[pk]AB[qk]AB[rk]AB[pl]C[black to do something effective on the side]SZ[19]GM[1]FF[3]RU[Japanese]HA[0]KM[5.5]PW[White]PB[Black]GN[White (W) vs. Black (B)]DT[1999-07-27]SY[Cgoban 1.9.2]TM[30:00(5x1:00)](;B[sh](;W[sj](;B[qf];W[rg];B[rf]C[RIGHT])(;B[rg];W[qf]))(;W[rg];B[sj];W[si];B[sj]C[snapbackRIGHT]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[rg];W[qf]C[black has nothing now])(;B[qf];W[rg](;B[sh];W[sg])(;B[rf];W[sh]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[qn];W[rg])(;B[qm];W[rg])(;B[rn];W[rg])(;B[rf];W[rg])(;B[sg];W[rg]))'
56
+
57
+ validated_sgf = @gparser.validate(incorrect_sgf)
58
+
59
+ refute_match /AB\[..\]AB/, validated_sgf
60
+ assert_match /AB/, validated_sgf
61
+ end
62
+
63
+ def test_full_incorrect_sgf_AW
64
+ incorrect_sgf = '(;AW[pe]AW[pf]AW[qg]AW[qh]AW[oi]AW[ri]AW[oj]AW[pj]AW[rj]AW[sk]AW[rl]AW[sl]AW[rm]AB[pg]AB[ph]AB[rh]AB[pi]AB[qi]AB[qj]AB[pk]AB[qk]AB[rk]AB[pl]C[black to do something effective on the side]SZ[19]GM[1]FF[3]RU[Japanese]HA[0]KM[5.5]PW[White]PB[Black]GN[White (W) vs. Black (B)]DT[1999-07-27]SY[Cgoban 1.9.2]TM[30:00(5x1:00)](;B[sh](;W[sj](;B[qf];W[rg];B[rf]C[RIGHT])(;B[rg];W[qf]))(;W[rg];B[sj];W[si];B[sj]C[snapbackRIGHT]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[rg];W[qf]C[black has nothing now])(;B[qf];W[rg](;B[sh];W[sg])(;B[rf];W[sh]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[qn];W[rg])(;B[qm];W[rg])(;B[rn];W[rg])(;B[rf];W[rg])(;B[sg];W[rg]))'
65
+
66
+ validated_sgf = @gparser.validate(incorrect_sgf)
67
+
68
+ refute_match /AW\[..\]AW/, validated_sgf
69
+ assert_match /AW/, validated_sgf
70
+ end
71
+
72
+ def test_does_not_start_with_stone_positions
73
+ incorrect_sgf = '(;GM[1]FF[3]RU[Japanese]SZ[19]HA[0]KM[5.5]PW[White]PB[Black]GN[White (W) vs. Black (B)]DT[1999-07-28]SY[Cgoban 1.9.2]TM[30:00(5x1:00)];AW[ba]AW[ca]AW[ea]AW[bb]AW[eb]AW[cc]AW[ec]AW[ed]AW[ce]AW[de]AW[ee]AB[fa][fb][fc][bd][cd][dd][fd][be][fe][bf][cf][df][ef][ff](;B[dc];W[db];B[bc];W[ab]C[only ko])(;B[db];W[dc];B[ab];W[ac];B[bc]C[RIGHT])(;B[bc];W[db];B[dc];W[ab]C[only ko])(;B[ab];W[db](;B[dc];W[bc])(;B[bc];W[dc]))(;B[ac];W[ab]))'
74
+
75
+ validated_sgf = @gparser.validate(incorrect_sgf)
76
+
77
+ assert_match /AW/, validated_sgf
78
+ refute_match /AW\[..\]AW/, validated_sgf
79
+ end
80
+
81
+ end
@@ -0,0 +1,27 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/gotasku'
3
+
4
+ class GotaskuFeatureTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @problem = Gotasku::Problem.new("id" => 1000)
7
+ end
8
+
9
+ def test_gets_difficulty
10
+ # 30k-1k, 1d-9d, 1p-9p => 30 - 1, -1 - -9, -11 - -19
11
+ assert_equal '6 dan', @problem.difficulty
12
+ end
13
+
14
+ def test_gets_difficulty_conversion_for_sorting
15
+ # 30k-1k, 1d-9d, 1p-9p => 30 - 1, -1 - -9, -11 - -19
16
+ assert_equal -6, @problem.difficulty.convert
17
+ end
18
+
19
+ def test_gets_type
20
+ assert_equal @problem.type, 'life and death'
21
+ end
22
+
23
+ def test_gets_rating
24
+ assert_equal @problem.rating, 3
25
+ end
26
+ end
27
+
@@ -0,0 +1,52 @@
1
+ require_relative '../lib/gotasku'
2
+
3
+ require 'minitest/autorun'
4
+
5
+ require 'mongo'
6
+ require 'stringio'
7
+
8
+ class GotaskuQueryTest < MiniTest::Unit::TestCase
9
+
10
+ def test_makes_query
11
+ query = Gotasku::Query.new({"id" => 1000})
12
+ assert_kind_of Mongo::Cursor, query.found
13
+ end
14
+
15
+ def test_builds_query
16
+ output = StringIO.new
17
+ file = StringIO.new
18
+ file << "y\n" << "1, 3\n" << "y\n"
19
+ file << "9, 15, 1\n" << "y\n" << "3\n"
20
+ file.rewind
21
+
22
+ temp_in = $stdin
23
+ temp_out = $stdout
24
+ $stdin = file
25
+ $stdout = output
26
+
27
+ query = Gotasku::Query.create
28
+
29
+ $stdin = temp_in
30
+ $stdout = temp_out
31
+
32
+ assert_kind_of Mongo::Cursor, query.found
33
+ end
34
+
35
+ def test_handles_big_query
36
+ query = Gotasku::Query.new({
37
+ "id" => {"$lt" => 15000},
38
+ "diff_num" => {"$gt" => 1, "$lt" => 9},
39
+ "rating" => {"$in" =>
40
+ ["life and death",
41
+ "elementary"]}
42
+ })
43
+ assert_kind_of Mongo::Cursor, query.found
44
+ end
45
+
46
+ def test_new_problem_from_database
47
+ query = Gotasku::Query.new({"id" => 17642})
48
+ prob = Gotasku::Problem.new("data" => query.to_a.first)
49
+ assert prob.sgf
50
+ assert prob.id
51
+ end
52
+ end
@@ -0,0 +1,16 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/gotasku'
3
+
4
+ require_relative '../variables'
5
+
6
+ class GotaskuSessionTest < MiniTest::Unit::TestCase
7
+
8
+ def setup
9
+ @user = Gotasku::User.new(::USERNAME, ::PASSWORD)
10
+ @session = Gotasku::Session.new(@user)
11
+ end
12
+
13
+ def test_creates_session
14
+ assert_kind_of Gotasku::Session, @session
15
+ end
16
+ end
@@ -0,0 +1,79 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/gotasku'
3
+
4
+ class GotaskuProblemTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @problem = Gotasku::Problem.new("id" => 17625)
7
+ end
8
+
9
+ def test_gets_id
10
+ assert_equal 17625, @problem.id
11
+ end
12
+
13
+ def test_gets_sgf
14
+ sgf = '(;AB[se]AB[rd]AB[qd]AB[qe]AB[qf]AB[qg]AB[oe]AB[oc]AB[pc]AB[ob]AW[mb]AW[mc]AW[me]AW[mg]AW[og]AW[ph]AW[qh]AW[rg]AW[re]AW[rf]AW[sc]AW[qc]AW[qb]AW[pb]AW[rb]AP[goproblems]
15
+ (;B[ra]
16
+ (;W[rc]
17
+ (;B[sd];W[sa];B[sb]C[CHOICERIGHT])
18
+ (;B[pa];W[qa])
19
+ (;B[qa];W[pa])
20
+ (;B[sa];W[sd])
21
+ (;B[sb];W[qa]))
22
+ (;W[sd];B[sb]C[RIGHT]))
23
+ (;B[oa];W[sd])
24
+ (;B[pa];W[sd])
25
+ (;B[qa];W[pa])
26
+ (;B[sa];W[sd])
27
+ (;B[sb];W[rc]TR[sd]TR[sa])
28
+ (;B[rc];W[sb]TR[sd]TR[pa]))'.gsub(/\r?\n/, '')
29
+
30
+ assert_equal sgf, @problem.sgf
31
+ end
32
+
33
+ def test_gets_another_sgf
34
+ sgf = '(;GM[1]FF[4]VW[]AP[Many Faces of Go:10.0]
35
+ SZ[13]
36
+ HA[0]
37
+ ST[1]
38
+ DT[2001-08-13]
39
+ KM[0.0]
40
+ RU[GOE]
41
+ AB[lb][lc][ld][kc][jb][ka][ck][dg][ei][fb][fc][fd][ee][db][cd][ej][fk][fl][fh][eg][fg][lj][kk][jj][ij][md][lf][gm][fm][gc]
42
+ AW[ib][jc][kd][jd][le][jf][hc][fi][gb][gd][fe][ff][ia][fj][gl][gk][gh][gg][jk][kg][lh][kl][ll][ik][me][ke][hm][hl]
43
+ C[After the previous problem, Black has played at 1 rather than
44
+ protecting his corner. How can white punish him?
45
+ ]
46
+ PL[W]LB[gc:A]
47
+ (;W[mb]
48
+ (;B[ma]
49
+ (;W[la];B[ge];W[ma];B[mc];W[ja];B[ma];W[kb]
50
+ C[White has given up too much when black plays A
51
+ ]LB[ge:A]
52
+ )
53
+ (;W[ja];B[kb];W[la]
54
+ C[RIGHT
55
+ ]))
56
+ (;B[ja];W[la]))
57
+ (;W[ja];B[mb]
58
+ C[Black is unconditionally alive.
59
+ ])
60
+ (;W[ge];B[ja]
61
+ C[Black has had another chance to repair his defect.
62
+ ]))'.gsub(/\r?\n/, '')
63
+
64
+ assert_equal sgf, Gotasku::Problem.new("id" => 1000).sgf
65
+ end
66
+
67
+ def test_gets_tree_for_sgf
68
+ assert_kind_of ::SGF::Tree, @problem.tree
69
+ end
70
+
71
+ def test_saves_problem
72
+ file = Dir.pwd + '/17625.sgf'
73
+
74
+ File.delete(file) if File.exist?(file)
75
+ @problem.save
76
+
77
+ assert_includes Dir.glob(Dir.pwd + '/*.sgf'), file
78
+ end
79
+ end
@@ -0,0 +1,15 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/gotasku'
3
+
4
+ require_relative '../variables'
5
+
6
+ class GotaskuUserTest < MiniTest::Unit::TestCase
7
+
8
+ def setup
9
+ @user = Gotasku::User.new(::USERNAME, ::PASSWORD)
10
+ end
11
+
12
+ def test_creates_user
13
+ assert_kind_of Gotasku::User, @user
14
+ end
15
+ end
@@ -0,0 +1,2 @@
1
+ ::USERNAME = 'moax'
2
+ ::PASSWORD = 'regah2'
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gotasku
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - jphager2
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A gem to download SGFs of go problems
14
+ email: jphager2@gmail.com
15
+ executables:
16
+ - gotasku
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - bin/gotasku
23
+ - db/tasks/db-tasks.rb
24
+ - db/tasks/populate_database.rb
25
+ - lib/gotasku.rb
26
+ - lib/gotasku/difficulty_string.rb
27
+ - lib/gotasku/display.rb
28
+ - lib/gotasku/exceptions.rb
29
+ - lib/gotasku/gotasku.rb
30
+ - lib/gotasku/parser.rb
31
+ - lib/gotasku/problem.rb
32
+ - lib/gotasku/query.rb
33
+ - lib/gotasku/rating_string.rb
34
+ - lib/gotasku/session.rb
35
+ - lib/gotasku/user.rb
36
+ - rakefile
37
+ - test/cli.rb
38
+ - test/parser.rb
39
+ - test/problem_features.rb
40
+ - test/query.rb
41
+ - test/sessions.rb
42
+ - test/sgf.rb
43
+ - test/user.rb
44
+ - variables.rb
45
+ homepage: https://github.com/jphager2/gotasku
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.2.2
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Downloads SGFs of go problems
69
+ test_files: []