flight_radar 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51bece5148b583b098a6821b34c81bb4e8412023f20288133668e420872d4a02
4
- data.tar.gz: cee75c30c0d41a9da385a0efeeeca33ba8c1c0aae7f30e075408a1e49581a40e
3
+ metadata.gz: a90f6971658d712566667b5602acbc862430ae4b97e0ed7286846dbe5e107066
4
+ data.tar.gz: ee9dafc558f2c91303c5ecaa55a65de73be55c0f656a62b5528b8b0da0e93b93
5
5
  SHA512:
6
- metadata.gz: 701fe16b782798feda39119cc70149266a94922315c9784328e74d6e3254aac70dd8f795d20fb0320c93d0bbccd2652f2444cd29b486874b6b3652d121dcdf3b
7
- data.tar.gz: 3478f53760cc3478b4c4645e66eacf7e45075f315a79b04b086a2ca575202fb53d203926e4bf5953fa450446dda3e631e7e6bcd8afd59ce7d6c50248d77f3a57
6
+ metadata.gz: 880969b6f309af65e1de1bba3a0a2f715ae2a1035a8b45b19872935862a86b9e633fa5256e7daa9b9e989af0d58eeae11149f339944b045e8a9ed137167f223c
7
+ data.tar.gz: ede6e35686c08e885dcc3760c531beffbde7c61a473eea060cb6d2f95c89b2268b3b78e36e8cd88be391298d7a30061036fa3a0d8372be7dd8e8d0a49064484a
@@ -27,4 +27,4 @@ jobs:
27
27
  bundle install
28
28
 
29
29
  - name: Run RSpec tests
30
- run: bundle exec rake spec
30
+ run: bundle exec rake
data/.rubocop.yml CHANGED
@@ -1,16 +1,4 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.7
3
-
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
- Style/StringLiteralsInInterpolation:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
11
-
12
- Layout/LineLength:
13
- Max: 120
14
-
15
- Layout/EndOfLine:
16
- Enabled: False
2
+ TargetRubyVersion: 3.2
3
+ NewCops: enable
4
+ SuggestExtensions: false
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.1] - 2023-12-21
4
+ - Code refactor
5
+ - Add documentation to the code
6
+ - Apply rubocop suggestions
7
+
3
8
  ## [0.2.0] - 2023-12-20
4
9
 
5
10
  - Make the gem work again
data/Gemfile CHANGED
@@ -1,6 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in flight_radar.gemspec
6
6
  gemspec
7
+
8
+ group :development do
9
+ gem 'rake'
10
+ gem 'redcarpet'
11
+ gem 'rspec'
12
+ gem 'rubocop'
13
+ gem 'yard'
14
+ end
data/README.md CHANGED
@@ -3,6 +3,24 @@
3
3
 
4
4
  **Fetch aircraft data from Flightradar24.**
5
5
 
