ubcbooker 0.1.0 → 0.2.1

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: 615c2cd68c874a04a2ba4e9cbf6bab3cd0d5ee64
4
- data.tar.gz: 90f94a06606db213ff52446fb0b00d2b33ebcfbf
3
+ metadata.gz: feb2fd80cc7567a4cfd673c7245ac5bd4251fa8b
4
+ data.tar.gz: f1f96fc070d9e8f66d955f103fa3c3c950597a74
5
5
  SHA512:
6
- metadata.gz: e81ef07ceb6a01cd5f7dc437c4e4c3955d8304c4457bf9d7fb1487fdd204e7860534573c57654a6c56fac0b43230cca4718c7d7e96397677f42e5bc192b1f02a
7
- data.tar.gz: e5f4330ec570ff27dac25f3842ac236611c58e6650e602a4b320c335464a1c82cb31f3acf7ff7935c53650ce94c14bd01d7f933543111dcca605298777cc65df
6
+ metadata.gz: 702723bae1f951c48ceddbf121100b9858fc25e32aa48d00b9fd0cd26c5029cc34fdb2b6173e4bcee2077dc37114310fa4f3950cb6a35cd214822ce480107e6e
7
+ data.tar.gz: 9dd869bad331752435f49821fc93d1a345dcefbf99ff3a77f7a7ccafea3b25350f074404e7a0929eb4007e9aab49f3f59d22abc37270a5dd4a8b8580d7df0046
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  .DS_Store
2
2
  *.gem
3
3
  *.rbc
4
+ lib/ubcbooker/config.yml
4
5
  /.config
5
6
  /coverage/
6
7
  /InstalledFiles
@@ -25,6 +26,7 @@
25
26
  Gemfile.lock
26
27
  .ruby-version
27
28
  .ruby-gemset
29
+ .rspec_status
28
30
 
29
31
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
30
32
  .rvmrc
data/README.md CHANGED
@@ -4,7 +4,11 @@ CLI tool to book project rooms in UBC
4
4
 
5
5
  ## Installation
6
6
 
7
- Add this line to your application's Gemfile:
7
+ Install it as:
8
+
9
+ $ gem install ubcbooker
10
+
11
+ For use in your application, add this line to your Gemfile:
8
12
 
9
13
  ```ruby
10
14
  gem 'ubcbooker'
@@ -14,21 +18,20 @@ And then execute:
14
18
 
15
19
  $ bundle
16
20
 
17
- Or install it yourself as:
18
-
19
- $ gem install ubcbooker
20
-
21
21
  ## Usage
22
22
 
23
23
  ```
24
- # Call help
25
- $ ubcbooker -h
26
24
  # Book Computer Science project rooms
27
25
  $ ubcbooker cs
28
26
  # Book Sauder project rooms
29
27
  $ ubcbooker sauder
