-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
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
Showing
9 changed files
with
128 additions
and
1,315 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters