nacofetch 1.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.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # This is my README
data/bin/nacofetch ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'nacofetch'
data/lib/nacofetch.rb ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'cri'
5
+
6
+ require 'nacofetch/naco_interface'
7
+ require 'nacofetch/config'
8
+
9
+ #initialize here
10
+
11
+ @default_config_dir = File.expand_path("~/.naco")
12
+ @config_file_name = "config.rb"
13
+
14
+ begin
15
+ NacoConfig.from_file(File.expand_path('~/.naco/config.rb'))
16
+ rescue
17
+ config_filename = File.expand_path(@config_file_name, @default_config_dir)
18
+ puts "No config file found... creating #{config_filename}"
19
+
20
+ # ensure directory exists
21
+ begin
22
+ Dir::mkdir(@default_config_dir)
23
+ puts "Creating #{@default_config_dir}"
24
+ rescue
25
+ puts "#{@default_config_dir} already exists"
26
+ end
27
+
28
+ File.new(File.expand_path(@config_file_name, @default_config_dir), "w") do |f|
29
+ # file closes on block termination
30
+ # write into f if I want to pre-configure the file. For now it's just empty.
31
+ end
32
+ end
33
+
34
+ def print_cycles(cycles)
35
+ puts "\tCycle\tEffective\tEnding"
36
+ puts "-------------------------------------------"
37
+ cycles.each do |cycle|
38
+ puts "\t#{cycle.number}\t#{cycle.effective.strftime("%b %d %Y")}\t#{cycle.ending.strftime("%b %d %Y")}"
39
+ end
40
+ end
41
+
42
+ def select_cycle(opts)
43
+ ni = NacoInterface.new
44
+ cycles = ni.cycles
45
+
46
+ cycle = nil
47
+
48
+ unless opts[:current] or opts[:pending] or opts[:cycle]
49
+ cycle = ni.current_cycle
50
+ if cycles.size > 1
51
+ puts "WARN: no cycle specified, and multiple cycles are available. Defaulting to current cycle (#{cycle.number})"
52
+ end
53
+ end
54
+
55
+ if(opts[:current])
56
+ cycle = ni.current_cycle
57
+ end
58
+
59
+ if(opts[:pending])
60
+ cycle = ni.pending_cycle
61
+ end
62
+
63
+ if(opts[:cycle])
64
+ cycle = cycles.select { |c| c.number == opts[:cycle] }.first
65
+ if cycle.nil?
66
+ puts "ERROR: cycle #{opts[:cycle]} not found on dTPP. Available cycles:\n\n"
67
+ print_cycles(cycles)
68
+ exit 0
69
+ end
70
+ end
71
+
72
+ if cycle.nil?
73
+ raise "Ended up here with a nil cycle, and I can't handle that"
74
+ end
75
+
76
+ status = cycle.status == "PENDING" ? "(effective #{cycle.effective.strftime("%b %d %Y")})" : "(expires #{cycle.ending.strftime("%b %d %Y")})"
77
+ puts "Desired cycle is #{cycle.number} #{status}"
78
+
79
+ cycle
80
+ end
81
+
82
+
83
+ super_cmd = Cri::Command.define do
84
+ name 'nacofetch'
85
+ usage 'nacofetch fetches and packages naco/faa plates from the dTPP'
86
+ summary 'naco digital terminal procedure fetch tool'
87
+ description 'coming soon'
88
+
89
+ run do |opts, args, cmd|
90
+ puts cmd.help
91
+ end
92
+ end
93
+
94
+ super_cmd.define_command do
95
+ name 'cycle'
96
+ summary 'list currently available dTPP cycles'
97
+
98
+ run do |opts, args|
99
+ print_cycles(NacoInterface.new.cycles)
100
+ end
101
+ end
102
+
103
+
104
+ super_cmd.define_command do
105
+ name 'metafile'
106
+ summary 'prefetch metafile for cycle'
107
+ usage 'metafile <-p|-c|-y cycle>'
108
+
109
+ flag :h, :help, 'show help for this command' do |value, cmd|
110
+ puts cmd.help
111
+ exit 0
112
+ end
113
+
114
+ flag :c, :current, 'Fetch current cycle (default)'
115
+ flag :p, :pending, 'Fetch pending cycle if available, otherwise fall back to current cycle'
116
+ option :y, :cycle, 'Fetch given cycle (-y 1207 will fetch cycle 1207)', :argument => :required
117
+
118
+ run do |opts, args, cmd|
119
+ cycle = select_cycle(opts)
120
+
121
+ metafile = NacoInterface.new.metafile_for_cycle(cycle)
122
+
123
+ puts "Metafile fetched: #{metafile}"
124
+ end
125
+ end
126
+
127
+
128
+ super_cmd.define_command do
129
+ name 'airport'
130
+ summary 'fetch plates for one or more airports'
131
+
132
+ flag :h, :help, 'show help for this command' do |value, cmd|
133
+ puts cmd.help
134
+ exit 0
135
+ end
136
+
137
+ flag :c, :current, 'Fetch current cycle (default)'
138
+ flag :p, :pending, 'Fetch pending cycle if available, otherwise fall back to current cycle'
139
+ option :y, :cycle, 'Fetch given cycle (-y 1207 will fetch cycle 1207)', :argument => :required
140
+
141
+ run do |opts, args|
142
+ ni = NacoInterface.new
143
+ cycle = select_cycle(opts)
144
+ metafile = ni.metafile_for_cycle(cycle)
145
+
146
+ puts "Fetching cycle #{cycle.number} plates for: " + args.map{|a| a.upcase}.join(", ")
147
+
148
+ args.each do |a|
149
+ ni.fetch_plates_for_airport(metafile,a)
150
+ end
151
+ end
152
+ end
153
+
154
+ super_cmd.define_command do
155
+ name 'state'
156
+ summary 'fetch plates for all airports in one or more states'
157
+
158
+ flag :h, :help, 'show help for this command' do |value, cmd|
159
+ puts cmd.help
160
+ exit 0
161
+ end
162
+
163
+ flag :c, :current, 'Fetch current cycle (default)'
164
+ flag :p, :pending, 'Fetch pending cycle if available, otherwise fall back to current cycle'
165
+ option :y, :cycle, 'Fetch given cycle (-y 1207 will fetch cycle 1207)', :argument => :required
166
+
167
+ run do |opts, args|
168
+ ni = NacoInterface.new
169
+ cycle = select_cycle(opts)
170
+ metafile = ni.metafile_for_cycle(cycle)
171
+
172
+ puts "Fetching cycle #{cycle.number} plates for: " + args.map{|a| a.upcase}.join(", ")
173
+
174
+ args.each do |a|
175
+ ni.fetch_plates_for_state(metafile,a)
176
+ end
177
+ end
178
+ end
179
+
180
+ super_cmd.define_command do
181
+ name 'volume'
182
+ summary 'fetch plates for an entire NACO volume'
183
+
184
+ flag :h, :help, 'show help for this command' do |value, cmd|
185
+ puts cmd.help
186
+ exit 0
187
+ end
188
+
189
+ flag :c, :current, 'Fetch current cycle (default)'
190
+ flag :p, :pending, 'Fetch pending cycle if available, otherwise fall back to current cycle'
191
+ option :y, :cycle, 'Fetch given cycle (-y 1207 will fetch cycle 1207)', :argument => :required
192
+
193
+ run do |opts, args|
194
+ ni = NacoInterface.new
195
+ cycle = select_cycle(opts)
196
+ metafile = ni.metafile_for_cycle(cycle)
197
+
198
+ puts "Fetching cycle #{cycle.number} plates for: " + args.map{|a| a.upcase}.join(", ")
199
+
200
+ args.each do |a|
201
+ ni.fetch_plates_for_volume(metafile,a)
202
+ end
203
+ end
204
+ end
205
+
206
+ super_cmd.add_command Cri::Command.new_basic_help
207
+ super_cmd.run(ARGV)
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'mixlib/config'
3
+
4
+ class NacoConfig
5
+ extend(Mixlib::Config)
6
+
7
+ configure do |c|
8
+ c[:config_dir] = File.expand_path('~/.naco')
9
+ c[:plate_storage_dir] = File.expand_path('plates', c[:config_dir])
10
+ c[:metafile_storage_dir] = File.expand_path('metafiles', c[:config_dir])
11
+ c[:cache_storage_dir] = File.expand_path('cache', c[:config_dir])
12
+ end
13
+ end
14
+
@@ -0,0 +1,21 @@
1
+ require 'nacofetch/date_util'
2
+
3
+ class Cycle
4
+ attr_accessor :url, :number, :effective, :ending
5
+
6
+ def initialize(url, number, effective, ending)
7
+ @url = url
8
+ @number = number
9
+ @effective = effective
10
+ @ending = ending
11
+ end
12
+
13
+ def status
14
+ now = Time.new
15
+
16
+ return "PENDING" if now.before?@effective
17
+ return "CURRENT" if now.before?@ending and now.after?@effective
18
+ "EXPIRED" if now.after?@ending
19
+ end
20
+
21
+ end
@@ -0,0 +1,22 @@
1
+ require 'date'
2
+
3
+ def string_to_time time_, format_
4
+ time = Date._strptime(time_, format_)
5
+ return Time.local(time[:year], time[:mon], time[:mday], time[:hour], time[:min], time[:sec], time[:sec_fraction], time[:zone])
6
+ end
7
+
8
+ module DateUtil
9
+
10
+ LEFT_SIDE_LATER = 1
11
+ RIGHT_SIDE_LATER = -1
12
+
13
+ def before?(input_time)
14
+ (self <=> input_time) == RIGHT_SIDE_LATER
15
+ end
16
+
17
+ def after?(input_time)
18
+ (self <=> input_time) == LEFT_SIDE_LATER
19
+ end
20
+ end
21
+
22
+ Time.send :include , DateUtil
@@ -0,0 +1,142 @@
1
+
2
+ require 'rubygems'
3
+ require 'open-uri'
4
+ require 'nokogiri'
5
+ require 'httpclient'
6
+
7
+ require 'nacofetch/config'
8
+ require 'nacofetch/cycle'
9
+
10
+
11
+ class NacoInterface
12
+ def initialize
13
+ @base_url = "http://aeronav.faa.gov"
14
+ end
15
+
16
+ def cycles
17
+ unless @cycles_var
18
+ url = "/index.asp?xml=aeronav/applications/d_tpp"
19
+ doc = Nokogiri::HTML(open(@base_url + url))
20
+ @cycles_var = []
21
+
22
+ tables = doc.xpath('//table[@title="Digital Terminal Procedures Publication"]')
23
+
24
+ tables.xpath('//td[@headers="header1"]').each do |cycle|
25
+ url = cycle.xpath('./a/@href').to_s()
26
+ @cycles_var << Cycle.new(
27
+ url.sub(/digital_tpp/, "digital_tpp_search"), # todo: there's gotta be a less hacky way to do this'
28
+ /ver=(\d+)/.match(url)[1],
29
+ string_to_time(/eff=(\d+-\d+-\d+)/.match(url)[1], '%m-%d-%Y'),
30
+ string_to_time(/end=(\d+-\d+-\d+)/.match(url)[1], '%m-%d-%Y')
31
+ )
32
+ end
33
+ end
34
+
35
+ @cycles_var
36
+ end
37
+
38
+ def current_cycle
39
+ self.cycles.select { |cycle| cycle.status == "CURRENT" }.first
40
+ end
41
+
42
+ def pending_cycle
43
+ self.cycles.select { |cycle| cycle.status == "PENDING" }.first
44
+ end
45
+
46
+ def metafile_for_cycle(cycle)
47
+ fetch_metafile(cycle) unless File.exists?(metafilename(cycle))
48
+ metafilename(cycle)
49
+ end
50
+
51
+ def fetch_plates_for_airport(metafile_url, ident)
52
+ xpath = "/digital_tpp/state_code/city_name/airport_name[@icao_ident = '#{ident}' or @apt_ident = '#{ident}']"
53
+ #xpath = "/digital_tpp/state_code[@ID = 'GA']"
54
+
55
+ fetch_plates_for_xpath(metafile_url, xpath)
56
+ end
57
+
58
+ def fetch_plates_for_state(metafile_url, statecode)
59
+ xpath = "/digital_tpp/state_code[@ID = '#{statecode}']"
60
+
61
+ fetch_plates_for_xpath(metafile_url, xpath)
62
+ end
63
+
64
+ def fetch_plates_for_volume(metafile_url, volumecode)
65
+ xpath = "/digital_tpp/state_code[city_name/@volume = '#{volumecode}']"
66
+
67
+ fetch_plates_for_xpath(metafile_url, xpath)
68
+ end
69
+
70
+ private
71
+
72
+ def fetch_plates_for_xpath(metafile_url, xpath)
73
+ doc = Nokogiri::XML(open(metafile_url))
74
+
75
+ cycle = doc.xpath('/digital_tpp/@cycle')
76
+ base_path = "#{@base_url}/d-tpp/#{cycle}/"
77
+
78
+ plate_dir = NacoConfig.plate_storage_dir
79
+
80
+ unless File.directory?plate_dir
81
+ Dir::mkdir(File.expand_path(plate_dir))
82
+ end
83
+
84
+
85
+ doc.xpath(xpath).each do |node|
86
+ node.xpath(".//record").each do |chart|
87
+ url = chart.xpath("pdf_name").inner_html
88
+ name = chart.xpath("chart_name").inner_html.gsub(/\//, "-")
89
+ type = chart.xpath("chart_code").inner_html
90
+
91
+ apt_ident = determine_ident(chart.xpath(".."))
92
+
93
+ full_url = base_path + url
94
+
95
+ unless File.directory?(File.expand_path(apt_ident, plate_dir))
96
+ Dir::mkdir(File.expand_path(apt_ident, plate_dir))
97
+ end
98
+
99
+ download(full_url, File.expand_path("#{apt_ident}/#{name}.pdf", plate_dir))
100
+ end
101
+ end
102
+ end
103
+
104
+ def determine_ident(apt_node)
105
+ icao_ident = apt_node.xpath("@icao_ident").inner_html
106
+
107
+ if icao_ident.empty?
108
+ return apt_node.xpath("@apt_ident").inner_html
109
+ end
110
+
111
+ icao_ident
112
+ end
113
+
114
+ def metafilename(cycle)
115
+ File.expand_path("#{cycle.number}-metafile.xml", NacoConfig.metafile_storage_dir)
116
+ end
117
+
118
+ def fetch_metafile(cycle)
119
+ url = "/d-tpp/#{cycle.number}/xml_data/d-TPP_Metafile.xml"
120
+ download(@base_url+url, metafilename(cycle))
121
+ end
122
+
123
+ def download(url, to_file)
124
+ client = HTTPClient.new(nil)
125
+
126
+ begin
127
+ Dir::mkdir(File.dirname(File.expand_path(to_file)))
128
+ rescue
129
+ end
130
+
131
+ file = File.new(to_file, "wb")
132
+ puts "Downloading #{url} to #{to_file}"
133
+ begin
134
+ file.write(client.get_content(url))
135
+ rescue
136
+ puts "ERROR: problem fetching #{url} (skipping)"
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nacofetch
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Ian McMahon
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-07-03 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: httpclient
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: nokogiri
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :runtime
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: cri
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :runtime
55
+ version_requirements: *id003
56
+ - !ruby/object:Gem::Dependency
57
+ name: mixlib-config
58
+ prerelease: false
59
+ requirement: &id004 !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ type: :runtime
67
+ version_requirements: *id004
68
+ description: A tool to fetch and manage naco plates from the FAA aeronav digital terminal plates website
69
+ email: imcmahon@prototechnical.com
70
+ executables:
71
+ - nacofetch
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - lib/nacofetch/config.rb
78
+ - lib/nacofetch/cycle.rb
79
+ - lib/nacofetch/date_util.rb
80
+ - lib/nacofetch/naco_interface.rb
81
+ - lib/nacofetch.rb
82
+ - bin/nacofetch
83
+ - README.md
84
+ has_rdoc: true
85
+ homepage: http://nacofetch.prototechnical.com/
86
+ licenses: []
87
+
88
+ post_install_message:
89
+ rdoc_options: []
90
+
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ requirements: []
108
+
109
+ rubyforge_project:
110
+ rubygems_version: 1.3.6
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: NacoFetch
114
+ test_files: []
115
+