30
28
  ```
31
29
 
30
+ ```
31
+ # Call help
32
+ $ ubcbooker -h
33
+ ```
34
+
32
35
  ## Development
33
36
 
34
37
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`.
data/bin/console CHANGED
@@ -2,13 +2,7 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "ubcbooker"
5
+ require "pry"
5
6
 
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
7
+ Pry.start
8
8
 
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/ubcbooker ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ubcbooker"
5
+
6
+ Ubcbooker::CLI.new.start
@@ -0,0 +1,31 @@
1
+ module Ubcbooker
2
+ class Account
3
+ attr_accessor :username, :password
4
+
5
+ def initialize
6
+ @config_path = File.expand_path("../config.yml", __FILE__)
7
+ @account_info = YAML.load_file(@config_path)
8
+ @username = @account_info["username"]
9
+ @password = @account_info["password"]
10
+ end
11
+
12
+ def write(username, password)
13
+ @account["username"] = username
14
+ @account["password"] = password
15
+ new_yml = YAML.dump(@account)
16
+ open(@config_path, "w") { |f| f.write(new_yml) }
17
+ @account = YAML.load_file(@config_path)
18
+ end
19
+
20
+ def print_supported_departments
21
+ puts "Supported department options in #{Ubcbooker::VERSION}:"
22
+ BOOKING_URL.keys.each do |d|
23
+ puts " - #{d}"
24
+ end
25
+ end
26
+
27
+ def defined?
28
+ return @account["username"] != "hoge" && @account["password"] != "hoge"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,156 @@
1
+ module Ubcbooker
2
+ class CLI
3
+ attr_accessor :options
4
+
5
+ def initialize
6
+ @config = Ubcbooker::Config.new
7
+ @options = nil
8
+ end
9
+
10
+ def ask_config
11
+ print "Your CWL username: "
12
+ username = gets.chomp
13
+ print "Your CWL password: "
14
+ # Hide the password input
15
+ password = STDIN.noecho(&:gets).chomp
16
+ @config.write(username, password)
17
+ puts
18
+ end
19
+
20
+ def parse_options
21
+
22
+ # This will hold the options we parse
23
+ options = {
24
+ name: nil,
25
+ date: nil,
26
+ time: nil,
27
+ department: nil,
28
+ }
29
+
30
+ # TODO: Change department to building
31
+ OptionParser.new do |parser|
32
+ parser.on("-b", "--building BUILDING", String, "Specify which department to book rooms from") do |v|
33
+ if CLI::Validator.is_valid_department(v)
34
+ options[:department] = v
35
+ else
36
+ raise Ubcbooker::Error::UnsupportedDepartment.new(v)
37
+ end
38
+ end
39
+
40
+ parser.on("-d", "--date DATE", String, "Specify date to book rooms for (MM/DD)") do |v|
41
+ if CLI::Validator.is_valid_date(v)
42
+ options[:date] = v
43
+ else
44
+ raise Ubcbooker::Error::UnsupportedDate.new(v)
45
+ end
46
+ end
47
+
48
+ parser.on("-h", "--help", "Show this help message") do
49
+ puts parser
50
+ puts
51
+ puts "ex. Book a room in CS from 11am to 1pm on March 5th with the name 'Study Group'"
52
+ puts " $>ubcbooker -b cs -n 'Study Group' -d 03/05 -t 11:00-13:00"
53
+ exit(0)
54
+ end
55
+
56
+ parser.on("-l", "--list", "List supported departments") do |v|
57
+ @config.print_supported_departments
58
+ exit(0)
59
+ end
60
+
61
+ parser.on("-n", "--name NAME", String, "Name of the booking") do |v|
62
+ if CLI::Validator.is_valid_name(v)
63
+ options[:name] = v
64
+ else
65
+ raise Ubcbooker::Error::ProfaneName.new(v)
66
+ end
67
+ end
68
+ parser.on("-t", "--time TIME", String,
69
+ "Specify time to book rooms for (HH:MM-HH:MM)") do |v|
70
+ if CLI::Validator.is_valid_time(v)
71
+ options[:time] = v
72
+ else
73
+ raise Ubcbooker::Error::UnsupportedTime.new(v)
74
+ end
75
+ end
76
+
77
+ parser.on("-u", "--update", "Update username and password") do |v|
78
+ ask_config
79
+ exit(0)
80
+ end
81
+
82
+ parser.on("-v", "--version", "Show version") do |v|
83
+ puts Ubcbooker::VERSION
84
+ exit(0)
85
+ end
86
+ end.parse!
87
+
88
+ spinner = get_spinner("Verifying inputs")
89
+ spinner.success("Done!") # Stop animation
90
+
91
+ if CLI::Validator.is_required_missing(options)
92
+ raise OptionParser::MissingArgument
93
+ end
94
+
95
+ return options
96
+ end
97
+
98
+ def get_options
99
+ option_errors = [
100
+ Ubcbooker::Error::UnsupportedDepartment,
101
+ Ubcbooker::Error::UnsupportedTime,
102
+ Ubcbooker::Error::UnsupportedDate,
103
+ Ubcbooker::Error::ProfaneName,
104
+ ]
105
+
106
+ begin
107
+ return parse_options
108
+ rescue OptionParser::MissingArgument
109
+ puts "Error: Missing Option\n".red <<
110
+ "One or more of required option are missing values\n" <<
111
+ "Please check if options -b, -d, -n, and -t all have values passed"
112
+ exit(1)
113
+ rescue *option_errors => e
114
+ puts e.message
115
+ exit(1)
116
+ end
117
+ end
118
+
119
+ def get_department_scraper(department)
120
+ case department
121
+ when "cs"
122
+ return Ubcbooker::Scraper::Cs
123
+ when "sauder_ugrad"
124
+ return Ubcbooker::Scraper::SauderUgrad
125
+ else
126
+ raise Ubcbooker::Error::UnsupportedDepartment.new(department)
127
+ end
128
+ end
129
+
130
+ def get_scraper(department, username, password)
131
+ scraper_client = get_department_scraper(department)
132
+ return scraper_client.new(username, password)
133
+ end
134
+
135
+ def get_spinner(text)
136
+ spinner = ::TTY::Spinner.new("[:spinner] #{text} ... ", format: :dots)
137
+ spinner.auto_spin # Automatic animation with default interval
138
+ return spinner
139
+ end
140
+
141
+ def start
142
+ @options = get_options
143
+ ask_config if !@config.defined?
144
+
145
+ @client = get_scraper(@options[:department],
146
+ @config.account["username"],
147
+ @config.account["password"])
148
+ begin
149
+ room_id = @client.book(@options)
150
+ puts "Success! #{room_id} is booked".green
151
+ rescue Ubcbooker::Error::NoAvailableRoom => e
152
+ puts e.message
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,50 @@
1
+ module Ubcbooker
2
+ class CLI
3
+ module Validator
4
+ def self.is_valid_department(d)
5
+ return BOOKING_URL.keys.include?(d.to_sym)
6
+ end
7
+
8
+ def self.is_valid_date(d)
9
+ date = nil
10
+ begin
11
+ date = Date.parse(d)
12
+ # Expect MM/DD
13
+ rescue ArgumentError
14
+ return false
15
+ end
16
+ return /^\d\d\/\d\d$/.match?(d) && # Match format
17
+ date.weekday? && # Not on weekend
18
+ !date.past? && # Not in the past
19
+ (date < Date.today + 7) # Within a week
20
+ end
21
+
22
+ def self.is_valid_time(t)
23
+ if /^\d\d:\d\d-\d\d:\d\d$/.match?(t)
24
+ times = t.split("-")
25
+ times.each do |time|
26
+ begin
27
+ DateTime.parse(time)
28
+ # Expect HH:MM
29
+ rescue ArgumentError
30
+ return false
31
+ end
32
+ end
33
+ return true
34
+ else
35
+ return false
36
+ end
37
+ end
38
+
39
+ def self.is_required_missing(options)
40
+ return options[:name].nil? || options[:date].nil? ||
41
+ options[:time].nil? || options[:department].nil?
42
+ end
43
+
44
+ # False if the name contains any profanity
45
+ def self.is_valid_name(name)
46
+ return !Obscenity.profane?(name)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ class String
2
+ def black; "\e[30m#{self}\e[0m" end
3
+ def red; "\e[31m#{self}\e[0m" end
4
+ def green; "\e[32m#{self}\e[0m" end
5
+ def brown; "\e[33m#{self}\e[0m" end
6
+ def blue; "\e[34m#{self}\e[0m" end
7
+ def magenta; "\e[35m#{self}\e[0m" end
8
+ def cyan; "\e[36m#{self}\e[0m" end
9
+ def gray; "\e[37m#{self}\e[0m" end
10
+
11
+ def bold; "\e[1m#{self}\e[22m" end
12
+ def italic; "\e[3m#{self}\e[23m" end
13
+ def underline; "\e[4m#{self}\e[24m" end
14
+ def blink; "\e[5m#{self}\e[25m" end
15
+ def reverse_color; "\e[7m#{self}\e[27m" end
16
+ end
@@ -0,0 +1,39 @@
1
+ module Ubcbooker
2
+ class Config
3
+ attr_accessor :account
4
+
5
+ def initialize
6
+ @config_path = File.expand_path("../config.yml", __FILE__)
7
+ if !File.exist?(@config_path)
8
+ create_config_file(@config_path)
9
+ end
10
+
11
+ @account = YAML.load_file(@config_path)
12
+ end
13
+
14
+ def create_config_file(config_path)
15
+ File.open(config_path, "w") do |f|
16
+ f.write("---\nusername: sample\npassword: sample")
17
+ end
18
+ end
19
+
20
+ def write(username, password)
21
+ @account["username"] = username
22
+ @account["password"] = password
23
+ new_yml = YAML.dump(@account)
24
+ open(@config_path, "w") { |f| f.write(new_yml) }
25
+ @account = YAML.load_file(@config_path)
26
+ end
27
+
28
+ def print_supported_departments
29
+ puts "Supported department options in #{Ubcbooker::VERSION}:"
30
+ BOOKING_URL.keys.each do |d|
31
+ puts " - #{d}"
32
+ end
33
+ end
34
+
35
+ def defined?
36
+ return @account["username"] != "sample" && @account["password"] != "sample"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,70 @@
1
+ module Ubcbooker
2
+ module Error
3
+ class UnsupportedDepartment < StandardError
4
+ attr_reader :message
5
+ def initialize(department = "unknown")
6
+ @message = "\"#{department}\" is an unsupported department\n".red <<
7
+ "Check supported departments with `ubcbooker -l`"
8
+ super
9
+ end
10
+ end
11
+
12
+ class UnsupportedDate < StandardError
13
+ attr_reader :message, :date
14
+ def initialize(date = "unknown")
15
+ @date = date
16
+ @message = "Error: Unsupported Date\n".red <<
17
+ "Date must not be:\n" <<
18
+ " - in the past\n" <<
19
+ " - in a weekend\n" <<
20
+ " - beyond a week\n" <<
21
+ "Please check if the time is in the format of MM/DD\n" <<
22
+ "Ex. 03/05"
23
+ super
24
+ end
25
+ end
26
+
27
+ class UnsupportedTime < StandardError
28
+ attr_reader :message, :time
29
+ def initialize(time = "unknown")
30
+ @time = time
31
+ @message = "Error: Unsupported Time\n".red <<
32
+ "Please check if the time is in the format of HH:MM-HH:MM\n" <<
33
+ "Ex. 11:00-13:00"
34
+ super
35
+ end
36
+ end
37
+
38
+ class ProfaneName < StandardError
39
+ attr_reader :message, :name
40
+ def initialize(name = "unknown")
41
+ @name = name
42
+ @message = "Error: Name includes profanity\n".red <<
43
+ "Remember that other students might see the booking name\n" <<
44
+ "Please try again with a different name"
45
+ super
46
+ end
47
+ end
48
+
49
+ class NoAvailableRoom < StandardError
50
+ attr_reader :message, :time_range
51
+ def initialize(time_range)
52
+ @time_range = time_range
53
+ @message = "Error: No Available Room\n".red <<
54
+ "There are no room available for #{time_range} range\n" <<
55
+ "Please try again with a different time range"
56
+ super
57
+ end
58
+ end
59
+
60
+ class LoginFailed < StandardError
61
+ attr_reader :message
62
+ def initialize
63
+ @message = "\nLogin Failed :/\n".red <<
64
+ "Please try logging in with a different username or password\n" <<
65
+ "You can use `-u` flag to update saved accout info"
66
+ super
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ username: sample
3
+ password: sample
@@ -0,0 +1,43 @@
1
+ module Ubcbooker
2
+ module Scraper
3
+ class BaseScraper
4
+ def initialize(username, password)
5
+ @agent = Mechanize.new do |agent|
6
+ agent.user_agent_alias = "Linux Mozilla"
7
+ end
8
+ @username = username
9
+ @password = password
10
+ end
11
+
12
+ def is_logged_in(page)
13
+ page_body = Nokogiri::HTML(page.body)
14
+ login_status_text = page_body.css("p").first.text
15
+ return !login_status_text.include?("Login Failed")
16
+ end
17
+
18
+ def populate_account_info(login_page)
19
+ username_feild = login_page.form.field_with(name: "j_username")
20
+ username_feild.value = @username
21
+ password_field = login_page.form.field_with(name: "j_password")
22
+ password_field.value = @password
23
+ redirect_page = login_page.form.submit
24
+ return redirect_page.form.submit
25
+ end
26
+
27
+ # Do login for UBC CWL system
28
+ def login_ubc_cwl(login_page)
29
+ begin
30
+ after_login_page = populate_account_info(login_page)
31
+ if is_logged_in(after_login_page)
32
+ return after_login_page
33
+ else
34
+ raise Ubcbooker::Error::LoginFailed
35
+ end
36
+ rescue Ubcbooker::Error::LoginFailed => e
37
+ puts e.message
38
+ exit(1)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,175 @@
1
+ module Ubcbooker
2
+ module Scraper
3
+ class Cs < BaseScraper
4
+ CS_ROOM_BASE_URL = "https://my.cs.ubc.ca/space/"
5
+ CS_BOOK_URL = "https://my.cs.ubc.ca/node/add/pr-booking"
6
+ CS_ROOMS = ["X139", "X141", "X151", "X153", # 1st Floor
7
+ "X237", "X239", "X241", # 2nd Floor
8
+ "X337", "X339", "X341"] # 3rd Floor
9
+
10
+ def book(options)
11
+ login
12
+ title = options[:name]
13
+ book_date = Date.parse(options[:date])
14
+ book_slot = get_time_slot(options[:time])
15
+
16
+ room_pages = batch_request(CS_ROOMS, book_date)
17
+ room_pages.select! { |r| is_available(r, book_date, book_slot) }
18
+
19
+ if room_pages.any?
20
+ # TODO: If -c CHOOSE option then run CLI-UI here
21
+ room = room_pages.first # Choose the first available room
22
+ submit_booking(room, title, book_date, book_slot)
23
+ return get_room_page_id(room)
24
+ else
25
+ raise Ubcbooker::Error::NoAvailableRoom.new(options[:time])
26
+ end
27
+ end
28
+
29
+ def login
30
+ spinner = get_spinner("Logging into CWL")
31
+ booking_url = BOOKING_URL[:cs]
32
+ @agent.get(booking_url) do |page|
33
+ login_page = page.link_with(text: "CWL Login Redirect").click
34
+ login_ubc_cwl(login_page)
35
+ end
36
+ spinner.success("Done!") # Stop animation
37
+ end
38
+
39
+ def submit_booking(room, title, book_date, book_slot)
40
+ spinner = get_spinner("Submitting booking request")
41
+ booking_form = @agent.get(CS_BOOK_URL).forms[1]
42
+ booking_form["title"] = title
43
+ book_date_str = book_date.strftime("%Y/%m/%d") # ex 2018/03/08
44
+ select_room_option(booking_form, room)
45
+ booking_form["field_date[und][0][value][date]"] = book_date_str
46
+ booking_form["field_date[und][0][value][time]"] = time_to_ampm(book_slot.min)
47
+ booking_form["field_date[und][0][value2][date]"] = book_date_str
48
+ booking_form["field_date[und][0][value2][time]"] = time_to_ampm(book_slot.max)
49
+ spinner.success("Done!")
50
+ booking_form.submit
51
+ end
52
+
53
+ # Select the form otpion with right room id
54
+ def select_room_option(booking_form, page)
55
+ room_id = get_room_page_id(page)
56
+ select_options = booking_form.field_with(name: "field_space_project_room[und]").options
57
+ select_options.each do |o|
58
+ if o.text.include?(room_id)
59
+ o.click
60
+ end
61
+ end
62
+ end
63
+
64
+ def get_room_page_id(page)
65
+ return page.form.action.split("/").last
66
+ end
67
+
68
+ def batch_request(room_list, book_date)
69
+ cookie = @agent.cookies.join("; ")
70
+ spinner = get_spinner("Checking room availabilities")
71
+
72
+ hydra = Typhoeus::Hydra.new
73
+ requests = room_list.map do |room_id|
74
+ room_url = get_room_cal_url(book_date, room_id)
75
+ request = Typhoeus::Request.new(room_url, headers: { Cookie: cookie })
76
+ hydra.queue(request)
77
+ request
78
+ end
79
+ hydra.run # Start requests
80
+ spinner.success("Done!")
81
+ return typhoeus_to_mechanize(requests)
82
+ end
83
+
84
+ def get_room_cal_url(book_date, room_id)
85
+ if CS_ROOMS.include?(room_id)
86
+ room_url = CS_ROOM_BASE_URL + "ICCS" + room_id
87
+ if is_next_month(book_date)
88
+ today = Date.today
89
+ month_query = "?month=" + today.year + "-" + (today.month + 1)
90
+ return room_url + month_query
91
+ else
92
+ return room_url
93
+ end
94
+ end
95
+ end
96
+
97
+ # Turn typhoneus obj to mechanize page obj
98
+ def typhoeus_to_mechanize(requests)
99
+ pages = requests.map do |request|
100
+ html = request.response.body
101
+ Mechanize::Page.new(nil, {"ontent-type" => "text/html"}, html, nil, @agent)
102
+ end
103
+ return pages
104
+ end
105
+
106
+ def is_next_month(date)
107
+ return date.month != Date.today.month
108
+ end
109
+
110
+ def is_available(room_page, book_date, book_slot)
111
+ slot_booked = get_slot_booked(room_page, book_date)
112
+ return !is_slot_booked(slot_booked, book_slot)
113
+ end
114
+
115
+ def is_slot_booked(slot_booked, book_slot)
116
+ booked = false
117
+ slot_booked.each do |s|
118
+ if s.include?(book_slot)
119
+ booked = true
120
+ end
121
+ end
122
+ return booked
123
+ end
124
+
125
+ def get_slot_booked(room_page, book_date)
126
+ day_div_id = get_date_div_id(book_date)
127
+ day_div = room_page.search("td##{day_div_id}").first
128
+ slot_num = day_div.search("div.item").size
129
+ start_times = day_div.search("span.date-display-start")
130
+ end_times = day_div.search("span.date-display-end")
131
+
132
+ slot_booked = []
133
+ (0..slot_num-1).each do |i|
134
+ slot_start = ampm_to_time(start_times[i].text) # 01:00
135
+ slot_end = ampm_to_time(end_times[i].text) # 05:00
136
+ slot_booked.push((slot_start..slot_end))
137
+ end
138
+ return slot_booked
139
+ end
140
+
141
+ def get_date_div_id(date)
142
+ date_div_base = "calendar_space_entity_project_room-"
143
+ date_div_base += "#{date.year}-#{date.strftime("%m")}-#{date.strftime("%d")}-0"
144
+ return date_div_base
145
+ end
146
+
147
+ def get_spinner(text)
148
+ spinner = ::TTY::Spinner.new("[:spinner] #{text} ... ", format: :dots)
149
+ spinner.auto_spin # Automatic animation with default interval
150
+ return spinner
151
+ end
152
+
153
+ # =========================
154
+ # Time operations
155
+
156
+ # Expect HH:MM-HH:MM
157
+ def get_time_slot(time_str)
158
+ times = time_str.split("-")
159
+ return (times[0]..times[1])
160
+ end
161
+
162
+ # 05:00pm -> 17:99
163
+ def ampm_to_time(str)
164
+ time = Time.parse(str)
165
+ return time.strftime("%H:%M")
166
+ end
167
+
168
+ # 17:00 -> 05:00pm
169
+ def time_to_ampm(str)
170
+ time = Time.parse(str)
171
+ return time.strftime("%I:%M%P")
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "base_scraper"
2
+ require_relative "cs"
@@ -1,3 +1,3 @@
1
1
  module Ubcbooker
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/lib/ubcbooker.rb CHANGED
@@ -1,50 +1,24 @@
1
- require "ubcbooker/version"
2
- require "io/console" # For handling password
3
- require "mechanize"
4
1
  require "pry"
