apod-cli 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/apod-cli.gemspec +22 -0
- data/bin/apod-cli +5 -0
- data/lib/apod_cli/cli.rb +219 -0
- data/lib/apod_cli/printer.rb +46 -0
- data/lib/apod_cli/scraper.rb +80 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 223a6404f484b7850e8e04841585001c6c65a378
|
4
|
+
data.tar.gz: 6b1134be8d60f878e241b3dc0631d15ef441aff6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e76bb2dbf87217e6f2e698432cd51aa1ae1250e00d5134b151c16ae66242efc9e7a5b6f0fe77947ecf058a3058f956d89a099580c946e7ef6a73934fc770f6ba
|
7
|
+
data.tar.gz: 5be2030ada450f52d9828c634503a1074d53ef53c294320f992035fe58d6fe9110497c002935c2a0d051e06097c72f30a0787c5f73ac7f4eea5d083acb539c87
|
data/apod-cli.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.authors = ["David Ball"]
|
7
|
+
spec.email = ["davidnoahball@gmail.com"]
|
8
|
+
spec.summary = "Displays APOD information through CLI"
|
9
|
+
spec.description = "Uses web scraping to pull Astronomy Picture of the Day information from http://apod.nasa.gov and provides a basic CLI interface for that data"
|
10
|
+
spec.homepage = "http://davidnoahball.com"
|
11
|
+
|
12
|
+
spec.files = ["apod-cli.gemspec", "lib/apod_cli/cli.rb", "lib/apod_cli/printer.rb", "lib/apod_cli/scraper.rb"]
|
13
|
+
spec.executables = ["apod-cli"]
|
14
|
+
spec.name = "apod-cli"
|
15
|
+
spec.require_paths = ["lib", "lib/apod_cli"]
|
16
|
+
spec.version = "0.0.0"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
20
|
+
spec.add_development_dependency "nokogiri", "~> 1.6"
|
21
|
+
spec.add_development_dependency "colorize", "~> 0.7"
|
22
|
+
end
|
data/bin/apod-cli
ADDED
data/lib/apod_cli/cli.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
require "nokogiri"
|
3
|
+
require "colorize"
|
4
|
+
|
5
|
+
require_relative "printer"
|
6
|
+
require_relative "scraper"
|
7
|
+
|
8
|
+
class CLI
|
9
|
+
def call #All CLI logic should come from this command
|
10
|
+
puts "\nHello.\n"
|
11
|
+
@scraper = Scraper.new
|
12
|
+
@printer = Printer.new
|
13
|
+
@data = @scraper.data
|
14
|
+
start
|
15
|
+
end
|
16
|
+
|
17
|
+
def start
|
18
|
+
puts "Which type of APOD lookup would you like to perform?"
|
19
|
+
puts "[1]".colorize(:red) + " Search by date"
|
20
|
+
puts "[2]".colorize(:red) + " Search by name"
|
21
|
+
puts "[3]".colorize(:red) + " Search by date and name"
|
22
|
+
puts "[4]".colorize(:red) + " Sample data"
|
23
|
+
search_type = valid_input(["1", "2", "3", "4"]).to_i
|
24
|
+
case search_type
|
25
|
+
when 1
|
26
|
+
date_search
|
27
|
+
when 2
|
28
|
+
name_search
|
29
|
+
when 3
|
30
|
+
date_search(true)
|
31
|
+
when 4
|
32
|
+
sample
|
33
|
+
end
|
34
|
+
puts "Would you like to perform another lookup?"
|
35
|
+
puts "[y/n]".colorize(:red)
|
36
|
+
if valid_input(["y", "n"]) == "y"
|
37
|
+
puts ""
|
38
|
+
start
|
39
|
+
else
|
40
|
+
puts "Goodbye."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sample
|
45
|
+
puts "\nPlease enter the number (" + "[1]".colorize(:red) + " - " + "[#{@data.length}]".colorize(:red) + ") of links you would\nlike to sample. Or, type " + "'all'".colorize(:red) + " for information on all results."
|
46
|
+
wanted = [(1..@data.length).to_a.map{|e| e.to_s}, "all"].flatten
|
47
|
+
num = valid_input(wanted)
|
48
|
+
sample = @data.sample(num.to_i)
|
49
|
+
puts ""
|
50
|
+
print_links(sample)
|
51
|
+
puts ""
|
52
|
+
more_info(sample)
|
53
|
+
end
|
54
|
+
|
55
|
+
def date_search(multisearch=false)
|
56
|
+
puts "\nWhich type of date search would you like to perform?"
|
57
|
+
puts "[1]".colorize(:red) + " exact date"
|
58
|
+
puts "[2]".colorize(:red) + " date in each year"
|
59
|
+
puts "[3]".colorize(:red) + " date range"
|
60
|
+
date_search_type = valid_input(["1", "2", "3"]).to_i
|
61
|
+
results = []
|
62
|
+
case date_search_type
|
63
|
+
when 1
|
64
|
+
puts "\nPlease enter a " + "date".colorize(:red) + " in the format " + "yyyy-mm-dd".colorize(:red) + ". The oldest is 1995-06-16."
|
65
|
+
input = gets.chomp.strip
|
66
|
+
@data.each do |hash|
|
67
|
+
if hash[:date] == input
|
68
|
+
results << hash
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
if results == []
|
73
|
+
puts "\nDate not found! Did you use the correct format?\n\n"
|
74
|
+
return
|
75
|
+
end
|
76
|
+
puts ""
|
77
|
+
print_links(results)
|
78
|
+
print_pages(results)
|
79
|
+
return
|
80
|
+
when 2
|
81
|
+
puts "\nPlease enter a " + "day of the year".colorize(:red) + " in the format " + "mm-dd".colorize(:red) + "."
|
82
|
+
input = gets.chomp.strip
|
83
|
+
@data.each do |hash|
|
84
|
+
if hash[:date].slice(5..-1) == input
|
85
|
+
results << hash
|
86
|
+
end
|
87
|
+
end
|
88
|
+
if results == []
|
89
|
+
puts "\nDay not found! Did you use the correct format?\n\n"
|
90
|
+
return
|
91
|
+
end
|
92
|
+
when 3
|
93
|
+
inputs = []
|
94
|
+
puts "\nPlease enter a " + "start date".colorize(:red) + " in the format " + "yyyy-mm-dd".colorize(:red) + ". The oldest is 1995-06-16."
|
95
|
+
start = gets.chomp.strip #This part of the code could be much less verbose and more compact/concise but it works and was already written and I want to try to finish today.
|
96
|
+
@data.each do |hash|
|
97
|
+
if hash[:date] == start
|
98
|
+
inputs << start
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
if inputs.length != 1
|
103
|
+
puts "\nDate not found! Did you use the correct format?\n\n"
|
104
|
+
return
|
105
|
+
end
|
106
|
+
puts "\nPlease enter an " + "end date".colorize(:red) + ". The most recent in the database is #{@data[0][:date]}."
|
107
|
+
finish = gets.chomp.strip
|
108
|
+
@data.each do |hash|
|
109
|
+
if hash[:date] == finish
|
110
|
+
inputs << finish
|
111
|
+
break
|
112
|
+
end
|
113
|
+
end
|
114
|
+
if inputs.length != 2
|
115
|
+
puts "\nDate not found! Did you use the correct format?\n\n"
|
116
|
+
return
|
117
|
+
end
|
118
|
+
inputs.sort!
|
119
|
+
@data.each do |hash|
|
120
|
+
if hash[:date] >= inputs[0] && hash[:date] <= inputs[1]
|
121
|
+
results << hash
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
if multisearch then results = name_search(results) end
|
126
|
+
puts ""
|
127
|
+
print_links(results)
|
128
|
+
puts ""
|
129
|
+
more_info(results)
|
130
|
+
end
|
131
|
+
|
132
|
+
def name_search(searchspace=@data)
|
133
|
+
puts "\nPlease enter one or multiple " + "search terms".colorize(:red) + ", with each query comma\nseparated. Ex: 'lunar eclipse, blood moon'"
|
134
|
+
searchterms = gets.chomp.strip.downcase.split(",")
|
135
|
+
results = []
|
136
|
+
searchterms.each do |searchterm|
|
137
|
+
searchterm.strip!
|
138
|
+
results << searchspace.select{|hash| hash[:name].downcase.include?(searchterm)}
|
139
|
+
end
|
140
|
+
unique = results.flatten.uniq.sort{|h1, h2| h1[:date] > h2[:date] ? 1 : -1}
|
141
|
+
if searchspace != @data then return unique end
|
142
|
+
if unique == []
|
143
|
+
puts "\nNo results found!\n"
|
144
|
+
return
|
145
|
+
end
|
146
|
+
puts ""
|
147
|
+
print_links(unique)
|
148
|
+
puts ""
|
149
|
+
more_info(unique)
|
150
|
+
end
|
151
|
+
|
152
|
+
def more_info(arr)
|
153
|
+
if arr.length > 1
|
154
|
+
puts "Would you like more information on one or more of these matches?"
|
155
|
+
else
|
156
|
+
puts "Would you like more information on this match?"
|
157
|
+
end
|
158
|
+
puts "[y/n]".colorize(:red)
|
159
|
+
if valid_input(["y", "n"]) == "y"
|
160
|
+
if arr.length > 1
|
161
|
+
puts "Please enter the " + "results number".colorize(:red) + " of any link(s) you would like more\ninformation on, comma separated. Or, type " + "'all'".colorize(:red) + " for more information on all results."
|
162
|
+
wanted = [(1..arr.length).to_a.map{|e| e.to_s}, "all"].flatten
|
163
|
+
validated = false #Should maybe turn this into a valid_split method or something, but it's not necessary right now.
|
164
|
+
while !validated
|
165
|
+
searchterms = gets.chomp.strip.downcase.split(",")
|
166
|
+
failed = false
|
167
|
+
searchterms.each do |searchterm|
|
168
|
+
searchterm.strip!
|
169
|
+
if !wanted.include?(searchterm)
|
170
|
+
puts "That's not a valid input! Please try again."
|
171
|
+
failed = true
|
172
|
+
break
|
173
|
+
end
|
174
|
+
end
|
175
|
+
if !failed then validated = true end
|
176
|
+
end
|
177
|
+
else
|
178
|
+
searchterms = ["all"]
|
179
|
+
end
|
180
|
+
if searchterms.include?("all")
|
181
|
+
print_pages(arr)
|
182
|
+
else
|
183
|
+
selected = []
|
184
|
+
searchterms.each do |searchterm|
|
185
|
+
selected << arr[searchterm.to_i - 1]
|
186
|
+
end
|
187
|
+
print_pages(selected)
|
188
|
+
end
|
189
|
+
else
|
190
|
+
puts ""
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def print_links(arr)
|
195
|
+
arr.each_with_index do |hash, idx|
|
196
|
+
starter = "[#{idx + 1}]"
|
197
|
+
((arr.length.to_s.length + 3) - starter.length).times do |n|
|
198
|
+
starter += " "
|
199
|
+
end
|
200
|
+
@printer.print_link(hash, starter)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def print_pages(arr)
|
205
|
+
arr.each do |hash|
|
206
|
+
@printer.print_page(@scraper.pic_data(hash[:link]))
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def valid_input(wanted)
|
211
|
+
input = gets.chomp.strip.downcase
|
212
|
+
if wanted.include?(input)
|
213
|
+
return input
|
214
|
+
else
|
215
|
+
puts "That's not a valid input! Please try again."
|
216
|
+
valid_input(wanted)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
require "nokogiri"
|
3
|
+
require "colorize"
|
4
|
+
|
5
|
+
require_relative "cli"
|
6
|
+
require_relative "scraper"
|
7
|
+
|
8
|
+
class Printer
|
9
|
+
def print_link(link_hash, start="")
|
10
|
+
lines = start.colorize(:red) + "The astronomy picture of the day on " + link_hash[:date].colorize(:green) + " was " + link_hash[:name].colorize(:green) + ".\n"
|
11
|
+
start.length.times do |n|
|
12
|
+
lines += " "
|
13
|
+
end
|
14
|
+
lines += "The link is " + link_hash[:link].colorize(:green) + "."
|
15
|
+
puts lines
|
16
|
+
end
|
17
|
+
def print_page(page_hash)
|
18
|
+
words_arr = page_hash[:expl].split(" ")
|
19
|
+
line = ""
|
20
|
+
lines_arr = []
|
21
|
+
max_line_length = 0
|
22
|
+
words_arr.each_with_index do |word, idx|
|
23
|
+
line += word + " "
|
24
|
+
if line.length >= 100 || idx == words_arr.length - 1
|
25
|
+
lines_arr << line.strip
|
26
|
+
if line.strip.length > max_line_length then max_line_length = line.strip.length end
|
27
|
+
line = ""
|
28
|
+
end
|
29
|
+
end
|
30
|
+
puts ""
|
31
|
+
puts pad(max_line_length, page_hash[:name], "-").colorize(:green)
|
32
|
+
puts pad(max_line_length, page_hash[:link], "-").colorize(:green)
|
33
|
+
lines_arr.each do |line|
|
34
|
+
puts pad(max_line_length, line)
|
35
|
+
end
|
36
|
+
puts ""
|
37
|
+
end
|
38
|
+
def pad(max, str, char=" ")
|
39
|
+
pad = char
|
40
|
+
padding_amount = ((max - str.length) / 2).ceil
|
41
|
+
padding_amount.times do |n|
|
42
|
+
pad += char
|
43
|
+
end
|
44
|
+
centered = "#{pad}#{str}#{pad}"
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
require "nokogiri"
|
3
|
+
require "colorize"
|
4
|
+
|
5
|
+
require_relative "cli"
|
6
|
+
require_relative "printer"
|
7
|
+
|
8
|
+
class Scraper
|
9
|
+
def initialize
|
10
|
+
@@data = self.class.index_data
|
11
|
+
end
|
12
|
+
|
13
|
+
def data
|
14
|
+
@@data
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.get_page
|
18
|
+
Nokogiri::HTML(open("http://apod.nasa.gov/apod/archivepix.html"))
|
19
|
+
end
|
20
|
+
|
21
|
+
def pic_data(url)
|
22
|
+
explanation = Nokogiri::HTML(open(url)).css("body").text.match(/Explanation:[\s\S]+?(\n(\s*)){3}/).to_s.gsub(/\n/, " ").gsub(/\s{2,}/, " ").strip
|
23
|
+
name = self.data.select{|hash| url.include?(hash[:link])}[0][:name]
|
24
|
+
if Nokogiri::HTML(open(url)).css("p a img").to_a != []
|
25
|
+
link = "http://apod.nasa.gov/apod/#{Nokogiri::HTML(open(url)).css("p a img").attribute("src").to_s}"
|
26
|
+
else
|
27
|
+
link = self.data.select{|hash| url.include?(hash[:link])}[0][:link]
|
28
|
+
end
|
29
|
+
hash = {name: name, expl: explanation, link:link}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.index_data
|
33
|
+
array = []
|
34
|
+
months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
35
|
+
content = self.get_page.css("body b")
|
36
|
+
links = content.css("a")
|
37
|
+
date_titles = content.text.split("\n").reject!{|item| item == ""}
|
38
|
+
date_titles.pop
|
39
|
+
|
40
|
+
links_hash = {}
|
41
|
+
links.each do |link|
|
42
|
+
links_hash[link.text.strip] = link.attribute("href").value
|
43
|
+
end
|
44
|
+
|
45
|
+
toggle_2007 = false
|
46
|
+
date_titles.each_with_index do |dt, idx|
|
47
|
+
hash = {}
|
48
|
+
#There's one bloody link with a \n typo in its name that requires me to write this code.
|
49
|
+
if idx == date_titles.length - 1 || months.index(date_titles[idx + 1].match(/[a-zA-Z]{1,}/).to_s).nil?
|
50
|
+
if idx != date_titles.length - 1
|
51
|
+
toggle_2007 = true
|
52
|
+
next
|
53
|
+
end
|
54
|
+
end
|
55
|
+
if toggle_2007
|
56
|
+
toggle_2007 = false
|
57
|
+
next
|
58
|
+
end
|
59
|
+
|
60
|
+
month_str = ""
|
61
|
+
month_num = months.index(dt.match(/[a-zA-Z]{1,}/).to_s) + 1
|
62
|
+
if month_num.to_s.length == 1
|
63
|
+
month_str = "0#{month_num}"
|
64
|
+
else
|
65
|
+
month_str = month_num.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
name_i = dt.match(/:.+/).to_s
|
69
|
+
name_i[0] = " "
|
70
|
+
|
71
|
+
hash[:date] = "#{dt.match(/[0-9]{4}/)}-#{month_str}-#{dt.match(/[^0-9][0-9]{2}[^0-9]/).to_s.gsub(/[: ]/, "")}"
|
72
|
+
hash[:name] = name_i.strip
|
73
|
+
hash[:link] = "http://apod.nasa.gov/apod/" + links_hash[hash[:name]]
|
74
|
+
array << hash
|
75
|
+
end
|
76
|
+
|
77
|
+
array.insert(-4411, {date: "2007-07-16", name: "The Lagoon Nebula in Gas, Dust, and Stars", link: "http://apod.nasa.gov/apod/ap070716.html"}) #This is the dumb typo'd link that I decided to hardcode.
|
78
|
+
array
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apod-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Ball
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.7'
|
55
|
+
description: Uses web scraping to pull Astronomy Picture of the Day information from
|
56
|
+
http://apod.nasa.gov and provides a basic CLI interface for that data
|
57
|
+
email:
|
58
|
+
- davidnoahball@gmail.com
|
59
|
+
executables:
|
60
|
+
- apod-cli
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- apod-cli.gemspec
|
65
|
+
- bin/apod-cli
|
66
|
+
- lib/apod_cli/cli.rb
|
67
|
+
- lib/apod_cli/printer.rb
|
68
|
+
- lib/apod_cli/scraper.rb
|
69
|
+
homepage: http://davidnoahball.com
|
70
|
+
licenses:
|
71
|
+
- MIT
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
- lib/apod_cli
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.4.5.1
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: Displays APOD information through CLI
|
94
|
+
test_files: []
|