From 4f05c1cbae2de9a3b59f6f43f787b5e19354196f Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 21 Jan 2023 20:29:36 -0500 Subject: [PATCH] It is a google sheets function now --- .rubocop.yml | 15 -------- .rubocop_todo.yml | 7 ---- Code.gs | 41 +++++++++++++++++++++ Gemfile | 11 ------ Gemfile.lock | 62 ------------------------------- Procfile | 1 - README.md | 12 ++---- Rakefile | 11 ------ config.ru | 3 -- cpi_fetcher.rb | 94 ----------------------------------------------- mini_s3put.rb | 30 --------------- sync | 2 - 12 files changed, 45 insertions(+), 244 deletions(-) delete mode 100644 .rubocop.yml delete mode 100644 .rubocop_todo.yml create mode 100644 Code.gs delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 Procfile delete mode 100644 Rakefile delete mode 100644 config.ru delete mode 100644 cpi_fetcher.rb delete mode 100644 mini_s3put.rb delete mode 100755 sync diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index dbc55a8..0000000 --- a/.rubocop.yml +++ /dev/null @@ -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 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index d0169f5..0000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -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. diff --git a/Code.gs b/Code.gs new file mode 100644 index 0000000..69fde3c --- /dev/null +++ b/Code.gs @@ -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; +} diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 7daa627..0000000 --- a/Gemfile +++ /dev/null @@ -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] diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 4bb864a..0000000 --- a/Gemfile.lock +++ /dev/null @@ -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 diff --git a/Procfile b/Procfile deleted file mode 100644 index c3936b2..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: bundle exec rackup -p $PORT diff --git a/README.md b/README.md index 853d380..f3777fb 100644 --- a/README.md +++ b/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/). diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 3745221..0000000 --- a/Rakefile +++ /dev/null @@ -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] diff --git a/config.ru b/config.ru deleted file mode 100644 index c45ab08..0000000 --- a/config.ru +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -run ->(*) { [301, { 'Location' => ENV.fetch('CPI_FEED_URL', nil) }, []] } diff --git a/cpi_fetcher.rb b/cpi_fetcher.rb deleted file mode 100644 index 4021259..0000000 --- a/cpi_fetcher.rb +++ /dev/null @@ -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 diff --git a/mini_s3put.rb b/mini_s3put.rb deleted file mode 100644 index a4f8087..0000000 --- a/mini_s3put.rb +++ /dev/null @@ -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 diff --git a/sync b/sync deleted file mode 100755 index 7b44e7c..0000000 --- a/sync +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -ruby cpi_fetcher.rb | ruby mini_s3put.rb