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
|
# cpi-feed
|
||||||
|
|
||||||
Transforms [this](https://api.bls.gov/publicAPI/v2/timeseries/data/CUUSA210SA0)
|
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
|
## usage
|
||||||
|
|
||||||
This is intended for use with Google Sheets via:
|
This is intended for use with Google Sheets via:
|
||||||
|
|
||||||
```
|
```
|
||||||
=IMPORTDATA("https://s3.amazonaws.com/meatballhat/cpi/current.csv")
|
=CURRENTCPI("{bls-token}")
|
||||||
```
|
```
|
||||||
|
|
||||||
## deployment
|
## deployment
|
||||||
|
|
||||||
A copy of this thing is deployed to Heroku with a Heroku Scheduler addon
|
Just like [this
|
||||||
configured to run the following once daily:
|
example](https://apipheny.io/import-json-google-sheets/).
|
||||||
|
|
||||||
``` bash
|
|
||||||
bundle exec ./sync
|
|
||||||
```
|
|
||||||
|
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