Skip to content

Commit

Permalink
Add CLI with root command that uses embedded dataset (#14)
Browse files Browse the repository at this point in the history
* Add main.go as entrypoint for CLI

* Add CLI with root command and embed Ember 2021 data

* Add unit test coverage

* Add data section to README for Ember
  • Loading branch information
rossf7 authored Jun 15, 2022
1 parent bbae1f9 commit dd6b352
Show file tree
Hide file tree
Showing 16 changed files with 359 additions and 10 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ We call this the fuel mix, and this fuel mix can impact on the carbon intensity
## Move your code through time and space

Because the fuel mix will be different depending when and where you run your code, you can influence the carbon intensity of the code you write by moving it through time and space - either by making it run when the grid is greener, or making it run where it's greener, like a CDN running on green power.

## Data

Carbon intensity data from [Ember](https://ember-climate.org/), in accordance of their licensing - [CC-BY-SA 4.0](https://ember-climate.org/creative-commons/
)
2 changes: 1 addition & 1 deletion errors.go → api/errors.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gridintensity
package api

import "errors"

Expand Down
2 changes: 1 addition & 1 deletion multi.go → api/multi.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gridintensity
package api

import (
"context"
Expand Down
4 changes: 2 additions & 2 deletions multi_test.go → api/multi_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package gridintensity_test
package api_test

import (
"context"
"errors"
"testing"

gridintensity "github.com/thegreenwebfoundation/grid-intensity-go"
gridintensity "github.com/thegreenwebfoundation/grid-intensity-go/api"
)

var responseTable = map[string]float64{
Expand Down
2 changes: 1 addition & 1 deletion provider.go → api/provider.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gridintensity
package api

import (
"context"
Expand Down
2 changes: 1 addition & 1 deletion carbonintensity/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"net/http"
"time"

gridintensity "github.com/thegreenwebfoundation/grid-intensity-go"
gridintensity "github.com/thegreenwebfoundation/grid-intensity-go/api"
)

type ApiOption func(*ApiClient) error
Expand Down
79 changes: 79 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cmd

import (
"encoding/json"
"fmt"
"log"
"os"

"github.com/Xuanwo/go-locale"
"github.com/spf13/cobra"

"github.com/thegreenwebfoundation/grid-intensity-go/data"
)

const (
countryCode = "country-code"
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "grid-intensity",
Short: "Get carbon intensity data for electricity grids",
Long: `A tool for getting the carbon intensity data for electricity grids.
This can be used to make your sofware carbon aware so it runs at times when the
grid is greener or at locations where carbon intensity is lower.
grid-intensity --country-code ARG
grid-intensity -c BOL`,

Run: func(cmd *cobra.Command, args []string) {
country, err := cmd.Flags().GetString(countryCode)
if err != nil {
log.Fatal(err)
}

err = getGridIntensityForCountry(country)
if err != nil {
log.Fatal(err)
}
},
}

func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

func init() {
rootCmd.Flags().StringP(countryCode, "c", "", "Country code in ISO 3 character format")
}

func getGridIntensityForCountry(countryCode string) error {
if countryCode == "" {
// No country code provided so try to detect it from the user's locale.
tag, err := locale.Detect()
if err != nil {
return err
}

region, _ := tag.Region()
countryCode = region.ISO3()
}

result, err := data.GetGridIntensityForCountry(countryCode)
if err != nil {
return err
}

bytes, err := json.MarshalIndent(result, "", "\t")
if err != nil {
return err
}

fmt.Println(string(bytes))
return nil
}
87 changes: 87 additions & 0 deletions data/co2-intensities-ember-2021.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
country_code,country_or_region,year,latest_year,emissions_intensity_gco2_per_kwh
,Africa,2021,2021,489.26
ARG,Argentina,2021,2021,365.292
ARM,Armenia,2021,2021,206.522
,Asia,2021,2021,543.57
AUS,Australia,2021,2021,526.876
AUT,Austria,2021,2021,145.083
AZE,Azerbaijan,2021,2021,536.585
BGD,Bangladesh,2021,2021,559.606
BLR,Belarus,2021,2021,472.727
BEL,Belgium,2021,2021,156.063
BOL,Bolivia,2021,2021,311.475
BIH,Bosnia Herzegovina,2021,2021,470.982
BRA,Brazil,2021,2021,144.677
BGR,Bulgaria,2021,2021,364.136
BDI,Burundi,2021,2021,275.862
CAN,Canada,2021,2021,123.859
CHL,Chile,2021,2021,395.565
CHN,China,2021,2021,549.288
CRI,Costa Rica,2021,2021,30.903
HRV,Croatia,2021,2021,212.161
CYP,Cyprus,2021,2021,601.19
CZE,Czechia,2021,2021,401.272
DNK,Denmark,2021,2021,240.419
ECU,Ecuador,2021,2021,132.964
EGY,Egypt,2021,2021,470.879
SLV,El Salvador,2021,2021,180.87
EST,Estonia,2021,2021,488.529
,EU,2021,2021,261.43
,Europe,2021,2021,277.64
FIN,Finland,2021,2021,152.651
FRA,France,2021,2021,67.781
,G20,2021,2021,445.9
,G7,2021,2021,338.04
GEO,Georgia,2021,2021,105.685
DEU,Germany,2021,2021,363.982
GRC,Greece,2021,2021,363.388
HUN,Hungary,2021,2021,236.271
IND,India,2021,2021,632.656
IRL,Ireland,2021,2021,361.274
ITA,Italy,2021,2021,340.937
JPN,Japan,2021,2021,460.647
KAZ,Kazakhstan,2021,2021,656.097
KEN,Kenya,2021,2021,104.0
,Latin America and Caribbean,2021,2021,261.51
LVA,Latvia,2021,2021,226.351
LTU,Lithuania,2021,2021,247.475
LUX,Luxembourg,2021,2021,183.824
MLT,Malta,2021,2021,452.055
MEX,Mexico,2021,2021,391.582
MDA,Moldova,2021,2021,642.512
MNG,Mongolia,2021,2021,725.26
MNE,Montenegro,2021,2021,335.958
NLD,Netherlands,2021,2021,386.189
,North America,2021,2021,345.38
MKD,North Macedonia,2021,2021,444.191
NOR,Norway,2021,2021,26.131
,Oceania,2021,2021,479.98
,OECD,2021,2021,338.24
PAK,Pakistan,2021,2021,363.065
PER,Peru,2021,2021,241.492
PHL,Philippines (the),2021,2021,579.689
POL,Poland,2021,2021,657.138
PRT,Portugal,2021,2021,222.632
ROU,Romania,2021,2021,255.718
RUS,Russian Federation (the),2021,2021,355.431
SAU,Saudi Arabia,2021,2021,568.967
SEN,Senegal,2021,2021,540.098
SRB,Serbia,2021,2021,549.083
SGP,Singapore,2021,2021,488.21
SVK,Slovakia,2021,2021,173.854
SVN,Slovenia,2021,2021,241.956
ZAF,South Africa,2021,2021,706.991
KOR,South Korea,2021,2021,442.389
ESP,Spain,2021,2021,193.737
SWE,Sweden,2021,2021,43.9
CHE,Switzerland,2021,2021,58.952
TWN,Taiwan (Province of China),2021,2021,565.629
TJK,Tajikistan,2021,2021,72.823
THA,Thailand,2021,2021,503.034
TUN,Tunisia,2021,2021,470.848
TUR,Turkey,2021,2021,432.293
UKR,Ukraine,2021,2021,240.28
GBR,United Kingdom,2021,2021,268.255
USA,United States of America,2021,2021,378.625
VNM,Viet Nam,2021,2021,491.192
,World,2021,2021,442.3
58 changes: 58 additions & 0 deletions data/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package data

import (
"bytes"
_ "embed"
"encoding/csv"
"fmt"
"strconv"
"strings"
)

type GridIntensity struct {
CountryCode string `json:"country_code"`
CountryOrRegion string `json:"country_or_region"`
Year int `json:"year"`
LatestYear int `json:"latest_year"`
EmissionsIntensityGCO2PerKWH float64 `json:"emissions_intensity_gco2_per_kwh"`
}

//go:embed co2-intensities-ember-2021.csv
var data []byte

func GetGridIntensityForCountry(countryCode string) (*GridIntensity, error) {
reader := bytes.NewReader(data)
rows, err := csv.NewReader(reader).ReadAll()
if err != nil {
return nil, err
}

for _, row := range rows {
if strings.EqualFold(row[0], countryCode) {
year, err := strconv.Atoi(row[2])
if err != nil {
return nil, err
}

latestYear, err := strconv.Atoi(row[3])
if err != nil {
return nil, err
}

intensity, err := strconv.ParseFloat(row[4], 64)
if err != nil {
return nil, err
}

return &GridIntensity{
CountryCode: row[0],
CountryOrRegion: row[1],
Year: year,
LatestYear: latestYear,
EmissionsIntensityGCO2PerKWH: intensity,
}, nil
}
}

return nil, fmt.Errorf("country code %q not found", countryCode)
}
62 changes: 62 additions & 0 deletions data/data_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package data

import (
"reflect"
"testing"
)

func Test_GetGridIntensityForCountry(t *testing.T) {
tests := []struct {
name string
countryCode string
result *GridIntensity
expectedErr string
}{
{
name: "country exists",
countryCode: "ESP",
result: &GridIntensity{
CountryCode: "ESP",
CountryOrRegion: "Spain",
Year: 2021,
LatestYear: 2021,
EmissionsIntensityGCO2PerKWH: 193.737,
},
},
{
name: "lower case country code",
countryCode: "gbr",
result: &GridIntensity{
CountryCode: "GBR",
CountryOrRegion: "United Kingdom",
Year: 2021,
LatestYear: 2021,
EmissionsIntensityGCO2PerKWH: 268.255,
},
},
{
name: "invalid country code",
countryCode: "AAA",
expectedErr: "country code \"AAA\" not found",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result, err := GetGridIntensityForCountry(tc.countryCode)
switch {
case err != nil && tc.expectedErr == "":
t.Fatalf("error == %#v want nil", err)
case err == nil && tc.expectedErr != "":
t.Fatalf("error == nil want non-nil")
}

if !reflect.DeepEqual(tc.result, result) {
t.Fatalf("expected %#v got %#v", tc.result, result)
}
if tc.expectedErr != "" && tc.expectedErr != err.Error() {
t.Fatalf("expected error %q got %q", tc.expectedErr, err.Error())
}
})
}
}
2 changes: 1 addition & 1 deletion electricitymap/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"net/http"
"time"

gridintensity "github.com/thegreenwebfoundation/grid-intensity-go"
gridintensity "github.com/thegreenwebfoundation/grid-intensity-go/api"
)

type ApiOption func(*ApiClient) error
Expand Down
2 changes: 1 addition & 1 deletion electricitymap/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"testing"
"time"

gridintensity "github.com/thegreenwebfoundation/grid-intensity-go"
gridintensity "github.com/thegreenwebfoundation/grid-intensity-go/api"
"github.com/thegreenwebfoundation/grid-intensity-go/electricitymap"
)

Expand Down
2 changes: 1 addition & 1 deletion examples/electricitymap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"log"
"os"

gridintensity "github.com/thegreenwebfoundation/grid-intensity-go"
gridintensity "github.com/thegreenwebfoundation/grid-intensity-go/api"
"github.com/thegreenwebfoundation/grid-intensity-go/electricitymap"
)

Expand Down
14 changes: 13 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
module github.com/thegreenwebfoundation/grid-intensity-go

go 1.15
go 1.18

require (
github.com/Xuanwo/go-locale v1.1.0
github.com/spf13/cobra v1.4.0
)

require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
golang.org/x/text v0.3.7 // indirect
)
Loading

0 comments on commit dd6b352

Please sign in to comment.