Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use Ruby instead of JS for getting donations data from Notion
Browse files Browse the repository at this point in the history
patbl committed Feb 8, 2024
1 parent 451352b commit b719229
Showing 9 changed files with 128 additions and 1,315 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@ jobs:
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
run: |
bundle exec middleman build
env:
NOTION_DONATIONS_UPDATER_API_KEY: ${{ secrets.NOTION_DONATIONS_UPDATER_API_KEY }}

- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4
79 changes: 39 additions & 40 deletions lib/donation.rb
Original file line number Diff line number Diff line change
@@ -1,82 +1,81 @@
# frozen_string_literal: true

class Donation
ATTRIBUTES = [
{name: :organization},
{name: :date},
{name: :amount},
{name: :is_grant, new_name: :grant?, default: false},
{name: :is_daf_contribution, new_name: :daf_contribution?, default: false},
{name: :note, default: nil},
:organization,
:date,
:amount,
:is_grant,
:is_daf_contribution,
:note,
]
def initialize(values)
ATTRIBUTES.each do |attrs|
original_name = attrs.fetch(:name)
new_name = attrs.fetch(:new_name, original_name)
value = values.fetch(original_name) { attrs.fetch(:default) }
if new_name == :date
value = Date.parse(value)
end
instance_variable_set(:"@#{original_name}", value)
define_singleton_method(new_name) do
instance_variable_get(:"@#{original_name}")
ATTRIBUTES.each do |attr|
value = values.fetch(attr)
instance_variable_set(:"@#{attr}", value)
define_singleton_method(attr) do
instance_variable_get(:"@#{attr}")
end
end
end

def amount_donated_by_me
grant? ? 0 : amount
is_grant ? 0 : amount
end

def amount_received_by_charity
daf_contribution? ? 0 : amount
is_daf_contribution ? 0 : amount
end

def formatted_date
date.strftime("%-d %B %Y")
end

def url
case organization
when "EA Giving Group donor-advised fund"
return "/misc/other/donations/ea_giving_group.html"
end

return unless (org_data = organizations[organization])

org_data.fetch("url")
org_data = self.class.organizations[organization] or return
org_data.fetch(:url)
end

def organizations
@organizations ||= File.read("lib/organizations.json")
.then { |json| JSON.parse(json) }
.index_by { |org| org.fetch("name") }
def self.organizations
@organizations ||= donation_data_fetcher
.organizations
.index_by { |org| org.fetch(:name) }
end

def css_class
if grant?
if is_grant
"text-grey-dark"
end
end

def self.load_donations(*)
File.read("lib/donations.json")
.then { |json| JSON.parse(json) }
def self.load_donations
@donations ||= donation_data_fetcher
.donations
.map { |donation| Donation.new(donation.symbolize_keys) }
.sort_by { |donation| [donation.date, donation.organization, donation.amount, donation.note] }
.reverse
end

def self.total_donated_by_me(show_hidden:)
load_donations(show_hidden: show_hidden).sum(&:amount_donated_by_me)
def self.total_donated_by_me
load_donations.sum(&:amount_donated_by_me)
end

def self.total_received_by_charities(show_hidden:)
load_donations(show_hidden: show_hidden).sum(&:amount_received_by_charity)
def self.total_received_by_charities
load_donations.sum(&:amount_received_by_charity)
end

def self.donations_by_year(show_hidden:)
load_donations(show_hidden: show_hidden)
def self.donations_by_year
load_donations
.group_by { |donation| donation.date.year }
.sort_by(&:first)
.reverse
end

def self.donation_data_fetcher
@donation_data_fetcher ||= DonationDataFetcher.new(
ENV['NOTION_DONATIONS_UPDATER_API_KEY'],
"6721200455be4b7c820df4a9ce51fd30",
"738df83195f74d66b466c71519dc5a1b",
)
end
end
84 changes: 84 additions & 0 deletions lib/donation_data_fetcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# frozen_string_literal: true