2
+ require "business_time"
3
+ require "tty-spinner"
4
+ require "mechanize"
5
+ require "typhoeus"
6
+ require "obscenity"
7
+ require "io/console" # For handling password
8
+ require "optparse"
9
+ require "yaml"
5
10
 
6
11
  module Ubcbooker
7
- # TODO:
8
- # - Gemify
9
- # - TDD
10
- # - Catch when login fails
11
- # - Feature to save password
12
- # - Use Thor for CLI
13
- # - Divide up to files
14
-
15
- class User
16
- attr_accessor :username, :password
17
- end
18
-
19
- print "CWL username: "
20
- username = gets.chomp
21
- print "CWL password: "
22
- # Hide the password input
23
- password = STDIN.noecho(&:gets).chomp
24
- puts
25
-
26
- a = Mechanize.new do |agent|
27
- agent.user_agent_alias = "Linux Mozilla"
28
- end
29
-
30
- # What happens if I'm already logged in here?
31
- # Maybe check by seeing if the CWL link pops up after GET
32
-
33
- # Do login for UBC CWL system
34
- def self.login_ubc_cwl(login_page)
35
- username_feild = login_page.form.field_with(name => "j_username")
36
- username_feild.value = username
37
- password_field = login_page.form.field_with(name => "j_password")
38
- password_field.value = password
39
- redirect_page = login_page.form.submit
40
- return reditect_page.form.submit
41
- end
42
-
43
- puts "Requesting the page ..."
44
- a.get("https://my.cs.ubc.ca/docs/project-rooms-and-dlc") do |page|
45
- login_page = page.link_with(text => "CWL Login Redirect").click
46
- login_ubc_cwl(login_page)
47
- binding.pry
48
- end
49
-
12
+ BOOKING_URL = {
13
+ cs: "https://my.cs.ubc.ca/docs/project-rooms-and-dlc",
14
+ # sauder_ugrad: "https://booking.sauder.ubc.ca/ugr/cwl-login",
15
+ }
50
16
  end
