TeaLI 0.1.1
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 +7 -0
- data/bin/TeaLI +7 -0
- data/config/env.rb +7 -0
- data/lib/category.rb +23 -0
- data/lib/scraper.rb +79 -0
- data/lib/tea.rb +45 -0
- data/lib/teaLI.rb +205 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '058401081b5bed7164b1f22db49aa7533c552a73ad2ec6dc298bbcd173098997'
|
4
|
+
data.tar.gz: 6e755df9c2baa820b11627204a75da8099fab163e1b9f433c20c1867b8f49f12
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8d7ea8766dce8e50f0e5d840bd5f06560f1026c02f28209ac11849650b5f9007ac66280dfd10a4ddcc1857787df258cf748f8efd4619b9c268c10ced2ed5bfe1
|
7
|
+
data.tar.gz: 63dccf2c4eb44d93011aff6af0c62d53f2191f935ba496b44fe59343ef1fbfb04512d3dd2e955f115cbbc3af42938380e2f871bb6f319f3b5dc8969c2298d50b
|
data/bin/TeaLI
ADDED
data/config/env.rb
ADDED
data/lib/category.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "../config/env.rb"
|
2
|
+
|
3
|
+
class Category
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
attr_accessor :url, :teas
|
7
|
+
@@all = []
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
@teas = []
|
12
|
+
@@all << self
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all
|
16
|
+
@@all
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.check_for_category(category_name)
|
20
|
+
self.all.detect { |cat| cat.name == category_name }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/scraper.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative "../config/env.rb"
|
2
|
+
|
3
|
+
class Scraper
|
4
|
+
|
5
|
+
def get_page(url)
|
6
|
+
page = Nokogiri::HTML(open(url))
|
7
|
+
page
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_categories
|
11
|
+
page = get_page("https://www.adagio.com/list/best_sellers.html")
|
12
|
+
page.css("div#accountNav.marginLeft.categoryLeft div.hide_768").each do |category|
|
13
|
+
if category.values != ["breakVerySmall hide_768"] && category.css("a").text != "Advanced Search" && Category.all.length < 10
|
14
|
+
category_name = category.css("a").text
|
15
|
+
category_url = category.css("a").attribute("href").value
|
16
|
+
new_category = Category.new(category_name)
|
17
|
+
new_category.url = "https://www.adagio.com#{category_url}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_teas
|
23
|
+
get_categories
|
24
|
+
Category.all.each do |category|
|
25
|
+
page = get_page(category.url)
|
26
|
+
page.css("div.productIndexParent").each do |product|
|
27
|
+
if category.teas.length < 10
|
28
|
+
new_tea = Tea.new(product.css("h6").text)
|
29
|
+
new_tea.url = "https://www.adagio.com" + product.css("a").attribute("href").value
|
30
|
+
new_tea.category = category
|
31
|
+
category.teas << new_tea
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_tea_info(tea)
|
38
|
+
page = get_page(tea.url)
|
39
|
+
tea.description = page.css("div.description div").text.split(' | ')[0] + "."
|
40
|
+
tea.ingredients = page.css("h5.titlepadding.contentTitleItalics").text
|
41
|
+
page.css("div#pricesDiv.pricesList div.itemBlock").each do |price|
|
42
|
+
item_size = price.css("div.size").text
|
43
|
+
item_size_array = item_size.split(/\W/)
|
44
|
+
item_size_array.map do |int|
|
45
|
+
if int == ""
|
46
|
+
item_size_array.delete(int)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
trimmed_item_size = item_size_array.join(" ")
|
50
|
+
weird_price = price.css("div.price").text
|
51
|
+
item_price = weird_price.split(/\W/).last
|
52
|
+
# this checks for out of stock or unavailable
|
53
|
+
if price.css("div.notifyMe").text == "NOTIFY ME" || item_price == ""
|
54
|
+
tea.pricing[trimmed_item_size] = "out of stock"
|
55
|
+
elsif item_price.to_i >= 1 && item_price != ""
|
56
|
+
tea.pricing[trimmed_item_size] = "$" + item_price
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_this_teas_info(tea_name)
|
62
|
+
# when we only need info about a single tea
|
63
|
+
# utilize check_for_tea_info and find_by_name here
|
64
|
+
tea = Tea.find_by_name(tea_name)
|
65
|
+
if !tea.check_for_tea_info
|
66
|
+
get_tea_info(tea)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_all_tea_info
|
71
|
+
Tea.all.each do |tea|
|
72
|
+
if !tea.check_for_tea_info
|
73
|
+
get_tea_info(tea)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
data/lib/tea.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative "../config/env.rb"
|
2
|
+
|
3
|
+
class Tea
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
attr_accessor :url, :ingredients, :description, :pricing, :category
|
7
|
+
@@all = []
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
@@all << self
|
12
|
+
self.description = ""
|
13
|
+
self.ingredients = ""
|
14
|
+
self.pricing = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.all
|
18
|
+
@@all
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.find_by_name(name)
|
22
|
+
@@all.detect {|tea| tea.name == name}
|
23
|
+
end
|
24
|
+
|
25
|
+
# checks whether or not we've already scraped the info about this particular tea object
|
26
|
+
# returns true if tea info has already been acquired
|
27
|
+
def check_for_tea_info
|
28
|
+
self.ingredients.length > 0 && self.description.length > 0 && self.pricing.length > 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.all_teas_in(tea_category)
|
32
|
+
@@all.select {|tea| tea.category.name == tea_category}
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.find_by_ingredient(ingredient)
|
36
|
+
@@all.select { |tea| tea.ingredients.include?(ingredient) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.get_random_tea
|
40
|
+
rand_tea = Tea.all.sample
|
41
|
+
rand_tea.name
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
data/lib/teaLI.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require_relative '../config/env.rb'
|
2
|
+
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
class TeaLI
|
6
|
+
attr_accessor :scraper
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.scrape_for_tea
|
10
|
+
end
|
11
|
+
|
12
|
+
def scrape_for_tea
|
13
|
+
puts "Loading your TeaLI session. Please wait..."
|
14
|
+
@scraper = Scraper.new
|
15
|
+
@scraper.get_teas
|
16
|
+
end
|
17
|
+
|
18
|
+
def titleize(word)
|
19
|
+
title = word.split.map(&:capitalize).join(' ')
|
20
|
+
title
|
21
|
+
end
|
22
|
+
|
23
|
+
def display_categories
|
24
|
+
puts "Here are all of our available varieties: "
|
25
|
+
puts " "
|
26
|
+
Category.all.each_with_index do |category, index|
|
27
|
+
puts " #{index+1}. #{category.name}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def display_tea_info(tea_name)
|
32
|
+
tea = Tea.find_by_name(tea_name)
|
33
|
+
puts " "
|
34
|
+
puts "Here are the details for our #{titleize(tea.name)} Tea"
|
35
|
+
puts "- - - - - - - - - - - - - - - "
|
36
|
+
puts " Description: "
|
37
|
+
puts " #{tea.description}"
|
38
|
+
puts " "
|
39
|
+
puts " Ingredients: "
|
40
|
+
if tea.ingredients.length > 0
|
41
|
+
puts " This tea is #{tea.ingredients}"
|
42
|
+
else
|
43
|
+
puts " This is a single origin (non-blended) tea."
|
44
|
+
end
|
45
|
+
puts " "
|
46
|
+
puts " Purchasing Options: "
|
47
|
+
tea.pricing.each do |size, price|
|
48
|
+
puts " #{size.capitalize}: #{price}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def display_category_teas(category)
|
53
|
+
category_name = titleize(category)
|
54
|
+
category_teas = Tea.all_teas_in(category_name)
|
55
|
+
if category_teas.length > 0
|
56
|
+
puts "Here are all of our #{category}: "
|
57
|
+
category_teas.each_with_index do |tea, index|
|
58
|
+
puts " #{index+1}. #{titleize(tea.name)}"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
puts "Sorry that doesn't appear to be a valid category..."
|
62
|
+
display_categories
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def info_for_one_tea(tea)
|
67
|
+
@scraper.get_this_teas_info(tea)
|
68
|
+
end
|
69
|
+
|
70
|
+
def menu_options
|
71
|
+
puts "1. See all available tea types."
|
72
|
+
puts " "
|
73
|
+
puts "2. Search by a specific ingredient."
|
74
|
+
puts " "
|
75
|
+
puts "3. Surprise me with a random tea!"
|
76
|
+
puts " "
|
77
|
+
puts "Type 1, 2, or 3 and press ENTER."
|
78
|
+
puts " "
|
79
|
+
puts "If you are done using the TeaLI, just type EXIT and press ENTER."
|
80
|
+
puts "- - - - - - - - - - - - - - - "
|
81
|
+
end
|
82
|
+
|
83
|
+
def surprise_tea
|
84
|
+
surprise = Tea.get_random_tea
|
85
|
+
@scraper.get_this_teas_info(surprise)
|
86
|
+
puts "SURPRISE! You got #{titleize(surprise)} Tea!"
|
87
|
+
self.display_tea_info(surprise)
|
88
|
+
end
|
89
|
+
|
90
|
+
def run_categories
|
91
|
+
input = nil
|
92
|
+
|
93
|
+
display_categories
|
94
|
+
puts "Which type of teas would you like to see?"
|
95
|
+
puts "(Ex: Type 'Black Teas' and press ENTER) "
|
96
|
+
puts " "
|
97
|
+
puts "(to go back type 'back' and press ENTER)"
|
98
|
+
puts " "
|
99
|
+
|
100
|
+
input = gets.strip.downcase
|
101
|
+
|
102
|
+
if Category.check_for_category(titleize(input))
|
103
|
+
display_category_teas(titleize(input))
|
104
|
+
puts " "
|
105
|
+
puts "Which tea would you like to know more about? (just type its name and press ENTER)"
|
106
|
+
puts "(to go back type 'back' and press ENTER)"
|
107
|
+
puts " "
|
108
|
+
|
109
|
+
input = gets.strip.downcase
|
110
|
+
|
111
|
+
if input == 'back'
|
112
|
+
run_categories
|
113
|
+
else
|
114
|
+
tea = Tea.find_by_name(input)
|
115
|
+
if tea != nil
|
116
|
+
info_for_one_tea(tea.name)
|
117
|
+
display_tea_info(tea.name)
|
118
|
+
else
|
119
|
+
puts " "
|
120
|
+
puts "Bummer! It looks like that isn't a valid tea name."
|
121
|
+
puts " "
|
122
|
+
end
|
123
|
+
end
|
124
|
+
elsif input == 'back'
|
125
|
+
return
|
126
|
+
elsif !Category.all.include?(titleize(input)) && input != 'back'
|
127
|
+
puts "Sorry, please choose a valid tea type!"
|
128
|
+
puts " "
|
129
|
+
run_categories
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def run_all_info_search
|
134
|
+
puts " "
|
135
|
+
puts "Great! Please wait while we gather some information. This will take about 60 seconds..."
|
136
|
+
puts " "
|
137
|
+
@scraper.get_all_tea_info
|
138
|
+
puts " "
|
139
|
+
puts "Thanks for waiting!"
|
140
|
+
end
|
141
|
+
|
142
|
+
def run_ingredient_search
|
143
|
+
puts " "
|
144
|
+
puts "What ingredient are you looking for?"
|
145
|
+
puts "(to go back, please type 'back' and press ENTER)"
|
146
|
+
user_input = nil
|
147
|
+
user_input = gets.strip.downcase
|
148
|
+
if user_input == 'back'
|
149
|
+
return
|
150
|
+
else
|
151
|
+
teas = Tea.find_by_ingredient(user_input)
|
152
|
+
if teas.length > 0
|
153
|
+
puts " "
|
154
|
+
puts "Here are all the teas we have that contain #{user_input}"
|
155
|
+
puts " "
|
156
|
+
teas.each_with_index do |tea, index|
|
157
|
+
puts "#{index+1}. #{titleize(tea.name)}"
|
158
|
+
end
|
159
|
+
puts " "
|
160
|
+
puts "Which tea would you like to know more about? (just type its name and press ENTER)"
|
161
|
+
puts "or type 'back' to search for a different ingredient..."
|
162
|
+
user_input = gets.strip.downcase
|
163
|
+
if user_input == 'back'
|
164
|
+
run_ingredient_search
|
165
|
+
else
|
166
|
+
display_tea_info(user_input)
|
167
|
+
end
|
168
|
+
else
|
169
|
+
puts "Sorry, it looks like we don't carry any teas with that ingredient"
|
170
|
+
run_ingredient_search
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def call
|
176
|
+
puts " "
|
177
|
+
puts "Welcome to the TeaLI! A CLI for finding teas you never knew you needed..."
|
178
|
+
user_input = nil
|
179
|
+
until user_input == 'exit'
|
180
|
+
puts "- - - - - - - - - - - - - - - "
|
181
|
+
puts "What would you like to do?"
|
182
|
+
puts " "
|
183
|
+
menu_options
|
184
|
+
user_input = gets.strip.downcase
|
185
|
+
if user_input.to_i == 1
|
186
|
+
run_categories
|
187
|
+
elsif user_input.to_i == 2
|
188
|
+
run_all_info_search
|
189
|
+
run_ingredient_search
|
190
|
+
elsif user_input.to_i == 3
|
191
|
+
surprise_tea
|
192
|
+
end
|
193
|
+
end
|
194
|
+
goodbye
|
195
|
+
end
|
196
|
+
|
197
|
+
def goodbye
|
198
|
+
puts " "
|
199
|
+
puts "Thanks for using TeaLI! Happy Sipping!"
|
200
|
+
puts " "
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: TeaLI
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emily Harber
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-11-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple CLI application to scrape and search for teas using the Adagio
|
14
|
+
Teas website
|
15
|
+
email: theoriginalpixi@gmail.com
|
16
|
+
executables:
|
17
|
+
- TeaLI
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/TeaLI
|
22
|
+
- config/env.rb
|
23
|
+
- lib/category.rb
|
24
|
+
- lib/scraper.rb
|
25
|
+
- lib/tea.rb
|
26
|
+
- lib/teaLI.rb
|
27
|
+
homepage: https://rubygems.org/profiles/TheCodePixi
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.0.6
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: A simple scrape/search Tea CLI app
|
50
|
+
test_files: []
|