fcc 1.0.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fcc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: '1.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Keen
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-12 00:00:00.000000000 Z
11
+ date: 2011-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -39,89 +39,75 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.18'
41
41
  - !ruby/object:Gem::Dependency
42
- name: lightly
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.3.3
48
- type: :runtime
47
+ version: '2.1'
48
+ type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.3.3
54
+ version: '2.1'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rubyzip
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 2.3.2
62
- type: :runtime
61
+ version: 12.3.3
62
+ type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 2.3.2
69
- - !ruby/object:Gem::Dependency
70
- name: logger
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
68
+ version: 12.3.3
83
69
  - !ruby/object:Gem::Dependency
84
- name: bundler
70
+ name: rspec
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - "~>"
88
74
  - !ruby/object:Gem::Version
89
- version: '2.1'
75
+ version: 3.9.0
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
80
  - - "~>"
95
81
  - !ruby/object:Gem::Version
96
- version: '2.1'
82
+ version: 3.9.0
97
83
  - !ruby/object:Gem::Dependency
98
- name: rake
84
+ name: byebug
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - "~>"
87
+ - - ">="
102
88
  - !ruby/object:Gem::Version
103
- version: 12.3.3
89
+ version: '0'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - "~>"
94
+ - - ">="
109
95
  - !ruby/object:Gem::Version
110
- version: 12.3.3
96
+ version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
- name: rspec
98
+ name: awesome_print
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
- - - "~>"
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
- version: 3.9.0
103
+ version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - "~>"
108
+ - - ">="
123
109
  - !ruby/object:Gem::Version
124
- version: 3.9.0
110
+ version: '0'
125
111
  description: ''
126
112
  email: jeff@keen.me
127
113
  executables:
@@ -131,15 +117,13 @@ extensions: []
131
117
  extra_rdoc_files: []
132
118
  files:
133
119
  - ".document"
134
- - ".github/workflows/release.yml"
135
- - ".github/workflows/tests.yml"
136
120
  - ".gitignore"
137
- - CHANGELOG.md
138
121
  - Gemfile
139
122
  - Gemfile.lock
140
123
  - LICENSE.txt
141
124
  - README.md
142
125
  - Rakefile
126
+ - VERSION
143
127
  - bin/console
144
128
  - bin/setup
145
129
  - fcc.gemspec
@@ -147,23 +131,18 @@ files:
147
131
  - lib/fcc/station.rb
148
132
  - lib/fcc/station/cache.rb
149
133
  - lib/fcc/station/extended_info.rb
134
+ - lib/fcc/station/extended_info/parser.rb
150
135
  - lib/fcc/station/index.rb
151
136
  - lib/fcc/station/info.rb
152
- - lib/fcc/station/lms_data.rb
153
- - lib/fcc/station/parsers/extended_info.rb
154
- - lib/fcc/station/parsers/lms_data.rb
155
- - lib/fcc/station/record_delegate.rb
156
- - lib/fcc/station/record_group.rb
157
- - lib/fcc/station/result.rb
137
+ - lib/fcc/station/result_delegate.rb
158
138
  - lib/version.rb
159
- - package.json
160
139
  - spec/fcc_spec.rb
161
140
  - spec/spec_helper.rb
162
141
  homepage: http://github.com/jkeen/fcc
163
142
  licenses:
164
143
  - MIT
165
144
  metadata: {}
166
- post_install_message:
145
+ post_install_message:
167
146
  rdoc_options: []
168
147
  require_paths:
169
148
  - lib
@@ -178,9 +157,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
157
  - !ruby/object:Gem::Version
179
158
  version: '0'
180
159
  requirements: []
181
- rubyforge_project:
182
- rubygems_version: 2.7.6
183
- signing_key:
160
+ rubygems_version: 3.1.2
161
+ signing_key:
184
162
  specification_version: 4
185
163
  summary: Searches the FCC's FM, AM, and TV databases
186
164
  test_files:
