apod-cli 0.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 +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: []
|