17
+
18
+ require "ubcbooker/version"
19
+ require "ubcbooker/error"
20
+ require "ubcbooker/color"
21
+ require "ubcbooker/config"
22
+ require "ubcbooker/scrapers/scraper"
23
+ require "ubcbooker/cli_validator"
24
+ require "ubcbooker/cli"
data/ubcbooker.gemspec CHANGED
@@ -16,15 +16,18 @@ Gem::Specification.new do |spec|
16
16
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
17
  f.match(%r{^(test|spec|features)/})
18
18
  end
19
- spec.bindir = "exe"
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.executables = ["ubcbooker"]
21
20
  spec.require_paths = ["lib"]
22
21
 
23
22
  spec.add_dependency "mechanize", "~> 2.7"
23
+ spec.add_dependency "business_time", "~> 0.9.3"
24
+ spec.add_dependency "typhoeus", "~> 1.1"
25
+ spec.add_dependency "obscenity", "~> 1.0.2"
26
+ spec.add_dependency "tty-spinner", "~> 0.7.0"
24
27
 
25
- spec.add_development_dependency "bundler", "~> 1.16"
26
- spec.add_development_dependency "pry", "0.11.3"
27
28
  spec.add_development_dependency "rake", "~> 10.0"
28
29
  spec.add_development_dependency "rspec", "~> 3.0"
