It is a google sheets function now
This commit is contained in:
parent
5aadce63fd
commit
4f05c1cbae
15
.rubocop.yml
15
.rubocop.yml
@ -1,15 +0,0 @@
|
||||
inherit_from: .rubocop_todo.yml
|
||||
|
||||
AllCops:
|
||||
NewCops: enable
|
||||
SuggestExtensions: false
|
||||
TargetRubyVersion: 3.2
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
Style/AccessModifierDeclarations:
|
||||
EnforcedStyle: inline
|
||||
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
EnforcedStyle: indented
|
@ -1,7 +0,0 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# on 2023-01-01 17:49:35 UTC using RuboCop version 1.42.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
41
Code.gs
Normal file
41
Code.gs
Normal file
@ -0,0 +1,41 @@
|
||||
const defaultSeriesURL = "https://api.bls.gov/publicAPI/v2/timeseries/data/";
|
||||
const defaultSeriesID = "CUUR0200SA0";
|
||||
const defaultStartYear = "2014";
|
||||
|
||||
function CURRENTCPI(blsToken, options) {
|
||||
var seriesURL = options?.seriesURL || defaultSeriesURL;
|
||||
var seriesID = options?.seriesID || defaultSeriesID;
|
||||
var startYear = options?.startYear || defaultStartYear;
|
||||
var endYear = options?.endYear || new Date().getFullYear().toString();
|
||||
|
||||
var reqOptions = {
|
||||
"method": "post",
|
||||
"payload": JSON.stringify({
|
||||
"seriesid": [seriesID],
|
||||
"startyear": startYear,
|
||||
"endyear": endYear
|
||||
}),
|
||||
"contentType": "application/json",
|
||||
"headers": {
|
||||
"Authorization": "token "+blsToken,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
var resp = UrlFetchApp.fetch(seriesURL, reqOptions);
|
||||
var rawData = resp.getContentText().toString().trim();
|
||||
var parsedData = JSON.parse(rawData);
|
||||
var respData = parsedData.Results.series[0].data;
|
||||
var rows = [["year", "period", "period_name", "value"]];
|
||||
|
||||
for (var i = 0; i < respData.length; i++) {
|
||||
rows.push([
|
||||
respData[i].year,
|
||||
respData[i].period,
|
||||
respData[i].periodName,
|
||||
respData[i].value
|
||||
]);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
11
Gemfile
11
Gemfile
@ -1,11 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '3.2.0' if ENV.key?('DYNO')
|
||||
|
||||
gem 'aws-sdk-s3', '~> 1'
|
||||
gem 'pry', group: %i[development test]
|
||||
gem 'rack'
|
||||
gem 'rake'
|
||||
gem 'rubocop', group: %i[development test]
|
62
Gemfile.lock
62
Gemfile.lock
@ -1,62 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ast (2.4.2)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.686.0)
|
||||
aws-sdk-core (3.168.4)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.61.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.117.2)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
coderay (1.1.3)
|
||||
jmespath (1.6.2)
|
||||
json (2.6.3)
|
||||
method_source (1.0.0)
|
||||
parallel (1.22.1)
|
||||
parser (3.1.3.0)
|
||||
ast (~> 2.4.1)
|
||||
pry (0.14.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
rack (3.0.3)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
regexp_parser (2.6.1)
|
||||
rexml (3.2.5)
|
||||
rubocop (1.42.0)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.1.2.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.24.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.24.1)
|
||||
parser (>= 3.1.1.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
unicode-display_width (2.3.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
aws-sdk-s3 (~> 1)
|
||||
pry
|
||||
rack
|
||||
rake
|
||||
rubocop
|
||||
|
||||
BUNDLED WITH
|
||||
2.1.2
|
12
README.md
12
README.md
@ -1,21 +1,17 @@
|
||||
# cpi-feed
|
||||
|
||||
Transforms [this](https://api.bls.gov/publicAPI/v2/timeseries/data/CUUSA210SA0)
|
||||
into [this](https://s3.amazonaws.com/meatballhat/cpi/current.csv) :tada:.
|
||||
into CSV rows.
|
||||
|
||||
## usage
|
||||
|
||||
This is intended for use with Google Sheets via:
|
||||
|
||||
```
|
||||
=IMPORTDATA("https://s3.amazonaws.com/meatballhat/cpi/current.csv")
|
||||
=CURRENTCPI("{bls-token}")
|
||||
```
|
||||
|
||||
## deployment
|
||||
|
||||
A copy of this thing is deployed to Heroku with a Heroku Scheduler addon
|
||||
configured to run the following once daily:
|
||||
|
||||
``` bash
|
||||
bundle exec ./sync
|
||||
```
|
||||
Just like [this
|
||||
example](https://apipheny.io/import-json-google-sheets/).
|
||||
|
11
Rakefile
11
Rakefile
@ -1,11 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
begin
|
||||
require 'rubocop/rake_task'
|
||||
rescue LoadError => e
|
||||
warn e
|
||||
end
|
||||
|
||||
RuboCop::RakeTask.new if defined?(RuboCop)
|
||||
|
||||
task default: %i[rubocop]
|
@ -1,3 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
run ->(*) { [301, { 'Location' => ENV.fetch('CPI_FEED_URL', nil) }, []] }
|
@ -1,94 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require 'json'
|
||||
require 'net/http'
|
||||
require 'net/https'
|
||||
require 'uri'
|
||||
|
||||
class CPIFetcher
|
||||
def cpi
|
||||
resp = fetch_raw_response
|
||||
return nil unless resp['Results']['series']
|
||||
|
||||
resp['Results']['series'].first['data']
|
||||
end
|
||||
|
||||
def cpi_csv
|
||||
CSV.generate do |csv|
|
||||
csv << %w[year period period_name value]
|
||||
cpi.each do |rec|
|
||||
csv << %w[
|
||||
year period periodName value
|
||||
].map { |k| rec.fetch(k) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private def url
|
||||
@url ||= URI(ENV['CPI_SERIES_URL'] || File.join(
|
||||
'https://api.bls.gov',
|
||||
'publicAPI/v2/timeseries/data/'
|
||||
))
|
||||
end
|
||||
|
||||
private def series_id
|
||||
@series_id ||= ENV.fetch(
|
||||
'CPI_SERIES_ID', 'CUUR0200SA0'
|
||||
).split(/[, ]/).map(&:strip).reject(&:empty?)
|
||||
end
|
||||
|
||||
private def start_year
|
||||
@start_year ||= ENV.fetch('CPI_SERIES_START_YEAR', '2014')
|
||||
end
|
||||
|
||||
private def end_year
|
||||
@end_year ||= ENV.fetch('CPI_SERIES_END_YEAR', Time.now.year.to_s)
|
||||
end
|
||||
|
||||
private def bls_token
|
||||
@bls_token ||= ENV.fetch('BLS_TOKEN')
|
||||
end
|
||||
|
||||
private def start_cpi_value(data)
|
||||
data = data.sort do |a, b|
|
||||
record_key(a) <=> record_key(b)
|
||||
end
|
||||
data.first.fetch('value')
|
||||
end
|
||||
|
||||
private def record_key(record)
|
||||
"#{record['year']}.#{record['period']}"
|
||||
end
|
||||
|
||||
private def latest_cpi_value(data)
|
||||
data.find { |d| d['latest'] == 'true' }.fetch('value')
|
||||
end
|
||||
|
||||
private def fetch_raw_response
|
||||
Net::HTTP.new(url.hostname, url.port)
|
||||
.tap { |h| h.use_ssl = true }
|
||||
.tap { |h| h.verify_mode = OpenSSL::SSL::VERIFY_PEER }
|
||||
.then { |h| JSON.parse(h.request(build_request).body) }
|
||||
end
|
||||
|
||||
private def build_request
|
||||
Net::HTTP::Post.new(url)
|
||||
.tap { |r| r['Authorization'] = "token #{bls_token}" }
|
||||
.tap { |r| r['Content-Type'] = 'application/json' }
|
||||
.tap { |r| r.body = JSON.generate(build_request_body) }
|
||||
end
|
||||
|
||||
private def build_request_body
|
||||
{
|
||||
'seriesid' => series_id,
|
||||
'startyear' => start_year,
|
||||
'endyear' => end_year
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if $PROGRAM_NAME == __FILE__
|
||||
puts CPIFetcher.new.cpi_csv
|
||||
exit 0
|
||||
end
|
@ -1,30 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'aws-sdk-s3'
|
||||
|
||||
class MiniS3put
|
||||
def initialize(key: nil, instream: $stdin)
|
||||
@bucket = ENV.fetch('CPI_FEED_AWS_BUCKET')
|
||||
@key = key || ENV.fetch('CPI_FEED_AWS_KEY')
|
||||
@instream = instream
|
||||
end
|
||||
|
||||
attr_reader :bucket, :key, :instream
|
||||
private :bucket, :key, :instream
|
||||
|
||||
def put
|
||||
body = instream.read
|
||||
puts Aws::S3::Resource.new.bucket(bucket).object(key).put(body:)
|
||||
|
||||
puts body
|
||||
|
||||
puts Aws::S3::Client.new.put_object_acl(
|
||||
bucket:, key:, acl: 'public-read'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if $PROGRAM_NAME == __FILE__
|
||||
MiniS3put.new(key: ARGV.first).put
|
||||
exit 0
|
||||
end
|
Loading…
Reference in New Issue
Block a user