vatsim_online 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vatsim_online.gemspec
4
+ gemspec
@@ -0,0 +1,5 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Svilen Vassilev
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,202 @@
1
+ # Vatsim Online
2
+
3
+ A Ruby gem for selectively pulling, parsing and displaying Vatsim online
4
+ stations data. Essentially it's a "Who's online" library, capable of displaying
5
+ online ATC and/or pilots for given airports, areas or globally. Stations are
6
+ returned as objects, exposing a rich set of attributes. Vatsim data is pulled
7
+ on preset intervals and cached locally to avoid flooding the servers.
8
+
9
+ ### Badges of (dis)honour
10
+
11
+ * Testing (Travis CI): [![Build Status](https://secure.travis-ci.org/tarakanbg/vatsim_online.png?branch=master)](http://travis-ci.org/tarakanbg/vatsim_online)
12
+ * Code Analysis (CodeClimate): [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/tarakanbg/vatsim_online)
13
+ * Dependencies: (Gemnasium) [![Gemnasium](https://gemnasium.com/tarakanbg/vatsim_online.png?travis)](https://gemnasium.com/tarakanbg/vatsim_online)
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'vatsim_online'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install vatsim_online
28
+
29
+ ## Usage
30
+
31
+ This gem provides one public method: `vatsim_online`, which can be applied to
32
+ any string (or variable containing a string) representing a full or partial ICAO
33
+ code. The provided ICAO code or fragment will be used as a search criteria and
34
+ matched against the current vatsim data.
35
+
36
+ For example if you want to retrieve all active stations (ATC positions and pilots)
37
+ for Vienna airport (ICAO: LOWW), then you can use:
38
+
39
+ ```ruby
40
+ # Attaching the method directly to a string:
41
+ "LOWW".vatsim_online
42
+
43
+ # Attaching the method to a variable containing a string:
44
+ icao = "LOWW"
45
+ icao.vatsim_online
46
+ ```
47
+ If you want to retrieve the currently active stations for an entire region
48
+ (FIR/ARTCC), then you can use the first 2-3 letters of the region's ICAO name.
49
+ For example if you want to pull all the stations active in Austria (ICAO code
50
+ for the FIR is LOVV), you can use `"LO"` as your ICAO search string: all Austrian
51
+ airports and ATC station callsigns start with `"LO"`:
52
+
53
+ ```ruby
54
+ # Attaching the method directly to a string:
55
+ "LO".vatsim_online
56
+
57
+ # Attaching the method to a variable containing a string:
58
+ icao = "LO"
59
+ icao.vatsim_online
60
+ ```
61
+ When parsing the pilot stations for particular airport or area, the library will
62
+ return the pilots that are flying **to or from** the given area or airport,
63
+ not the current enroute stations. The discovery algorithm is based on **origin
64
+ and destination**.
65
+
66
+
67
+ ### Anatomy of method returns
68
+
69
+ The `vatsim_online` method returns a **hash** of 2 elements: the matching atc
70
+ stations and pilots stations. Each of those is an **array**, cosnsisting of
71
+ station **objects**. Each of these objects includes a number of **attributes**:
72
+
73
+ ```ruby
74
+ icao_vatsim_online # => {:atc => [a1, a2, a3 ...], :pilots => [p1, p2, p3 ...]}
75
+
76
+ icao_vatsim_online[:atc] #=> [a1, a2, a3 ...]
77
+ icao_vatsim_online[:pilots] #=> [p1, p2, p3 ...]
78
+
79
+ icao_vatsim_online[:atc].first #=> a1
80
+ icao_vatsim_online[:pilots].first #=> p1
81
+
82
+ a1.callsign #=> "LQSA_TWR"
83
+ a1.frequency #=> "118.25"
84
+ a1.name #=> "Svilen Vassilev"
85
+ ...
86
+
87
+ p1.callsign #=> "ACH217S"
88
+ p1.departure #=> "LQSA"
89
+ p1.destination #=> "LDSP"
90
+ p1.remarks #=> "/V/ RMK/CHARTS"
91
+ ...
92
+ ```
93
+
94
+ ### Station attributes
95
+
96
+ Here's a complete list of the station object attributes that can be accessed:
97
+
98
+ * callsign
99
+ * name
100
+ * role
101
+ * frequency
102
+ * altitude
103
+ * groundspeed
104
+ * aircraft
105
+ * departure
106
+ * destination
107
+ * rating
108
+ * facility
109
+ * remarks
110
+ * route
111
+ * atis
112
+ * logon
113
+
114
+ ### Customizing the request
115
+
116
+ The `vatsim online` method can be customized by passing in a hash-style collection
117
+ of arguments. The currently supported arguments and their defaults are:
118
+
119
+ * :atc => true (Possible values: true, false. Default value: true)
120
+ * :pilots => true (Possible values: true, false. Default value: true)
121
+
122
+ Both options can be used to exclude all ATC or pilots stations respectively from
123
+ the request, in order to speed it up and avoid processing useless data.
124
+
125
+ **Examples:**
126
+
127
+ ```ruby
128
+ # Lets exclude all ATC from our request and get the pilots only
129
+ "LO".vatsim_online(:atc => false)[:pilots] #=> [p1, p2, p3...]
130
+
131
+ # Lets exclude all pilots from our request and get the ATC only
132
+ "LO".vatsim_online(:pilots => false)[:atc] #=> [a1, a2, a3...]
133
+
134
+ "LO".vatsim_online(:atc => false)[:pilots].first.callsign #=> "ACH0838"
135
+ "LO".vatsim_online(:pilots => false)[:atc].first.callsign #=> "LOVV_CTR"
136
+
137
+ ```
138
+
139
+ ### Example of Ruby on Rails implementation
140
+
141
+ Here's a possible scenario of using this gem in a Ruby on Rails application.
142
+ Verbosity is kept on purpose for clarity.
143
+
144
+ **In your controller:**
145
+ ```ruby
146
+ def index
147
+ # We want to retrieve all Austrian online stations (ATC and pilots)
148
+ icao = "LO"
149
+ stations = icao.vatsim_online
150
+
151
+ # Now we will assign the ATCs and the pilots to separate instance variables,
152
+ # to be able to loop through them separately in the view
153
+ @atc = stations[:atc]
154
+ @pilots = stations[:pilots]
155
+ end
156
+ ```
157
+
158
+ **In your view (HAML is used for clarity):**
159
+
160
+ ```haml
161
+ - for atc in @atc
162
+ %li
163
+ = atc.callsign
164
+ = atc.frequency
165
+ = atc.rating
166
+ = atc.name
167
+ = atc.atis
168
+
169
+ - for pilot in @pilots
170
+ %li
171
+ = pilot.callsign
172
+ = atc.name
173
+ = atc.origin
174
+ = atc.destination
175
+ = atc.route
176
+ = atc.altitude
177
+ = atc.groundspeed
178
+ = atc.remarks
179
+ ```
180
+
181
+ ### Notes
182
+
183
+ * Vatsim status and data files are cached locally to reduce the load on vatsim
184
+ servers. Random server is chosen to retrieve the data each time. By default the
185
+ status file is updated once every 4 hours and the data file once every 3 minutes
186
+ regardless of the number of incoming requests.
187
+ * The data is cached in your default TEMP directory (OS specific)
188
+ * All the data retrieval and caching logic is encapsulated in a separate class
189
+ `VatsimTools::DataDownloader` which can be mixed in other applications and
190
+ libraries too.
191
+ * The ICAO string used as a search criteria **is not** case sensitive
192
+ * Pilot stations returned are based on origin and destination airports, the
193
+ current algorithm does not evaluate enroute flights.
194
+
195
+ ## Contributing
196
+
197
+ 1. Fork it
198
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
199
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
200
+ 4. Make sure all tests are passing!
201
+ 5. Push to the branch (`git push origin my-new-feature`)
202
+ 6. Create new Pull Request
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,16 @@
1
+ %w{vatsim_online/version vatsim_online/station vatsim_online/data_downloader
2
+ vatsim_online/station_parser}.each { |lib| require lib }
3
+
4
+ class String
5
+ def vatsim_online(args={})
6
+ VatsimOnline.vatsim_online(self, args)
7
+ end
8
+ end
9
+
10
+ module VatsimOnline
11
+
12
+ def self.vatsim_online(icao, args)
13
+ VatsimTools::StationParser.new(icao,args).sorted_station_objects
14
+ end
15
+
16
+ end
@@ -0,0 +1,58 @@
1
+ module VatsimTools
2
+
3
+ class DataDownloader
4
+
5
+ %w{curb tempfile time_diff tmpdir csv}.each { |lib| require lib }
6
+
7
+ STATUS_URL = "http://status.vatsim.net/status.txt"
8
+ LOCAL_STATUS = "#{Dir.tmpdir}/vatsim_status.txt"
9
+ LOCAL_DATA = "#{Dir.tmpdir}/vatsim_data.txt"
10
+
11
+ def initialize
12
+ data_file
13
+ end
14
+
15
+ def create_status_tempfile
16
+ status = Tempfile.new('vatsim_status')
17
+ File.rename status.path, LOCAL_STATUS
18
+ File.open(LOCAL_STATUS, "w+") {|f| f.write(Curl::Easy.perform(STATUS_URL).body_str) }
19
+ end
20
+
21
+ def read_status_tempfile
22
+ status = File.open(LOCAL_STATUS)
23
+ difference = Time.diff(status.ctime, Time.now)[:hour]
24
+ difference > 3 ? create_status_tempfile : status.read
25
+ end
26
+
27
+ def status_file
28
+ File.exists?(LOCAL_STATUS) ? read_status_tempfile : create_status_tempfile
29
+ LOCAL_STATUS
30
+ end
31
+
32
+ def servers
33
+ urls = []
34
+ CSV.foreach(status_file, :col_sep =>'=') {|row| urls << row[1] if row[0] == "url0"}
35
+ urls
36
+ end
37
+
38
+ def create_local_data_file
39
+ data = Tempfile.new('vatsim_data', :encoding => 'iso-8859-15')
40
+ File.rename data.path, LOCAL_DATA
41
+ data = Curl::Easy.perform(servers.sample).body_str.gsub(/["]/, '\s').force_encoding('iso-8859-15')
42
+ File.open(LOCAL_DATA, "w+") {|f| f.write(data)}
43
+ end
44
+
45
+ def read_local_datafile
46
+ data = File.open(LOCAL_DATA)
47
+ difference = Time.diff(data.ctime, Time.now)[:minute]
48
+ difference > 2 ? create_local_data_file : data.read
49
+ end
50
+
51
+ def data_file
52
+ File.exists?(LOCAL_DATA) ? read_local_datafile : create_local_data_file
53
+ LOCAL_DATA
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,27 @@
1
+ module VatsimTools
2
+ class Station
3
+ attributes = %w{callsign name role frequency altitude groundspeed aircraft
4
+ departure destination rating facility remarks route atis logon}
5
+ attributes.each {|attribute| attr_accessor attribute.to_sym }
6
+
7
+
8
+ def initialize(station)
9
+ @callsign = station[0]
10
+ @name = station[2]
11
+ @role = station[3]
12
+ @frequency = station[4]
13
+ @altitude = station[7]
14
+ @groundspeed = station[8]
15
+ @aircraft = station[9]
16
+ @departure = station[11]
17
+ @destination = station[13]
18
+ @rating = station[16]
19
+ @facility = station[18]
20
+ @remarks = station[29]
21
+ @route = station[30]
22
+ @atis = station[35]
23
+ @logon = station[37]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ module VatsimTools
2
+
3
+ class StationParser
4
+
5
+ %w{tmpdir csv}.each { |lib| require lib }
6
+ require_relative "data_downloader"
7
+ require_relative "station"
8
+
9
+ attr_accessor :role
10
+ attr_accessor :icao
11
+
12
+ LOCAL_DATA = "#{Dir.tmpdir}/vatsim_data.txt"
13
+
14
+ def initialize(icao, args = nil)
15
+ VatsimTools::DataDownloader.new
16
+ args.class == Hash ? @role = determine_role(args) : @role = "all"
17
+ @icao = icao.upcase
18
+ end
19
+
20
+ def determine_role(args)
21
+ args[:atc] == false ? role = "pilot" : role = "all"
22
+ args[:pilots] == false ? role = "atc" : role = role
23
+ role = "all" if args[:pilots] == false && args[:atc] == false
24
+ role
25
+ end
26
+
27
+ def stations
28
+ stations = []
29
+ CSV.foreach(LOCAL_DATA, :col_sep =>':', encoding: "iso-8859-15") do |row|
30
+ callsign, origin, destination, client = row[0].to_s, row[11].to_s, row[13].to_s, row[3].to_s
31
+ stations << row if (callsign[0...@icao.length] == @icao && client == "ATC") unless @role == "pilot"
32
+ stations << row if (origin[0...@icao.length] == @icao || destination[0...@icao.length] == @icao) unless @role == "atc"
33
+ end
34
+ stations
35
+ end
36
+
37
+ def station_objects
38
+ station_objects= []
39
+ stations.each {|station| station_objects << VatsimTools::Station.new(station) }
40
+ station_objects
41
+ end
42
+
43
+ def sorted_station_objects
44
+ atc = []; pilots = []
45
+ station_objects.each {|sobj| sobj.role == "ATC" ? atc << sobj : pilots << sobj}
46
+ {:atc => atc, :pilots => pilots}
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,3 @@
1
+ module VatsimOnline
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper.rb'
2
+ require 'data_downloader_spec_helper.rb'
3
+
4
+ describe VatsimTools::DataDownloader do
5
+
6
+ target = VatsimTools::DataDownloader
7
+ LOCAL_STATUS = "#{Dir.tmpdir}/vatsim_status.txt"
8
+ LOCAL_DATA = "#{Dir.tmpdir}/vatsim_data.txt"
9
+
10
+ describe "create_status_tempfile" do
11
+ it "should create a file" do
12
+ delete_local_files
13
+ File.exists?(LOCAL_STATUS).should be false
14
+ target.new.create_status_tempfile
15
+ File.exists?(LOCAL_STATUS).should be true
16
+ File.open(LOCAL_STATUS).path.should eq("#{Dir.tmpdir}/vatsim_status.txt")
17
+ File.open(LOCAL_STATUS).size.should be > 100
18
+ end
19
+ end
20
+
21
+ describe "read_status_tempfile" do
22
+ it "should confirm a file exists" do
23
+ target.new.read_status_tempfile
24
+ File.exists?(LOCAL_STATUS).should be true
25
+ File.open(LOCAL_STATUS).size.should be > 100
26
+ end
27
+ end
28
+
29
+ describe "status_file" do
30
+ it "should return status.txt path" do
31
+ delete_local_files
32
+ File.exists?(LOCAL_STATUS).should be false
33
+ target.new.status_file.class.should eq(String)
34
+ target.new.status_file.should include("vatsim_status.txt")
35
+ target.new.status_file.should eq(LOCAL_STATUS)
36
+ target.new.status_file.should eq("#{Dir.tmpdir}/vatsim_status.txt")
37
+ File.exists?(LOCAL_STATUS).should be true
38
+ end
39
+ end
40
+
41
+ describe "servers" do
42
+ it "should contain an array of server URLs" do
43
+ File.exists?(LOCAL_STATUS).should be true
44
+ target.new.servers.class.should eq(Array)
45
+ target.new.servers.size.should eq(5)
46
+ end
47
+ end
48
+
49
+ describe "create_local_data_file" do
50
+ it "should confirm a file exists" do
51
+ target.new.create_local_data_file
52
+ File.exists?(LOCAL_DATA).should be true
53
+ File.open(LOCAL_DATA).path.should eq("#{Dir.tmpdir}/vatsim_data.txt")
54
+ File.open(LOCAL_DATA).size.should be > 100
55
+ end
56
+ end
57
+
58
+ describe "read_local_datafile" do
59
+ it "should confirm a file exists" do
60
+ target.new.read_local_datafile
61
+ File.exists?(LOCAL_DATA).should be true
62
+ File.open(LOCAL_DATA).size.should be > 100
63
+ end
64
+ end
65
+
66
+ describe "data_file" do
67
+ it "should contain file path" do
68
+ delete_local_files
69
+ File.exists?(LOCAL_DATA).should be false
70
+ target.new.data_file.class.should eq(String)
71
+ target.new.data_file.should include("vatsim_data.txt")
72
+ target.new.data_file.should eq(LOCAL_DATA)
73
+ target.new.data_file.should eq("#{Dir.tmpdir}/vatsim_data.txt")
74
+ File.exists?(LOCAL_DATA).should be true
75
+ end
76
+ end
77
+
78
+ describe "new" do
79
+ it "should return" do
80
+ delete_local_files
81
+ File.exists?(LOCAL_DATA).should be false
82
+ File.exists?(LOCAL_STATUS).should be false
83
+ target.new
84
+ File.exists?(LOCAL_DATA).should be true
85
+ File.exists?(LOCAL_STATUS).should be true
86
+ File.open(LOCAL_DATA).size.should be > 100
87
+ File.open(LOCAL_STATUS).size.should be > 100
88
+ end
89
+ end
90
+
91
+ end