feat(display-attendies): split list

Split up suiter list in attendies and suiterWaitingList

and added localization with goi18n, btw
This commit is contained in:
nikurasu 2024-03-03 22:55:50 +01:00
parent a914d6a328
commit 0965f9e115
Signed by: Nikurasu
GPG key ID: 9E7F14C03EF1F271
21 changed files with 345 additions and 17 deletions

4
.vscode/launch.json vendored
View file

@ -8,8 +8,8 @@
"name": "Launch Package", "name": "Launch Package",
"type": "go", "type": "go",
"request": "launch", "request": "launch",
"mode": "auto", "mode": "debug",
"program": "src/main.go" "program": "src/main.go",
} }
] ]
} }

View file

@ -2,9 +2,9 @@ FROM golang:alpine3.17 AS build
WORKDIR /build WORKDIR /build
COPY ./src ./src COPY ./src ./src
WORKDIR /build/src WORKDIR /build/src
RUN ls RUN apk add gcc libc-dev
RUN go get . RUN go get .
RUN go build -o /build/pretix-proxy RUN CGO_ENABLED=1 go build -o /build/pretix-proxy
FROM alpine:3.17 AS final FROM alpine:3.17 AS final
COPY --from=build /build/pretix-proxy /bin/pretix-proxy COPY --from=build /build/pretix-proxy /bin/pretix-proxy

View file

@ -1,6 +0,0 @@
DOMAIN="reg.ulmer-furs.de"
API_KEY="NeedForEep"
DEBUG="true"
DATABASE_PATH="/home/tamaki/Documents/coding/pretix-proxy/main.db"
API_KEY_PRETIX="ktb7ahh2dzkultyk2vqkafkd7s06fj25lie55a9tx9g2is682an55v1wx8zsh4ij"
SECRET="dfdsfdsfdsfsdfsf"

View file

@ -2,9 +2,15 @@ package controller
import ( import (
"fmt" "fmt"
"sort"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"ulmer-furs.de/pretix-proxy/v2/app/service" "ulmer-furs.de/pretix-proxy/v2/app/service"
"ulmer-furs.de/pretix-proxy/v2/app/util"
"ulmer-furs.de/pretix-proxy/v2/config"
"ulmer-furs.de/pretix-proxy/v2/entities"
) )
// @Description Get the attendies of an event by the name // @Description Get the attendies of an event by the name
@ -23,6 +29,13 @@ func GetAttendiesByEventPublic(c *fiber.Ctx) error {
} }
func GetAttendiesByEventPublicTable(c *fiber.Ctx) error { func GetAttendiesByEventPublicTable(c *fiber.Ctx) error {
siteLang := c.Query("lang")
var localizer *i18n.Localizer
if siteLang == "de-DE" {
localizer = i18n.NewLocalizer(config.LocaleBundle, language.German.String())
} else {
localizer = i18n.NewLocalizer(config.LocaleBundle)
}
name := c.Params("name") name := c.Params("name")
event, err := service.Get_db_event_by_event(name) event, err := service.Get_db_event_by_event(name)
if err != nil { if err != nil {
@ -32,8 +45,37 @@ func GetAttendiesByEventPublicTable(c *fiber.Ctx) error {
if err != nil { if err != nil {
return c.Status(fiber.ErrNotFound.Code).SendString("attendies not found") return c.Status(fiber.ErrNotFound.Code).SendString("attendies not found")
} }
localizations := map[string]string{
"Attendie": localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "Attendie",
PluralCount: 2,
}),
"HelloWorld": localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "HelloWorld",
}),
"Role": localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "Role",
PluralCount: 1,
}),
"Name": localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "Name",
PluralCount: 1,
}),
}
sort.Sort(entities.ByRegistrationTime(attendies))
roleSuiter, _ := service.GetRoleByName("Suiter")
roleSpotter, _ := service.GetRoleByName("Spotter")
suiterWaitingList := util.CreateSuiterWaitingList(
&attendies,
event.SuiterPerSpotter,
&roleSuiter,
&roleSpotter,
)
fmt.Printf("len(attendies): %v\n", len(attendies))
return c.Render("app/views/index", fiber.Map{ return c.Render("app/views/index", fiber.Map{
"Title": fmt.Sprintf("%s Attendies", *event.Event), "Event Name": event.Name,
"Attendies": attendies, "Attendies": attendies,
"SuiterWaitingList": suiterWaitingList,
"Locals": localizations,
}) })
} }

View file

