rubedility 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/rubedility +1 -2
- data/lib/rubedility/difficulty.rb +47 -0
- data/lib/rubedility/lesson.rb +103 -2
- data/lib/rubedility/scraper.rb +76 -0
- data/lib/rubedility/task.rb +64 -2
- data/lib/rubedility.rb +62 -10
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e24c19eb9bf3ce8b7308fa7a0e1ca658cc1600a
|
4
|
+
data.tar.gz: 30d4aa32e0d4bd4205e4115d7bbe438cd15ff3f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20d46cf961d88b5958b163d519124ff0fd5a3024dc2ebe35a10f73aef9eee091b387df1f198bc954adf1e152117995a61dcde3ed42223e8b6b9dfc07e0ba306f
|
7
|
+
data.tar.gz: 372d73efbf9659961cd90005028624e8ef844e0a20cf69dbdc41cd8be7699d78c71665bd07ed3e11a14f2d659c316ee3585668a0f6d7f7eec667dcdcf7c1a652
|
data/bin/rubedility
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
class Difficulty
|
2
|
+
attr_accessor :level
|
3
|
+
def initialize(difficulty)
|
4
|
+
@level = difficulty
|
5
|
+
@@all.push(self)
|
6
|
+
@all_tasks = []
|
7
|
+
end
|
8
|
+
|
9
|
+
@@all = []
|
10
|
+
@all_tasks = []
|
11
|
+
|
12
|
+
def self.all
|
13
|
+
@@all
|
14
|
+
end
|
15
|
+
|
16
|
+
def all
|
17
|
+
@all_tasks
|
18
|
+
end
|
19
|
+
|
20
|
+
def display_tasks
|
21
|
+
self.all.each do |task|
|
22
|
+
puts task.name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.display_all
|
27
|
+
self.all.each do |diff|
|
28
|
+
puts "\n=====#{diff.level}====="
|
29
|
+
diff.display_tasks
|
30
|
+
puts "=======================\n"
|
31
|
+
end
|
32
|
+
puts "\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.find_or_create(task_difficulty)
|
36
|
+
self.all.each do |d|
|
37
|
+
if d.level == task_difficulty
|
38
|
+
return d
|
39
|
+
end
|
40
|
+
end
|
41
|
+
return self.new(task_difficulty)
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_task(task)
|
45
|
+
self.all.push(task)
|
46
|
+
end
|
47
|
+
end
|
data/lib/rubedility/lesson.rb
CHANGED
@@ -1,5 +1,106 @@
|
|
1
1
|
class Rubedility::Lesson
|
2
|
-
|
3
|
-
|
2
|
+
attr_accessor :name, :number, :lesson_url, :reading_url, :tests_started, :tests_solved
|
3
|
+
|
4
|
+
@tasks = []
|
5
|
+
|
6
|
+
def initialize(lesson_hash)
|
7
|
+
add_lesson_attributes(lesson_hash)
|
8
|
+
@tasks = []
|
9
|
+
@@all.push(self)
|
4
10
|
end
|
11
|
+
|
12
|
+
@@all = []
|
13
|
+
|
14
|
+
def self.populate_from_scraping(lessons_array)
|
15
|
+
lessons_array.each do |lesson|
|
16
|
+
self.new(lesson)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.all
|
21
|
+
@@all
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.user_display_one
|
25
|
+
self.display_all
|
26
|
+
print "Select Lesson Number:"
|
27
|
+
input = gets.strip.to_i
|
28
|
+
self.all.each do |lesson|
|
29
|
+
if lesson.number==input
|
30
|
+
lesson.display_lesson
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
puts "Try selecting a correct number next time."
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_lesson
|
38
|
+
self.tasks.each do |task|
|
39
|
+
task.display_row
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.display_all
|
44
|
+
puts "\nAvailable Lessons: \n"
|
45
|
+
self.all.each do |les|
|
46
|
+
puts "#{les.number}. #{les.name}"
|
47
|
+
end
|
48
|
+
puts "\n"
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.user_display_stats
|
53
|
+
self.display_all
|
54
|
+
print "Select Lesson Number for stats:"
|
55
|
+
input = gets.strip.to_i
|
56
|
+
self.all.each do |lesson|
|
57
|
+
if lesson.number==input
|
58
|
+
lesson.display_stats
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
return "Try selecting a correct number next time."
|
63
|
+
end
|
64
|
+
|
65
|
+
def display_stats
|
66
|
+
puts "\n#{self.tests_started} tests have been started from this lesson."
|
67
|
+
puts "#{self.tests_solved} tests have been solved from this lesson."
|
68
|
+
puts "#{self.tasks.length} task(s) gives an average success rate of #{(self.tests_solved.to_f/self.tests_started.to_f).round(3)*100}%"
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.user_open_reading
|
72
|
+
self.display_all
|
73
|
+
print "Select Lesson Number for reading:"
|
74
|
+
input = gets.strip.to_i
|
75
|
+
self.all.each do |lesson|
|
76
|
+
if lesson.number==input
|
77
|
+
#lesson.open_reading
|
78
|
+
return lesson.reading_url
|
79
|
+
end
|
80
|
+
end
|
81
|
+
return "Try selecting a correct number next time."
|
82
|
+
end
|
83
|
+
|
84
|
+
def open_reading
|
85
|
+
puts "launchy: #{@reading_url}"
|
86
|
+
launchy @reading_url
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_tasks(task_array)
|
90
|
+
task_array.each do |task_row|
|
91
|
+
self.tasks.push(Rubedility::Task.new(task_row))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def tasks
|
96
|
+
@tasks
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_lesson_attributes(attributes_hash)
|
100
|
+
if attributes_hash == nil
|
101
|
+
return
|
102
|
+
end
|
103
|
+
attributes_hash.each{|key, val| self.send(("#{key}="), val)}
|
104
|
+
end
|
105
|
+
|
5
106
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
class Scraper
|
6
|
+
|
7
|
+
def self.scrape_index_page(index_url)
|
8
|
+
index = Nokogiri::HTML(open(index_url))
|
9
|
+
|
10
|
+
lessons = []
|
11
|
+
index.css("div.lessons_list a").each do |lesson|
|
12
|
+
print "."
|
13
|
+
name = lesson.css("div.title").text
|
14
|
+
number = lesson.css("div.num").text.delete("Lesson").to_i
|
15
|
+
lesson_url = "".concat(index_url).concat(lesson.attr("href").split("/").last)
|
16
|
+
lessons.push({:name=>name, :number=>number, :lesson_url=>lesson_url})
|
17
|
+
end
|
18
|
+
|
19
|
+
return lessons
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.scrape_lesson_page(lesson_url)
|
23
|
+
begin
|
24
|
+
print "."
|
25
|
+
lesson = Nokogiri::HTML(open(lesson_url))
|
26
|
+
if lesson.css("a#readings").length > 0
|
27
|
+
reading_url = lesson.css("a#readings").attr("href").value
|
28
|
+
end
|
29
|
+
tests_started = lesson.css("span.started span.num").text.to_i
|
30
|
+
tests_solved = lesson.css("span.finished span.num").text.to_i
|
31
|
+
task_hashes_array = []
|
32
|
+
lesson.css("div.task-box").each do |task_row|
|
33
|
+
name = task_row.css("h4.title").text.strip
|
34
|
+
#url is just the last 'piece' of the task URL
|
35
|
+
url = task_row.css("a").attr("href").text
|
36
|
+
#have to add that to the end of the 'real' URL, but take off part of it
|
37
|
+
task_url = lesson_url.split("/")[0..2].join("/").concat(url)
|
38
|
+
difficulty = task_row.css("div.difficulty").text.strip
|
39
|
+
tagline = task_row.css("div.synopsis").text.strip
|
40
|
+
task_hashes_array.push({:name=>name, :task_url=>task_url, :difficulty=>difficulty, :tagline=>tagline, :task_reading_url=>reading_url})
|
41
|
+
end
|
42
|
+
lesson_details = {:reading_url=>reading_url, :tests_started=>tests_started, :tests_solved=>tests_solved}
|
43
|
+
#return [hash-of-lesson-details, array-of-task-detail-hashes]
|
44
|
+
return [lesson_details, task_hashes_array]
|
45
|
+
rescue OpenURI::HTTPError => er
|
46
|
+
puts "404, Lesson not found"
|
47
|
+
puts lesson_url
|
48
|
+
puts er
|
49
|
+
return nil
|
50
|
+
else
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.scrape_task_page(task_url)
|
55
|
+
print "."
|
56
|
+
begin
|
57
|
+
task = Nokogiri::HTML(open(task_url))
|
58
|
+
content = task.css("div.desc-rb-en div").first.text
|
59
|
+
#the way they have the content is not best for command line display.
|
60
|
+
#some '\n' and some '\n\n', command line looks better with '\n\n'
|
61
|
+
#substitube singles for doubles
|
62
|
+
content.gsub!(/[\n]+/,"\n")
|
63
|
+
#substitute doubles for singles
|
64
|
+
content.gsub!(/[\n]/,"\n\n")
|
65
|
+
return {:content=>content}
|
66
|
+
rescue OpenURI::HTTPError => er
|
67
|
+
puts "404'd!"
|
68
|
+
puts task_url
|
69
|
+
puts er
|
70
|
+
return nil
|
71
|
+
else
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
data/lib/rubedility/task.rb
CHANGED
@@ -1,5 +1,67 @@
|
|
1
1
|
class Rubedility::Task
|
2
|
-
|
3
|
-
|
2
|
+
attr_accessor :name, :difficulty, :content, :task_url, :task_reading_url, :tagline
|
3
|
+
|
4
|
+
def initialize(task_row)
|
5
|
+
add_task_attributes(task_row)
|
6
|
+
@@all.push(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
@@all = []
|
10
|
+
|
11
|
+
def self.create_from_collection(task_hash)
|
12
|
+
task_hash.each do |task|
|
13
|
+
Task.new(task)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_task_attributes(attributes_hash)
|
18
|
+
if attributes_hash == nil
|
19
|
+
return
|
20
|
+
end
|
21
|
+
attributes_hash.each do |key, val|
|
22
|
+
self.send(("#{key}="), val)
|
23
|
+
if key.to_s == "difficulty"
|
24
|
+
d = Difficulty.find_or_create(val)
|
25
|
+
d.add_task(self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.all
|
31
|
+
@@all
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.user_display_one
|
35
|
+
print "Enter Task Name:"
|
36
|
+
input = gets.strip.downcase
|
37
|
+
self.all.each do |task|
|
38
|
+
if task.name.downcase==input
|
39
|
+
task.display_content
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
self.display_all
|
44
|
+
puts "Try better next time."
|
45
|
+
end
|
46
|
+
|
47
|
+
def display_row
|
48
|
+
puts "=#{self.name}= (#{self.difficulty})"
|
49
|
+
puts "#{self.tagline}\n\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
def display_content
|
53
|
+
puts "========================================="
|
54
|
+
puts "==========#{self.name}=========="
|
55
|
+
puts "========================================="
|
56
|
+
puts "Difficulty: #{self.difficulty}"
|
57
|
+
puts @content
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.display_all
|
61
|
+
puts "\nAvailable Tasks: \n"
|
62
|
+
self.all.each do |task|
|
63
|
+
task.display_row
|
64
|
+
end
|
65
|
+
return nil
|
4
66
|
end
|
5
67
|
end
|
data/lib/rubedility.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
require 'launchy'
|
2
|
+
|
1
3
|
class Rubedility
|
2
4
|
|
3
5
|
def initialize
|
6
|
+
scrape_index
|
4
7
|
scrape_lessons
|
5
8
|
scrape_tasks
|
6
9
|
display_greeting
|
7
|
-
display_menu
|
10
|
+
#display_menu (menu will display from the 'while' loop below)
|
8
11
|
end
|
9
12
|
|
10
13
|
def self.list
|
@@ -12,27 +15,76 @@ class Rubedility
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def run(commands=nil)
|
15
|
-
|
16
|
-
|
18
|
+
input = ""
|
19
|
+
until input=="exit" || input=="quit" || input=="close"
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
case input
|
22
|
+
when "list lessons"
|
23
|
+
Lesson.display_all
|
24
|
+
|
25
|
+
when "list tasks"
|
26
|
+
Task.display_all
|
27
|
+
|
28
|
+
when "list stats"
|
29
|
+
Lesson.user_display_stats
|
30
|
+
|
31
|
+
when "open lesson"
|
32
|
+
Lesson.user_display_one
|
33
|
+
|
34
|
+
when "open task"
|
35
|
+
Task.user_display_one
|
36
|
+
|
37
|
+
when "list by difficulty"
|
38
|
+
Difficulty.display_all
|
39
|
+
|
40
|
+
when "open reading"
|
41
|
+
Launchy.open(Lesson.user_open_reading)
|
42
|
+
|
43
|
+
else
|
44
|
+
display_menu
|
45
|
+
|
46
|
+
end
|
47
|
+
input = gets.strip
|
48
|
+
end
|
21
49
|
end
|
22
50
|
|
23
|
-
def
|
24
|
-
|
51
|
+
def scrape_index
|
52
|
+
print "fetching index ..."
|
53
|
+
Lesson.populate_from_scraping(Scraper.scrape_index_page('https://codility.com/programmers/lessons/'))
|
54
|
+
puts "\n"
|
25
55
|
end
|
26
56
|
|
27
57
|
def scrape_lessons
|
28
|
-
|
58
|
+
#this would be an ideal time for some Asynchronous magical powers of awesome
|
59
|
+
print "fetching lessons..."
|
60
|
+
Lesson.all.each do |lesson|
|
61
|
+
attributes_hash, task_array = Scraper.scrape_lesson_page(lesson.lesson_url)
|
62
|
+
lesson.add_lesson_attributes(attributes_hash)
|
63
|
+
lesson.add_tasks(task_array)
|
64
|
+
end
|
65
|
+
puts "\n"
|
29
66
|
end
|
30
67
|
|
31
68
|
def scrape_tasks
|
32
|
-
|
69
|
+
print "fetching tasks ..."
|
70
|
+
Task.all.each do |task|
|
71
|
+
task.add_task_attributes(Scraper.scrape_task_page(task.task_url))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def display_greeting
|
76
|
+
puts "\nWelcome to Rubedility!"
|
77
|
+
puts "Your command line access point for Ruby-flavored Codility lessons and tasks.\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
def display_menu
|
81
|
+
puts "Commands: '<list/open> <lesson(s)/task(s)/stats/reading>', 'list by difficulty', 'exit'"
|
82
|
+
#puts "Commands: 'list lessons', 'open lesson', 'list tasks', 'open task', 'list stats', 'open reading', 'exit'"
|
33
83
|
end
|
34
84
|
|
35
85
|
end
|
36
86
|
|
37
87
|
require 'rubedility/lesson'
|
38
88
|
require 'rubedility/task'
|
89
|
+
require 'rubedility/scraper'
|
90
|
+
require 'rubedility/difficulty'
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubedility
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Holland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
12
|
-
dependencies:
|
11
|
+
date: 2016-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: launchy
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
description: Command line access to Codility.com programming lessons and tests
|
14
42
|
email: beehollander@gmail.com
|
15
43
|
executables:
|
@@ -19,7 +47,9 @@ extra_rdoc_files: []
|
|
19
47
|
files:
|
20
48
|
- bin/rubedility
|
21
49
|
- lib/rubedility.rb
|
50
|
+
- lib/rubedility/difficulty.rb
|
22
51
|
- lib/rubedility/lesson.rb
|
52
|
+
- lib/rubedility/scraper.rb
|
23
53
|
- lib/rubedility/task.rb
|
24
54
|
homepage: http://rubygems.org/gems/rubedility
|
25
55
|
licenses:
|