codebadges 2.0.1 → 3.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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -2
- data/README.md +34 -5
- data/bin/codebadges +14 -6
- data/codebadges.gemspec +6 -8
- data/lib/codebadges.rb +1 -0
- data/lib/codebadges/badges.rb +15 -30
- data/lib/codebadges/courses.rb +10 -20
- data/lib/codebadges/site.rb +60 -0
- data/lib/codebadges/version.rb +2 -2
- data/spec/badges_spec.rb +11 -19
- data/spec/courses_spec.rb +8 -18
- metadata +20 -51
- data/spec/fixtures/vcr_cassettes/badges.yml +0 -1202
- data/spec/fixtures/vcr_cassettes/courses.yml +0 -1367
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0cc98b2e33fbeaa97d3d051504dd02780de3003
|
4
|
+
data.tar.gz: 585b3efee9ca4c07d320c5628caba2374cdd5611
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e4e3b8c75dabcf07f503dc0c8662739af9ae37156913da8447d79271ca8a7b43fdc744af3e68bb76cc99cbf9b32d3ad91cb86a904a7fbed651075367012c84d
|
7
|
+
data.tar.gz: 9b8f8910a88f9a41889b753c9596a6ddc5f7173e245af68ce312d08255a0ada72a09fc8649178d0415476e53f78c389da067c09e9e9d51056957a569874b997c
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# CodeBadges [](https://badge.fury.io/rb/codebadges)[](https://travis-ci.org/ISS-SOA/Codecademy-Ruby)
|
2
2
|
|
3
3
|
Codecademy is a great place to learn coding! Unfortunately, Codecademy's website
|
4
|
-
doesn't provide an API
|
4
|
+
doesn't provide an API.
|
5
|
+
|
5
6
|
So we made the CodeBadges gem to scrape user achievements from Codecademy!
|
6
7
|
|
7
8
|
Note that we respect Codecademy's `robots.txt`
|
8
9
|
|
9
|
-
##
|
10
|
+
## Installation
|
10
11
|
|
11
12
|
Install it with the following command:
|
12
13
|
```sh
|
@@ -17,15 +18,43 @@ Or else put it in your `Gemfile` as:
|
|
17
18
|
```ruby
|
18
19
|
gem codebadges
|
19
20
|
```
|
21
|
+
and run `bundle install`.
|
22
|
+
|
23
|
+
## Usage
|
20
24
|
|
25
|
+
### CLI: Finding user badges
|
21
26
|
Run it from the command line. For example, to get the achievements of user `chenlizhan`:
|
22
27
|
```
|
23
28
|
$ codebadges chenlizhan
|
24
29
|
```
|
25
30
|
|
26
|
-
|
31
|
+
You will be required to enter username/password credentials to access codecademy user achievements.
|
32
|
+
This gem *never* stores your credentials nor uses them for any other purpose.
|
33
|
+
### Code: Finding user badges
|
34
|
+
Use it from your Ruby code:
|
27
35
|
````ruby
|
28
36
|
require 'codebadges'
|
29
|
-
|
30
|
-
|
37
|
+
|
38
|
+
cademy_badges = CodeBadges::GetCodecademyBadges.new('my_username', 'my_password')
|
39
|
+
badges = cademy_badges.call('chenlizhan')
|
31
40
|
````
|
41
|
+
|
42
|
+
### Code: Finding full course list
|
43
|
+
Use it from your Ruby code:
|
44
|
+
```ruby
|
45
|
+
courses = CodeBadges::GetCodecademyCourses.call
|
46
|
+
```
|
47
|
+
|
48
|
+
## Testing
|
49
|
+
|
50
|
+
First, setup environment variables called `CC_USERNAME` and `CC_PASSWORD` with your Codecademy username and password.
|
51
|
+
|
52
|
+
Then:
|
53
|
+
```
|
54
|
+
rake spec
|
55
|
+
```
|
56
|
+
|
57
|
+
Alternatively:
|
58
|
+
```
|
59
|
+
CC_USERNAME=myusername CC_PASSWORD=mypassword rake spec
|
60
|
+
```
|
data/bin/codebadges
CHANGED
@@ -1,14 +1,22 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
3
|
+
require 'io/console'
|
4
|
+
require 'codebadges' # for production
|
5
|
+
# require '../lib/codebadges/badges.rb' # for cmd line testing purposes
|
5
6
|
|
6
|
-
|
7
|
+
abort "Usage: codebadges [username]\n" if ARGV.count == 0
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
print 'Login Username: '
|
10
|
+
username = STDIN.gets.chomp
|
10
11
|
|
11
|
-
|
12
|
+
print 'Login Password: '
|
13
|
+
password = STDIN.noecho(&:gets).chomp
|
14
|
+
|
15
|
+
cadet_name = ARGV[0]
|
16
|
+
cademy = CodeBadges::GetCodecademyBadges.new(username, password)
|
17
|
+
badges_found = cademy.call(cadet_name)
|
18
|
+
|
19
|
+
puts "#{cadet_name} has collected #{badges_found.size} badges."
|
12
20
|
badges_found.each do |title, date|
|
13
21
|
puts "Earned '#{title}' on #{date}."
|
14
22
|
end
|
data/codebadges.gemspec
CHANGED
@@ -7,17 +7,15 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.date = CodeBadges::DATE
|
8
8
|
s.executables << 'codebadges'
|
9
9
|
s.summary = 'Access user and course information on Codecademy'
|
10
|
-
s.description = 'Get user achievements and course
|
11
|
-
s.authors = ['
|
12
|
-
s.email = ['
|
10
|
+
s.description = 'Get user user achievements and course lists from Codecademy'
|
11
|
+
s.authors = ['Soumya Ray', 'Lee Chen']
|
12
|
+
s.email = ['soumya.ray@gmail.com', 'chung1350@hotmail.com']
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.test_files = `git ls-files spec/*`.split("\n")
|
15
15
|
s.homepage = 'https://github.com/ISS-SOA/Codecademy-Ruby'
|
16
16
|
s.license = 'MIT'
|
17
17
|
|
18
|
-
s.add_development_dependency 'minitest'
|
19
|
-
s.add_development_dependency 'minitest-rg'
|
20
|
-
s.
|
21
|
-
s.add_development_dependency 'webmock'
|
22
|
-
s.add_runtime_dependency 'oga'
|
18
|
+
s.add_development_dependency 'minitest', '~> 5.9'
|
19
|
+
s.add_development_dependency 'minitest-rg', '~> 5.2'
|
20
|
+
s.add_runtime_dependency 'mechanize', '~> 2.7'
|
23
21
|
end
|
data/lib/codebadges.rb
CHANGED
data/lib/codebadges/badges.rb
CHANGED
@@ -1,38 +1,23 @@
|
|
1
|
-
|
2
|
-
require 'open-uri'
|
3
|
-
require 'date'
|
1
|
+
require_relative 'site'
|
4
2
|
|
5
3
|
module CodeBadges
|
6
|
-
#
|
7
|
-
#
|
8
|
-
class
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
ACH_TITLE_XPATH = 'h5'
|
13
|
-
ACH_DATE_XPATH = 'small/small'
|
14
|
-
|
15
|
-
def initialize(username)
|
16
|
-
parse_html(username)
|
17
|
-
end
|
18
|
-
|
19
|
-
def badges
|
20
|
-
@badges ||= extract_achievements
|
4
|
+
# Service object class that takes credentials and username
|
5
|
+
# returns a hash of user's badges information
|
6
|
+
class GetCodecademyBadges
|
7
|
+
def initialize(username, password)
|
8
|
+
@username = username
|
9
|
+
@password = password
|
21
10
|
end
|
22
11
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
def call(cadet)
|
13
|
+
CademySite.new do |site|
|
14
|
+
site.goto CademySite::LOGIN_URL
|
15
|
+
site.login(@username, @password)
|
16
|
+
site.goto CademySite.cadet_achievements_url(cadet)
|
17
|
+
@badges = site.extract_achievements
|
18
|
+
end
|
29
19
|
|
30
|
-
|
31
|
-
@document.xpath(ACHIEVEMENT_XPATH).map do |achievement|
|
32
|
-
title = achievement.xpath(ACH_TITLE_XPATH).text
|
33
|
-
date = Date.parse(achievement.xpath(ACH_DATE_XPATH).text)
|
34
|
-
[title, date]
|
35
|
-
end.to_h
|
20
|
+
@badges
|
36
21
|
end
|
37
22
|
end
|
38
23
|
end
|
data/lib/codebadges/courses.rb
CHANGED
@@ -1,25 +1,15 @@
|
|
1
|
-
|
2
|
-
require 'open-uri'
|
3
|
-
require 'date'
|
1
|
+
require_relative 'site'
|
4
2
|
|
5
3
|
module CodeBadges
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def extract_main_courses
|
19
|
-
doc = Oga.parse_html(open(MAIN_URL))
|
20
|
-
doc.xpath(XPATH_COURSES).map do |course|
|
21
|
-
[course.text, URI.join(MAIN_URL, course.get('href'))]
|
22
|
-
end.to_h
|
4
|
+
# Service object class that returns all courses
|
5
|
+
class GetCodecademyCourses
|
6
|
+
def self.call
|
7
|
+
CademySite.new do |site|
|
8
|
+
site.goto CademySite::MAIN_URL
|
9
|
+
@courses = site.extract_main_courses
|
10
|
+
end
|
11
|
+
|
12
|
+
@courses
|
23
13
|
end
|
24
14
|
end
|
25
15
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module CodeBadges
|
5
|
+
## CodeCademy web automation and scraper
|
6
|
+
class CademySite
|
7
|
+
MAIN_URL = 'https://www.codecademy.com'.freeze
|
8
|
+
USERS_URL = "#{MAIN_URL}/users".freeze
|
9
|
+
ACHIEVEMENTS_DIR = 'achievements'.freeze
|
10
|
+
|
11
|
+
LOGIN_URL = "#{MAIN_URL}/login".freeze
|
12
|
+
USERNAME_TEXTFIELD_ID = 'user_login'.freeze
|
13
|
+
PASSWORD_TEXTFIELD_ID = 'user_password'.freeze
|
14
|
+
LOGIN_BUTTON_ID = 'user_submit'.freeze
|
15
|
+
|
16
|
+
ACHIEVEMENT_XPATH = "//div[contains(@class,'achievement-card')]".freeze
|
17
|
+
ACH_TITLE_XPATH = 'h5'.freeze
|
18
|
+
ACH_DATE_XPATH = 'small/small'.freeze
|
19
|
+
|
20
|
+
COURSES_XPATH = "//div[h5/strong/text()='Learn To Code']/div/div/ul/li/a".freeze
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@browser = Mechanize.new do |agent|
|
24
|
+
agent.user_agent_alias = 'Linux Firefox'
|
25
|
+
end
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.cadet_achievements_url(cadet)
|
31
|
+
"#{USERS_URL}/#{cadet}/#{ACHIEVEMENTS_DIR}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def goto(url)
|
35
|
+
@browser.get(url)
|
36
|
+
end
|
37
|
+
|
38
|
+
def login(username, password)
|
39
|
+
@browser.page.form do |form|
|
40
|
+
form.field(id: USERNAME_TEXTFIELD_ID).value = username
|
41
|
+
form.field(id: PASSWORD_TEXTFIELD_ID).value = password
|
42
|
+
form.submit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def extract_achievements
|
47
|
+
@browser.page.xpath(ACHIEVEMENT_XPATH).map do |achievement|
|
48
|
+
title = achievement.xpath(ACH_TITLE_XPATH).text
|
49
|
+
date = Date.parse(achievement.xpath(ACH_DATE_XPATH).text)
|
50
|
+
[title, date]
|
51
|
+
end.to_h
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_main_courses
|
55
|
+
@browser.page.xpath(COURSES_XPATH).map do |course|
|
56
|
+
[course.text, URI.join(MAIN_URL, course.attribute('href').value)]
|
57
|
+
end.to_h
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/codebadges/version.rb
CHANGED
data/spec/badges_spec.rb
CHANGED
@@ -1,30 +1,22 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'minitest/rg'
|
3
3
|
require 'yaml'
|
4
|
-
require '
|
5
|
-
require 'webmock/minitest'
|
6
|
-
require './lib/codebadges.rb'
|
4
|
+
require './lib/codebadges/badges.rb'
|
7
5
|
|
8
|
-
USERNAME = 'soumya.ray'
|
6
|
+
USERNAME = 'soumya.ray'.freeze
|
9
7
|
badges_from_file = YAML.load(File.read('./spec/fixtures/badges.yml'))
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
VCR.use_cassette('badges') do
|
17
|
-
badges_found = CodeBadges::CodecademyBadges.new(USERNAME).badges
|
9
|
+
describe 'Get badges for one cadet' do
|
10
|
+
cademy = CodeBadges::GetCodecademyBadges.new(ENV['CC_USERNAME'], ENV['CC_PASSWORD'])
|
11
|
+
badges_found = cademy.call('soumya.ray')
|
18
12
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
13
|
+
it 'has the right number of badges' do
|
14
|
+
badges_found.size.wont_be :<, badges_from_file[USERNAME].size
|
15
|
+
end
|
23
16
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
17
|
+
badges_from_file[USERNAME].map do |b_name, b_date|
|
18
|
+
it "finds '#{b_name}' badge" do
|
19
|
+
badges_found[b_name].must_equal Date.parse(b_date)
|
28
20
|
end
|
29
21
|
end
|
30
22
|
end
|
data/spec/courses_spec.rb
CHANGED
@@ -1,30 +1,20 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'minitest/rg'
|
3
3
|
require 'yaml'
|
4
|
-
require 'vcr'
|
5
|
-
require 'webmock/minitest'
|
6
4
|
require './lib/codebadges.rb'
|
7
5
|
|
8
6
|
test_courses = YAML.load(File.read('spec/fixtures/courses.yml'))
|
9
7
|
|
10
|
-
|
11
|
-
config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
12
|
-
config.hook_into :webmock
|
13
|
-
end
|
14
|
-
|
15
|
-
VCR.use_cassette('courses') do
|
16
|
-
codecademy = CodeBadges::CodecademyCourses.new
|
17
|
-
courses = codecademy.courses
|
8
|
+
courses = CodeBadges::GetCodecademyCourses.call
|
18
9
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
describe 'Find all test courses' do
|
11
|
+
it 'has the right number of badges' do
|
12
|
+
courses.size.wont_be :<, test_courses.size
|
13
|
+
end
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
15
|
+
test_courses.map do |course_name|
|
16
|
+
it "finds course '#{course_name}'" do
|
17
|
+
courses.keys.include? course_name
|
28
18
|
end
|
29
19
|
end
|
30
20
|
end
|
metadata
CHANGED
@@ -1,90 +1,62 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codebadges
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Lee Chen
|
8
7
|
- Soumya Ray
|
8
|
+
- Lee Chen
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-07-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '5.9'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '5.9'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: minitest-rg
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
35
|
-
type: :development
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: '0'
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: vcr
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
32
|
+
- - "~>"
|
47
33
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
34
|
+
version: '5.2'
|
49
35
|
type: :development
|
50
36
|
prerelease: false
|
51
37
|
version_requirements: !ruby/object:Gem::Requirement
|
52
38
|
requirements:
|
53
|
-
- - "
|
39
|
+
- - "~>"
|
54
40
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
41
|
+
version: '5.2'
|
56
42
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
43
|
+
name: mechanize
|
58
44
|
requirement: !ruby/object:Gem::Requirement
|
59
45
|
requirements:
|
60
|
-
- - "
|
46
|
+
- - "~>"
|
61
47
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: oga
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
48
|
+
version: '2.7'
|
77
49
|
type: :runtime
|
78
50
|
prerelease: false
|
79
51
|
version_requirements: !ruby/object:Gem::Requirement
|
80
52
|
requirements:
|
81
|
-
- - "
|
53
|
+
- - "~>"
|
82
54
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
84
|
-
description: Get user achievements and course
|
55
|
+
version: '2.7'
|
56
|
+
description: Get user user achievements and course lists from Codecademy
|
85
57
|
email:
|
86
|
-
- chung1350@hotmail.com
|
87
58
|
- soumya.ray@gmail.com
|
59
|
+
- chung1350@hotmail.com
|
88
60
|
executables:
|
89
61
|
- codebadges
|
90
62
|
extensions: []
|
@@ -101,13 +73,12 @@ files:
|
|
101
73
|
- lib/codebadges.rb
|
102
74
|
- lib/codebadges/badges.rb
|
103
75
|
- lib/codebadges/courses.rb
|
76
|
+
- lib/codebadges/site.rb
|
104
77
|
- lib/codebadges/version.rb
|
105
78
|
- spec/badges_spec.rb
|
106
79
|
- spec/courses_spec.rb
|
107
80
|
- spec/fixtures/badges.yml
|
108
81
|
- spec/fixtures/courses.yml
|
109
|
-
- spec/fixtures/vcr_cassettes/badges.yml
|
110
|
-
- spec/fixtures/vcr_cassettes/courses.yml
|
111
82
|
homepage: https://github.com/ISS-SOA/Codecademy-Ruby
|
112
83
|
licenses:
|
113
84
|
- MIT
|
@@ -128,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
99
|
version: '0'
|
129
100
|
requirements: []
|
130
101
|
rubyforge_project:
|
131
|
-
rubygems_version: 2.
|
102
|
+
rubygems_version: 2.6.6
|
132
103
|
signing_key:
|
133
104
|
specification_version: 4
|
134
105
|
summary: Access user and course information on Codecademy
|
@@ -137,5 +108,3 @@ test_files:
|
|
137
108
|
- spec/courses_spec.rb
|
138
109
|
- spec/fixtures/badges.yml
|
139
110
|
- spec/fixtures/courses.yml
|
140
|
-
- spec/fixtures/vcr_cassettes/badges.yml
|
141
|
-
- spec/fixtures/vcr_cassettes/courses.yml
|