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 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: []