30
+ spec.add_development_dependency "pry", "~> 0.11.3"
31
+ spec.add_development_dependency "pry-byebug", "~> 3.4"
29
32
  spec.add_development_dependency "rubocop-github", "~> 0.8.1"
30
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ubcbooker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Atsushi Yamamoto
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-25 00:00:00.000000000 Z
11
+ date: 2018-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mechanize
@@ -25,33 +25,61 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.7'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: business_time
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.16'
34
- type: :development
33
+ version: 0.9.3
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.16'
40
+ version: 0.9.3
41
41
  - !ruby/object:Gem::Dependency
42
- name: pry
42
+ name: typhoeus
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '='
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.11.3
48
- type: :development
47
+ version: '1.1'
48
+ type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '='
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.11.3
54
+ version: '1.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: obscenity
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: tty-spinner
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.7.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.7.0
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rake
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +108,34 @@ dependencies:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
110
  version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.11.3
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.11.3
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry-byebug
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.4'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.4'
83
139
  - !ruby/object:Gem::Dependency
84
140
  name: rubocop-github
85
141
  requirement: !ruby/object:Gem::Requirement
@@ -97,7 +153,8 @@ dependencies:
97
153
  description: CLI tool to book project rooms in UBC
98
154
  email:
99
155
  - hi@atsushi.me
