Files
abuse-registration-api-golang/internal/models/registration.go
T
2026-06-25 12:42:23 +01:00

149 lines
4.5 KiB
Go

package models
import (
"errors"
"strings"
"time"
)
const (
RoleReader = "Reader"
RoleAdmin = "Admin"
)
var AbuseCategories = []string{
"physical",
"psychological",
"sexual",
"economic",
"material",
"digital",
"stalking",
"threats",
"honor_related",
}
var Genders = []string{"female", "male", "non_binary", "unknown"}
var Statuses = []string{"new", "open", "referred", "closed"}
var FaroeLocations = []string{
"Akrar", "Argir", "Ánirnar", "Árnafjørður", "Bøur", "Dalur", "Depil", "Eiði",
"Elduvík", "Fámjin", "Froðba", "Fuglafjørður", "Funningsfjørður", "Funningur",
"Gásadalur", "Gjógv", "Glyvrar", "Gøtugjógv", "Haldórsvík", "Haraldssund",
"Hattarvík", "Hellurnar", "Hestur", "Hov", "Hoyvík", "Hósvík", "Húsar",
"Húsavík", "Hvalba", "Hvalvík", "Hvannasund", "Hvítanes", "Innan Glyvur",
"Kaldbak", "Kaldbaksbotnur", "Kirkja", "Kirkjubøur", "Klaksvík", "Kolbeinagjógv",
"Kollafjørður", "Koltur", "Kunoy", "Kvívík", "Lamba", "Lambareiði", "Langasandur",
"Leirvík", "Leynar", "Ljósá", "Lopra", "Miðvágur", "Mikladalur", "Mjørkadalur",
"Morskranes", "Múli", "Mykines", "Nes", "Nesvík", "Nólsoy", "Norðdepil",
"Norðoyri", "Norðradalur", "Norðragøta", "Norðskáli", "Norðtoftir", "Oyndarfjørður",
"Oyrarbakki", "Oyrareingir", "Oyri", "Porkeri", "Rituvík", "Runavík", "Saksun",
"Saltangará", "Saltnes", "Sandavágur", "Sandur", "Sandvík", "Selatrað", "Signabøur",
"Skarvanes", "Skála", "Skálafjørður/Skálabotnur", "Skálafjørður (Eysturkommuna)",
"Skálavík", "Skipanes", "Skopun", "Skúgvoy", "Skælingur", "Stóra Dímun",
"Strendur", "Streymnes", "Stykkið", "Sumba", "Sund", "Svínáir", "Svínoy",
"Syðradalur (Kalsoy)", "Syðradalur (Streymoy)", "Syðrugøta", "Søldarfjørður",
"Sørvágur", "Tjørnuvík", "Toftir", "Tórshavn", "Trongisvágur", "Trøllanes",
"Tvøroyri", "Undir Gøtueiði", "Vatnsoyrar", "Vágur", "Válur", "Velbastaður",
"Vestmanna", "Viðareiði", "Víkarbyrgi", "Æðuvík", "Ørðavík/Øravík", "Øravíkarlíð",
}
type Registration struct {
ID string `json:"id"`
RegisteredAt string `json:"registered_at"`
Gender string `json:"gender"`
Location string `json:"location"`
AbuseType string `json:"abuse_type"`
Status string `json:"status"`
}
type RegistrationFilter struct {
Location string
AbuseType string
Gender string
Status string
Search string
From time.Time
To time.Time
HasFrom bool
HasTo bool
Limit int
Offset int
}
func NormalizeRegistration(input *Registration) error {
registeredAt := strings.TrimSpace(input.RegisteredAt)
if registeredAt == "" {
input.RegisteredAt = time.Now().UTC().Format(time.RFC3339)
} else {
parsed, err := ParseInputTime(registeredAt)
if err != nil {
return errors.New("registered_at must be RFC3339 or YYYY-MM-DD")
}
input.RegisteredAt = parsed.UTC().Format(time.RFC3339)
}
gender := strings.ToLower(strings.TrimSpace(input.Gender))
if gender == "" {
gender = "unknown"
}
if !ContainsFold(Genders, gender) {
return errors.New("gender must be one of: female, male, non_binary, unknown")
}
input.Gender = gender
location, ok := CanonicalLocation(input.Location)
if !ok {
return errors.New("location must be one of the allowed Faroese towns/villages")
}
input.Location = location
abuseType := strings.ToLower(strings.TrimSpace(input.AbuseType))
if !ContainsFold(AbuseCategories, abuseType) {
return errors.New("abuse_type must be one of the allowed categories")
}
input.AbuseType = abuseType
status := strings.ToLower(strings.TrimSpace(input.Status))
if status == "" {
status = "new"
}
if !ContainsFold(Statuses, status) {
return errors.New("status must be one of: new, open, referred, closed")
}
input.Status = status
return nil
}
func ParseInputTime(value string) (time.Time, error) {
value = strings.TrimSpace(value)
if parsed, err := time.Parse(time.RFC3339, value); err == nil {
return parsed, nil
}
if parsed, err := time.Parse("2006-01-02", value); err == nil {
return parsed, nil
}
return time.Time{}, errors.New("invalid time")
}
func CanonicalLocation(value string) (string, bool) {
needle := strings.ToLower(strings.TrimSpace(value))
for _, location := range FaroeLocations {
if strings.ToLower(location) == needle {
return location, true
}
}
return "", false
}
func ContainsFold(values []string, needle string) bool {
needle = strings.ToLower(strings.TrimSpace(needle))
for _, value := range values {
if strings.ToLower(strings.TrimSpace(value)) == needle {
return true
}
}
return false
}