feat: add the study data models and schema migrations
Courses, meetings, class sessions, deadlines, readings with per-user progress, materials with tags and file attachments. Timestamps are stored in UTC and held as Europe/Warsaw text so the admin text inputs can write them.
This commit is contained in:
89
go.mod
89
go.mod
@@ -2,39 +2,122 @@ module git.golem15.com/jakub/sm-bm-plugin
|
||||
|
||||
go 1.27.0
|
||||
|
||||
toolchain go1.27.0
|
||||
|
||||
require (
|
||||
git.golem15.com/golem15/sm-user-plugin v0.0.0
|
||||
git.golem15.com/golem15/summercms v0.0.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7
|
||||
github.com/jackc/pgx/v5 v5.10.0
|
||||
github.com/testcontainers/testcontainers-go v0.44.0
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0
|
||||
gorm.io/gorm v1.31.2
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.7.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ebitengine/purego v0.10.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.4 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||
github.com/knadh/koanf/parsers/yaml v1.1.1 // indirect
|
||||
github.com/knadh/koanf/providers/confmap v1.0.1 // indirect
|
||||
github.com/knadh/koanf/providers/env/v2 v2.0.1 // indirect
|
||||
github.com/knadh/koanf/providers/file v1.2.1 // indirect
|
||||
github.com/knadh/koanf/v2 v2.3.6 // indirect
|
||||
github.com/leodido/go-urn v1.5.0 // indirect
|
||||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/moby/api v1.55.0 // indirect
|
||||
github.com/moby/moby/client v0.5.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||
github.com/moby/sys/sequential v0.7.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/riverqueue/river v0.47.0 // indirect
|
||||
github.com/riverqueue/river/riverdriver v0.47.0 // indirect
|
||||
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.47.0 // indirect
|
||||
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.47.0 // indirect
|
||||
github.com/riverqueue/river/rivershared v0.47.0 // indirect
|
||||
github.com/riverqueue/river/rivertype v0.47.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.26.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/spf13/cobra v1.10.2 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/stretchr/testify v1.12.1 // indirect
|
||||
github.com/tidwall/gjson v1.19.0 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/tklauser/go-sysconf v0.4.0 // indirect
|
||||
github.com/tklauser/numcpus v0.12.0 // indirect
|
||||
github.com/wneessen/go-mail v0.8.1 // indirect
|
||||
github.com/yuin/goldmark v1.8.6 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
gocloud.dev v0.46.0 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/image v0.46.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sync v0.23.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/term v0.46.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
gorm.io/gorm v1.31.2 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/api v0.272.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gorm.io/driver/postgres v1.6.3 // indirect
|
||||
)
|
||||
|
||||
replace git.golem15.com/golem15/summercms => ../../../../summercms.go
|
||||
|
||||
replace git.golem15.com/golem15/sm-user-plugin => ../../golem15/user
|
||||
|
||||
208
go.sum
208
go.sum
@@ -1,15 +1,79 @@
|
||||
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
|
||||
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
|
||||
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
|
||||
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
|
||||
cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KMOg=
|
||||
cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.9 h1:/rYeyO2+HrMztAmxAq9++XJtFMqSIpSsNA0yDGALYq4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.9/go.mod h1:+HsoOEX80qAVUitj1A2DhCNTjmb3edVyuDypb6LNEeo=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.11 h1:h5+3VT69KUBK24grGuuA5saDJTj2IIjLb9au668Fo5I=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.11/go.mod h1:dnakxebH6UwFvcvujL0LVggYQ8nEvBGjU4G/V79Nv94=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.20 h1:8VMDnWc/kEzxsI/1ngGM9mG81a8IGmIHD8KLcYGwagc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.20/go.mod h1:PuwEpciweIXGULWeOeSTXtSbH4CW9mWdWrhdCKQI1sM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.19 h1:yuFzSV1U0aRNYCQGVaTY2zW2M/L93pYHnXnrJUphYhU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.19/go.mod h1:7y63L1kGzeoDlJaQ3Z578KrnmfBut96JjvJUzGwR+YE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.25 h1:0w6dCiO8iez+YKwRhRBlL1CH/E3GTfdkuzrwj1by8vo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.25/go.mod h1:9FDWUothyr5RCRAHc45XOiVCzUR8n/IhCYX+uVqw6vk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.2.3 h1:w5OoDiMN6x53ROmiIImGzmVcxXv2q1GXY+aKV4WAJYM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.2.3/go.mod h1:dAhgYp776bX3LuWvnSCFwQEjNs6fuFg7YXIy5PXcP3Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.25 h1:Uii3frf9ztec/ABM2/FSH9/z7PLzxfpG8h4RpkUFflQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.25/go.mod h1:G6kntsA2GorAxDPbap6xgB2F+amSLUF8GJTi7PUoX44=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.25 h1:r1+/l6m+WaUJF9HISEsNOLHSNj5EXYQxK8VX6Cz9NlA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.25/go.mod h1:cKf+D+NMDK1LndD7BowHbBZPgR9V0/5HubH0PFWvA+c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.26 h1:A1PmWU2zfkIm9EyFlJncFXL4W4phML+h8KjltUsCvNQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.26/go.mod h1:dY4MRzXEizrD4hqtpKvWVGPX7QleSGGVY+EBolo1RmM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.10 h1:d5/908OJ4bXg8lyjeMPvXetEKqoDoLi5Owy1zNue3yg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.10/go.mod h1:a57l7Hwh+FWI+we50g5NPJHYUKeJKfXbc4w8SyXu8Ig=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.18 h1:W/EyPFl9A5rXrtoilfwHYEvzHER+K4SpBPtMXi24Mos=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.18/go.mod h1:UG50K+pvd/uy6xExbobg0rjqFBFZe6I3l75EPDZw4tg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.25 h1:dD3dhHNglpd98gs72my22Ndqi1hqQGllFFg1F+twfxg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.25/go.mod h1:0yAbjPfd64gG7mj85RW+fMEYdfBgCRZw8g/oWcL1pjc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.25 h1:2pQEbwf+/6EDbiit/GcBE2K4IUpMZymaA0kOz3xK978=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.25/go.mod h1:KvT6NCcQ0EZ+ZkVRrlBMt04Po3ok23YELEp7WimhLhM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.102.2 h1:ie4ElCmUKS26pzrZcIk/lmt4yWjAqLLcawstyQCh298=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.102.2/go.mod h1:zjsomFeX5duj+4PlMB+o4JoWTIx+G0XMyzjYrUbQkN0=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.1 h1:1VwbP3qMNfxUDEXWki4rCE5iA+44VA1lokTz9HasGzw=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.1/go.mod h1:vUtyoSj0OPji3kjIVSc/GlKuWEiL33f/WFxl6dmpy/A=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.19 h1:N6pIsdFOW1Kd9S4KyFKXdGRBojPPxkP32+uHFWLv4Hc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.19/go.mod h1:3gt5WJArFooNmyLONS+h/R4J+o86II8du38IgCwj9dE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.2 h1:hc+lBYiiTr8Zk4MTzIsQ92MeDWCIDvWGmzKUWOaBcOg=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.2/go.mod h1:hU6fqB3OJA6/ePheD47LQnxvjYk6br6PtQxs+Q9ojvk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.3 h1:ErklX/7uhSbkAAeyQD/Y1OoQ9hO3SJXQNEgksORW3Js=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.3/go.mod h1:ULe4HCzfKPiR6R3HEurE3b1upEkuk8AkMrOKtaOxKO8=
|
||||
github.com/aws/smithy-go v1.26.0 h1:9ouqbi+NyKP7fV3Te7UElCwdAb6Y8uk7LGwPE5tVe/s=
|
||||
github.com/aws/smithy-go v1.26.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
@@ -21,6 +85,11 @@ github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7np
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
||||
@@ -29,26 +98,69 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
|
||||
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7 h1:PdT4jVPbRb4R+0Ey2R0yJOdctVf4Whiq1Qi4necaZdg=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7/go.mod h1:3ouXglTuPrKF5+7cQyVGfvAXTU4vLMaYh9+EPl03uog=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.4 h1:9Rcod2ZPO6mOEG6b4GqyoHE/H6//Ze0RuhOo1hT1x0w=
|
||||
github.com/go-playground/validator/v10 v10.30.4/go.mod h1:numpT+RPLE91R9oYWMY/R9zRgJBewr3IXHko4OISPpk=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
||||
github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
|
||||
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
@@ -67,12 +179,18 @@ github.com/knadh/koanf/providers/file v1.2.1 h1:bEWbtQwYrA+W2DtdBrQWyXqJaJSG3KrP
|
||||
github.com/knadh/koanf/providers/file v1.2.1/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
|
||||
github.com/knadh/koanf/v2 v2.3.6 h1:JoQPSJmvS4aP0xNc8xMDr5tcrkSEInL23/Il7pITAKo=
|
||||
github.com/knadh/koanf/v2 v2.3.6/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28=
|
||||
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
|
||||
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak=
|
||||
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
|
||||
github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
@@ -101,8 +219,25 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/riverqueue/river v0.47.0 h1:j8HOEyiOE8gRRhVS2wllKams372TH1WWiH6xCakXHqc=
|
||||
github.com/riverqueue/river v0.47.0/go.mod h1:Wgmwx475ZBd8lQnNrJgyG2DWH7BfyNiSAtv0rC9bJBQ=
|
||||
github.com/riverqueue/river/riverdriver v0.47.0 h1:qU8VkjdMl9plqeRg57SxsDUM/i/eECaSYejZ7HynC60=
|
||||
github.com/riverqueue/river/riverdriver v0.47.0/go.mod h1:NOXl0fUiF1AT/TaQOjdx2A/c0Davn+SKbW8nAXWjfC4=
|
||||
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.47.0 h1:4wckhMlLN08F76/GWxGF6vlrbs5HCMuV01dpUnvOGI4=
|
||||
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.47.0/go.mod h1:EME1EGr7RU/vMUf61mpf9YXhsZJ+GlFk4AHrIHT+YdE=
|
||||
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.47.0 h1:5N9nvemhQwbUElMxASw4oEaYJ/v6hiS5Y9VcOQfdC5g=
|
||||
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.47.0/go.mod h1:ZboiXXZKC4+fTkxBxGRVmAsCuUu0NPYliqbWYuQAZyw=
|
||||
github.com/riverqueue/river/rivershared v0.47.0 h1:jdtFsBexCvLqTXf8wnDnGXvB/eeOtPKQZAmThkjFpLs=
|
||||
github.com/riverqueue/river/rivershared v0.47.0/go.mod h1:w8Pi1T+6ypyko5/hs9Mv7IIIKo4fAL9eXYnkVV/Y418=
|
||||
github.com/riverqueue/river/rivertype v0.47.0 h1:SzNavtLGR4nMT1QkrEYQ7n96OMatYsn/z3aJWhewmv0=
|
||||
github.com/riverqueue/river/rivertype v0.47.0/go.mod h1:XKkcRQR6zm8RR/JQa1Q2ywpj8uXQu21quPa4Lpw1Xhw=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs=
|
||||
github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||
@@ -112,10 +247,30 @@ github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/testcontainers/testcontainers-go v0.44.0 h1:/Fwh6HY1mIikhnm9e7HwoxGycx0lzRAE0f5VQpjFxzI=
|
||||
github.com/testcontainers/testcontainers-go v0.44.0/go.mod h1:IcnwQrYTO86xHXu5bvMaBH7ATlbS3Qn1M1QWW3c66rE=
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0 h1:8fdv/9y3JMxjQ+ULAcOG8RtgeNu5t9XF9LolSXDuTwM=
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0/go.mod h1:CFr2LncGYokw+OKjXcr8ARCKG1SaC2UEnGxFBovE86g=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU=
|
||||
github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI=
|
||||
github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4=
|
||||
@@ -128,27 +283,80 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
gocloud.dev v0.46.0 h1:niIuZwSjMtBx8K+ITB2s5kZullB13PGOS2ZoQPZxQ4Q=
|
||||
gocloud.dev v0.46.0/go.mod h1:ACQe+2qO+hEO+pdcvvsM+RB63r8TyGD1W3ESCLFyzvM=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.46.0 h1:b1+oYj0Jbp6K5MDT4i4/eZpYlk3V8SJhhDKh6LBHAyQ=
|
||||
golang.org/x/image v0.46.0/go.mod h1:3B3W05VGVQyuXucLINLjXKrqISASfi4Xj+iCVkLMwew=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=
|
||||
google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 h1:aJmi6DVGGIStN9Mobk/tZOOQUBbj0BPjZjjnOdoZKts=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.6.3 h1:bAn6O2pUa8LtpWEvL5NFU4+52Tfx8Ut7IVaIacCLcI0=
|
||||
gorm.io/driver/postgres v1.6.3/go.mod h1:0c4fQA44XhOklXDkgtuKqysHCycTa5i9e3EIpDGCwXk=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
|
||||
gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
|
||||
63
models/class_session.go
Normal file
63
models/class_session.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ClassSession is one block of classes (zajęcia) of a course.
|
||||
type ClassSession struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
CourseID uint `gorm:"column:course_id"`
|
||||
Course *Course `gorm:"foreignKey:CourseID"`
|
||||
MeetingID *uint `gorm:"column:meeting_id"`
|
||||
Meeting *Meeting `gorm:"foreignKey:MeetingID"`
|
||||
StartsAt DateTime `gorm:"column:starts_at"`
|
||||
EndsAt DateTime `gorm:"column:ends_at"`
|
||||
Room *string `gorm:"column:room"`
|
||||
Kind *string `gorm:"column:kind"`
|
||||
Notes *string `gorm:"column:notes"`
|
||||
IsOnline bool `gorm:"column:is_online"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (ClassSession) TableName() string { return "jz_bm_class_sessions" }
|
||||
|
||||
// Fillable lists the columns mass assignment may set. course_id and
|
||||
// meeting_id are set through the admin relation pickers.
|
||||
func (ClassSession) Fillable() []string {
|
||||
return []string{"course_id", "meeting_id", "starts_at", "ends_at", "room", "kind", "notes", "is_online"}
|
||||
}
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (ClassSession) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"starts_at": "required",
|
||||
"ends_at": "required",
|
||||
"room": "nullable|max:100",
|
||||
"kind": "nullable|max:100",
|
||||
"is_online": "boolean",
|
||||
}
|
||||
}
|
||||
|
||||
// TemporalErrors checks the timestamps, which lagoon.Validate cannot.
|
||||
func (s *ClassSession) TemporalErrors() map[string][]string {
|
||||
e := temporalErrors{}
|
||||
e.dateTime("starts_at", s.StartsAt, true)
|
||||
e.dateTime("ends_at", s.EndsAt, true)
|
||||
if len(e) == 0 && !s.EndsAt.MustTime().After(s.StartsAt.MustTime()) {
|
||||
e.add("ends_at", "The ends at must be a date after starts at.")
|
||||
}
|
||||
return e.result()
|
||||
}
|
||||
|
||||
// BeforeSave stores NULL for empty optional text.
|
||||
func (s *ClassSession) BeforeSave(*gorm.DB) error {
|
||||
blank(&s.Room, &s.Kind, &s.Notes)
|
||||
return nil
|
||||
}
|
||||
72
models/course.go
Normal file
72
models/course.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Course forms, the values of Course.Form.
|
||||
const (
|
||||
FormWyklad = "wyklad"
|
||||
FormCwiczenia = "cwiczenia"
|
||||
FormKonwersatorium = "konwersatorium"
|
||||
FormSeminarium = "seminarium"
|
||||
FormInne = "inne"
|
||||
)
|
||||
|
||||
// CourseForms lists the allowed Course.Form values.
|
||||
var CourseForms = []string{FormWyklad, FormCwiczenia, FormKonwersatorium, FormSeminarium, FormInne}
|
||||
|
||||
// Course is one przedmiot of the programme in one form (a lecture and its
|
||||
// tutorial are two courses).
|
||||
type Course struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Code *string `gorm:"column:code"`
|
||||
Name string `gorm:"column:name"`
|
||||
Form string `gorm:"column:form"`
|
||||
Lecturer *string `gorm:"column:lecturer"`
|
||||
Semester int `gorm:"column:semester"`
|
||||
ECTS *float64 `gorm:"column:ects"`
|
||||
Assessment *string `gorm:"column:assessment"`
|
||||
// ElectiveGroup marks alternatives ("do wyboru"): courses that share a
|
||||
// value are options of one choice, such as "specjalnosc".
|
||||
ElectiveGroup *string `gorm:"column:elective_group"`
|
||||
Description *string `gorm:"column:description"`
|
||||
SyllabusURL *string `gorm:"column:syllabus_url"`
|
||||
SortOrder int `gorm:"column:sort_order"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (Course) TableName() string { return "jz_bm_courses" }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Course) Fillable() []string {
|
||||
return []string{"code", "name", "form", "lecturer", "semester", "ects", "assessment", "elective_group", "description", "syllabus_url", "sort_order"}
|
||||
}
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (Course) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"code": "nullable|max:50",
|
||||
"name": "required|max:255",
|
||||
"form": "required|in:wyklad,cwiczenia,konwersatorium,seminarium,inne",
|
||||
"lecturer": "nullable|max:255",
|
||||
"semester": "required|integer|between:1,4",
|
||||
"ects": "nullable|numeric|between:0,60",
|
||||
"assessment": "nullable|max:100",
|
||||
"elective_group": "nullable|max:100",
|
||||
"syllabus_url": "nullable|max:2048",
|
||||
"sort_order": "nullable|integer",
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeSave stores NULL for empty optional text.
|
||||
func (c *Course) BeforeSave(*gorm.DB) error {
|
||||
blank(&c.Code, &c.Lecturer, &c.Assessment, &c.ElectiveGroup, &c.Description, &c.SyllabusURL)
|
||||
return nil
|
||||
}
|
||||
56
models/deadline.go
Normal file
56
models/deadline.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// DeadlineKinds lists the allowed Deadline.Kind values.
|
||||
var DeadlineKinds = []string{"zaliczenie", "egzamin", "praca", "organizacyjne", "inne"}
|
||||
|
||||
// Deadline is one entry of the terminarz: an exam, a submission or an
|
||||
// administrative date, optionally tied to a course.
|
||||
type Deadline struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Title string `gorm:"column:title"`
|
||||
DueAt DateTime `gorm:"column:due_at"`
|
||||
CourseID *uint `gorm:"column:course_id"`
|
||||
Course *Course `gorm:"foreignKey:CourseID"`
|
||||
Kind string `gorm:"column:kind"`
|
||||
Description *string `gorm:"column:description"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (Deadline) TableName() string { return "jz_bm_deadlines" }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Deadline) Fillable() []string {
|
||||
return []string{"title", "due_at", "course_id", "kind", "description"}
|
||||
}
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (Deadline) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"title": "required|max:255",
|
||||
"due_at": "required",
|
||||
"kind": "required|in:zaliczenie,egzamin,praca,organizacyjne,inne",
|
||||
}
|
||||
}
|
||||
|
||||
// TemporalErrors checks the due time, which lagoon.Validate cannot.
|
||||
func (d *Deadline) TemporalErrors() map[string][]string {
|
||||
e := temporalErrors{}
|
||||
e.dateTime("due_at", d.DueAt, true)
|
||||
return e.result()
|
||||
}
|
||||
|
||||
// BeforeSave stores NULL for empty optional text.
|
||||
func (d *Deadline) BeforeSave(*gorm.DB) error {
|
||||
blank(&d.Description)
|
||||
return nil
|
||||
}
|
||||
75
models/material.go
Normal file
75
models/material.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MaterialTypes lists the allowed Material.Type values.
|
||||
var MaterialTypes = []string{"pdf", "notatka", "link", "inne"}
|
||||
|
||||
// MaterialFilesField is the system_files field of a material's attachments
|
||||
// (attachMany "files").
|
||||
const MaterialFilesField = "files"
|
||||
|
||||
// Material is a study material: a note, a link or uploaded files, optionally
|
||||
// tied to a course. A material without published_at is a draft and is not
|
||||
// served by the API.
|
||||
type Material struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
CourseID *uint `gorm:"column:course_id"`
|
||||
Course *Course `gorm:"foreignKey:CourseID"`
|
||||
Title string `gorm:"column:title"`
|
||||
Type string `gorm:"column:type"`
|
||||
Body *string `gorm:"column:body"`
|
||||
Source *string `gorm:"column:source"`
|
||||
URL *string `gorm:"column:url"`
|
||||
PublishedAt *DateTime `gorm:"column:published_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
|
||||
Tags []Tag `gorm:"many2many:jz_bm_material_tag;joinForeignKey:material_id;joinReferences:tag_id"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (Material) TableName() string { return "jz_bm_materials" }
|
||||
|
||||
// MorphName is the attachment_type of the material's system_files rows.
|
||||
func (Material) MorphName() string { return `Jz\Bm\Models\Material` }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Material) Fillable() []string {
|
||||
return []string{"course_id", "title", "type", "body", "source", "url", "published_at"}
|
||||
}
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (Material) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"title": "required|max:255",
|
||||
"type": "required|in:pdf,notatka,link,inne",
|
||||
"source": "nullable|max:500",
|
||||
"url": "nullable|max:2048",
|
||||
}
|
||||
}
|
||||
|
||||
// TemporalErrors checks published_at, which lagoon.Validate cannot.
|
||||
func (m *Material) TemporalErrors() map[string][]string {
|
||||
e := temporalErrors{}
|
||||
if m.PublishedAt != nil {
|
||||
e.dateTime("published_at", *m.PublishedAt, false)
|
||||
}
|
||||
return e.result()
|
||||
}
|
||||
|
||||
// BeforeSave stores NULL for empty optional text and an empty publication
|
||||
// time.
|
||||
func (m *Material) BeforeSave(*gorm.DB) error {
|
||||
blank(&m.Body, &m.Source, &m.URL)
|
||||
if m.PublishedAt != nil && *m.PublishedAt == "" {
|
||||
m.PublishedAt = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
12
models/material_tag.go
Normal file
12
models/material_tag.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// MaterialTag is the pivot row between a material and a tag.
|
||||
type MaterialTag struct {
|
||||
MaterialID uint `gorm:"column:material_id;primaryKey"`
|
||||
TagID uint `gorm:"column:tag_id;primaryKey"`
|
||||
}
|
||||
|
||||
// TableName is the pivot table.
|
||||
func (MaterialTag) TableName() string { return "jz_bm_material_tag" }
|
||||
52
models/meeting.go
Normal file
52
models/meeting.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Meeting is one zjazd: a weekend of classes of the part-time programme.
|
||||
type Meeting struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Number int `gorm:"column:number"`
|
||||
StartsOn Date `gorm:"column:starts_on"`
|
||||
EndsOn Date `gorm:"column:ends_on"`
|
||||
Notes *string `gorm:"column:notes"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (Meeting) TableName() string { return "jz_bm_meetings" }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Meeting) Fillable() []string { return []string{"number", "starts_on", "ends_on", "notes"} }
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (Meeting) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"number": "required|integer|min:1|unique:jz_bm_meetings",
|
||||
"starts_on": "required",
|
||||
"ends_on": "required",
|
||||
}
|
||||
}
|
||||
|
||||
// TemporalErrors checks the dates, which lagoon.Validate cannot.
|
||||
func (m *Meeting) TemporalErrors() map[string][]string {
|
||||
e := temporalErrors{}
|
||||
e.date("starts_on", m.StartsOn, true)
|
||||
e.date("ends_on", m.EndsOn, true)
|
||||
if len(e) == 0 && m.EndsOn < m.StartsOn {
|
||||
e.add("ends_on", "The ends on must be a date after or equal to starts on.")
|
||||
}
|
||||
return e.result()
|
||||
}
|
||||
|
||||
// BeforeSave stores NULL for empty optional text.
|
||||
func (m *Meeting) BeforeSave(*gorm.DB) error {
|
||||
blank(&m.Notes)
|
||||
return nil
|
||||
}
|
||||
50
models/nullable.go
Normal file
50
models/nullable.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package models
|
||||
|
||||
import "strings"
|
||||
|
||||
// blank clears each optional text column whose value is empty or only
|
||||
// whitespace, so an emptied admin text input stores NULL rather than "".
|
||||
func blank(fields ...**string) {
|
||||
for _, f := range fields {
|
||||
if *f != nil && strings.TrimSpace(**f) == "" {
|
||||
*f = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// temporalErrors is a field-to-messages map in the shape of lagoon.Validate.
|
||||
type temporalErrors map[string][]string
|
||||
|
||||
func (e temporalErrors) add(field, msg string) { e[field] = append(e[field], msg) }
|
||||
|
||||
func (e temporalErrors) dateTime(field string, v DateTime, required bool) {
|
||||
switch {
|
||||
case v == "" && required:
|
||||
e.add(field, "The "+strings.ReplaceAll(field, "_", " ")+" field is required.")
|
||||
case v != "" && !v.Valid():
|
||||
e.add(field, "The "+strings.ReplaceAll(field, "_", " ")+" must be a date and time such as 2026-10-03 08:00.")
|
||||
}
|
||||
}
|
||||
|
||||
func (e temporalErrors) date(field string, v Date, required bool) {
|
||||
switch {
|
||||
case v == "" && required:
|
||||
e.add(field, "The "+strings.ReplaceAll(field, "_", " ")+" field is required.")
|
||||
case v != "" && !v.Valid():
|
||||
e.add(field, "The "+strings.ReplaceAll(field, "_", " ")+" must be a date in the YYYY-MM-DD form.")
|
||||
}
|
||||
}
|
||||
|
||||
func (e temporalErrors) result() map[string][]string {
|
||||
if len(e) == 0 {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// TemporalValidator is implemented by models with Date or DateTime columns.
|
||||
// The admin controllers call it before a save, because lagoon.Validate has
|
||||
// no date rules.
|
||||
type TemporalValidator interface {
|
||||
TemporalErrors() map[string][]string
|
||||
}
|
||||
50
models/reading.go
Normal file
50
models/reading.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Reading is one lektura on a course's reading list.
|
||||
type Reading struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
CourseID uint `gorm:"column:course_id"`
|
||||
Course *Course `gorm:"foreignKey:CourseID"`
|
||||
Title string `gorm:"column:title"`
|
||||
Author *string `gorm:"column:author"`
|
||||
Source *string `gorm:"column:source"`
|
||||
URL *string `gorm:"column:url"`
|
||||
IsRequired bool `gorm:"column:is_required"`
|
||||
SortOrder int `gorm:"column:sort_order"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (Reading) TableName() string { return "jz_bm_readings" }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Reading) Fillable() []string {
|
||||
return []string{"course_id", "title", "author", "source", "url", "is_required", "sort_order"}
|
||||
}
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (Reading) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"title": "required|max:500",
|
||||
"author": "nullable|max:255",
|
||||
"source": "nullable|max:500",
|
||||
"url": "nullable|max:2048",
|
||||
"is_required": "boolean",
|
||||
"sort_order": "nullable|integer",
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeSave stores NULL for empty optional text.
|
||||
func (r *Reading) BeforeSave(*gorm.DB) error {
|
||||
blank(&r.Author, &r.Source, &r.URL)
|
||||
return nil
|
||||
}
|
||||
17
models/reading_progress.go
Normal file
17
models/reading_progress.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// ReadingProgress records that a user (a golem15.user users row) has read a
|
||||
// reading. One row per reading and user.
|
||||
type ReadingProgress struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
ReadingID uint `gorm:"column:reading_id"`
|
||||
UserID uint `gorm:"column:user_id"`
|
||||
ReadAt time.Time `gorm:"column:read_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (ReadingProgress) TableName() string { return "jz_bm_reading_progress" }
|
||||
28
models/tag.go
Normal file
28
models/tag.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// Tag labels materials.
|
||||
type Tag struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Name string `gorm:"column:name"`
|
||||
Slug string `gorm:"column:slug"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
// TableName is the plugin table.
|
||||
func (Tag) TableName() string { return "jz_bm_tags" }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Tag) Fillable() []string { return []string{"name", "slug"} }
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
func (Tag) Rules() map[string]string {
|
||||
return map[string]string{
|
||||
"name": "required|max:100",
|
||||
"slug": "required|max:100|unique:jz_bm_tags",
|
||||
}
|
||||
}
|
||||
186
models/temporal.go
Normal file
186
models/temporal.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
_ "time/tzdata" // Europe/Warsaw must resolve on hosts without zoneinfo.
|
||||
)
|
||||
|
||||
// Location is the time zone the programme runs in. Timestamps are stored in
|
||||
// UTC (TIMESTAMPTZ) and shown in this zone; a date or time typed without an
|
||||
// offset is read in it.
|
||||
var Location = mustLoadLocation("Europe/Warsaw")
|
||||
|
||||
func mustLoadLocation(name string) *time.Location {
|
||||
loc, err := time.LoadLocation(name)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("jz.bm: load location %s: %v", name, err))
|
||||
}
|
||||
return loc
|
||||
}
|
||||
|
||||
// DateLayout is the textual form of a Date.
|
||||
const DateLayout = "2006-01-02"
|
||||
|
||||
// dateTimeLayouts are the forms ParseDateTime accepts. RFC 3339 carries its
|
||||
// own offset; the others are wall-clock times in Location.
|
||||
var dateTimeLayouts = []string{
|
||||
"2006-01-02T15:04",
|
||||
"2006-01-02 15:04",
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02 15:04:05",
|
||||
}
|
||||
|
||||
// ParseDateTime reads an RFC 3339 timestamp, or a wall-clock time
|
||||
// ("2026-10-03 08:00") in Location.
|
||||
func ParseDateTime(s string) (time.Time, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if t, err := time.Parse(time.RFC3339, s); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
for _, layout := range dateTimeLayouts {
|
||||
if t, err := time.ParseInLocation(layout, s, Location); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("jz.bm: %q is not a date and time", s)
|
||||
}
|
||||
|
||||
// ParseDate reads a YYYY-MM-DD calendar date.
|
||||
func ParseDate(s string) (time.Time, error) {
|
||||
t, err := time.ParseInLocation(DateLayout, strings.TrimSpace(s), Location)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("jz.bm: %q is not a YYYY-MM-DD date", s)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// DateTime is a TIMESTAMPTZ column held as RFC 3339 text in Location
|
||||
// ("2026-10-03T08:00:00+02:00").
|
||||
//
|
||||
// It is a string type rather than time.Time because the admin form has no
|
||||
// date picker and lagoon.Fill cannot write text into a time.Time field, while
|
||||
// cabana treats any struct-typed field as a relation. A string type is filled
|
||||
// from the form's text input, and Value checks and normalises it.
|
||||
// TODO: requires SummerCMS change (a datetime form field type and Fill
|
||||
// support for time.Time) to use time.Time here.
|
||||
type DateTime string
|
||||
|
||||
// NewDateTime returns t as a DateTime in Location.
|
||||
func NewDateTime(t time.Time) DateTime {
|
||||
return DateTime(t.In(Location).Format(time.RFC3339))
|
||||
}
|
||||
|
||||
// Time parses the value.
|
||||
func (d DateTime) Time() (time.Time, error) { return ParseDateTime(string(d)) }
|
||||
|
||||
// MustTime parses the value and returns the zero time when it does not parse.
|
||||
// Values read from the database always parse.
|
||||
func (d DateTime) MustTime() time.Time {
|
||||
t, _ := d.Time()
|
||||
return t
|
||||
}
|
||||
|
||||
// Valid reports whether the value parses.
|
||||
func (d DateTime) Valid() bool {
|
||||
_, err := d.Time()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Value stores the instant in UTC. An empty value is SQL NULL.
|
||||
func (d DateTime) Value() (driver.Value, error) {
|
||||
if strings.TrimSpace(string(d)) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
t, err := d.Time()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.UTC(), nil
|
||||
}
|
||||
|
||||
// Scan reads a TIMESTAMPTZ (or its text) and normalises it to Location.
|
||||
func (d *DateTime) Scan(src any) error {
|
||||
switch v := src.(type) {
|
||||
case nil:
|
||||
*d = ""
|
||||
case time.Time:
|
||||
*d = NewDateTime(v)
|
||||
case string:
|
||||
return d.scanText(v)
|
||||
case []byte:
|
||||
return d.scanText(string(v))
|
||||
default:
|
||||
return fmt.Errorf("jz.bm: cannot scan %T into DateTime", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DateTime) scanText(s string) error {
|
||||
if strings.TrimSpace(s) == "" {
|
||||
*d = ""
|
||||
return nil
|
||||
}
|
||||
t, err := ParseDateTime(s)
|
||||
if err != nil {
|
||||
// Postgres text output: "2026-10-03 06:00:00+00".
|
||||
t, err = time.Parse("2006-01-02 15:04:05-07", s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*d = NewDateTime(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Date is a DATE column held as YYYY-MM-DD text, for the same reason as
|
||||
// DateTime.
|
||||
type Date string
|
||||
|
||||
// NewDate returns the calendar date of t in Location.
|
||||
func NewDate(t time.Time) Date { return Date(t.In(Location).Format(DateLayout)) }
|
||||
|
||||
// Time returns midnight of the date in Location.
|
||||
func (d Date) Time() (time.Time, error) { return ParseDate(string(d)) }
|
||||
|
||||
// Valid reports whether the value is a YYYY-MM-DD date.
|
||||
func (d Date) Valid() bool {
|
||||
_, err := d.Time()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Value stores the date as text; Postgres casts it to DATE. An empty value is
|
||||
// SQL NULL.
|
||||
func (d Date) Value() (driver.Value, error) {
|
||||
if strings.TrimSpace(string(d)) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
t, err := d.Time()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.Format(DateLayout), nil
|
||||
}
|
||||
|
||||
// Scan reads a DATE. The driver hands it over as midnight UTC, so the
|
||||
// calendar fields are taken as they are, without a zone conversion.
|
||||
func (d *Date) Scan(src any) error {
|
||||
switch v := src.(type) {
|
||||
case nil:
|
||||
*d = ""
|
||||
case time.Time:
|
||||
*d = Date(v.Format(DateLayout))
|
||||
case string:
|
||||
*d = Date(strings.TrimSpace(v))
|
||||
if len(*d) > len(DateLayout) {
|
||||
*d = (*d)[:len(DateLayout)]
|
||||
}
|
||||
case []byte:
|
||||
return d.Scan(string(v))
|
||||
default:
|
||||
return fmt.Errorf("jz.bm: cannot scan %T into Date", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
136
models/temporal_test.go
Normal file
136
models/temporal_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.golem15.com/jakub/sm-bm-plugin/models"
|
||||
)
|
||||
|
||||
func TestParseDateTimeForms(t *testing.T) {
|
||||
want := time.Date(2026, 10, 3, 6, 0, 0, 0, time.UTC)
|
||||
for _, in := range []string{
|
||||
"2026-10-03T08:00:00+02:00",
|
||||
"2026-10-03T06:00:00Z",
|
||||
"2026-10-03 08:00",
|
||||
"2026-10-03T08:00",
|
||||
" 2026-10-03 08:00:00 ",
|
||||
} {
|
||||
got, err := models.ParseDateTime(in)
|
||||
if err != nil {
|
||||
t.Errorf("ParseDateTime(%q): %v", in, err)
|
||||
continue
|
||||
}
|
||||
if !got.Equal(want) {
|
||||
t.Errorf("ParseDateTime(%q) = %v, want %v", in, got, want)
|
||||
}
|
||||
}
|
||||
// Winter time: a wall-clock time in December is UTC+1.
|
||||
got, err := models.ParseDateTime("2026-12-01 08:00")
|
||||
if err != nil || !got.Equal(time.Date(2026, 12, 1, 7, 0, 0, 0, time.UTC)) {
|
||||
t.Errorf("winter wall clock = %v, %v", got, err)
|
||||
}
|
||||
for _, bad := range []string{"", "tomorrow", "2026-13-01 08:00", "2026-10-03"} {
|
||||
if _, err := models.ParseDateTime(bad); err == nil {
|
||||
t.Errorf("ParseDateTime(%q) accepted", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateTimeRoundTrip(t *testing.T) {
|
||||
d := models.DateTime("2026-10-03 08:00")
|
||||
v, err := d.Value()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
stored, ok := v.(time.Time)
|
||||
if !ok || stored.Location() != time.UTC || stored.Hour() != 6 {
|
||||
t.Fatalf("Value = %#v, want 06:00 UTC", v)
|
||||
}
|
||||
var read models.DateTime
|
||||
if err := read.Scan(stored); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if read != "2026-10-03T08:00:00+02:00" {
|
||||
t.Errorf("Scan = %q, want the Warsaw RFC 3339 form", read)
|
||||
}
|
||||
if err := read.Scan("2026-10-03 06:00:00+00"); err != nil || read != "2026-10-03T08:00:00+02:00" {
|
||||
t.Errorf("Scan(postgres text) = %q, %v", read, err)
|
||||
}
|
||||
if err := read.Scan(nil); err != nil || read != "" {
|
||||
t.Errorf("Scan(nil) = %q, %v", read, err)
|
||||
}
|
||||
if v, err := models.DateTime("").Value(); err != nil || v != nil {
|
||||
t.Errorf("empty Value = %v, %v; want NULL", v, err)
|
||||
}
|
||||
if _, err := models.DateTime("soon").Value(); err == nil {
|
||||
t.Error("an unparsable DateTime was stored")
|
||||
}
|
||||
if err := read.Scan(42); err == nil {
|
||||
t.Error("Scan(int) succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateRoundTrip(t *testing.T) {
|
||||
var d models.Date
|
||||
if err := d.Scan(time.Date(2026, 10, 3, 0, 0, 0, 0, time.UTC)); err != nil || d != "2026-10-03" {
|
||||
t.Errorf("Scan(time) = %q, %v", d, err)
|
||||
}
|
||||
if err := d.Scan([]byte("2026-10-04")); err != nil || d != "2026-10-04" {
|
||||
t.Errorf("Scan(bytes) = %q, %v", d, err)
|
||||
}
|
||||
if v, err := models.Date("2026-10-03").Value(); err != nil || v != "2026-10-03" {
|
||||
t.Errorf("Value = %v, %v", v, err)
|
||||
}
|
||||
if _, err := models.Date("03.10.2026").Value(); err == nil {
|
||||
t.Error("a non-ISO date was stored")
|
||||
}
|
||||
if models.NewDate(time.Date(2026, 10, 2, 23, 30, 0, 0, time.UTC)) != "2026-10-03" {
|
||||
t.Error("NewDate does not use the Warsaw calendar day")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemporalErrors(t *testing.T) {
|
||||
s := &models.ClassSession{StartsAt: "2026-10-03 10:00", EndsAt: "2026-10-03 09:00"}
|
||||
if errs := s.TemporalErrors(); len(errs["ends_at"]) != 1 {
|
||||
t.Errorf("end before start: %v", errs)
|
||||
}
|
||||
s = &models.ClassSession{StartsAt: "bad"}
|
||||
errs := s.TemporalErrors()
|
||||
if len(errs["starts_at"]) != 1 || len(errs["ends_at"]) != 1 {
|
||||
t.Errorf("bad and missing: %v", errs)
|
||||
}
|
||||
m := &models.Meeting{StartsOn: "2026-10-04", EndsOn: "2026-10-03"}
|
||||
if errs := m.TemporalErrors(); len(errs["ends_on"]) != 1 {
|
||||
t.Errorf("meeting order: %v", errs)
|
||||
}
|
||||
ok := &models.Meeting{StartsOn: "2026-10-03", EndsOn: "2026-10-04"}
|
||||
if errs := ok.TemporalErrors(); errs != nil {
|
||||
t.Errorf("valid meeting: %v", errs)
|
||||
}
|
||||
draft := &models.Material{}
|
||||
if errs := draft.TemporalErrors(); errs != nil {
|
||||
t.Errorf("draft material: %v", errs)
|
||||
}
|
||||
bad := models.DateTime("yesterday")
|
||||
if errs := (&models.Material{PublishedAt: &bad}).TemporalErrors(); len(errs["published_at"]) != 1 {
|
||||
t.Errorf("bad published_at: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeforeSaveBlanksOptionalText(t *testing.T) {
|
||||
empty, space := "", " "
|
||||
c := &models.Course{Code: &empty, Lecturer: &space}
|
||||
if err := c.BeforeSave(nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.Code != nil || c.Lecturer != nil {
|
||||
t.Errorf("blank text kept: %v %v", c.Code, c.Lecturer)
|
||||
}
|
||||
pub := models.DateTime("")
|
||||
m := &models.Material{PublishedAt: &pub}
|
||||
_ = m.BeforeSave(nil)
|
||||
if m.PublishedAt != nil {
|
||||
t.Error("an empty published_at was not cleared to a draft")
|
||||
}
|
||||
}
|
||||
31
updates/20261002120154_create_jz_bm_meetings.go
Normal file
31
updates/20261002120154_create_jz_bm_meetings.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateMeetings returns the 20261002120154_create_jz_bm_meetings gormigrate entry:
|
||||
// the zjazdy.
|
||||
func CreateMeetings() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120154_create_jz_bm_meetings",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_meetings (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
number INTEGER NOT NULL UNIQUE CHECK (number > 0),
|
||||
starts_on DATE NOT NULL,
|
||||
ends_on DATE NOT NULL,
|
||||
notes TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CHECK (ends_on >= starts_on)
|
||||
)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_meetings").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
38
updates/20261002120203_create_jz_bm_courses.go
Normal file
38
updates/20261002120203_create_jz_bm_courses.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateCourses returns the 20261002120203_create_jz_bm_courses gormigrate entry:
|
||||
// the przedmioty, one row per course and form.
|
||||
func CreateCourses() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120203_create_jz_bm_courses",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_courses (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
code VARCHAR(50) NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
form VARCHAR(20) NOT NULL CHECK (form IN ('wyklad', 'cwiczenia', 'konwersatorium', 'seminarium', 'inne')),
|
||||
lecturer VARCHAR(255) NULL,
|
||||
semester INTEGER NOT NULL CHECK (semester > 0),
|
||||
ects NUMERIC(4,1) NULL,
|
||||
assessment VARCHAR(100) NULL,
|
||||
description TEXT NULL,
|
||||
syllabus_url VARCHAR(2048) NULL,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (name, form, semester)
|
||||
);
|
||||
CREATE INDEX jz_bm_courses_semester_sort_index ON jz_bm_courses (semester, sort_order)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_courses").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
38
updates/20261002120204_create_jz_bm_class_sessions.go
Normal file
38
updates/20261002120204_create_jz_bm_class_sessions.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateClassSessions returns the 20261002120204_create_jz_bm_class_sessions gormigrate entry:
|
||||
// the zajęcia. Deleting a course deletes its sessions; deleting a meeting keeps them.
|
||||
func CreateClassSessions() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120204_create_jz_bm_class_sessions",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_class_sessions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
course_id BIGINT NOT NULL REFERENCES jz_bm_courses (id) ON DELETE CASCADE,
|
||||
meeting_id BIGINT NULL REFERENCES jz_bm_meetings (id) ON DELETE SET NULL,
|
||||
starts_at TIMESTAMPTZ NOT NULL,
|
||||
ends_at TIMESTAMPTZ NOT NULL,
|
||||
room VARCHAR(100) NULL,
|
||||
kind VARCHAR(100) NULL,
|
||||
notes TEXT NULL,
|
||||
is_online BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CHECK (ends_at > starts_at),
|
||||
UNIQUE (course_id, starts_at)
|
||||
);
|
||||
CREATE INDEX jz_bm_class_sessions_starts_at_index ON jz_bm_class_sessions (starts_at);
|
||||
CREATE INDEX jz_bm_class_sessions_meeting_id_index ON jz_bm_class_sessions (meeting_id)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_class_sessions").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
33
updates/20261002120205_create_jz_bm_deadlines.go
Normal file
33
updates/20261002120205_create_jz_bm_deadlines.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateDeadlines returns the 20261002120205_create_jz_bm_deadlines gormigrate entry:
|
||||
// the terminarz. Deleting a course keeps its deadlines, unlinked.
|
||||
func CreateDeadlines() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120205_create_jz_bm_deadlines",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_deadlines (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
due_at TIMESTAMPTZ NOT NULL,
|
||||
course_id BIGINT NULL REFERENCES jz_bm_courses (id) ON DELETE SET NULL,
|
||||
kind VARCHAR(20) NOT NULL CHECK (kind IN ('zaliczenie', 'egzamin', 'praca', 'organizacyjne', 'inne')),
|
||||
description TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX jz_bm_deadlines_due_at_index ON jz_bm_deadlines (due_at);
|
||||
CREATE INDEX jz_bm_deadlines_course_id_index ON jz_bm_deadlines (course_id)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_deadlines").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
34
updates/20261002120206_create_jz_bm_readings.go
Normal file
34
updates/20261002120206_create_jz_bm_readings.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateReadings returns the 20261002120206_create_jz_bm_readings gormigrate entry:
|
||||
// the lektury. Deleting a course deletes its readings.
|
||||
func CreateReadings() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120206_create_jz_bm_readings",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_readings (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
course_id BIGINT NOT NULL REFERENCES jz_bm_courses (id) ON DELETE CASCADE,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
author VARCHAR(255) NULL,
|
||||
source VARCHAR(500) NULL,
|
||||
url VARCHAR(2048) NULL,
|
||||
is_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (course_id, title)
|
||||
)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_readings").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
29
updates/20261002120207_create_jz_bm_reading_progress.go
Normal file
29
updates/20261002120207_create_jz_bm_reading_progress.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateReadingProgress returns the 20261002120207_create_jz_bm_reading_progress gormigrate entry:
|
||||
// who has read which reading. The rows go with the reading or the golem15.user user.
|
||||
func CreateReadingProgress() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120207_create_jz_bm_reading_progress",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_reading_progress (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
reading_id BIGINT NOT NULL REFERENCES jz_bm_readings (id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
read_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (reading_id, user_id)
|
||||
);
|
||||
CREATE INDEX jz_bm_reading_progress_user_id_index ON jz_bm_reading_progress (user_id)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_reading_progress").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
28
updates/20261002120208_create_jz_bm_tags.go
Normal file
28
updates/20261002120208_create_jz_bm_tags.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateTags returns the 20261002120208_create_jz_bm_tags gormigrate entry:
|
||||
// the material tags.
|
||||
func CreateTags() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120208_create_jz_bm_tags",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_tags (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_tags").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
35
updates/20261002120209_create_jz_bm_materials.go
Normal file
35
updates/20261002120209_create_jz_bm_materials.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateMaterials returns the 20261002120209_create_jz_bm_materials gormigrate entry:
|
||||
// the study materials; a NULL published_at is a draft. Deleting a course keeps its materials, unlinked.
|
||||
func CreateMaterials() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120209_create_jz_bm_materials",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_materials (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
course_id BIGINT NULL REFERENCES jz_bm_courses (id) ON DELETE SET NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('pdf', 'notatka', 'link', 'inne')),
|
||||
body TEXT NULL,
|
||||
source VARCHAR(500) NULL,
|
||||
url VARCHAR(2048) NULL,
|
||||
published_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX jz_bm_materials_published_at_index ON jz_bm_materials (published_at);
|
||||
CREATE INDEX jz_bm_materials_course_id_index ON jz_bm_materials (course_id)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_materials").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
27
updates/20261002120210_create_jz_bm_material_tag.go
Normal file
27
updates/20261002120210_create_jz_bm_material_tag.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateMaterialTag returns the 20261002120210_create_jz_bm_material_tag gormigrate entry:
|
||||
// the material-tag pivot.
|
||||
func CreateMaterialTag() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002120210_create_jz_bm_material_tag",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return execAll(tx, `CREATE TABLE jz_bm_material_tag (
|
||||
material_id BIGINT NOT NULL REFERENCES jz_bm_materials (id) ON DELETE CASCADE,
|
||||
tag_id BIGINT NOT NULL REFERENCES jz_bm_tags (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (material_id, tag_id)
|
||||
);
|
||||
CREATE INDEX jz_bm_material_tag_tag_id_index ON jz_bm_material_tag (tag_id)`)
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS jz_bm_material_tag").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
22
updates/20261002121227_add_elective_group_to_courses.go
Normal file
22
updates/20261002121227_add_elective_group_to_courses.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AddElectiveGroupToCourses returns the 20261002121227_add_elective_group_to_courses gormigrate entry:
|
||||
// courses that share an elective_group are alternatives ("do wyboru").
|
||||
func AddElectiveGroupToCourses() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20261002121227_add_elective_group_to_courses",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec("ALTER TABLE jz_bm_courses ADD COLUMN elective_group VARCHAR(100) NULL").Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("ALTER TABLE jz_bm_courses DROP COLUMN IF EXISTS elective_group").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
21
updates/exec.go
Normal file
21
updates/exec.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// execAll runs each semicolon-separated statement of sql in order. The
|
||||
// statements hold no semicolons of their own.
|
||||
func execAll(tx *gorm.DB, sql string) error {
|
||||
for _, stmt := range strings.Split(sql, ";") {
|
||||
if strings.TrimSpace(stmt) == "" {
|
||||
continue
|
||||
}
|
||||
if err := tx.Exec(stmt).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user