@@ -1,27 +0,0 @@
1
- ---
2
- name: Generate New Release
3
-
4
- on:
5
- workflow_dispatch:
6
-
7
- jobs:
8
- release:
9
- name: Release
10
- runs-on: ubuntu-18.04
11
- steps:
12
- - name: Checkout
13
- uses: actions/checkout@v2
14
- with:
15
- fetch-depth: 0
16
- persist-credentials: false
17
- - name: Setup Node.js
18
- uses: actions/setup-node@v2
19
- with:
20
- node-version: 14
21
- - name: Install Dependencies
22
- run: yarn install --frozen-lockfile
23
- - name: Release
24
- env:
25
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26
- GEM_HOST_API_KEY: ${{ secrets.GEM_HOST_API_KEY }}
27
- run: node_modules/.bin/semantic-release
@@ -1,53 +0,0 @@
1
- name: Tests
2
- on:
3
- push:
4
- pull_request:
5
- concurrency:
6
- group: ci-tests-${{ github.ref }}-1
7
- cancel-in-progress: true
8
- jobs:
9
- test:
10
- name: Run Tests
11
- strategy:
12
- fail-fast: false
13
- matrix:
14
- os: [ ubuntu-latest ]
15
- ruby: [ '2.7' ]
16
- runs-on: ${{ matrix.os }}
17
- steps:
18
- - uses: actions/checkout@v3
19
- - name: Set up Ruby
20
- uses: ruby/setup-ruby@v1
21
- with:
22
- ruby-version: ${{ matrix.ruby }}
23
- - uses: actions/cache@v3
24
- with:
25
- path: vendor/bundle
26
- key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
27
- restore-keys: |
28
- ${{ runner.os }}-gems-
29
- - name: Bundle Install
30
- run: |
31
- gem install bundler
32
- bundle install --jobs 4 --retry 3
33
-
34
- - name: Run RSpec
35
- run: bundle exec rspec -f j -o tmp/rspec_results.json -f p
36
-
37
- - name: RSpec Report
38
- uses: SonicGarden/rspec-report-action@v2
39
- with:
40
- token: ${{ secrets.GITHUB_TOKEN }}
41
- json-path: tmp/rspec_results.json
42
- if: always()
43
- publish:
44
- name: Release
45
- runs-on: ubuntu-latest
46
- if: github.ref == 'refs/heads/main'
47
- needs: [test]
48
- steps:
49
- - name: Dispatch Release
50
- uses: benc-uk/workflow-dispatch@v1
51
- with:
52
- workflow: Generate New Release
53
- token: ${{ secrets.GITHUB_TOKEN }}
data/CHANGELOG.md DELETED
@@ -1,11 +0,0 @@
1
- fcc changelog
2
-
3
- # 1.0.0 (2023-02-12)
4
-
5
- ## [1.4.0](https://github.com/jkeen/fcc/compare/v1.4.0...v1.3.0) (2022-11-25)
6
-
7
-
8
- ## [1.3.0](https://github.com/jkeen/fcc/compare/v1.3.0...v1.2.1) (2022-07-10)
9
-
10
- - Combine data from multiple sources to get a full picture of the station data (transition api, public api, LMS csv files)
11
- - Now includes related transmitters, repeaters, low power stations, etc
@@ -1,139 +0,0 @@
1
- require 'httparty'
2
- require 'open-uri'
3
- require 'net/http'
4
- require 'uri'
5
- require 'date'
6
- require_relative './parsers/lms_data'
7
-
8
- module FCC
9
- module Station
10
- class LmsData
11
- BASE_URI = 'https://enterpriseefiling.fcc.gov/dataentry/api/download/dbfile'
12
- # include HTTParty
13
-
14
- def find_all_related(call_sign: )
15
- stations = find_related_stations(call_sign: call_sign)
16
- translators = find_translators_for(call_sign: stations.keys)
17
- stations.merge(translators)
18
- end
19
-
20
- def find_translators_for(call_sign: )
21
- call_signs = [call_sign].flatten
22
-
23
- records = common_station_data.entries.select do |entry|
24
- call_signs.any? { |call_sign| call_signs_match?(call_sign, entry['callsign']) }
25
- end
26
-
27
- facility_ids = records.map { |r| r['facility_id'] }.uniq.compact
28
-
29
- matched_facilities = facilities.entries.select do |facility|
30
- facility_ids.include?(facility['primary_station']) #{}|| facility_ids.include?(facility['facility_id'])
31
- end
32
-
33
- {}.tap do |hash|
34
- matched_facilities.each do |record|
35
- hash[record['callsign']] = record['facility_id']
36
- end
37
- end
38
- end
39
-
40
- def find_related_stations(call_sign: )
41
- call_signs = [call_sign].flatten
42
-
43
- records = common_station_data.entries.select do |entry|
44
- call_signs.any? { call_signs_match?(call_sign, entry['callsign']) }
45
- end
46
-
47
- correlated_app_ids = records.map { |m| m['eeo_application_id'] }
48
- matches = common_station_data.entries.select do |entry|
49
- correlated_app_ids.include?(entry['eeo_application_id'])
50
- end
51
-
52
- {}.tap do |hash|
53
- matches.each do |record|
54
- hash[record['callsign']] = record['facility_id']
55
- end
56
- end
57
- end
58
-
59
- def facilities
60
- @facilities ||= read(:facility)
61
- end
62
-
63
- def common_station_data
64
- @common_station_data ||= read(:common_station)
65
- end
66
-
67
- def find_facilities(facility_ids:, call_signs: [])
68
- matched_facilities = facilities.entries.select do |facility|
69
- facility_ids.include?(facility['primary_station']) || facility_ids.include?(facility['facility_id']) || call_signs.include?(facility['callsign'])
70
- end
71
-
72
- {}.tap do |hash|
73
- matched_facilities.each do |record|
74
- hash[record['callsign']] = record['facility_id']
75
- end
76
- end
77
- end
78
-
79
- def find_call_signs(call_signs:)
80
- common_station_data.entries.select do |entry|
81
- call_signs.any? do |call_sign|
82
- call_signs_match?(call_sign, entry['callsign'])
83
- end
84
- end
85
- end
86
-
87
- def read(file_name)
88
- key = "#{lms_date}-#{file_name}"
89
- remote_url = URI("#{BASE_URI}/#{lms_date}/#{file_name}.zip")
90
- FCC.log remote_url
91
- contents = FCC.cache.fetch key do
92
- begin
93
- temp_file = http_download_uri(remote_url)
94
- break if temp_file.empty?
95
-
96
- contents = ""
97
- Zip::File.open_buffer(temp_file) do |zf|
98
- contents = zf.read(zf.entries.first)
99
- break
100
- end
101
-
102
- value = contents
103
- rescue Exception => e
104
- FCC.error(e.message)
105
- value = nil
106
- ensure
107
- value
108
- end
109
- end
110
-
111
- if contents
112
- CSV.parse(contents, col_sep: '|', headers: true)
113
- end
114
- end
115
-
116
- protected
117
-
118
- def call_signs_match?(ours, theirs)
119
- theirs.to_s.upcase.to_s == ours.to_s.upcase.to_s || theirs.to_s.upcase =~ Regexp.new("^#{ours.to_s.upcase}[-—–][A-Z0-9]+$")
120
- end
121
-
122
- def http_download_uri(uri)
123
- FCC.log 'Downloading ' + uri.to_s
124
- begin
125
- Tempfile.create { HTTParty.get(uri)&.body }
126
- rescue Exception => e
127
- FCC.error "=> Exception: '#{e}'. Skipping download."
128
-
129
- raise e
130
- return false
131
- end
132
- end
133
-
134
- def lms_date
135
- Date.today.strftime('%m-%d-%Y')
136
- end
137
- end
138
- end
139
- end
@@ -1,78 +0,0 @@
1
- require 'httparty'
2
-
3
- module FCC
4
- module Station
5
- module Parsers
6
- class ExtendedInfo < HTTParty::Parser
7
- def parse
8
- results = []
9
- body.each_line do |row|
10
- attrs = {}
11
- attrs[:raw] = row
12
- fields = row.split('|').slice(1...-1).collect(&:strip).map { |v| v == '-' ? "" : v }
13
-
14
- attrs[:call_sign] = fields[0]
15
- attrs[:frequency] = parse_frequency(fields[1])
16
- attrs[:band] = fields[2]
17
- attrs[:channel] = fields[3]
18
- attrs[:antenna_type] = fields[4] # Directional Antenna (DA) or NonDirectional (ND)
19
- attrs[:operating_hours] = fields[5] if fields[5] && attrs[:band]&.upcase == "AM" # (Only used for AM)
20
- attrs[:station_class] = fields[6]
21
- attrs[:region_2_station_class] = fields[7] if fields[7] && attrs[:band]&.upcase == "AM" # (only used for AM)
22
- attrs[:status] = fields[8]
23
- attrs[:city] = fields[9]
24
- attrs[:state] = fields[10]
25
- attrs[:country] = fields[11]
26
- attrs[:file_number] = fields[12] #File Number (Application, Construction Permit or License) or
27
- attrs[:signal_strength] = parse_signal_strength(fields[13]) # Effective Radiated Power --
28
- attrs[:effective_radiated_power] = parse_signal_strength(fields[14]) # Effective Radiated Power -- vertically polarized (maximum)
29
- attrs[:haat_horizontal] = fields[15] # Antenna Height Above Average Terrain (HAAT) -- horizontal polarization
30
- attrs[:haat_vertical] = fields[16] # Antenna Height Above Average Terrain (HAAT) -- vertical polarization
31
- attrs[:fcc_id] = fields[17] # Facility ID Number (unique to each station)
32
- attrs[:latitude] = parse_latitude(fields[18], fields[19], fields[20], fields[21])
33
- attrs[:longitude] = parse_longitude(fields[22], fields[23], fields[24], fields[25])
34
- attrs[:licensed_to] = fields[26] # Licensee or Permittee
35
-
36
- results << attrs
37
- end
38
-
39
- results
40
- end
41
-
42
- def parse_longitude(direction, degrees, minutes, seconds)
43
- decimal_degrees = degrees.to_i + (minutes.to_f / 60) + (seconds.to_f / 3600)
44
-
45
- "#{direction =~ /W/ ? '-' : ''}#{decimal_degrees}"
46
- end
47
-
48
- def parse_latitude(direction, degrees, minutes, seconds)
49
- decimal_degrees = degrees.to_i + (minutes.to_f / 60) + (seconds.to_f / 3600)
50
-
51
- "#{direction =~ /S/ ? '-' : ''}#{decimal_degrees}"
52
- end
53
-
54
- def parse_signal_strength(power_string)
55
- return unless power_string.present?
56
-
57
- number, unit = power_string.strip.scan(/^([0-9.]+)\s+(\w+)$?/).flatten
58
- multiplier = case unit&.downcase
59
- when "w"
60
- 1
61
- when "kw"
62
- 1000
63
- when "mw"
64
- 1000000
65
- else
66
- 1
67
- end
68
-
69
- number.to_f * multiplier
70
- end
71
-
72
- def parse_frequency(freq)
73
- freq.to_f
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,13 +0,0 @@
1
- require 'httparty'
2
-
3
- module FCC
4
- module Station
5
- module Parsers
6
- class LmsData < HTTParty::Parser
7
- def parse
8
- body
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,121 +0,0 @@
1
- require 'active_support/inflector'
2
-
3
- module FCC
4
- module Station
5
- class RecordDelegate
6
- def initialize(result)
7
- @result = result
8
- end
9
-
10
- def method_missing(m, *args, &block)
11
- return find_result(@result, m) unless @result.is_a?(Array)
12
- return find_result(@result.first, m) if @result.size == 1
13
-
14
- filtered_results = @result.filter { |result|
15
- result[:status] == 'LIC' # Licensed only, no construction permits
16
- }
17
-
18
- filtered_results = filtered_results.collect { |res|
19
- find_result(res, m)
20
- }.uniq
21
-
22
- filtered_results.size == 1 ? filtered_results.first : filtered_results
23
- end
24
-
25
- def to_json
26
- return {}.tap do |record|
27
- [Station::Result::EXTENDED_ATTRIBUTES | Station::Result::BASIC_ATTRIBUTES].flatten.each do |attr|
28
- record[attr.to_sym] = send(attr.to_sym)
29
- end
30
-
31
- %i[contact owner community].each do |attr|
32
- result = send(attr.to_sym)
33
- next unless result
34
-
35
- record[attr] ||= if result.is_a?(Struct)
36
- result.to_h.compact
37
- elsif result.is_a?(Array) && result.compact.size > 0
38
- result
39
- elsif result.present?
40
- result.to_s
41
- end
42
- end
43
- end
44
- end
45
-
46
- def has_data?
47
- @result.present?
48
- end
49
-
50
- def id
51
- super || send(:fcc_id)
52
- end
53
-
54
- def frequency
55
- super&.to_f
56
- end
57
-
58
- def rf_channel
59
- super || send(:channel)
60
- end
61
-
62
- def operating_hours
63
- super&.downcase
64
- end
65
-
66
- def owner
67
- @owner ||= begin
68
- contact = Contact.new(
69
- name: party_name || licensed_to,
70
- address: party_address_1,
71
- address2: party_address_2,
72
- city: (party_city || city),
73
- state: (party_state || state),
74
- zip_code: party_zip_1,
75
- country: country,
76
- phone: party_phone
77
- )
78
-
79
- contact if contact.to_h.compact.any?
80
- end
81
- end
82
-
83
- def community
84
- @community ||= begin
85
- community = Community.new(city: community_city || city, state: community_state || state, country: country)
86
- community if community.to_h.compact.any?
87
- end
88
- end
89
-
90
- def contact
91
- contact = main_studio_contact
92
-
93
- return unless contact
94
- @contact ||= begin
95
- contact = Contact.new(name: contact['contactName'], title: contact['contactTitle'], address: contact['contactAddress1'], address2: contact['contactAddress2'], city: contact['contactCity'], state: contact['contactState'], zip_code: contact['contactZip'], phone: contact['contactPhone'], fax: contact['contactFax'], email: contact['contactEmail'], website: contact['contactWebsite'])
96
- contact if contact.to_h.compact.any?
97
- end
98
- end
99
-
100
- def inspect
101
- "<RecordDelegate:[#{band}] #{frequency} #{call_sign} — #{community.city} #{community.state} />"
102
- end
103
-
104
- private
105
-
106
- def find_key(result, name)
107
- result&.keys&.detect { |d| name.to_s == d.to_s } || result&.keys&.detect { |d| name.to_s == d.to_s.underscore }
108
- end
109
-
110
- def find_result(result, name)
111
- matched_key = find_key(result, name)
112
-
113
- if matched_key
114
- result[matched_key]
115
- else
116
- nil
117
- end
118
- end
119
- end
120
- end
121
- end
@@ -1,49 +0,0 @@
1
- require 'active_support/inflector'
2
-
3
- module FCC
4
- module Station
5
- class RecordGroup
6
- def initialize(results = [])
7
- @results = results.map do |result|
8
- result.is_a?(RecordDelegate) ? result : RecordDelegate.new(result)
9
- end
10
- end
11
-
12
- def to_json
13
- return {}.tap do |record|
14
- [Station::Result::EXTENDED_ATTRIBUTES | Station::Result::BASIC_ATTRIBUTES].flatten.each do |attr|
15
- record[attr.to_sym] = result_attribute(attr.to_sym)
16
- end
17
-
18
- %i[contact owner community].each do |attr|
19
- result = result_attribute(attr.to_sym)
20
- next unless result
21
-
22
- record[attr] ||= if result.is_a?(Struct)
23
- result.to_h.compact
24
- elsif result.is_a?(Array) && result.compact.size > 0
25
- result
26
- elsif result.present?
27
- result.to_s
28
- end
29
- end
30
- end
31
- end
32
-
33
- def result_attribute(attr)
34
- @results.collect { |r| r.send(attr.to_sym) }.compact.first
35
- end
36
-
37
- def method_missing(m, *args, &block)
38
- result = result_attribute(m.to_sym)
39
-
40
- if result.is_a?(Array) && result.size == 1
41
- result = result.first
42
- end
43
-
44
- result
45
- end
46
-
47
- end
48
- end
49
- end