-
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.
- Loading branch information
1 parent
7ff8912
commit b4b07a6
Showing
9 changed files
with
309 additions
and
7 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
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,29 @@ | ||
package main | ||
|
||
import ( | ||
"electricity-prices/pkg/db" | ||
"electricity-prices/pkg/service" | ||
"github.com/joho/godotenv" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
) | ||
|
||
func init() { | ||
// Load .env file if it exists | ||
_ = godotenv.Load() | ||
} | ||
|
||
func main() { | ||
// Catch SIGINT or SIGTERM and gracefully close the database connection. | ||
c := make(chan os.Signal, 1) | ||
signal.Notify(c, os.Interrupt, syscall.SIGTERM) | ||
go func() { | ||
<-c | ||
db.CloseMongoConnection() | ||
os.Exit(0) | ||
}() | ||
|
||
// Sync with the API. | ||
service.SyncWithAPI() | ||
} |
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,75 @@ | ||
package client | ||
|
||
import ( | ||
"electricity-prices/pkg/model" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
const urlTemplate = "https://apidatos.ree.es/en/datos/mercados/precios-mercados-tiempo-real?time_trunc=hour&start_date=%sT00:00&end_date=%sT23:59" | ||
|
||
// GetPricesFromRee returns the prices for the given date from the REE API | ||
func GetPricesFromRee(date time.Time) ([]model.Price, bool, error) { | ||
// Parse date to day string | ||
day := date.Format("2006-01-02") | ||
|
||
// Call to endpoint | ||
resp, err := http.Get(fmt.Sprintf(urlTemplate, day, day)) | ||
if err != nil { | ||
log.Fatalf("Error occurred while sending request to the server: %s", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
// Read the response body | ||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
log.Fatalf("Error occurred while reading response body: %s", err) | ||
} | ||
|
||
// Check if the status code indicates success | ||
if resp.StatusCode >= 200 && resp.StatusCode < 300 { | ||
// Initialize an instance of PVPC | ||
var res model.ReeResponse | ||
|
||
// Parse the JSON response body into the PVPC struct | ||
err := json.Unmarshal(body, &res) | ||
if err != nil { | ||
log.Fatalf("Error occurred while unmarshaling the response body: %s", err) | ||
} | ||
if len(res.Errors) > 0 { | ||
if res.Errors[0].Detail == "There are no data for the selected filters." { | ||
return nil, true, nil | ||
} | ||
return nil, false, fmt.Errorf("error returned from API: %v", res.Errors[0]) | ||
} | ||
|
||
var included model.ReeIncluded | ||
for _, inc := range res.Included { | ||
if inc.ID == "600" { | ||
included = inc | ||
continue | ||
} | ||
} | ||
if len(included.Attributes.Values) == 0 { | ||
return nil, false, fmt.Errorf("no prices returned from API") | ||
} | ||
|
||
prices := make([]model.Price, len(included.Attributes.Values)) | ||
|
||
for i, p := range included.Attributes.Values { | ||
prices[i] = model.Price{ | ||
DateTime: p.DateTime, | ||
Price: p.Price / 1000, | ||
} | ||
} | ||
|
||
return prices, false, nil | ||
} | ||
|
||
return nil, false, fmt.Errorf("server responded with a non-successful status code: %d", resp.StatusCode) | ||
|
||
} |
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
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,29 @@ | ||
package model | ||
|
||
import "time" | ||
|
||
type ReePrices struct { | ||
Price float64 `json:"value"` | ||
DateTime time.Time `json:"datetime"` | ||
} | ||
type ReeAttributes struct { | ||
Values []ReePrices `json:"values"` | ||
} | ||
|
||
type ReeIncluded struct { | ||
Type string `json:"type"` | ||
ID string `json:"id"` | ||
GroupID string `json:"groupId"` | ||
Attributes ReeAttributes `json:"attributes"` | ||
} | ||
|
||
type ReeError struct { | ||
Code string `json:"code"` | ||
Status string `json:"status"` | ||
Title string `json:"title"` | ||
Detail string `json:"detail"` | ||
} | ||
type ReeResponse struct { | ||
Included []ReeIncluded `json:"included"` | ||
Errors []ReeError `json:"errors"` | ||
} |
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,55 @@ | ||
package service | ||
|
||
import ( | ||
"electricity-prices/pkg/client" | ||
"electricity-prices/pkg/db" | ||
"electricity-prices/pkg/model" | ||
"electricity-prices/pkg/utils" | ||
"log" | ||
"time" | ||
) | ||
|
||
func SyncWithAPI() { | ||
log.Println("Starting to sync with API...") | ||
|
||
// Get last day that was synced from database. | ||
p, err := db.GetLatestPrice() | ||
if err != nil { | ||
p = model.Price{DateTime: utils.StartOfDay(time.Date(2014, 3, 31, 0, 0, 0, 0, time.Local))} | ||
} | ||
currentDate := utils.StartOfDay(p.DateTime).AddDate(0, 0, 1) | ||
log.Println("Last day synced: ", currentDate) | ||
|
||
// If last day is after tomorrow then exit | ||
today := time.Now() | ||
tomorrow := utils.StartOfDay(today.AddDate(0, 0, 1)) | ||
|
||
// Keep processing until we reach tomorrow | ||
for { | ||
if currentDate.After(tomorrow) { | ||
log.Println("Fully synced. Exiting...") | ||
break | ||
} | ||
|
||
// Get the prices from the API | ||
prices, synced, err := client.GetPricesFromRee(currentDate) | ||
|
||
if synced { | ||
log.Println("Fully synced. Exiting...") | ||
break | ||
} | ||
|
||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
log.Printf("Syncing prices for %s", currentDate.Format("January 2 2006")) | ||
|
||
// Save the prices in the database | ||
err = db.SavePrices(prices) | ||
if err != nil { | ||
panic(err) | ||
} | ||
currentDate = currentDate.AddDate(0, 0, 1) | ||
} | ||
} |
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