100
- executables: []
156
+ executables:
157
+ - ubcbooker
101
158
  extensions: []
102
159
  extra_rdoc_files: []
103
160
  files:
@@ -107,13 +164,23 @@ files:
107
164
  - ".travis.yml"
108
165
  - CODE_OF_CONDUCT.md
109
166
  - Gemfile
110
- - Gemfile.lock
111
167
  - LICENSE
112
168
  - README.md
113
169
  - Rakefile
114
170
  - bin/console
115
171
  - bin/setup
172
+ - bin/ubcbooker
116
173
  - lib/ubcbooker.rb
174
+ - lib/ubcbooker/account.rb
175
+ - lib/ubcbooker/cli.rb
176
+ - lib/ubcbooker/cli_validator.rb
177
+ - lib/ubcbooker/color.rb
178
+ - lib/ubcbooker/config.rb
179
+ - lib/ubcbooker/error.rb
180
+ - lib/ubcbooker/sample.yml
181
+ - lib/ubcbooker/scrapers/base_scraper.rb
182
+ - lib/ubcbooker/scrapers/cs.rb
183
+ - lib/ubcbooker/scrapers/scraper.rb
117
184
  - lib/ubcbooker/version.rb
118
185
  - ubcbooker.gemspec
119
186
  homepage: https://github.com/jumbosushi/ubcbooker
