codebadges 2.0.1 → 3.0.0

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: 54242705f83a6dadf672c4fe0ce820f3006dbe7b
4
- data.tar.gz: 29dfc653ddb4432b5c53013fa0f93fa5aa59ad8f
3
+ metadata.gz: c0cc98b2e33fbeaa97d3d051504dd02780de3003
4
+ data.tar.gz: 585b3efee9ca4c07d320c5628caba2374cdd5611
5
5
  SHA512:
6
- metadata.gz: 69193d21274ae16b60a5db2b3588f0077d75ee38bbfba8f7bc03c6f0e1175286c62944720fd99cb72e663d9cbadb71fa05a3e907e140f5595c99ec6b3e18b95a
7
- data.tar.gz: 7af7e76a996ad85af191b66690adc751c449c9b71bad7229a1c1bfc1763995d844dea80e23126598ae83fbb8f58d768dd7e1bcc7a5b1fd1cdd78378cfe32acc5
6
+ metadata.gz: 3e4e3b8c75dabcf07f503dc0c8662739af9ae37156913da8447d79271ca8a7b43fdc744af3e68bb76cc99cbf9b32d3ad91cb86a904a7fbed651075367012c84d
7
+ data.tar.gz: 9b8f8910a88f9a41889b753c9596a6ddc5f7173e245af68ce312d08255a0ada72a09fc8649178d0415476e53f78c389da067c09e9e9d51056957a569874b997c
@@ -1,8 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
- - ruby-head
4
- - jruby-head
3
+ - 2.2.5
4
+ - 2.3.1
5
5
  branches:
6
6
  only:
7
7
  - master
8
8
  - develop
9
+ before_install:
10
+ - "export DISPLAY=:99.0"
11
+ - "sh -e /etc/init.d/xvfb start"
12
+ - sleep 3 # give xvfb some time to start
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # CodeBadges [![Gem Version](https://badge.fury.io/rb/codebadges.svg)](https://badge.fury.io/rb/codebadges)[![Build Status](https://travis-ci.org/ISS-SOA/Codecademy-Ruby.svg?branch=develop)](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 to help us monitor our own coding.
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
- ## Usage
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
- Or use it from your Ruby code:
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
- lee = CodeBadges::CodecademyBadges.new('chenlizhan')
30
- badges = lee.badges
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
+ ```
@@ -1,14 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'codebadges' # for production
4
- # require '../lib/codebadges/badges.rb' # for cmd line testing purposes
3
+ require 'io/console'
4
+ require 'codebadges' # for production
5
+ # require '../lib/codebadges/badges.rb' # for cmd line testing purposes
5
6
 
6
- fail ArgumentError, "Usage: codebadges [username]\n" if ARGV.count == 0
7
+ abort "Usage: codebadges [username]\n" if ARGV.count == 0
7
8
 
8
- username = ARGV[0]
9
- badges_found = CodeBadges::CodecademyBadges.new(username).badges
9
+ print 'Login Username: '
10
+ username = STDIN.gets.chomp
10
11
 
11
- puts "#{username} has collected #{badges_found.size} badges."
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
@@ -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 info from Codecademy'
11
- s.authors = ['Lee Chen', 'Soumya Ray']
12
- s.email = ['chung1350@hotmail.com', 'soumya.ray@gmail.com']
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.add_development_dependency 'vcr'
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
@@ -1,2 +1,3 @@
1
+ require 'codebadges/site.rb'
1
2
  require 'codebadges/badges.rb'
2
3
  require 'codebadges/courses.rb'
@@ -1,38 +1,23 @@
1
- require 'oga'
2
- require 'open-uri'
3
- require 'date'
1
+ require_relative 'site'
4
2
 
5
3
  module CodeBadges
6
- # This class get the user account as an input
7
- # return a hash of user's badges information
8
- class CodecademyBadges
9
- USERS_URL = 'https://www.codecademy.com/users'
10
- ACHIEVEMENTS_DIR = 'achievements'
11
- ACHIEVEMENT_XPATH = "//div[contains(@class,'achievement-card')]"
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
- private
24
-
25
- def parse_html(username)
26
- url = "#{USERS_URL}/#{username}/#{ACHIEVEMENTS_DIR}"
27
- @document = Oga.parse_html(open(url))
28
- end
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
- def extract_achievements
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
@@ -1,25 +1,15 @@
1
- require 'oga'
2
- require 'open-uri'
3
- require 'date'
1
+ require_relative 'site'
4
2
 
5
3
  module CodeBadges
6
- # This class get the user account as an input
7
- # return a hash of user's badges information
8
- class CodecademyCourses
9
- MAIN_URL = 'https://www.codecademy.com'
10
- XPATH_COURSES = "//div[h5/strong/text()='Learn To Code']/div/div/ul/li/a"
11
-
12
- def courses
13
- @courses ||= extract_main_courses
14
- end
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
@@ -1,5 +1,5 @@
1
1
  # Versioning
2
2
  module CodeBadges
3
- VERSION = '2.0.1'
4
- DATE = '2015-10-11'
3
+ VERSION = '3.0.0'
4
+ DATE = '2016-07-10'
5
5
  end
@@ -1,30 +1,22 @@
1
1
  require 'minitest/autorun'
2
2
  require 'minitest/rg'
3
3
  require 'yaml'
4
- require 'vcr'
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
- VCR.configure do |config|
12
- config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
13
- config.hook_into :webmock
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
- describe 'Get badges for one user' do
20
- it 'has the right number of badges' do
21
- badges_found.size.wont_be :<, badges_from_file[USERNAME].size
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
- badges_from_file[USERNAME].map do |b_name, b_date|
25
- it "finds '#{b_name}' badge" do
26
- badges_found[b_name].must_equal Date.parse(b_date)
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
@@ -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
- VCR.configure do |config|
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
- describe 'Find all test courses' do
20
- it 'has the right number of badges' do
21
- courses.size.wont_be :<, test_courses.size
22
- end
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
- test_courses.map do |course_name|
25
- it "finds course '#{course_name}'" do
26
- codecademy.courses.keys.include? course_name
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: 2.0.1
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: 2015-10-11 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
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: '0'
41
+ version: '5.2'
56
42
  - !ruby/object:Gem::Dependency
57
- name: webmock
43
+ name: mechanize
58
44
  requirement: !ruby/object:Gem::Requirement
59
45
  requirements:
60
- - - ">="
46
+ - - "~>"
61
47
  - !ruby/object:Gem::Version
62
- version: '0'
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: '0'
84
- description: Get user achievements and course info from Codecademy
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.4.6
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