nacofetch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+