require "json"
require "net/http"
require "uri"

class DonationDataFetcher
API_VERSION = "2022-06-28"
BASE_URL = "https://api.notion.com/v1"

attr_accessor :auth_token, :donations_database_id, :organizations_database_id

def initialize(auth_token, donations_database_id, organizations_database_id)
@auth_token = auth_token
@donations_database_id = donations_database_id
@organizations_database_id = organizations_database_id
end

def organizations
organizations = fetch_all_items(organizations_database_id)
organization_infos = organizations.map { |org|
{
id: org.dig!("id"),
name: org.dig!("properties", "organization", "title", 0, "plain_text"),
url: org.dig!("properties", "URL", "url"),
}
}.sort_by { |info| info.fetch(:name) }
end

def donations
donations = fetch_all_items(
donations_database_id,
filter: { property: "hidden", checkbox: { equals: false } },
)
donation_infos = donations.map { |donation|
{
date: donation.dig!("properties", "date", "date", "start").then { |date| Date.parse(date) },
is_grant: donation.dig!("properties", "grant", "checkbox"),
is_daf_contribution: donation.dig!("properties", "DAF contribution", "checkbox"),
amount: donation.dig!("properties", "amount", "number"),
organization: donation.dig!("properties", "organization name", "rollup", "array", 0, "title", 0, "plain_text"),
note: donation.dig!("properties", "public note", "rich_text")[0]&.fetch("plain_text"),
}
}.sort_by { |info| info.values_at(:date, :organization, :amount) }.
reverse
end

private

def fetch_all_items(database_id, filter: nil)
items = []
start_cursor = nil
loop do
response = query_database(database_id, start_cursor, filter)
items.concat(response["results"])
start_cursor = response["next_cursor"]
break unless start_cursor
end
items.map { |item| Data[item] }
end

def query_database(database_id, start_cursor = nil, filter = nil)
uri = URI("#{BASE_URL}/databases/#{database_id}/query")
request = Net::HTTP::Post.new(uri.request_uri, {
"Authorization" => "Bearer #{@auth_token}",
"Notion-Version" => API_VERSION,
"Content-Type" => "application/json",
}).tap do |req|
body = { start_cursor: start_cursor, filter: filter }.compact
req.body = body.to_json
end
response = Net::HTTP.new(uri.host, uri.port).then do |http|
http.use_ssl = true
http.request(request)
end
JSON.parse(response.body)
end

Data = Struct.new(:hash) do
def dig!(*keys)
keys.reduce(hash) { |h, key| h.fetch(key) }
end
end
end
789 changes: 0 additions & 789 deletions lib/donations.json

This file was deleted.

252 changes: 0 additions & 252 deletions lib/organizations.json

This file was deleted.

140 changes: 0 additions & 140 deletions lib/package-lock.json

This file was deleted.

5 changes: 0 additions & 5 deletions lib/package.json

This file was deleted.

86 changes: 0 additions & 86 deletions lib/update_donations.js

This file was deleted.

6 changes: 3 additions & 3 deletions source/misc/other/donations/index.html.haml
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ title: My Donations
%th Amount
%th Notes
%tbody
- Donation.load_donations(show_hidden: false).each do |donation|
- Donation.load_donations.each do |donation|
%tr
%td= donation.formatted_date
- if donation.url
@@ -38,9 +38,9 @@ title: My Donations
%td
.font-bold Total donated by me
%td
.font-bold.text-right= number_to_currency(Donation.total_donated_by_me(show_hidden: false))
.font-bold.text-right= number_to_currency(Donation.total_donated_by_me)
%tr(style="background-color: white;")
%td
.font-bold Total received by charities and external DAFs
%td
.font-bold.text-right= number_to_currency(Donation.total_received_by_charities(show_hidden: false))
.font-bold.text-right= number_to_currency(Donation.total_received_by_charities)

0 comments on commit b719229

Please sign in to comment.