Skip to content

Commit

Permalink
feat: add gamification integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Topvennie committed Nov 25, 2024
1 parent 73258bf commit d707b6e
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 4 deletions.
4 changes: 4 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ api = "http://localhost:4000/api"
interval_season_s = 300
interval_scan_s = 60

[gamification]
api = "https://gamification.zeus.gent"
interval_s = 3600

[buzzer]
song = [
"-n", "-f880", "-l100", "-d0",
Expand Down
14 changes: 14 additions & 0 deletions db/migrations/20241125113707_add_gamification_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS gamification (
id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
score INTEGER NOT NULL,
avatar VARCHAR(255)
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS gamification;
-- +goose StatementEnd
24 changes: 24 additions & 0 deletions db/queries/gamification.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- CRUD

-- name: GetAllGamification :many
SELECT *
FROM gamification;

-- name: CreateGamification :one
INSERT INTO gamification (name, score, avatar)
VALUES (?, ?, ?)
RETURNING *;

-- name: DeleteGamification :execrows
DELETE FROM gamification
WHERE id = ?;


-- Other


-- name: UpdateGamificationScore :one
UPDATE gamification
SET score = ?
WHERE id = ?
RETURNING *;
50 changes: 50 additions & 0 deletions internal/cmd/gamification.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cmd

import (
"time"

"github.com/zeusWPI/scc/internal/pkg/db"
"github.com/zeusWPI/scc/internal/pkg/gamification"
"github.com/zeusWPI/scc/pkg/config"
"go.uber.org/zap"
)

// Gamification starts the gamification instance
func Gamification(db *db.DB) (*gamification.Gamification, chan bool) {
gam := gamification.New(db)
done := make(chan bool)

go gamificationPeriodicUpdate(gam, done)

return gam, done
}

func gamificationPeriodicUpdate(gam *gamification.Gamification, done chan bool) {
interval := config.GetDefaultInt("gamification.interval_s", 3600)
zap.S().Info("Gamification: Starting periodic leaderboard update with an interval of ", interval, " seconds")

ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()

// Run immediatly once
zap.S().Info("Gamification: Updating leaderboard")
err := gam.Update()
if err != nil {
zap.S().Error("gamification: Error updating leaderboard\n", err)
}

for {
select {
case <-done:
zap.S().Info("Gamification: Stopping periodic leaderboard update")
return
case <-ticker.C:
// Update leaderboard
zap.S().Info("Gamification: Updating leaderboard")
err := gam.Update()
if err != nil {
zap.S().Error("gamification: Error updating leaderboard\n", err)
}
}
}
}
8 changes: 7 additions & 1 deletion internal/cmd/tap.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ func tapPeriodicUpdate(tap *tap.Tap, done chan bool) {
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()

// Run immediatly once
zap.S().Info("Tap: Updating tap")
err := tap.Update()
if err != nil {
zap.S().Error("Tap: Error updating tap\n", err)
}

for {
select {
case <-done:
Expand All @@ -40,5 +47,4 @@ func tapPeriodicUpdate(tap *tap.Tap, done chan bool) {
}
}
}

}
1 change: 1 addition & 0 deletions internal/cmd/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

var screens = map[string]func(*db.DB) screen.Screen{
"cammie": screen.NewCammie,
"test": screen.NewTest,
}

// TUI starts the terminal user interface
Expand Down
14 changes: 14 additions & 0 deletions internal/cmd/zess.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ func zessPeriodicSeasonUpdate(zess *zess.Zess, done chan bool) {
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()

// Run immediatly once
zap.S().Info("Zess: Updating seasons")
err := zess.UpdateSeasons()
if err != nil {
zap.S().Error("Zess: Error updating seasons\n", err)
}

for {
select {
case <-done:
Expand All @@ -51,6 +58,13 @@ func zessPeriodicScanUpdate(zess *zess.Zess, done chan bool) {
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()

// Run immediatly once
zap.S().Info("Zess: Updating scans")
err := zess.UpdateScans()
if err != nil {
zap.S().Error("Zess: Error updating scans\n", err)
}

for {
select {
case <-done:
Expand Down
38 changes: 38 additions & 0 deletions internal/pkg/db/dto/gamification.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dto

import "github.com/zeusWPI/scc/internal/pkg/db/sqlc"

// Gamification represents the DTO object for gamification
type Gamification struct {
ID int64 `json:"id"`
Name string `json:"github_name"`
Score int64 `json:"score"`
Avatar string `json:"avatar_url"`
}

// GamificationDTO converts a sqlc Gamification object to a DTO gamification
func GamificationDTO(gam sqlc.Gamification) *Gamification {
return &Gamification{
ID: gam.ID,
Name: gam.Name,
Score: gam.Score,
Avatar: gam.Avatar,
}
}

// CreateParams converts a Gamification DTO to a sqlc CreateGamificationParams object
func (g *Gamification) CreateParams() sqlc.CreateGamificationParams {
return sqlc.CreateGamificationParams{
Name: g.Name,
Score: g.Score,
Avatar: g.Avatar,
}
}

// UpdateScoreParams converts a Gamification DTO to a sqlc UpdateScoreParams object
func (g *Gamification) UpdateScoreParams() sqlc.UpdateGamificationScoreParams {
return sqlc.UpdateGamificationScoreParams{
ID: g.ID,
Score: g.Score,
}
}
109 changes: 109 additions & 0 deletions internal/pkg/db/sqlc/gamification.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions internal/pkg/db/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions internal/pkg/gamification/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package gamification

import (
"bytes"
"errors"
"fmt"
"io"
"os"

"github.com/gofiber/fiber/v2"
"github.com/zeusWPI/scc/internal/pkg/db/dto"
"go.uber.org/zap"
)

func (g *Gamification) getLeaderboard() (*[]*dto.Gamification, error) {
zap.S().Info("Gamification: Getting leaderboard")

req := fiber.Get(g.api+"/top4").Set("Accept", "application/json")

res := new([]*dto.Gamification)
status, _, errs := req.Struct(res)
if len(errs) > 0 {
return nil, errors.Join(append(errs, errors.New("Gamification: Leaderboard API request failed"))...)
}
if status != fiber.StatusOK {
return nil, fmt.Errorf("Gamification: Leaderboard API request returned bad status code %d", status)
}

errs = make([]error, 0)
for _, gam := range *res {
if err := dto.Validate.Struct(gam); err != nil {
errs = append(errs, err)
}
}

return res, errors.Join(errs...)
}

func downloadAvatar(gam dto.Gamification) (string, error) {
req := fiber.Get(gam.Avatar)
status, body, errs := req.Bytes()
if errs != nil {
return "", errors.Join(append(errs, errors.New("Gamification: Download avatar request failed"))...)
}
if status != fiber.StatusOK {
return "", fmt.Errorf("Gamification: Download avatar returned bad status code %d", status)
}

location := fmt.Sprintf("public/%s.png", gam.Name)
out, err := os.Create(location)
if err != nil && err != os.ErrExist {
return "", err
}
defer func() {
_ = out.Close()
}()

_, err = io.Copy(out, bytes.NewReader(body))

return location, err
}
Loading

0 comments on commit d707b6e

Please sign in to comment.