apod-cli 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/apod_cli"
4
+
5
+ ApodCli.new.call
@@ -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: []