Skip to content

Commit

Permalink
Add genre to concerts - WIP
Browse files Browse the repository at this point in the history
Fixes #36
  • Loading branch information
jakopako committed Dec 30, 2024
1 parent eaab65b commit c34a9e8
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 1 deletion.
15 changes: 14 additions & 1 deletion controllers/eventController.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/gofiber/fiber/v2"
"github.com/jakopako/event-api/config"
"github.com/jakopako/event-api/genre"
"github.com/jakopako/event-api/geo"
"github.com/jakopako/event-api/models"
"go.mongodb.org/mongo-driver/bson"
Expand Down Expand Up @@ -110,7 +111,7 @@ func AddEvents(c *fiber.Ctx) error {
continue
}

// check geolocation
// lookup geolocation if not given
if len(event.Geolocation) != 2 {
// lookup location based on city AND cache this info to not flood the geoloc service
// It's the client's responsibility to provide enough info (ie country if necessary)
Expand All @@ -130,6 +131,18 @@ func AddEvents(c *fiber.Ctx) error {
event.MongoGeolocation.Coordinates = event.Geolocation[:]
}

// lookup genres if not given
if len(event.Genres) == 0 {
genres, err := genre.LookupGenres(event.Title)
if err != nil {
errors = append(errors, fiber.Map{
"message": fmt.Sprintf("failed to find genre for event %+v", event),
"error": err.Error(),
})
}
event.Genres = genres
}

// add offset
_, offset := event.Date.Zone()
event.Offset = offset
Expand Down
96 changes: 96 additions & 0 deletions genre/genreLookup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package genre

import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"

"github.com/jakopako/event-api/config"
"go.mongodb.org/mongo-driver/mongo"
)

type GenreCache struct {
lookupSpotifyGenre bool
spotifyToken string
spotifyTokenExpiry time.Time
mu sync.RWMutex
eventsColl *mongo.Collection
spotifyErrorCount int // do we need this? if the spotify responds with an error we probably don't want to hammer it with 1000s of requests, so might need to stop querying the api after a certain error count
}

type spotifyTokenResponse struct {
AccessToken string `json:access_token"`
TokenType string `json:token_type"`
ExpiresIn int `json:expires_in"` // minutes
}

var GC *GenreCache

func (gc *GenreCache) renewSpotifyToken() error {
client := http.Client{}
if gc.spotifyToken == "" || gc.spotifyTokenExpiry.After(time.Now().UTC()) {
tokenUrl := "https://accounts.spotify.com/api/token"
clientId := os.Getenv("SPOTIFY_CLIENT_ID")
clientSecret := os.Getenv("SPOTIFY_CLIENT_SECRET")
if clientId == "" || clientSecret == "" {
return fmt.Errorf("env vars SPOTIFY_CLIENT_ID and/or SPOTIFY_CLIENT_SECRET are empty")
}

form := url.Values{}
form.Add("grant_type", "client_credentials")
form.Add("client_id", clientId)
form.Add("client_secret", clientSecret)

req, _ := http.NewRequest("POST", tokenUrl, strings.NewReader(form.Encode()))
// req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

var tokenResp spotifyTokenResponse
if err := json.Unmarshal(body, &tokenResp); err != nil {
return err
}

gc.spotifyToken = tokenResp.AccessToken
gc.spotifyTokenExpiry = time.Now().UTC().Add(time.Minute * time.Duration(tokenResp.ExpiresIn-1))
}
return nil
}

func (gc *GenreCache) getSpoticyGenres(artist string) ([]string, error) {
genres := []string{}

if gc.lookupSpotifyGenre {
gc.renewSpotifyToken()

// find genres
}

return genres, nil
}

func InitGenreCache() {
// this code assumes that the DB has already been initialized

GC = &GenreCache{
lookupSpotifyGenre: os.Getenv("LOOKUP_SPOTIFY_GENRE") == "true",
eventsColl: config.MI.DB.Collection("events"),
}
}

func LookupGenres(artist string) ([]string, error) {
return GC.getSpoticyGenres(artist)
}
4 changes: 4 additions & 0 deletions geo/geoLookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,21 @@ func LookupCityCoordinates(city, country string) (*models.MongoGeolocation, erro
searchKey += fmt.Sprintf("+%s", country)
}
searchKey = strings.ReplaceAll(searchKey, " ", "+")

// check memory cache
GC.mu.RLock()
coords, found := GC.memCache[searchKey]
GC.mu.RUnlock()
if found {
return coords, nil
}

// check database
filter := bson.D{{"name", city}, {"country", country}}
var result models.City
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
err := GC.cityColl.FindOne(ctx, filter).Decode(&result)

// fetch if not in database and write
if err != nil {
if err == mongo.ErrNoDocuments {
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/gofiber/fiber/v2/utils"
"github.com/jakopako/event-api/config"
_ "github.com/jakopako/event-api/docs"
"github.com/jakopako/event-api/genre"
"github.com/jakopako/event-api/geo"
"github.com/jakopako/event-api/routes"
_ "github.com/joho/godotenv/autoload"
Expand Down Expand Up @@ -56,6 +57,7 @@ func main() {
// initialize DB and geoloc cache
config.ConnectDB()
geo.InitGeolocCache()
genre.InitGenreCache()

setupRoutes(app)

Expand Down
1 change: 1 addition & 0 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Event struct {
SourceURL string `bson:"sourceUrl,omitempty" json:"sourceUrl,omitempty" validate:"required,url" example:"http://link.to/source"`
Geolocation []float64 `bson:"-" json:"geolocation,omitempty" example:"7.4514512,46.9482713"`
MongoGeolocation MongoGeolocation `bson:"geolocation,omitempty" json:"-"`
Genres []string `bson:"genres,omitempty", json:"genres,omitempty" example:"german trap"`
}

type MongoGeolocation struct {
Expand Down

0 comments on commit c34a9e8

Please sign in to comment.