data/Gemfile.lock DELETED
@@ -1,86 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- ubcbooker (0.1.0)
5
- mechanize (~> 2.7)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- ast (2.3.0)
11
- coderay (1.1.2)
12
- diff-lcs (1.3)
13
- domain_name (0.5.20170404)
14
- unf (>= 0.0.5, < 1.0.0)
15
- http-cookie (1.0.3)
16
- domain_name (~> 0.5)
17
- mechanize (2.7.5)
18
- domain_name (~> 0.5, >= 0.5.1)
19
- http-cookie (~> 1.0)
20
- mime-types (>= 1.17.2)
21
- net-http-digest_auth (~> 1.1, >= 1.1.1)
22
- net-http-persistent (~> 2.5, >= 2.5.2)
23
- nokogiri (~> 1.6)
24
- ntlm-http (~> 0.1, >= 0.1.1)
25
- webrobots (>= 0.0.9, < 0.2)
26
- method_source (0.9.0)
27
- mime-types (3.1)
28
- mime-types-data (~> 3.2015)
29
- mime-types-data (3.2016.0521)
30
- mini_portile2 (2.3.0)
31
- net-http-digest_auth (1.4.1)
32
- net-http-persistent (2.9.4)
33
- nokogiri (1.8.1)
34
- mini_portile2 (~> 2.3.0)
35
- ntlm-http (0.1.1)
36
- parallel (1.12.1)
37
- parser (2.4.0.2)
38
- ast (~> 2.3)
39
- powerpack (0.1.1)
40
- pry (0.11.3)
41
- coderay (~> 1.1.0)
42
- method_source (~> 0.9.0)
43
- rainbow (3.0.0)
44
- rake (10.5.0)
45
- rspec (3.6.0)
46
- rspec-core (~> 3.6.0)
47
- rspec-expectations (~> 3.6.0)
48
- rspec-mocks (~> 3.6.0)
49
- rspec-core (3.6.0)
50
- rspec-support (~> 3.6.0)
51
- rspec-expectations (3.6.0)
52
- diff-lcs (>= 1.2.0, < 2.0)
53
- rspec-support (~> 3.6.0)
54
- rspec-mocks (3.6.0)
55
- diff-lcs (>= 1.2.0, < 2.0)
56
- rspec-support (~> 3.6.0)
57
- rspec-support (3.6.0)
58
- rubocop (0.52.1)
59
- parallel (~> 1.10)
60
- parser (>= 2.4.0.2, < 3.0)
61
- powerpack (~> 0.1)
62
- rainbow (>= 2.2.2, < 4.0)
63
- ruby-progressbar (~> 1.7)
64
- unicode-display_width (~> 1.0, >= 1.0.1)
65
- rubocop-github (0.8.1)
66
- rubocop (~> 0.51)
67
- ruby-progressbar (1.9.0)
68
- unf (0.1.4)
69
- unf_ext
70
- unf_ext (0.0.7.4)
71
- unicode-display_width (1.3.0)
72
- webrobots (0.1.2)
73
-
74
- PLATFORMS
75
- ruby
76
-
77
- DEPENDENCIES
78
- bundler (~> 1.16)
79
- pry (= 0.11.3)
80
- rake (~> 10.0)
81
- rspec (~> 3.0)
82
- rubocop-github (~> 0.8.1)
83
- ubcbooker!
84
-
85
- BUNDLED WITH
86
- 1.16.1