fcc 1.0.0 → 1.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.
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