Last active
May 13, 2020 19:07
-
-
Save themartorana/12e0087353877c2f4f0e238e57b16576 to your computer and use it in GitHub Desktop.
Calculate your month-to-date bill from Fastly with the billing API
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
# Requires Python 3 | |
import requests | |
import locale | |
from typing import Dict, Tuple, List | |
from calendar import monthrange | |
from datetime import datetime | |
FASTLY_URL = "https://api.fastly.com/stats/usage_by_month?billable_units=true" | |
FASTLY_API_KEY = "<YOUR-API-KEY-HERE>" | |
FASTLY_ZONES = { | |
"anzac": "Australia & New Zealand", | |
"asia": "Asia", | |
"europe": "Europe", | |
"latam": "Latin America (Brazil)", | |
"south_africa": "South Africa", | |
"usa": "North America" | |
} | |
FASTLY_PRICING = { | |
"anzac": { | |
"gig_low": 0.19, | |
"gig_high": 0.14, | |
"req": 0.009 | |
}, | |
"asia": { | |
"gig_low": 0.19, | |
"gig_high": 0.14, | |
"req": 0.009 | |
}, | |
"europe": { | |
"gig_low": 0.12, | |
"gig_high": 0.08, | |
"req": 0.009 | |
}, | |
"latam": { | |
"gig_low": 0.28, | |
"gig_high": 0.24, | |
"req": 0.016 | |
}, | |
"south_africa": { | |
"gig_low": 0.28, | |
"gig_high": 0.24, | |
"req": 0.016 | |
}, | |
"usa": { | |
"gig_low": 0.12, | |
"gig_high": 0.08, | |
"req": 0.0075 | |
} | |
} | |
def ascurrency(amount: float) -> str: | |
return locale.currency( | |
amount, | |
"$", | |
grouping=True | |
) | |
def grab() -> Dict: | |
r = requests.get( | |
FASTLY_URL, | |
headers = { | |
"Fastly-Key": FASTLY_API_KEY, | |
} | |
) | |
if r.status_code != 200: | |
raise Exception("Received %d, was expecting 200" % r.status_code) | |
# Response is json | |
jpacket = r.json() | |
if jpacket["status"] != "success": | |
raise Exception("Status is not success: %s" % jpacket["status"]) | |
return jpacket | |
def calc_service(service: Dict) -> None: | |
# Just look at the production avatar cache | |
treq = 0.0 | |
tbw = 0.0 | |
tc = 0.0 | |
for region, v in service.items(): | |
if region == "name": | |
continue | |
# Get the matching region costs | |
regcost = FASTLY_PRICING[region] | |
# Numbers | |
bw = v["bandwidth"] | |
tbw += bw | |
req = v["requests"] | |
treq += req | |
# Calculations | |
creq = req * regcost["req"] | |
cbw = 0.0 | |
if bw < 1: | |
cbw = bw * regcost["gig_low"] | |
else: | |
clow = regcost["gig_low"] | |
chigh = (bw - 1) * regcost["gig_high"] | |
cbw = clow + chigh | |
tc += cbw + creq | |
print("""%s | |
bw: %s (%.4f GB * %.2f/%.2f/GB) | |
requests: %s (%s * %f/10k) | |
------ | |
total: %s""" % ( | |
FASTLY_ZONES[region].upper(), | |
# BW | |
ascurrency(cbw), | |
bw, | |
regcost["gig_low"], | |
regcost["gig_high"], | |
# Requests | |
locale.currency( | |
creq, | |
"$", | |
grouping=True | |
), | |
locale.format( | |
"%.2d", | |
req * 10000, | |
grouping=True | |
), | |
regcost["req"], | |
# Total | |
locale.currency( | |
creq + cbw, | |
"$", | |
grouping=True | |
), | |
)) | |
return tc | |
def grab_and_calc() -> None: | |
jpacket = grab() | |
services = jpacket["data"]["services"] | |
print("There are %d services" % len(services.keys())) | |
ts = 0.0 | |
for k, v in services.items(): | |
print("\n%s\n=========================" % v["name"]) | |
tsc = calc_service(v) | |
ts += tsc | |
print( | |
"\nTotal \'%s\' Cost: %s" % ( | |
v["name"], | |
locale.currency( | |
tsc, | |
"$", | |
grouping=True | |
) | |
) | |
) | |
print( | |
"\nMONTH SPEND TO DATE: %s" % ( | |
ascurrency(ts) | |
) | |
) | |
# Extract for month | |
today = datetime.today() | |
day_in_month = today.day | |
days_in_month = monthrange(today.year, today.month)[1] | |
tspace = ts/(day_in_month/days_in_month) | |
print( | |
"MONTH TOTAL SPEND PACE: %s" % ( | |
ascurrency(tspace) | |
) | |
) | |
print( | |
"\033[3mBased on %d/%d days in month\033[0m" % ( | |
day_in_month, | |
days_in_month | |
) | |
) | |
if __name__ == "__main__": | |
locale.setlocale(locale.LC_ALL, 'en_US') | |
grab_and_calc() |
Updated to fix day-of-month calculation.
Updated to display day/days in month calculations are based on.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated to estimate monthly pace