@ -44,7 +44,7 @@ func GetAttendiesByEvent(event entities.Db_Event) ([]entities.Db_Attendies, erro
return attendies, nil return attendies, nil
} }
func GetAttendiesByEventPrivacy(event entities.Db_Event, privacy bool) ([]entities.Db_Attendies, error) { func GetAttendiesByEventPrivacy(event entities.Db_Event, privacy bool) (entities.AttendiesList, error) {
var attendies []entities.Db_Attendies var attendies []entities.Db_Attendies
result := config.Database.Model(&entities.Db_Attendies{}).Preload("Event").Preload("Role").Where("event_id = ?", event.ID).Where("privacy = ?", privacy).Find(&attendies) result := config.Database.Model(&entities.Db_Attendies{}).Preload("Event").Preload("Role").Where("event_id = ?", event.ID).Where("privacy = ?", privacy).Find(&attendies)
if result.Error != nil { if result.Error != nil {

View file

@ -0,0 +1,28 @@
package util
import (
"fmt"
"ulmer-furs.de/pretix-proxy/v2/entities"
)
func CreateSuiterWaitingList(attendiesList *entities.AttendiesList, suiterPerSpotter *int, suiter, spotter *entities.Role) entities.AttendiesList {
spotterCount := attendiesList.CountByRole(*spotter)
suiterCount := spotterCount * *suiterPerSpotter
var suiterCounter int
var suiterWatinglist entities.AttendiesList
var i int
for i < len(*attendiesList) {
if (*attendiesList)[i].Role == *suiter {
suiterCounter++
}
if suiterCounter > suiterCount && (*attendiesList)[i].Role == *suiter {
suiterWatinglist = append(suiterWatinglist, (*attendiesList)[i])
*attendiesList = append((*attendiesList)[:i], (*attendiesList)[i+1:]...)
} else {
i++
}
}
fmt.Printf("len(a): %v\n", len(*attendiesList))
return suiterWatinglist
}

View file

@ -41,6 +41,7 @@ func GetAttendieListFromPretixJson(pretixEvent entities.Pretix_Event, event enti
var name string var name string
for _, order := range pretixEvent.Results { for _, order := range pretixEvent.Results {
indexParticipation := slices.IndexFunc(order.Positions, func(p entities.Pretix_Position) bool { return p.Item == *event.ItemIdParticipation }) indexParticipation := slices.IndexFunc(order.Positions, func(p entities.Pretix_Position) bool { return p.Item == *event.ItemIdParticipation })
indexBadge := slices.IndexFunc(order.Positions, func(p entities.Pretix_Position) bool { return p.Item == *event.ItemIdBadge }) indexBadge := slices.IndexFunc(order.Positions, func(p entities.Pretix_Position) bool { return p.Item == *event.ItemIdBadge })
indexRestaurant := slices.IndexFunc(order.Positions, func(p entities.Pretix_Position) bool { return p.Item == *event.ItemIdRestaurant }) indexRestaurant := slices.IndexFunc(order.Positions, func(p entities.Pretix_Position) bool { return p.Item == *event.ItemIdRestaurant })
@ -125,6 +126,8 @@ func GetAttendiesFromPretixJsonWithRoleQuestion(pretixEvent *entities.Pretix_Eve
return attendies, err return attendies, err
} }
attendie.RegistrationTime = order.Datetime
attendies = append(attendies, attendie) attendies = append(attendies, attendie)
} }
return attendies, nil return attendies, nil

View file

@ -10,6 +10,8 @@
<main> <main>
<h1>{{.Title}}</h1> <h1>{{.Title}}</h1>
{{template "table" .}} {{template "table" .}}
<p>Test:</p>
<p>{{.Locals.HelloWorld}}</p>
</main> </main>
</body> </body>
</html> </html>

View file

@ -1,9 +1,12 @@
{{define "table"}} {{define "table"}}
<h1>{{ index . "Event Name" }} {{ .Locals.Attendie }}</h1>
<h2></h2>
<table> <table>
<thead> <thead>
<tr class="tableHeading"> <tr class="tableHeading">
<th>Name</th> <th>{{ .Locals.Name }}</th>
<th>Role</th> <th>{{ .Locals.Role }}</th>
<th>Time</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -11,6 +14,7 @@
<tr> <tr>
<td>{{.Nickname}}</td> <td>{{.Nickname}}</td>
<td>{{.Role.Name}}</td> <td>{{.Role.Name}}</td>
<td>{{.RegistrationTime}}</td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>

View file

@ -0,0 +1,79 @@
package config
import (
"embed"
"encoding/json"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
var LocaleBundle *i18n.Bundle
func SetupLocalization(locaizationFS *embed.FS) {
LocaleBundle = i18n.NewBundle(language.English)
for _, message := range defaultMessages {
LocaleBundle.AddMessages(language.English, &message)
}
LocaleBundle.RegisterUnmarshalFunc("json", json.Unmarshal)
LocaleBundle.LoadMessageFileFS(locaizationFS, "localization/active.de.json")
}
var defaultMessages = []i18n.Message{
{
ID: "HelloWorld",
Other: "Hello newer World",
},
{
ID: "Attendie",
One: "Attendie",
Other: "Attendies",
},
{
ID: "Role",
One: "Role",
Other: "Roles",
},
{
ID: "Name",
One: "Name",
Other: "Names",
},
{
ID: "Registered",
Other: "Registered",
},
{
ID: "SuiterWatinglist",
Other: "Suiter Watinglist",
},
{
ID: "WatinglistDesc",
Other: "We set a ratio of Suiters per Spotter for each event in order to keep the count of suiters manageble for them. If you are listed here we sadly don't have enough spotters yet.",
},
{
ID: "Suiter",
One: "Suiter",
Other: "Suiters",
},
{
ID: "Guest",
One: "Guest",
Other: "Guets",
},
{
ID: "Spotter",
One: "Spotter",
Other: "Spotters",
},
{
ID: "Photographer",
One: "Photographer",
Other: "Photographers",
},
{
ID: "SpecialAnimal",
One: "#SpecialAnimal",
Other: "#SpecialAnimals",
},
}

View file

@ -0,0 +1,13 @@
package entities
type AttendiesList []Db_Attendies
func (a AttendiesList) CountByRole(role Role) int {
var count int
for _, attendie := range a {
if attendie.Role == role {
count++
}
}
return count
}

View file

@ -1,9 +1,25 @@
package entities package entities
import ( import (
"time"
"gorm.io/gorm" "gorm.io/gorm"
) )
type ByRegistrationTime AttendiesList
func (a ByRegistrationTime) Len() int {
return len(a)
}
func (a ByRegistrationTime) Less(i, j int) bool {
return a[i].RegistrationTime.Unix() < a[j].RegistrationTime.Unix()
}
func (a ByRegistrationTime) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
type Db_Attendies struct { type Db_Attendies struct {
gorm.Model gorm.Model
Nickname string `gorm:"column:nickname;not null"` Nickname string `gorm:"column:nickname;not null"`
@ -11,6 +27,7 @@ type Db_Attendies struct {
Event Db_Event Event Db_Event
RoleID int `gorm:"not null"` RoleID int `gorm:"not null"`
Role Role Role Role
AttendsRestaurant bool `gorm:"column:attends_restaurant;not null"` AttendsRestaurant bool `gorm:"column:attends_restaurant;not null"`
Privacy bool `gorm:"column:privacy;not null"` Privacy bool `gorm:"column:privacy;not null"`
RegistrationTime time.Time `gorm:"column:registration_time;not null;default:CURRENT_TIMESTAMP"`
} }

View file

@ -27,6 +27,7 @@ type Db_Event struct {
VariationIdPhotograph *int `gorm:"variation_id_photograph"` VariationIdPhotograph *int `gorm:"variation_id_photograph"`
VariationIdSpecialAnimal *int `gorm:"variation_id_special_animal"` VariationIdSpecialAnimal *int `gorm:"variation_id_special_animal"`
EventType *string `gorm:"cloumn:event_type;not null" validate:"required"` EventType *string `gorm:"cloumn:event_type;not null" validate:"required"`
SuiterPerSpotter *int `gorm:"column:suiter_per_spotter;not null;default:5" validate:"required"`
} }
type Db_Event_Public struct { type Db_Event_Public struct {

View file

@ -1,5 +1,7 @@
package entities package entities
import "time"
type Pretix_Event struct { type Pretix_Event struct {
Count int `json:"count"` Count int `json:"count"`
Results []Pretix_Result `json:"results"` Results []Pretix_Result `json:"results"`
@ -8,6 +10,7 @@ type Pretix_Event struct {
type Pretix_Result struct { type Pretix_Result struct {
Status string `json:"status"` Status string `json:"status"`
Positions []Pretix_Position `json:"positions"` Positions []Pretix_Position `json:"positions"`
Datetime time.Time `json:"datetime"`
} }
type Pretix_Position struct { type Pretix_Position struct {

View file

@ -20,6 +20,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
) )

View file

@ -0,0 +1,21 @@
{
"Attendie": {
"hash": "sha1-4c29d6c952ccd2b114d1792c7b18fa7f7052ebed",
"one": "Teilnehmer*in",
"other": "Teilnehmende"
},
"HelloWorld": {
"hash": "sha1-6225ffc638ce81e53cdf7868fe0c7435594f11e4",
"other": "Hallo neuere Welt"
},
"Name": {
"hash": "sha1-2b7c08c3ab75f37e2da656c1aab228f5f3a793b1",
"one": "Name",
"other": "Namen"
},
"Role": {
"hash": "sha1-47dcc27d6e87ece8baebe7e3877a261a5467093d",
"one": "Rolle",
"other": "Rollen"
}
}

View file

@ -0,0 +1,38 @@
{
"Attendie": {
"one": "Attendie",
"other": "Attendies"
},
"Guest": {
"one": "Guest",
"other": "Guets"
},
"HelloWorld": "Hello newer World",
"Name": {
"one": "Name",
"other": "Names"
},
"Photographer": {
"one": "Photographer",
"other": "Photographers"
},
"Registered": "Registered",
"Role": {
"one": "Role",
"other": "Roles"
},
"SpecialAnimal": {
"one": "#SpecialAnimal",
"other": "#SpecialAnimals"
},
"Spotter": {
"one": "Spotter",
"other": "Spotters"
},
"Suiter": {
"one": "Suiter",
"other": "Suiters"
},
"SuiterWatinglist": "Suiter Watinglist",
"WatinglistDesc": "We set a ratio of Suiters per Spotter for each event in order to keep the count of suiters manageble for them. If you are listed here we sadly don't have enough spotters yet."
}

View file

@ -0,0 +1,39 @@
{
"Guest": {
"hash": "sha1-7b7f214bbd4af36f25668b0ddade965422d342fd",
"one": "Guest",
"other": "Guets"
},
"Photographer": {
"hash": "sha1-dc41992cfe878fe6744f94c3a3fc5843d9f1bc11",
"one": "Photographer",
"other": "Photographers"
},
"Registered": {
"hash": "sha1-a844fcf834e7d026e7936e042c0ce2777b7fb4d9",
"other": "Registered"
},
"SpecialAnimal": {
"hash": "sha1-7f937e9e4906c8f52977132faa76b81f3a331e45",
"one": "#SpecialAnimal",
"other": "#SpecialAnimals"
},
"Spotter": {
"hash": "sha1-6f53dff03ced62b694851ffe37aab682b4b2b227",
"one": "Spotter",
"other": "Spotters"
},
"Suiter": {
"hash": "sha1-eb6248e60da77b4df76de9b35efe44178ee34ead",
"one": "Suiter",
"other": "Suiters"
},
"SuiterWatinglist": {
"hash": "sha1-a5d3dc94fc4f3f0604f0cc5e077c0c9eb43340d4",
"other": "Suiter Watinglist"
},
"WatinglistDesc": {
"hash": "sha1-aadc433a212fca8d927e9134520f5a10aa7e4263",
"other": "We set a ratio of Suiters per Spotter for each event in order to keep the count of suiters manageble for them. If you are listed here we sadly don't have enough spotters yet."
}
}

View file

@ -15,6 +15,9 @@ var viewFS embed.FS
//go:embed app/static/* //go:embed app/static/*
var staticFS embed.FS var staticFS embed.FS
//go:embed localization/active.*.json
var locaizationFS embed.FS
func main() { func main() {
config.LoadEnv() config.LoadEnv()
config.Connect() config.Connect()
@ -23,6 +26,7 @@ func main() {
config.SetupFiber(viewFS, staticFS) config.SetupFiber(viewFS, staticFS)
config.SetupCors() config.SetupCors()
config.SetupHttp() config.SetupHttp()
config.SetupLocalization(&locaizationFS)
if config.Env.Debug { if config.Env.Debug {
config.App.Use(cors.New(cors.Config{ config.App.Use(cors.New(cors.Config{

21
src/translate.de.json Normal file
View file

@ -0,0 +1,21 @@
{
"Attendie": {
"hash": "sha1-4c29d6c952ccd2b114d1792c7b18fa7f7052ebed",
"one": "Attendie",
"other": "Attendies"
},
"HelloWorld": {
"hash": "sha1-6225ffc638ce81e53cdf7868fe0c7435594f11e4",
"other": "Hello newer World"
},
"Name": {
"hash": "sha1-2b7c08c3ab75f37e2da656c1aab228f5f3a793b1",
"one": "Name",
"other": "Names"
},
"Role": {
"hash": "sha1-47dcc27d6e87ece8baebe7e3877a261a5467093d",
"one": "Role",
"other": "Roles"
}
}

18
src/translate.de.toml Normal file
View file

@ -0,0 +1,18 @@
[Attendie]
hash = "sha1-4c29d6c952ccd2b114d1792c7b18fa7f7052ebed"
one = "Attendie"
other = "Attendies"
[HelloWorld]
hash = "sha1-6225ffc638ce81e53cdf7868fe0c7435594f11e4"
other = "Hello newer World"
[Name]
hash = "sha1-2b7c08c3ab75f37e2da656c1aab228f5f3a793b1"
one = "Name"
other = "Names"
[Role]
hash = "sha1-47dcc27d6e87ece8baebe7e3877a261a5467093d"
one = "Role"
other = "Roles"