6
+ # Table of Contents
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [Airlines](#airlines)
10
+ - [Airport](#airport)
11
+ - [Zones](#zones)
12
+ - [Flights](#flights)
13
+ - [Flight Details](#flight-details)
14
+ - [Flights by Airline](#flights-by-airline)
15
+ - [Flights by Bounds](#flights-by-bounds)
16
+ - [Airline Logo](#airline-logo)
17
+ - [Country Flag](#country-flag)
18
+ - [Development](#development)
19
+ - [Contributing](#contributing)
20
+ - [License](#license)
21
+ - [Code of Conduct](#code-of-conduct)
22
+
23
+
6
24
  ## Installation
7
25
 
8
26
  Add this line to your application's Gemfile:
@@ -26,194 +44,177 @@ Or install it yourself as:
26
44
  ```ruby
27
45
  FlightRadar.airlines
28
46
  ```
47
+
29
48
  **Sample response:**
49
+
30
50
  ```ruby
31
51
  [
32
52
  {"Name"=>"21 Air", "Code"=>"2I", "ICAO"=>"CSB"},
33
53
  {"Name"=>"25Only Aviation", "Code"=>"", "ICAO"=>"ONY"},
34
54
  {"Name"=>"40-Mile Air", "Code"=>"Q5", "ICAO"=>"MLA"},
35
- ...
55
+ # ...
36
56
  ]
37
57
  ```
58
+
38
59
  ### Airport
60
+
39
61
  ```ruby
40
62
  airport = "LAX"
41
63
  FlightRadar.airport(airport)
42
64
  ```
65
+
43
66
  **Sample response:**
67
+
44
68
  ```ruby
45
69
  {
46
- "details""=>"{
47
- "name""=>""Lodz Wladyslaw Reymont Airport",
48
- "code""=>"{
49
- "iata""=>""LCJ",
50
- "icao""=>""EPLL"
51
- },
52
- "position""=>"{
53
- "latitude"=>51.721882,
54
- "longitude"=>19.39813,
55
- "altitude"=>604,
56
- "country""=>"{
57
- "id""=>nil",
58
- "name""=>""Poland",
59
- "code""=>""POL"
70
+ "details" => {
71
+ "name" => "Lodz Wladyslaw Reymont Airport",
72
+ "code" => {
73
+ "iata" => "LCJ",
74
+ "icao" => "EPLL"
60
75
  },
61
- "region""=>"{
62
- "city""=>""Lodz"
63
- }
64
- },
65
- ...
76
+ "position" => {
77
+ "latitude" => 51.721882,
78
+ "longitude" => 19.39813,
79
+ "altitude" => 604,
80
+ "country" => {
81
+ "id" => nil,
82
+ "name" => "Poland",
83
+ "code" => "POL"
84
+ },
85
+ "region" => {
86
+ "city" => "Lodz"
87
+ }
88
+ },
89
+ # ...
66
90
  }
67
91
  }
68
92
  ```
69
93
 
70
- ### Airports
71
- ```ruby
72
- airport = "LAX"
73
- FlightRadar.airports
74
- ```
75
- **Sample response:**
76
- ```ruby
77
- [
78
- {
79
- "name""=>""A Coruna Airport",
80
- "iata""=>""LCG",
81
- "icao""=>""LECO",
82
- "lat"=>43.302059,
83
- "lon"=>-8.37725,
84
- "country""=>""Spain",
85
- "alt"=>326
86
- },
87
- {
88
- "name""=>""Aachen Merzbruck Airport",
89
- "iata""=>""AAH",
90
- "icao""=>""EDKA",
91
- "lat"=>50.823051,
92
- "lon"=>6.186111,
93
- "country""=>""Germany",
94
- "alt"=>623
95
- },
96
- ...
97
- ]
98
- ```
99
-
100
94
  ### Zones
95
+
101
96
  ```ruby
102
97
  FlightRadar.zones
103
98
  ```
99
+
104
100
  **Sample response:**
105
101
  ```ruby
106
102
  {
107
- "europe""=>"{
108
- "tl_y"=>72.57,
109
- "tl_x"=>-16.96,
110
- "br_y"=>33.57,
111
- "br_x"=>53.05,
112
- "subzones""=>"{
113
- "poland""=>"{
114
- "tl_y"=>56.86,
115
- "tl_x"=>11.06,
116
- "br_y"=>48.22,
117
- "br_x"=>28.26
118
- },
119
- "germany""=>"{
120
- "tl_y"=>57.92,
121
- "tl_x"=>1.81,
122
- "br_y"=>45.81,
123
- "br_x"=>16.83
124
- },
125
- ...
126
- }
103
+ "europe" => {
104
+ "tl_y" => 72.57,
105
+ "tl_x" => -16.96,
106
+ "br_y" => 33.57,
107
+ "br_x" => 53.05,
108
+ "subzones" => {
109
+ "poland" => {
110
+ "tl_y" => 56.86,
111
+ "tl_x" => 11.06,
112
+ "br_y" => 48.22,
113
+ "br_x" => 28.26
114
+ },
115
+ "germany" => {
116
+ "tl_y" => 57.92,
117
+ "tl_x" => 1.81,
118
+ "br_y" => 45.81,
119
+ "br_x" => 16.83
120
+ },
121
+ # ...
127
122
  }
123
+ }
128
124
  }
129
125
  ```
130
126
 
131
127
  ### Flights
128
+
132
129
  ```ruby
133
130
  FlightRadar.flights
134
131
  ```
135
132
 
136
133
  **Sample response:**
134
+
137
135
  ```ruby
138
136
  [
139
- "<(GRND) - Altitude":"0 - Ground Speed":"146 - Heading":146>,
140
- "<(DRON) TEST4A - Altitude":"374 - Ground Speed":"1 - Heading":1>,
141
- "<(GLID) STM32 - Altitude":"2290 - Ground Speed":"1 - Heading":1>,
142
- "<() - Altitude":"508 - Ground Speed":"7 - Heading":7>,
143
- "<(B763) N142FE - Altitude":"0 - Ground Speed":"0 - Heading":0>,
144
- "<(GRND) - Altitude":"0 - Ground Speed":"278 - Heading":278>,
145
- "<(GLID) OM-2709 - Altitude":"1236 - Ground Speed":"1 - Heading":1>,
146
- "<(B763) N196FE - Altitude":"0 - Ground Speed":"0 - Heading":0>,
147
- "<(B77L) N869FD - Altitude":"0 - Ground Speed":"0 - Heading":0>,
148
- "<(B763) N111FE - Altitude":"0 - Ground Speed":"0 - Heading":0>,
149
- "<(B763) N193FE - Altitude":"0 - Ground Speed":"0 - Heading":0>,
150
- ...
137
+ "<(GRND) - Altitude: 0 - Ground Speed: 146 - Heading: 146>",
138
+ "<(DRON) TEST4A - Altitude: 374 - Ground Speed: 1 - Heading: 1>",
139
+ "<(GLID) STM32 - Altitude: 2290 - Ground Speed: 1 - Heading: 1>",
140
+ "<() - Altitude: 508 - Ground Speed: 7 - Heading: 7>",
141
+ "<(B763) N142FE - Altitude: 0 - Ground Speed: 0 - Heading: 0>",
142
+ "<(GRND) - Altitude: 0 - Ground Speed: 278 - Heading: 278>",
143
+ "<(GLID) OM-2709 - Altitude: 1236 - Ground Speed: 1 - Heading: 1>",
144
+ "<(B763) N196FE - Altitude: 0 - Ground Speed: 0 - Heading: 0>",
145
+ "<(B77L) N869FD - Altitude: 0 - Ground Speed: 0 - Heading: 0>",
146
+ "<(B763) N111FE - Altitude: 0 - Ground Speed: 0 - Heading: 0>",
147
+ "<(B763) N193FE - Altitude: 0 - Ground Speed: 0 - Heading: 0>",
148
+ # ...
151
149
  ]
152
150
  ```
153
151
 
154
-
155
-
156
152
  ### Flight Details
153
+
157
154
  ```ruby
158
155
  flight_id = FlightRadar.flights.sample.id
159
156
  FlightRadar.flight_details(flight_id)
160
157
  ```
161
158
 
162
159
  **Sample response:**
160
+
163
161
  ```ruby
164
162
  {
165
- "identification""=>"{
166
- "id""=>""2aaadc48",
167
- "row"=>5175915984,
168
- "number""=>"{
169
- "default""=>""F980",
170
- "alternative""=>nil"
163
+ "identification" => {
164
+ "id" => "2aaadc48",
165
+ "row" => 5175915984,
166
+ "number" => {
167
+ "default" => "F980",
168
+ "alternative" => nil
169
+ },
170
+ "callsign" => "FFT80"
171
+ },
172
+ "status" => {
173
+ "live" => true,
174
+ "text" => "Estimated- 14:01",
175
+ "icon" => "green",
176
+ "estimated" => nil,
177
+ "ambiguous" => false,
178
+ "generic" => {
179
+ "status" => {
180
+ "text" => "estimated",
181
+ "color" => "green",
182
+ "type" => "arrival"
171
183
  },
172
- "callsign""=>""FFT80"
173
- },
174
- "status""=>"{
175
- "live""=>true",
176
- "text""=>""Estimated- 14:01",
177
- "icon""=>""green",
178
- "estimated""=>nil",
179
- "ambiguous""=>false",
180
- "generic""=>"{
181
- "status""=>"{
182
- "text""=>""estimated",
183
- "color""=>""green",
184
- "type""=>""arrival"
185
- },
186
- "eventTime""=>"{
187
- "utc"=>1643652087,
188
- "local"=>1643637687
189
- }
184
+ "eventTime" => {
185
+ "utc" => 1643652087,
186
+ "local" => 1643637687
190
187
  }
191
- },
192
- ...
188
+ }
189
+ },
190
+ # ...
193
191
  }
194
192
  ```
195
193
 
196
194
  ### Flights by airline
195
+
197
196
  ```ruby
198
197
  airline = "LOT"
199
198
  FlightRadar.flights(airline: airline)
200
199
  ```
201
200
 
202
201
  **Sample response:**
202
+
203
203
  ```ruby
204
204
  [
205
- "<(B789) SP-LSG - Altitude":"39992 - Ground Speed":"451 - Heading":451>,
206
- "<(B788) SP-LRA - Altitude":"38975 - Ground Speed":"537 - Heading":537>,
207
- "<(B788) SP-LRB - Altitude":"38000 - Ground Speed":"463 - Heading":463>,
208
- "<(E195) SP-LNF - Altitude":"31675 - Ground Speed":"420 - Heading":420>,
209
- "<(B38M) SP-LVF - Altitude":"38000 - Ground Speed":"460 - Heading":460>,
210
- "<(E195) SP-LNB - Altitude":"22475 - Ground Speed":"366 - Heading":366>,
211
- "<(B789) SP-LSF - Altitude":"36000 - Ground Speed":"440 - Heading":440>,
212
- ...
205
+ "<(B789) SP-LSG - Altitude: 39992 - Ground Speed: 451 - Heading: 451>",
206
+ "<(B788) SP-LRA - Altitude: 38975 - Ground Speed: 537 - Heading: 537>",
207
+ "<(B788) SP-LRB - Altitude: 38000 - Ground Speed: 463 - Heading: 463>",
208
+ "<(E195) SP-LNF - Altitude: 31675 - Ground Speed: 420 - Heading: 420>",
209
+ "<(B38M) SP-LVF - Altitude: 38000 - Ground Speed: 460 - Heading: 460>",
210
+ "<(E195) SP-LNB - Altitude: 22475 - Ground Speed: 366 - Heading: 366>",
211
+ "<(B789) SP-LSF - Altitude: 36000 - Ground Speed: 440 - Heading: 440>",
212
+ # ...
213
213
  ]
214
214
  ```
215
215
 
216
216
  ### Flights by bounds
217
+
217
218
  ```ruby
218
219
  zone = FlightRadar.zones["oceania"]
219
220
  bounds = FlightRadar.bounds(zone)
@@ -221,46 +222,49 @@ FlightRadar.flights(bounds: bounds)
221
222
  ```
222
223
 
223
224
  **Sample response:**
225
+
224
226
  ```ruby
225
227
  [
226
- "<(B788) VT-ANP - Altitude":"41000 - Ground Speed":"494 - Heading":494>,
227
- "<(B738) VT-AXZ - Altitude":"37000 - Ground Speed":"436 - Heading":436>,
228
- "<(B772) PH-BQA - Altitude":"32000 - Ground Speed":"496 - Heading":496>,
229
- "<(A388) 9V-SKS - Altitude":"33996 - Ground Speed":"498 - Heading":498>,
230
- "<(B77L) A7-BFY - Altitude":"32000 - Ground Speed":"478 - Heading":478>,
231
- "<(B78X) 9V-SCA - Altitude":"38996 - Ground Speed":"514 - Heading":514>,
232
- "<(B77W) A6-ENU - Altitude":"34000 - Ground Speed":"504 - Heading":504>,
233
- "<(A359) OH-LWF - Altitude":"40004 - Ground Speed":"476 - Heading":476>,
234
- ...
228
+ "<(B788) VT-ANP - Altitude: 41000 - Ground Speed: 494 - Heading: 494>",
229
+ "<(B738) VT-AXZ - Altitude: 37000 - Ground Speed: 436 - Heading: 436>",
230
+ "<(B772) PH-BQA - Altitude: 32000 - Ground Speed: 496 - Heading: 496>",
231
+ "<(A388) 9V-SKS - Altitude: 33996 - Ground Speed: 498 - Heading: 498>",
232
+ "<(B77L) A7-BFY - Altitude: 32000 - Ground Speed: 478 - Heading: 478>",
233
+ "<(B78X) 9V-SCA - Altitude: 38996 - Ground Speed: 514 - Heading: 514>",
234
+ "<(B77W) A6-ENU - Altitude: 34000 - Ground Speed: 504 - Heading: 504>",
235
+ "<(A359) OH-LWF - Altitude: 40004 - Ground Speed: 476 - Heading: 476>",
236
+ # ...
235
237
  ]
236
238
  ```
237
239
 
238
240
 
239
241
  ### Airline logo
242
+
240
243
  ```ruby
241
244
  iata = "TK"
242
245
  icao = "THY"
243
- FlightRadar.logo(iata, icao)
246
+ FlightRadar.airline_logo(iata, icao)
244
247
  ```
245
248
 
246
249
  **Sample response:**
250
+
247
251
  ```ruby
248
- ["https://cdn.flightradar24.com/assets/airlines/logotypes/TK_THY.png", "https://www.flightradar24.com/static/images/data/operators/THY_logo0.png"]
252
+ %w[https://cdn.flightradar24.com/assets/airlines/logotypes/TK_THY.png https://www.flightradar24.com/static/images/data/operators/THY_logo0.png]
249
253
  ```
250
254
 
251
255
 
252
256
  ### Country flag
257
+
253
258
  ```ruby
254
259
  FlightRadar.country_flag("Poland")
255
260
  ```
256
261
 
257
262
  **Sample response:**
263
+
258
264
  ```ruby
259
265
  "https://www.flightradar24.com/static/images/data/flags-small/poland.gif"
260
266
  ```
261
267
 
262
-
263
-
264
268
  ## Development
265
269
 
266
270
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
data/Rakefile CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- require "rubocop/rake_task"
8
+ require 'rubocop/rake_task'
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
data/flight_radar.gemspec CHANGED
@@ -1,33 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = "flight_radar"
5
- spec.version = "0.2.0"
6
- spec.authors = ["Jakub Polak"]
7
- spec.email = ["jakub.polak.vz@gmail.com"]
4
+ spec.name = 'flight_radar'
5
+ spec.version = '0.2.1'
6
+ spec.authors = ['Jakub Polak']
7
+ spec.email = ['jakub.polak.vz@gmail.com']
8
8
 
9
- spec.summary = "Ruby gem for fetching aircraft data from Flightradar24"
10
- spec.description = "To use this API see more information at: https://www.flightradar24.com/terms-and-conditions"
11
- spec.homepage = "https://github.com/kupolak/flight_radar"
12
- spec.license = "MIT"
13
- spec.required_ruby_version = ">= 2.7.0"
9
+ spec.summary = 'Ruby gem for fetching aircraft data from Flightradar24'
10
+ spec.description = 'To use this API see more information at: https://www.flightradar24.com/terms-and-conditions'
11
+ spec.homepage = 'https://github.com/kupolak/flight_radar'
12
+ spec.license = 'MIT'
13
+ spec.required_ruby_version = '>= 3.2.0'
14
14
 
15
- spec.metadata["homepage_uri"] = spec.homepage
16
- spec.metadata["source_code_uri"] = "https://github.com/kupolak/flight_radar"
17
- spec.metadata["changelog_uri"] = "https://github.com/kupolak/flight_radar/blob/main/CHANGELOG.md"
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = 'https://github.com/kupolak/flight_radar'
17
+ spec.metadata['changelog_uri'] = 'https://github.com/kupolak/flight_radar/blob/main/CHANGELOG.md'
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
21
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
22
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
23
23
  end
24
- spec.bindir = "exe"
24
+ spec.bindir = 'exe'
25
25
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
26
+ spec.require_paths = ['lib']
27
27
 
28
- spec.add_dependency "httparty", "~> 0.21.0"
28
+ spec.add_dependency 'httparty', '~> 0.21.0'
29
29
 
30
- spec.add_development_dependency "rake", "~>13.1.0"
31
- spec.add_development_dependency "rspec", "~>3.12.0"
32
- spec.add_development_dependency "rubocop", "~>1.59.0"
30
+ spec.metadata['rubygems_mfa_required'] = 'true'
33
31
  end
@@ -1,59 +1,65 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # The `Core` module contains constants related to FlightRadar API endpoints,
4
+ # URLs for various data sources, and headers used in HTTP requests.
3
5
  module Core
4
- API_FLIGHT_RADAR_BASE_URL = "https://api.flightradar24.com/common/v1"
5
- CDN_FLIGHT_RADAR_BASE_URL = "https://cdn.flightradar24.com"
6
- FLIGHT_RADAR_BASE_URL = "https://www.flightradar24.com"
7
- DATA_LIVE_BASE_URL = "https://data-live.flightradar24.com"
8
- DATA_CLOUD_BASE_URL = "https://data-cloud.flightradar24.com"
6
+ API_FLIGHT_RADAR_BASE_URL = 'https://api.flightradar24.com/common/v1'
7
+ CDN_FLIGHT_RADAR_BASE_URL = 'https://cdn.flightradar24.com'
8
+ FLIGHT_RADAR_BASE_URL = 'https://www.flightradar24.com'
9
+ DATA_LIVE_BASE_URL = 'https://data-live.flightradar24.com'
10
+ DATA_CLOUD_BASE_URL = 'https://data-cloud.flightradar24.com'
9
11
 
10
12
  # User login URL.
11
- USER_LOGIN_URL = "#{FLIGHT_RADAR_BASE_URL}/user/login"
12
- USER_LOGOUT_URL = "#{FLIGHT_RADAR_BASE_URL}/user/logout"
13
+ USER_LOGIN_URL = "#{FLIGHT_RADAR_BASE_URL}/user/login".freeze
14
+ USER_LOGOUT_URL = "#{FLIGHT_RADAR_BASE_URL}/user/logout".freeze
13
15
 
14
16
  # Most tracked data URL
15
- MOST_TRACKED_URL = "#{FLIGHT_RADAR_BASE_URL}/flights/most-tracked"
17
+ MOST_TRACKED_URL = "#{FLIGHT_RADAR_BASE_URL}/flights/most-tracked".freeze
16
18
 
17
19
  # Search data URL
18
- SEARCH_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/v1/search/web/find?query={}&limit=50"
20
+ SEARCH_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/v1/search/web/find?query={}&limit=50".freeze
19
21
 
20
22
  # Flights data URLs.
21
- REAL_TIME_FLIGHT_TRACKER_DATA_URL = "#{DATA_CLOUD_BASE_URL}/zones/fcgi/feed.js"
22
- FLIGHT_DATA_URL = "#{DATA_LIVE_BASE_URL}/clickhandler/?flight={}"
23
+ REAL_TIME_FLIGHT_TRACKER_DATA_URL = "#{DATA_CLOUD_BASE_URL}/zones/fcgi/feed.js".freeze
24
+ FLIGHT_DATA_URL = "#{DATA_LIVE_BASE_URL}/clickhandler/?flight={}".freeze
23
25
 
24
26
  # Airports data URLs.
25
- API_AIRPORT_DATA_URL = "#{API_FLIGHT_RADAR_BASE_URL}/airport.json"
26
- AIRPORT_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/airports/traffic-stats/?airport="
27
- AIRPORTS_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/_json/airports.php"
27
+ API_AIRPORT_DATA_URL = "#{API_FLIGHT_RADAR_BASE_URL}/airport.json".freeze
28
+ AIRPORT_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/airports/traffic-stats/?airport=".freeze
29
+ AIRPORTS_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/_json/airports.php".freeze
28
30
 
29
31
  # Airlines data URL.
30
- AIRLINES_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/_json/airlines.php"
32
+ AIRLINES_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/_json/airlines.php".freeze
31
33
 
32
34
  # Zones data URL.
33
- ZONES_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/js/zones.js.php"
35
+ ZONES_DATA_URL = "#{FLIGHT_RADAR_BASE_URL}/js/zones.js.php".freeze
34
36
 
35
37
  # Country flag image URL.
36
- COUNTRY_FLAG_URL = "#{FLIGHT_RADAR_BASE_URL}/static/images/data/flags-small/"
38
+ COUNTRY_FLAG_URL = "#{FLIGHT_RADAR_BASE_URL}/static/images/data/flags-small/".freeze
37
39
 
38
40
  # Airline logo image URL.
39
- AIRLINE_LOGO_URL = "#{CDN_FLIGHT_RADAR_BASE_URL}/assets/airlines/logotypes/"
40
- ALTERNATIVE_AIRLINE_LOGO_URL = "#{FLIGHT_RADAR_BASE_URL}/static/images/data/operators/"
41
+ AIRLINE_LOGO_URL = "#{CDN_FLIGHT_RADAR_BASE_URL}/assets/airlines/logotypes/".freeze
42
+ ALTERNATIVE_AIRLINE_LOGO_URL = "#{FLIGHT_RADAR_BASE_URL}/static/images/data/operators/".freeze
41
43
 
44
+ # rubocop:disable Style/MutableConstant
42
45
  HEADERS = {
43
- "accept-encoding": "gzip, br",
44
- "accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
45
- "cache-control": "max-age=0",
46
- "origin": "#{FLIGHT_RADAR_BASE_URL}",
47
- "referer": "#{FLIGHT_RADAR_BASE_URL}/",
48
- "sec-fetch-dest": "empty",
49
- "sec-fetch-mode": "cors",
50
- "sec-fetch-site": "same-site",
51
- "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
46
+ 'accept-encoding': 'gzip, br',
47
+ 'accept-language': 'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7',
48
+ 'cache-control': 'max-age=0',
49
+ origin: FLIGHT_RADAR_BASE_URL.to_s,
50
+ referer: "#{FLIGHT_RADAR_BASE_URL}/",
51
+ 'sec-fetch-dest': 'empty',
52
+ 'sec-fetch-mode': 'cors',
53
+ 'sec-fetch-site': 'same-site',
54
+ 'user-agent': 'Mozilla/5.0 (Windows NT 6.1)
55
+ AppleWebKit/537.36 (KHTML, like Gecko)
56
+ Chrome/87.0.4280.88 Safari/537.36'
52
57
  }
58
+ # rubocop:enable Style/MutableConstant
53
59
 
54
60
  JSON_HEADERS = HEADERS
55
- JSON_HEADERS["accept"] = "application/json"
61
+ JSON_HEADERS['accept'] = 'application/json'
56
62
 
57
63
  IMAGE_HEADERS = HEADERS
58
- IMAGE_HEADERS["accept"] = "image/gif, image/jpg, image/jpeg, image/png"
64
+ IMAGE_HEADERS['accept'] = 'image/gif, image/jpg, image/jpeg, image/png'
59
65
  end
@@ -1,73 +1,98 @@
1
- # frozen_string_literal: true
2
-
3
- class Flight
4
- @default_text = "N/A"
5
-
6
- attr_accessor :id
7
-
8
- def initialize(flight_id, info)
9
- @id = flight_id
10
- @icao_24bit = get_info(info[0])
11
- @latitude = get_info(info[1])
12
- @longitude = get_info(info[2])
13
- @heading = get_info(info[3])
14
- @altitude = get_info(info[4])
15
- @ground_speed = get_info(info[5])
16
- @squawk = get_info(info[6])
17
- @aircraft_code = get_info(info[8])
18
- @registration = get_info(info[9])
19
- @time = get_info(info[10])
20
- @origin_airport_iata = get_info(info[11])
21
- @destination_airport_iata = get_info(info[12])
22
- @number = get_info(info[13])
23
- @airline_iata = get_info(info[13][0..1])
24
- @on_ground = get_info(info[14])
25
- @vertical_speed = get_info(info[15])
26
- @callsign = get_info(info[16])
27
- @airline_icao = get_info(info[18])
28
- end
29
-
30
- def inspect
31
- to_s
32
- end
33
-
34
- def to_s
35
- "<(#{@aircraft_code}) #{@registration} - Altitude: #{@altitude} - Ground Speed: #{@ground_speed} - Heading: #{@ground_speed}>"
36
- end
37
-
38
- def altitude
39
- "#{@altitude} ft"
40
- end
41
-
42
- def flight_level
43
- if @altitude > 10_000
44
- "#{@altitude[0..2]} FL"
45
- else
46
- altitude
47
- end
48
- end
49
-
50
- def ground_speed
51
- speed = "#{@ground_speed} kt"
52
- speed += "s" if @ground_speed > 1
53
- speed
54
- end
55
-
56
- def heading
57
- "#{@heading}°"
58
- end
59
-
60
- def vertical_speed
61
- "#{@vertical_speed} fpm"
62
- end
63
-
64
- private
65
-
66
- def get_info(info)
67
- if (info || info.zero?) && (info != @default_text)
68
- info
69
- else
70
- @default_text
71
- end
72
- end
73
- end
1
+ # frozen_string_literal: true
2
+
3
+ # The `Flight` class represents information about a flight obtained from a data source.
4
+ # It provides methods to access and format various attributes related to the flight,
5
+ # such as altitude, ground speed, heading, and more.
6
+ class Flight
7
+ DEFAULT_TEXT = 'N/A'
8
+
9
+ # Accessor for the flight ID.
10
+ attr_accessor :id
11
+
12
+ # Initializes a new instance of the `Flight` class with the given flight ID and information.
13
+ #
14
+ # @param flight_id [String] The unique identifier for the flight.
15
+ # @param info [Array<String>] An array containing various information about the flight.
16
+ # The order of elements in the array is assumed to follow a specific pattern.
17
+ # (e.g., info[0] is ICAO 24-bit address, info[1] is latitude, info[2] is longitude, and so on.)
18
+ # rubocop:disable Metrics
19
+ def initialize(flight_id, info)
20
+ @id = flight_id
21
+ @icao_24bit = get_info(info[0])
22
+ @latitude = get_info(info[1])
23
+ @longitude = get_info(info[2])
24
+ @heading = get_info(info[3])
25
+ @altitude = get_info(info[4])
26
+ @ground_speed = get_info(info[5])
27
+ @squawk = get_info(info[6])
28
+ @aircraft_code = get_info(info[8])
29
+ @registration = get_info(info[9])
30
+ @time = get_info(info[10])
31
+ @origin_airport_iata = get_info(info[11])
32
+ @destination_airport_iata = get_info(info[12])
33
+ @number = get_info(info[13])
34
+ @airline_iata = get_info(info[13][0..1])
35
+ @on_ground = get_info(info[14])
36
+ @vertical_speed = get_info(info[15])
37
+ @callsign = get_info(info[16])
38
+ @airline_icao = get_info(info[18])
39
+ end
40
+ # rubocop:enable Metrics
41
+
42
+ # Returns a string representation of the `Flight` object.
43
+ #
44
+ # @return [String] A formatted string containing relevant information about the flight.
45
+ def to_s
46
+ "<(#{@aircraft_code}) #{@registration}
47
+ - Altitude: #{@altitude}
48
+ - Ground Speed: #{@ground_speed}
49
+ - Heading: #{@ground_speed}>"
50
+ end
51
+
52
+ # Returns a formatted string representing the altitude of the flight.
53
+ #
54
+ # @return [String] Formatted altitude string (e.g., "5000 ft").
55
+ def altitude
56
+ "#{@altitude} ft"
57
+ end
58
+
59
+ # Returns a formatted string representing the flight level based on altitude.
60
+ #
61
+ # @return [String] Formatted flight level string (e.g., "FL350").
62
+ def flight_level
63
+ @altitude > 10_000 ? "#{@altitude[0..2]} FL" : altitude
64
+ end
65
+
66
+ # Returns a formatted string representing the ground speed of the flight.
67
+ #
68
+ # @return [String] Formatted ground speed string (e.g., "300 kts").
69
+ def ground_speed
70
+ speed = "#{@ground_speed} kt"
71
+ speed += 's' if @ground_speed > 1
72
+ speed
73
+ end
74
+
75
+ # Returns a formatted string representing the heading of the flight.
76
+ #
77
+ # @return [String] Formatted heading string (e.g., "120°").
78
+ def heading
79
+ "#{@heading}°"
80
+ end
81
+
82
+ # Returns a formatted string representing the vertical speed of the flight.
83
+ #
84
+ # @return [String] Formatted vertical speed string (e.g., "1000 fpm").
85
+ def vertical_speed
86
+ "#{@vertical_speed} fpm"
87
+ end
88
+
89
+ private
90
+
91
+ # Helper method to ensure that the information is not nil or the default text.
92
+ #
93
+ # @param info [String] The information to be validated.
94
+ # @return [String] The original information if it is not nil or the default text.
95
+ def get_info(info)
96
+ info && info != DEFAULT_TEXT ? info : DEFAULT_TEXT
97
+ end
98
+ end
@@ -1,38 +1,80 @@
1
- # frozen_string_literal: true
2
-
3
- require "httparty"
4
-
5
- # Request class for handling API requests
6
- class Request
7
- def initialize(url, headers = {}, params = {})
8
- @url = url
9
- @params = params
10
- @headers = headers
11
- @lang = "en"
12
-
13
- @response = send_request(url, headers, params)
14
- end
15
-
16
- def content
17
- @response.parsed_response
18
- end
19
-
20
- def content_type
21
- @response.content_type
22
- end
23
-
24
- def status_code
25
- @response.code
26
- end
27
-
28
- private
29
-
30
- def params_to_string(params)
31
- params.to_a.map { |k, v| "#{k}=#{v}" }.join("&")
32
- end
33
-
34
- def send_request(url, headers, params)
35
- url = "#{url}?#{params_to_string(params)}" if params
36
- HTTParty.get(url, headers)
37
- end
38
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'httparty'
4
+
5
+ # The `Request` class handles making HTTP requests using the HTTParty gem.
6
+ class Request
7
+ # Initializes a new instance of the `Request` class.
8
+ #
9
+ # @param url [String] The URL for the HTTP request.
10
+ # @param headers [Hash] (optional) The headers to include in the HTTP request.
11
+ # @param params [Hash] (optional) The parameters to include in the HTTP request.
12
+ def initialize(url, headers = {}, params = {})
13
+ @url = url
14
+ @params = params
15
+ @headers = headers
16
+ @lang = 'en'
17
+
18
+ @response = send_request(url, headers, params)
19
+ end
20
+
21
+ # Returns the parsed content of the HTTP response.
22
+ #
23
+ # @return [Hash] The parsed content of the HTTP response.
24
+ def content
25
+ @response.parsed_response
26
+ end
27
+
28
+ # Returns the content type of the HTTP response.
29
+ #
30
+ # @return [String] The content type of the HTTP response.
31
+ def content_type
32
+ @response.content_type
33
+ end
34
+
35
+ # Returns the status code of the HTTP response.
36
+ #
37
+ # @return [Integer] The status code of the HTTP response.
38
+ def status_code
39
+ @response.code
40
+ end
41
+
42
+ # Checks if the HTTP response code indicates success.
43
+ #
44
+ # @return [Boolean] Returns true if the response code is within the range 200 to 299 (inclusive), indicating success.
45
+ def success?
46
+ (200..299).include?(@response.code)
47
+ end
48
+
49
+ private
50
+
51
+ # Sends an HTTP GET request using the specified URL, headers, and parameters.
52
+ #
53
+ # @param url [String] The URL for the HTTP request.
54
+ # @param headers [Hash] The headers to include in the HTTP request.
55
+ # @param params [Hash] The parameters to include in the HTTP request.
56
+ # @return [HTTParty::Response] The HTTParty response object.
57
+ def send_request(url, headers, params)
58
+ full_url = build_full_url(url, params)
59
+ HTTParty.get(full_url, headers)
60
+ end
61
+
62
+ # Builds the full URL by appending parameters to the base URL.
63
+ #
64
+ # @param url [String] The base URL.
65
+ # @param params [Hash] The parameters to include in the URL.
66
+ # @return [String] The full URL for the HTTP request.
67
+ def build_full_url(url, params)
68
+ return url unless params
69
+
70
+ "#{url}?#{params_to_string(params)}"
71
+ end
72
+
73
+ # Converts parameters to a query string.
74
+ #
75
+ # @param params [Hash] The parameters to convert.
76
+ # @return [String] The query string representation of the parameters.
77
+ def params_to_string(params)
78
+ HTTParty::HashConversions.to_params(params)
79
+ end
80
+ end
data/lib/flight_radar.rb CHANGED
@@ -1,89 +1,121 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "flight_radar/core"
4
- require_relative "flight_radar/request"
5
- require_relative "flight_radar/flight"
3
+ require_relative 'flight_radar/core'
4
+ require_relative 'flight_radar/request'
5
+ require_relative 'flight_radar/flight'
6
6
 
7
7
  # FlightRadar module for sending requests to FlightRadar24 API
8
8
  module FlightRadar
9
- VERSION = "0.2.0"
9
+ VERSION = '0.2.1'
10
10
 
11
11
  module_function
12
12
 
13
+ # Default configuration parameters for FlightRadar requests.
13
14
  @config = {
14
- "faa": "1",
15
- "satellite": "1",
16
- "mlat": "1",
17
- "flarm": "1",
18
- "adsb": "1",
19
- "gnd": "1",
20
- "air": "1",
21
- "vehicles": "1",
22
- "estimated": "1",
23
- "maxage": "14400",
24
- "gliders": "1",
25
- "stats": "1",
26
- "limit": "5000"
15
+ faa: '1',
16
+ satellite: '1',
17
+ mlat: '1',
18
+ flarm: '1',
19
+ adsb: '1',
20
+ gnd: '1',
21
+ air: '1',
22
+ vehicles: '1',
23
+ estimated: '1',
24
+ maxage: '14400',
25
+ gliders: '1',
26
+ stats: '1',
27
+ limit: '5000'
27
28
  }
28
29
 
30
+ # Retrieves a list of airlines.
31
+ #
32
+ # @return [Array] An array containing information about airlines.
29
33
  def airlines
30
34
  request = Request.new(Core::AIRLINES_DATA_URL, Core::JSON_HEADERS)
31
- request.content["rows"]
35
+ request.content['rows']
32
36
  end
33
37
 
38
+ # Retrieves airline logos based on IATA and ICAO codes.
39
+ #
40
+ # @param iata [String] The IATA code of the airline.
41
+ # @param icao [String] The ICAO code of the airline.
42
+ # @return [Array] An array containing URLs of airline logos.
34
43
  def airline_logo(iata, icao)
35
44
  first_logo_url = "#{Core::AIRLINE_LOGO_URL}#{iata}_#{icao}.png"
36
45
  second_logo_url = "#{Core::ALTERNATIVE_AIRLINE_LOGO_URL}#{icao}_logo0.png"
37
46
 
38
- # check status codes
47
+ # Check the availability of logos and return URLs.
39
48
  result = []
40
49
  first_request = Request.new(first_logo_url, Core::IMAGE_HEADERS)
41
50
  second_request = Request.new(second_logo_url, Core::IMAGE_HEADERS)
42
- result << first_logo_url if first_request.status_code[0] != 4
43
- result << second_logo_url if second_request.status_code[0] != 4
44
- [first_logo_url, second_logo_url]
51
+ result << first_logo_url if first_request.success?
52
+ result << second_logo_url if second_request.success?
53
+ result
45
54
  end
46
55
 
56
+ # Retrieves traffic statistics for a specific airport.
57
+ #
58
+ # @param code [String] The code of the airport.
59
+ # @return [Hash] A hash containing traffic statistics for the specified airport.
47
60
  def airport(code)
48
61
  HTTParty.get("https://data-live.flightradar24.com/airports/traffic-stats/?airport=#{code}").parsed_response
49
62
  end
50
63
 
64
+ # Retrieves a list of airports.
65
+ #
66
+ # @return [Array] An array containing information about airports.
51
67
  def airports
52
68
  request = Request.new(Core::AIRPORTS_DATA_URL, Core::JSON_HEADERS)
53
- request.content["rows"]
69
+ request.content['rows']
54
70
  end
55
71
 
72
+ # Converts zone information into bounds string.
73
+ #
74
+ # @param zone [Hash] A hash containing zone information.
75
+ # @return [String] A string containing bounds information.
56
76
  def bounds(zone)
57
- "#{zone["tl_y"]},#{zone["br_y"]},#{zone["tl_x"]},#{zone["br_x"]}"
77
+ "#{zone['tl_y']},#{zone['br_y']},#{zone['tl_x']},#{zone['br_x']}"
58
78
  end
59
79
 
80
+ # Retrieves the URL of a country flag based on the country name.
81
+ #
82
+ # @param country [String] The name of the country.
83
+ # @return [String] The URL of the country flag.
60
84
  def country_flag(country)
61
- "#{Core::COUNTRY_FLAG_URL}#{country.downcase.gsub(" ", "-")}.gif"
85
+ "#{Core::COUNTRY_FLAG_URL}#{country.downcase.gsub(' ', '-')}.gif"
62
86
  end
63
87
 
88
+ # Retrieves detailed information about a specific flight.
89
+ #
90
+ # @param flight_id [String] The ID of the flight.
91
+ # @return [Hash] A hash containing detailed information about the specified flight.
64
92
  def flight_details(flight_id)
65
93
  HTTParty.get("https://data-live.flightradar24.com/clickhandler/?flight=#{flight_id}").parsed_response
66
94
  end
67
95
 
96
+ # Retrieves a list of flights based on specified parameters.
97
+ #
98
+ # @param params [Hash] (optional) Parameters for filtering flights.
99
+ # @return [Array] An array containing Flight objects.
68
100
  def flights(params = {})
69
- request_params = @config
101
+ request_params = @config.dup
70
102
  request_params[:airline] = params[:airline] if params[:airline]
71
- request_params[:bounds] = params[:bounds].gsub(",", "%2C") if params[:bounds]
103
+ request_params[:bounds] = params[:bounds]&.gsub(',', '%2C')
104
+
105
+ response = Request.new(Core::REAL_TIME_FLIGHT_TRACKER_DATA_URL, Core::JSON_HEADERS, request_params).content
72
106
 
73
- request = Request.new(Core::REAL_TIME_FLIGHT_TRACKER_DATA_URL, Core::JSON_HEADERS, request_params)
74
- response = request.content
75
107
  %w[full_count version stats].each { |k| response.delete(k) }
76
- flights = []
77
- response.each do |flight_id, flight_details|
78
- flights.append(Flight.new(flight_id, flight_details))
79
- end
80
- flights
108
+
109
+ response.map { |flight_id, flight_details| Flight.new(flight_id, flight_details) }
81
110
  end
82
111
 
112
+ # Retrieves information about flight tracking zones.
113
+ #
114
+ # @return [Hash] A hash containing information about flight tracking zones.
83
115
  def zones
84
116
  request = Request.new(Core::ZONES_DATA_URL, Core::JSON_HEADERS)
85
117
  request = request.content
86
- request.delete("version")
118
+ request.delete('version')
87
119
  request
88
120
  end
89
121
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flight_radar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Polak
@@ -24,48 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.21.0
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 13.1.0
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 13.1.0
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: 3.12.0
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: 3.12.0
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 1.59.0
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: 1.59.0
69
27
  description: 'To use this API see more information at: https://www.flightradar24.com/terms-and-conditions'
70
28
  email:
71
29
  - jakub.polak.vz@gmail.com
@@ -77,6 +35,7 @@ files:
77
35
  - ".gitignore"
78
36
  - ".rspec"
79
37
  - ".rubocop.yml"
38
+ - ".yardopts"
80
39
  - CHANGELOG.md
81
40
  - CODE_OF_CONDUCT.md
82
41
  - Gemfile
@@ -95,6 +54,7 @@ metadata:
95
54
  homepage_uri: https://github.com/kupolak/flight_radar
96
55
  source_code_uri: https://github.com/kupolak/flight_radar
97
56
  changelog_uri: https://github.com/kupolak/flight_radar/blob/main/CHANGELOG.md
57
+ rubygems_mfa_required: 'true'
98
58
  post_install_message:
99
59
  rdoc_options: []
100
60
  require_paths:
@@ -103,7 +63,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
63
  requirements:
104
64
  - - ">="
105
65
  - !ruby/object:Gem::Version
106
- version: 2.7.0
66
+ version: 3.2.0
107
67
  required_rubygems_version: !ruby/object:Gem::Requirement
108
68
  requirements:
109
69
  - - ">="