diff --git a/.editorconfig b/.editorconfig index e6aeb3c..e74daee 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,15 +3,9 @@ root = true [*] end_of_line = lf insert_final_newline = true - -[*.css] -indent_style = space -indent_size = 2 - -[*.go] indent_style = tab indent_size = 4 -[*.tmpl,*.html] +[*.{css,tmpl,templ,html,yaml,yml}] indent_style = space indent_size = 2 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8d6b4c2..7a8c024 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,8 +21,12 @@ jobs: run: | go install golang.org/x/tools/cmd/goimports@latest go install honnef.co/go/tools/cmd/staticcheck@latest + go install github.com/a-h/templ/cmd/templ@latest export PATH="$HOME/go/bin:$PATH" + - name: Run templ + run: templ generate + - name: Run pre-commit uses: pre-commit/action@v3.0.1 @@ -38,6 +42,12 @@ jobs: with: go-version-file: "go.mod" + - name: templ generate + run: | + go install github.com/a-h/templ/cmd/templ@latest + export PATH="$HOME/go/bin:$PATH" + templ generate + - run: go build ./cmd/mineshspc - uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 96735bf..6ff8783 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*_templ.go config.yaml credentials.json token.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5c21db..4a63912 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,10 +11,11 @@ repos: - repo: https://github.com/tekwizely/pre-commit-golang rev: v1.0.0-rc.1 hooks: - - id: go-imports-repo + - id: go-imports args: - "-local" - "github.com/ColoradoSchoolOfMines/mineshspc.com" - "-w" - - id: go-vet-repo-mod - - id: go-staticcheck-repo-mod + exclude: _templ\.go$ + - id: go-vet-mod + - id: go-staticcheck-mod diff --git a/flake.lock b/flake.lock index 4a799ca..29bf318 100644 --- a/flake.lock +++ b/flake.lock @@ -18,13 +18,89 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "templ", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gomod2nix": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "templ", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1710154385, + "narHash": "sha256-4c3zQ2YY4BZOufaBJB4v9VBBeN2dH7iVdoJw8SDNCfI=", + "owner": "nix-community", + "repo": "gomod2nix", + "rev": "872b63ddd28f318489c929d25f1f0a3c6039c971", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "gomod2nix", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1716293225, - "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", + "lastModified": 1716330097, + "narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", + "rev": "5710852ba686cc1fd0d3b8e22b3117d43ba374c2", "type": "github" }, "original": { @@ -37,7 +113,8 @@ "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "templ": "templ" } }, "systems": { @@ -54,6 +131,67 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "templ": { + "inputs": { + "gitignore": "gitignore", + "gomod2nix": "gomod2nix", + "nixpkgs": [ + "nixpkgs" + ], + "xc": "xc" + }, + "locked": { + "lastModified": 1716034419, + "narHash": "sha256-z/sb4AlFOU20sBEAu12VSXqhHQuqvj3mUu7JTvyc1pI=", + "owner": "a-h", + "repo": "templ", + "rev": "0c14a899236d115a790b5a960b5d2b50c277c77e", + "type": "github" + }, + "original": { + "owner": "a-h", + "ref": "v0.2.697", + "repo": "templ", + "type": "github" + } + }, + "xc": { + "inputs": { + "flake-utils": "flake-utils_3", + "nixpkgs": [ + "templ", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709043057, + "narHash": "sha256-F/u9u1vevCUqgCz0WjcCOPcN+h9kLEP73nBqie86J2w=", + "owner": "joerdav", + "repo": "xc", + "rev": "52cfa7e7f2d5a0d97b45a6f69ff1403a901e78c6", + "type": "github" + }, + "original": { + "owner": "joerdav", + "repo": "xc", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index d8b4e80..33c49f6 100644 --- a/flake.nix +++ b/flake.nix @@ -3,23 +3,36 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + templ = { + url = "github:a-h/templ/v0.2.697"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, flake-utils }: + outputs = { self, nixpkgs, flake-utils, templ }: (flake-utils.lib.eachDefaultSystem (system: - let pkgs = import nixpkgs { inherit system; }; + let + pkgs = import nixpkgs { + inherit system; + overlays = + [ (final: prev: { inherit (templ.packages.${system}) templ; }) ]; + }; in rec { packages.mineshspc = pkgs.buildGo122Module { pname = "mineshspc.com"; version = "unstable-2024-05-21"; src = self; subPackages = [ "cmd/mineshspc" ]; - vendorHash = "sha256-QjNH3O5/Hpu0OuVLUMN//f4d2j4wsDiUEfGLXH/Yf04="; + vendorHash = "sha256-iVuCWgtuYJ8ai7pSHO1bU9EaC9gEoWvtkEQUb6sUu+o="; + + preBuild = '' + ${pkgs.templ}/bin/templ generate + ''; }; packages.default = packages.mineshspc; devShells.default = pkgs.mkShell { - packages = with pkgs; [ go gopls gotools pre-commit ]; + packages = with pkgs; [ go gopls gotools pre-commit pkgs.templ ]; }; })); } diff --git a/go.mod b/go.mod index aa3e0b3..81b89b2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.22 toolchain go1.22.0 require ( + github.com/a-h/templ v0.2.697 + github.com/beeper/libserv v0.0.0-20231231202820-c7303abfc32c github.com/golang-jwt/jwt/v4 v4.5.0 github.com/mattn/go-sqlite3 v1.14.22 github.com/rs/zerolog v1.32.0 @@ -25,7 +27,6 @@ require ( github.com/rs/xid v1.5.0 // indirect github.com/sendgrid/rest v2.6.9+incompatible // indirect golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect - golang.org/x/net v0.24.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index fe0389d..fea993e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/a-h/templ v0.2.697 h1:OILxtWvD0NRJaoCOiZCopRDPW8paroKlGsrAiHLykNE= +github.com/a-h/templ v0.2.697/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8= +github.com/beeper/libserv v0.0.0-20231231202820-c7303abfc32c h1:WqjRVgUO039eiISCjsZC4F9onOEV93DJAk6v33rsZzY= +github.com/beeper/libserv v0.0.0-20231231202820-c7303abfc32c/go.mod h1:b9FFm9y4mEm36G8ytVmS1vkNzJa0KepmcdVY+qf7qRU= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -8,6 +12,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= diff --git a/internal/application.go b/internal/application.go index 798c49e..406ddb2 100644 --- a/internal/application.go +++ b/internal/application.go @@ -1,18 +1,25 @@ package internal import ( + "context" "fmt" "html/template" "net/http" "regexp" "strings" + "github.com/a-h/templ" + "github.com/beeper/libserv/pkg/requestlog" "github.com/rs/zerolog" "github.com/rs/zerolog/hlog" "github.com/sendgrid/sendgrid-go" "github.com/ColoradoSchoolOfMines/mineshspc.com/database" "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/config" + "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/contextkeys" + "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/templates" + "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/templates/partials" + registerteacher "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/templates/register/teacher" "github.com/ColoradoSchoolOfMines/mineshspc.com/website" ) @@ -25,7 +32,6 @@ type Application struct { ConfirmEmailRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) VolunteerConfirmEmailRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) AdminConfirmEmailRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) - TeacherLoginRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) EmailLoginRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) StudentConfirmInfoRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) TeamAddMemberRenderer func(w http.ResponseWriter, r *http.Request, extraData map[string]any) @@ -91,6 +97,25 @@ type renderInfo struct { RedirectIfLoggedIn bool } +func (a *Application) AuthenticatedTeacherMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, err := a.GetLoggedInTeacher(r) + if err == nil { + next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextkeys.ContextKeyLoggedInTeacher, user))) + } + next.ServeHTTP(w, r) + }) +} + +func (a *Application) SettingsContextMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + ctx = context.WithValue(ctx, contextkeys.ContextKeyRegistrationEnabled, a.Config.RegistrationEnabled) + ctx = context.WithValue(ctx, contextkeys.ContextKeyHostedByHTML, a.Config.HostedByHTML) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + func (a *Application) Start() { a.Log.Info().Msg("connecting to sendgrid") a.SendGridClient = sendgrid.NewSendClient(a.Config.SendgridAPIKey) @@ -98,46 +123,55 @@ func (a *Application) Start() { a.Log.Info().Msg("Starting router") router := http.NewServeMux() + router.Handle("GET /static/", http.FileServer(http.FS(website.StaticFS))) // Serve static files noArgs := func(r *http.Request) map[string]any { return nil } // Static pages staticPages := map[string]struct { - Template string - ArgGenerator func(r *http.Request) map[string]any + title string + pageName partials.PageName + content templ.Component }{ - "/{$}": {"home.html", noArgs}, - "/info": {"info.html", noArgs}, - "/authors": {"authors.html", noArgs}, - "/rules": {"rules.html", noArgs}, - "/register": {"register.html", noArgs}, - "/faq": {"faq.html", noArgs}, - "/archive": {"archive.html", a.GetArchiveTemplate}, + "GET /{$}": {"Home", partials.PageNameHome, templates.Home()}, + "GET /info/": {"Info", partials.PageNameInfo, templates.Info()}, + "GET /authors/": {"Authors", "", templates.Authors()}, + "GET /rules/": {"Rules", partials.PageNameRules, templates.Rules()}, + "GET /register/": {"Register", partials.PageNameRegister, templates.Register()}, + "GET /faq/": {"FAQ", partials.PageNameFAQ, templates.FAQ()}, + "GET /archive/": {"Archive", partials.PageNameArchive, templates.Archive(archiveInfo)}, } - for path, templateInfo := range staticPages { - router.HandleFunc("GET "+path, a.ServeTemplate(a.Log, templateInfo.Template, templateInfo.ArgGenerator)) + for path, pageInfo := range staticPages { + if len(pageInfo.pageName) == 0 { + router.Handle(path, templ.Handler(templates.Base(pageInfo.title, pageInfo.content))) + } else { + router.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), contextkeys.ContextKeyPageName, pageInfo.pageName) + templates.Base(pageInfo.title, pageInfo.content).Render(ctx, w) + }) + } } - // Serve static files - router.Handle("GET /static/", http.FileServer(http.FS(website.StaticFS))) - // Redirect pages - redirects := map[string]string{ - "/register/teacher": "/register/teacher/createaccount", - "/register/student": "/", - "/register/parent": "/", - } - for path, redirectPath := range redirects { - redirFn := func(redirectPath string) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, redirectPath, http.StatusTemporaryRedirect) + router.Handle("GET /register/teacher/", http.RedirectHandler("/register/teacher/createaccount", http.StatusTemporaryRedirect)) + router.Handle("GET /register/student/", http.RedirectHandler("/", http.StatusTemporaryRedirect)) + router.Handle("GET /register/parent/", http.RedirectHandler("/", http.StatusTemporaryRedirect)) + + router.HandleFunc("GET /register/teacher/login", func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + if teacher, ok := ctx.Value(contextkeys.ContextKeyLoggedInTeacher).(*database.Teacher); ok { + if teacher.SchoolCity == "" || teacher.SchoolName == "" || teacher.SchoolState == "" { + http.Redirect(w, r, "/register/teacher/schoolinfo", http.StatusSeeOther) + } else { + http.Redirect(w, r, "/register/teacher/teams", http.StatusSeeOther) } } - router.HandleFunc("GET "+path, redirFn(redirectPath)) - } + + templates.Base("Teacher Login", registerteacher.Login("", nil)).Render(ctx, w) + }) + router.HandleFunc("POST /register/teacher/login", a.HandleTeacherLogin) // Registration renderers - a.TeacherLoginRenderer = a.ServeTemplateExtra(a.Log, "teacherlogin.html", a.GetEmailLoginTemplate) a.TeacherCreateAccountRenderer = a.ServeTemplateExtra(a.Log, "teachercreateaccount.html", a.GetTeacherCreateAccountTemplate) a.ConfirmEmailRenderer = a.ServeTemplateExtra(a.Log, "confirmemail.html", a.GetEmailLoginTemplate) a.VolunteerConfirmEmailRenderer = a.ServeTemplateExtra(a.Log, "volunteerconfirmemail.html", a.GetEmailLoginTemplate) @@ -149,7 +183,6 @@ func (a *Application) Start() { registrationPages := map[string]renderInfo{ "/register/teacher/confirmemail": {a.ConfirmEmailRenderer, true}, "/register/teacher/createaccount": {a.TeacherCreateAccountRenderer, true}, - "/register/teacher/login": {a.TeacherLoginRenderer, true}, "/register/teacher/schoolinfo": {a.ServeTemplateExtra(a.Log, "schoolinfo.html", a.GetTeacherSchoolInfoTemplate), false}, "/register/teacher/teams": {a.ServeTemplateExtra(a.Log, "teams.html", a.GetTeacherTeamsTemplate), false}, "/register/teacher/team/edit": {a.ServeTemplateExtra(a.Log, "teamedit.html", a.GetTeacherTeamEditTemplate), false}, @@ -200,7 +233,6 @@ func (a *Application) Start() { // Form Post Handlers formHandlers := map[string]func(w http.ResponseWriter, r *http.Request){ - "/register/teacher/login": a.HandleTeacherLogin, "/register/teacher/createaccount": a.HandleTeacherCreateAccount, "/register/teacher/schoolinfo": a.HandleTeacherSchoolInfo, "/register/teacher/team/edit": a.HandleTeacherTeamEdit, @@ -243,7 +275,11 @@ func (a *Application) Start() { router.HandleFunc("GET /volunteer/scan", a.ServeTemplate(a.Log, "volunteerscan.html", a.GetVolunteerScanTemplate)) router.HandleFunc("GET /volunteer/checkin", a.HandleVolunteerCheckIn) + // Middleware goes from bottom up because it's doing function composition. var handler http.Handler = router + handler = a.AuthenticatedTeacherMiddleware(handler) + handler = a.SettingsContextMiddleware(handler) + handler = requestlog.AccessLogger(false)(handler) handler = hlog.RequestIDHandler("request_id", "RequestID")(handler) handler = hlog.NewHandler(*a.Log)(handler) diff --git a/internal/archive.go b/internal/archive.go index 7c8a73b..74ac427 100644 --- a/internal/archive.go +++ b/internal/archive.go @@ -1,227 +1,198 @@ package internal import ( - "net/http" + "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/templates" ) -type Link struct { - URL string - Title string -} - -type WinningTeam struct { - Place string - Name string - School string - Location string -} - -type CompetitionResult struct { - Name string - Shortname string - Teams []WinningTeam -} - -type YearInfo struct { - Year int - RecapParagraphs []string - Links []Link - Results []CompetitionResult -} - -func (a *Application) GetArchiveTemplate(*http.Request) map[string]any { - return map[string]any{ - "YearInfo": []YearInfo{ +var archiveInfo = []templates.YearInfo{ + { + Year: 2024, + RecapParagraphs: []string{ + "The 2024 competition returned to an in-person only competition, but we also had an open division. We gave a separate set of prizes for teams consisting of only first-time competitors. We did not award prizes for the open division.", + "The in-person competition had 27 teams while the open division had 31 teams.", + }, + Links: []templates.Link{ + {URL: "/static/2024-solutions.pdf", Title: "Solution Sketch Slides"}, + {URL: "https://sumnerevans.com/posts/school/2024-hspc/", Title: "Competition Recap and Solution Sketches"}, + {URL: "https://mines-hspc.kattis.com/contests/mines-hspc24/problems", Title: "Problems"}, + }, + Results: []templates.CompetitionResult{ { - Year: 2024, - RecapParagraphs: []string{ - "The 2024 competition returned to an in-person only competition, but we also had an open division. We gave a separate set of prizes for teams consisting of only first-time competitors. We did not award prizes for the open division.", - "The in-person competition had 27 teams while the open division had 31 teams.", - }, - Links: []Link{ - {"/static/2024-solutions.pdf", "Solution Sketch Slides"}, - {"https://sumnerevans.com/posts/school/2024-hspc/", "Competition Recap and Solution Sketches"}, - {"https://mines-hspc.kattis.com/contests/mines-hspc24/problems", "Problems"}, - }, - Results: []CompetitionResult{ - { - Name: "Overall Winners", - Shortname: "Overall", - Teams: []WinningTeam{ - {"1st", "Innovation Center 1", "Innovation Center SVVSD", "Longmont"}, - {"2nd", "Sigma Scripters", "Arapahoe High School", "Centennial"}, - {"3nd", "CyberRebels2", "Columbine High School", "Littleton"}, - }, - }, - { - Name: "First-Time Team Winners", - Shortname: "FirstTime", - Teams: []WinningTeam{ - {"1st", "Loopy Groupies", "Chatfield Senior High School", "Littleton"}, - {"2nd", "Lorem Ipsum", "Warren Tech", "Lakewood"}, - {"3nd", "the cows(mooooooooooooo)", "Cherry Creek High School", "Greenwood Village"}, - }, - }, + Name: "Overall Winners", + Shortname: "Overall", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Innovation Center 1", School: "Innovation Center SVVSD", Location: "Longmont"}, + {Place: "2nd", Name: "Sigma Scripters", School: "Arapahoe High School", Location: "Centennial"}, + {Place: "3nd", Name: "CyberRebels2", School: "Columbine High School", Location: "Littleton"}, }, }, { - Year: 2023, - RecapParagraphs: []string{ - "The 2023 competition again featured two divisions: beginner and advanced. As with 2022, it was a hybrid competition, but we awarded prizes for both in-person and remote winners in both divisions.", - "The advanced division featured 31 teams, while the beginner division had 34 teams.", - }, - Links: []Link{ - {"/static/2023-solutions.pdf", "Solution Sketch Slides"}, - {"https://sumnerevans.com/posts/school/2023-hspc/", "Competition Recap and Solution Sketches"}, - {"https://mines23advanced.kattis.com/problems", "Advanced Problems"}, - {"https://mines23beginner.kattis.com/problems", "Beginner Problems"}, - }, - Results: []CompetitionResult{ - { - Name: "Advanced In-Person", - Shortname: "AdvancedInPerson", - Teams: []WinningTeam{ - {"1st", "Code Rats", "Futures Lab", "Fort Collins, Colorado"}, - {"2nd", "The Spanish Inquisition", "Regis Jesuit High School", "Aurora, Colorado"}, - {"3nd", "CA is 202", "Colorado Academy", "Denver, Colorado"}, - }, - }, - { - Name: "Beginner In-Person", - Shortname: "BeginnerInPerson", - Teams: []WinningTeam{ - {"1st", "Spaghetti Code and Meatballs", "Warren Tech", "Lakewood, Colorado"}, - {"2nd", "Innovation Center 1", "Innovation Center SVVSD", "Longmont, Colorado"}, - {"3nd", "Team LuLo", "Colorado Academy", "Denver, Colorado"}, - }, - }, - { - Name: "Advanced Remote", - Shortname: "AdvancedRemote", - Teams: []WinningTeam{ - {"1st", "River Hill Team #1", "River Hill High School", "Clarksville, Maryland"}, - {"2nd", "CreekCyberBruins", "Cherry Creek High School", "Greenwood Village, Colorado"}, - {"3nd", "JMS", "Bergen County Academies", "Bergen County, New Jersey"}, - }, - }, - { - Name: "Beginner Remote", - Shortname: "BeginnerRemote", - Teams: []WinningTeam{ - {"1st", "Wormhole", "Voice of Calling NPO", "Northridge, California"}, - {"2nd", "Lineup", "Voice of Calling NPO", "Northridge, California"}, - {"3nd", "River Hill Team #2", "River Hill High School", "Clarksville, Maryland"}, - }, - }, + Name: "First-Time Team Winners", + Shortname: "FirstTime", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Loopy Groupies", School: "Chatfield Senior High School", Location: "Littleton"}, + {Place: "2nd", Name: "Lorem Ipsum", School: "Warren Tech", Location: "Lakewood"}, + {Place: "3nd", Name: "the cows(mooooooooooooo)", School: "Cherry Creek High School", Location: "Greenwood Village"}, }, }, + }, + }, + { + Year: 2023, + RecapParagraphs: []string{ + "The 2023 competition again featured two divisions: beginner and advanced. As with 2022, it was a hybrid competition, but we awarded prizes for both in-person and remote winners in both divisions.", + "The advanced division featured 31 teams, while the beginner division had 34 teams.", + }, + Links: []templates.Link{ + {URL: "/static/2023-solutions.pdf", Title: "Solution Sketch Slides"}, + {URL: "https://sumnerevans.com/posts/school/2023-hspc/", Title: "Competition Recap and Solution Sketches"}, + {URL: "https://mines23advanced.kattis.com/problems", Title: "Advanced Problems"}, + {URL: "https://mines23beginner.kattis.com/problems", Title: "Beginner Problems"}, + }, + Results: []templates.CompetitionResult{ { - Year: 2022, - RecapParagraphs: []string{ - "The 2022 competition was the first to feature two divisions: a beginner division and an advanced division. It was also the first hybrid competition with both remote and in-person contestants.", - "The advanced division had 26 teams, while the beginner division had 39 teams. Due to the number of teams, we decided to give awards to first place through fourth place.", - }, - Links: []Link{ - {"https://sumnerevans.com/posts/school/2022-hspc/", "Competition Recap and Solution Sketches"}, - {"https://mines22advanced.kattis.com/problems", "Advanced Problems"}, - {"https://mines22beginner.kattis.com/problems", "Beginner Problems"}, - }, - Results: []CompetitionResult{ - { - Name: "Advanced", - Teams: []WinningTeam{ - {"1st", "Pen A Team", "PEN Academy", "Cresskill, New Jersey"}, - {"2nd", "Cherry Creek Cobras", "Cherry Creek High School", "Greenwood Village, Colorado"}, - {"3nd", "River Hill Team 1", "River Hill High School", "Clarksville, Maryland"}, - {"4th", "The Spanish Inquisition", "Regis Jesuit High School", "Aurora, Colorado"}, - }, - }, - { - Name: "Beginner", - Teams: []WinningTeam{ - {"1st", "LLL", "Future Forward at Bollman", "Thornton, Colorado"}, - {"2nd", "Error 404: Name not found", "Colorado Academy", "Denver, Colorado"}, - {"3nd", "Liberty 1", "Liberty Common School", "Fort Collins, Colorado"}, - {"4th", "Cool Cats", "Arvada West High School", "Arvada, Colorado"}, - }, - }, + Name: "Advanced In-Person", + Shortname: "AdvancedInPerson", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Code Rats", School: "Futures Lab", Location: "Fort Collins, Colorado"}, + {Place: "2nd", Name: "The Spanish Inquisition", School: "Regis Jesuit High School", Location: "Aurora, Colorado"}, + {Place: "3nd", Name: "CA is 202", School: "Colorado Academy", Location: "Denver, Colorado"}, }, }, { - Year: 2021, - RecapParagraphs: []string{ - "The 2021 competition was an all-remote competition featuring 55 teams from across the nation.", - }, - Links: []Link{ - {"https://sumnerevans.com/posts/school/2021-hspc/", "Competition Recap and Solution Sketches"}, - {"https://mines21.kattis.com/problems", "Problems"}, - }, - Results: []CompetitionResult{ - { - Teams: []WinningTeam{ - {"1st", "River Hill HS Team 1", "River Hill High School", "Clarksville, Maryland"}, - {"2nd", "PEN A Team", "PEN Academy", "Cresskill, New Jersey"}, - {"3nd", "River Hill HS Team 2", "River Hill High School", "Clarksville, Maryland"}, - }, - }, + Name: "Beginner In-Person", + Shortname: "BeginnerInPerson", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Spaghetti Code and Meatballs", School: "Warren Tech", Location: "Lakewood, Colorado"}, + {Place: "2nd", Name: "Innovation Center 1", School: "Innovation Center SVVSD", Location: "Longmont, Colorado"}, + {Place: "3nd", Name: "Team LuLo", School: "Colorado Academy", Location: "Denver, Colorado"}, }, }, { - Year: 2020, - RecapParagraphs: []string{ - "Due to COVID, the 2020 competition was the first all-remote HSPC competition. The competition featured 30 teams.", + Name: "Advanced Remote", + Shortname: "AdvancedRemote", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "River Hill Team #1", School: "River Hill High School", Location: "Clarksville, Maryland"}, + {Place: "2nd", Name: "CreekCyberBruins", School: "Cherry Creek High School", Location: "Greenwood Village, Colorado"}, + {Place: "3nd", Name: "JMS", School: "Bergen County Academies", Location: "Bergen County, New Jersey"}, }, - Links: []Link{ - {"https://sumnerevans.com/posts/school/2020-hspc/", "Competition Recap and Solution Sketches"}, - {"https://mines20.kattis.com/problems", "Problems"}, - }, - Results: []CompetitionResult{ - { - Teams: []WinningTeam{ - {"1st", "Installation Wizards", "STEM School Highlands Ranch", "Highlands Ranch, Colorado"}, - {"2nd", "i", "STEM School Highlands Ranch", "Highlands Ranch, Colorado"}, - {"3nd", "Sun Devils", "Kent Denver", "Denver, Colorado"}, - }, - }, + }, + { + Name: "Beginner Remote", + Shortname: "BeginnerRemote", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Wormhole", School: "Voice of Calling NPO", Location: "Northridge, California"}, + {Place: "2nd", Name: "Lineup", School: "Voice of Calling NPO", Location: "Northridge, California"}, + {Place: "3nd", Name: "River Hill Team #2", School: "River Hill High School", Location: "Clarksville, Maryland"}, }, }, + }, + }, + { + Year: 2022, + RecapParagraphs: []string{ + "The 2022 competition was the first to feature two divisions: a beginner division and an advanced division. It was also the first hybrid competition with both remote and in-person contestants.", + "The advanced division had 26 teams, while the beginner division had 39 teams. Due to the number of teams, we decided to give awards to first place through fourth place.", + }, + Links: []templates.Link{ + {URL: "https://sumnerevans.com/posts/school/2022-hspc/", Title: "Competition Recap and Solution Sketches"}, + {URL: "https://mines22advanced.kattis.com/problems", Title: "Advanced Problems"}, + {URL: "https://mines22beginner.kattis.com/problems", Title: "Beginner Problems"}, + }, + Results: []templates.CompetitionResult{ { - Year: 2019, - RecapParagraphs: []string{ - "The second ever CS@Mines High School Programming Competition featured 22 teams from all around Colorado and from as far as Steamboat Springs.", + Name: "Advanced", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Pen A Team", School: "PEN Academy", Location: "Cresskill, New Jersey"}, + {Place: "2nd", Name: "Cherry Creek Cobras", School: "Cherry Creek High School", Location: "Greenwood Village, Colorado"}, + {Place: "3nd", Name: "River Hill Team 1", School: "River Hill High School", Location: "Clarksville, Maryland"}, + {Place: "4th", Name: "The Spanish Inquisition", School: "Regis Jesuit High School", Location: "Aurora, Colorado"}, }, - Links: []Link{ - {"https://sumnerevans.com/posts/school/2019-hspc/", "Competition Recap and Solution Sketches"}, - {"https://mines19.kattis.com/problems", "Problems"}, + }, + { + Name: "Beginner", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "LLL", School: "Future Forward at Bollman", Location: "Thornton, Colorado"}, + {Place: "2nd", Name: "Error 404: Name not found", School: "Colorado Academy", Location: "Denver, Colorado"}, + {Place: "3nd", Name: "Liberty 1", School: "Liberty Common School", Location: "Fort Collins, Colorado"}, + {Place: "4th", Name: "Cool Cats", School: "Arvada West High School", Location: "Arvada, Colorado"}, }, - Results: []CompetitionResult{ - { - Teams: []WinningTeam{ - {"1st", "STEM Team 1", "STEM School Highlands Ranch", "Highlands Ranch, Colorado"}, - {"2nd", "IntrospectionExceptions", "Colorado Academy", "Lakewood, Colorado"}, - {"3nd", "Team 2", "?", "?"}, - }, - }, + }, + }, + }, + { + Year: 2021, + RecapParagraphs: []string{ + "The 2021 competition was an all-remote competition featuring 55 teams from across the nation.", + }, + Links: []templates.Link{ + {URL: "https://sumnerevans.com/posts/school/2021-hspc/", Title: "Competition Recap and Solution Sketches"}, + {URL: "https://mines21.kattis.com/problems", Title: "Problems"}, + }, + Results: []templates.CompetitionResult{ + { + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "River Hill HS Team 1", School: "River Hill High School", Location: "Clarksville, Maryland"}, + {Place: "2nd", Name: "PEN A Team", School: "PEN Academy", Location: "Cresskill, New Jersey"}, + {Place: "3nd", Name: "River Hill HS Team 2", School: "River Hill High School", Location: "Clarksville, Maryland"}, }, }, + }, + }, + { + Year: 2020, + RecapParagraphs: []string{ + "Due to COVID, the 2020 competition was the first all-remote HSPC competition. The competition featured 30 teams.", + }, + Links: []templates.Link{ + {URL: "https://sumnerevans.com/posts/school/2020-hspc/", Title: "Competition Recap and Solution Sketches"}, + {URL: "https://mines20.kattis.com/problems", Title: "Problems"}, + }, + Results: []templates.CompetitionResult{ { - Year: 2018, - RecapParagraphs: []string{ - "The first ever CS@Mines High School Programming Competition featured 22 teams.", + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "Installation Wizards", School: "STEM School Highlands Ranch", Location: "Highlands Ranch, Colorado"}, + {Place: "2nd", Name: "i", School: "STEM School Highlands Ranch", Location: "Highlands Ranch, Colorado"}, + {Place: "3nd", Name: "Sun Devils", School: "Kent Denver", Location: "Denver, Colorado"}, }, - Links: []Link{ - {"https://mines18.kattis.com/problems", "Problems"}, + }, + }, + }, + { + Year: 2019, + RecapParagraphs: []string{ + "The second ever CS@Mines High School Programming Competition featured 22 teams from all around Colorado and from as far as Steamboat Springs.", + }, + Links: []templates.Link{ + {URL: "https://sumnerevans.com/posts/school/2019-hspc/", Title: "Competition Recap and Solution Sketches"}, + {URL: "https://mines19.kattis.com/problems", Title: "Problems"}, + }, + Results: []templates.CompetitionResult{ + { + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "STEM Team 1", School: "STEM School Highlands Ranch", Location: "Highlands Ranch, Colorado"}, + {Place: "2nd", Name: "IntrospectionExceptions", School: "Colorado Academy", Location: "Lakewood, Colorado"}, + {Place: "3nd", Name: "Team 2", School: "?", Location: "?"}, }, - Results: []CompetitionResult{ - { - Teams: []WinningTeam{ - {"1st", "The Crummies", "Warren Tech", "Arvada, Colorado"}, - {"2nd", "The Bean Beans", "Colorado Academy", "Lakewood, Colorado"}, - {"3nd", "Warriors", "Arapahoe High School", "Centennial, Colorado"}, - }, - }, + }, + }, + }, + { + Year: 2018, + RecapParagraphs: []string{ + "The first ever CS@Mines High School Programming Competition featured 22 teams.", + }, + Links: []templates.Link{ + {URL: "https://mines18.kattis.com/problems", Title: "Problems"}, + }, + Results: []templates.CompetitionResult{ + { + Teams: []templates.WinningTeam{ + {Place: "1st", Name: "The Crummies", School: "Warren Tech", Location: "Arvada, Colorado"}, + {Place: "2nd", Name: "The Bean Beans", School: "Colorado Academy", Location: "Lakewood, Colorado"}, + {Place: "3nd", Name: "Warriors", School: "Arapahoe High School", Location: "Centennial, Colorado"}, }, }, }, - } + }, } diff --git a/internal/contextkeys/keys.go b/internal/contextkeys/keys.go new file mode 100644 index 0000000..2d31ace --- /dev/null +++ b/internal/contextkeys/keys.go @@ -0,0 +1,10 @@ +package contextkeys + +type contextKey int + +const ( + ContextKeyLoggedInTeacher contextKey = iota + ContextKeyPageName + ContextKeyRegistrationEnabled + ContextKeyHostedByHTML +) diff --git a/internal/teacherlogin.go b/internal/teacherlogin.go index 28bded3..488e4ad 100644 --- a/internal/teacherlogin.go +++ b/internal/teacherlogin.go @@ -10,6 +10,9 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/sendgrid/sendgrid-go/helpers/mail" + + "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/templates" + registerteacher "github.com/ColoradoSchoolOfMines/mineshspc.com/internal/templates/register/teacher" ) type Issuer string @@ -35,7 +38,9 @@ func (a *Application) GetEmailLoginTemplate(r *http.Request) map[string]any { } func (a *Application) CreateEmailLoginJWT(email string) *jwt.Token { - // TODO invent some way to make this a one-time token + // TODO invent some way to make this a one-time token (maybe just add extra + // random token in here and then store all of the tokens we have seen in + // RAM) return jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.RegisteredClaims{ Issuer: string(IssuerEmailLogin), Subject: email, @@ -45,6 +50,7 @@ func (a *Application) CreateEmailLoginJWT(email string) *jwt.Token { func (a *Application) HandleTeacherLogin(w http.ResponseWriter, r *http.Request) { log := a.Log.With().Str("page_name", "teacher_create_account").Logger() + ctx := log.WithContext(r.Context()) if err := r.ParseForm(); err != nil { log.Err(err).Msg("failed to parse form") w.WriteHeader(http.StatusBadRequest) @@ -59,20 +65,14 @@ func (a *Application) HandleTeacherLogin(w http.ResponseWriter, r *http.Request) } log = log.With().Str("email", emailAddress).Logger() - teacher, err := a.DB.GetTeacherByEmail(r.Context(), emailAddress) + teacher, err := a.DB.GetTeacherByEmail(ctx, emailAddress) if err != nil { log.Warn().Err(err).Msg("failed to find teacher by email") - a.TeacherLoginRenderer(w, r, map[string]any{ - "Email": emailAddress, - "EmailNotFound": true, - }) + templates.Base("Teacher Login", registerteacher.Login(emailAddress, registerteacher.LoginEmailDoesNotExist())).Render(ctx, w) return } else if !teacher.EmailConfirmed { log.Warn().Err(err).Msg("teacher email not confirmed, not sending login code to avoid amplification attacks") - a.TeacherLoginRenderer(w, r, map[string]any{ - "Email": emailAddress, - "EmailNotConfirmed": true, - }) + templates.Base("Teacher Login", registerteacher.Login(emailAddress, registerteacher.LoginEmailNotConfirmed())).Render(ctx, w) return } diff --git a/internal/templates/archive.templ b/internal/templates/archive.templ new file mode 100644 index 0000000..fb1316c --- /dev/null +++ b/internal/templates/archive.templ @@ -0,0 +1,145 @@ +package templates + +import "strconv" + +type Link struct { + URL templ.SafeURL + Title string +} + +type WinningTeam struct { + Place string + Name string + School string + Location string +} + +type CompetitionResult struct { + Name string + Shortname string + Teams []WinningTeam +} + +type YearInfo struct { + Year int + RecapParagraphs []string + Links []Link + Results []CompetitionResult +} + +func accordionHeadingID(year int, shortname string) string { + return "heading" + strconv.Itoa(year) + shortname +} + +func accordionCollapseCSSID(year int, shortname string) string { + return "#" + accordionCollapseID(year, shortname) +} + +func accordionCollapseID(year int, shortname string) string { + return "winners" + strconv.Itoa(year) + shortname +} + +func trophyColor(place int) string { + switch place + 1 { + case 1: + return "#FFD700" + case 2: + return "#C0C0C0" + default: + return "#CD7F32" + } +} + +css trophyColorStyle(place int) { + color: { trophyColor(place) }; +} + +templ Archive(years []YearInfo) { +
+ Since 2018, the CS@Mines High School Programming + Competition has provided high school students an opportunity to + demonstrate their programming and problem solving skills in a + competitive environment. For more information on all of our competitions + and past problems, view + + competition summaries + . +
+{ p }
+ } ++ { team.School } • { team.Location } +
++ Note: This page is hidden on the website, but accessible at + mineshspc.com/authors. +
++ The CS@Mines High School Programming Competition is a competition for high + school students to write programs that solve problems. We model our programming + competition off well-known college-level competitions such as the + ACM ICPC, but bring an inviting set of + problems to the table suitable for high school students. +
++ Mines students are encouraged join our problem writing process. In the past, we + have had many authors from ACM, DECtech, and the CS@Mines department, as well + as help from alumni. All of our problems are reviewed by CS@Mines professors. +
++ See the sections below to get involved! + Problems are due December 31st, 2024. +
++ An outline of the problem process can be seen below. Once you join our GitHub + organization, a full contribution guide is available. The bulk of the + problem-making processes lies in making a well-defined problem and writing a + solution and test cases for it. +
++ +
++ Anyone can write and review problems! In the past, we have typically always + needed extra reviewers. Along with writing your own problem, you can help us by + writing solutions to other problems to ensure the best quality problems. +
++ We use GitHub to store + and collaborate on all of the problem code for HSPC. Due to the nature of the + competition, this repository is private. +
++ Please join our Discord server and + email erichards@mines.edu to get + involved in the problem writing process and to get access to the codebase. + Problem writers are also encouraged to volunteer the day of the competition. +
++ The best way to practice is by solving the problems from previous + competitions. You can find this all on our archive page. + We will use Kattis to run the competition, so all the problems will be given in a similar + input/output format to those seen in the + Open Kattis Archive. +
++ Teams must have a minimum of two students and a maximum of four students. +
++ We allow for two teams per school to attend our in-person competition + (we may allow more based on registration time and available space). + Any number of teams can join in the virtual open division. +
++ You can use C++, Python, Java or JavaScript. +
++ We recommend that everyone on your team know at least one of these languages in common. We + additionally recommend that teams practice submitting solutions on Kattis because the + input/output is done in a very specific manner that students may not be familiar with. +
++ Ensure that your team knows how to take in Kattis input before the competition day; + see the Kattis input tutorial { `for` } more details. + We will also send out a link to a practice competition to practice submitting code. +
++ Starting in the 2024 competition, we will only have one division for everyone. There will be 12-15 + problems, + and about a third will be "beginner", a third "novice", and a third "advanced" difficulty. +
++ "Beginner" problem concepts include: arithmetic, expressions, conditionals, and lists. +
++ "Novice" problems concepts include: loops, nested loops, lists, and maybe some data structures. +
++ "Advanced" problems concepts include: data structures (maps, sets) and algorithms (sorting/BFS/DFS) +
++ View + + last year's beginner + problems + and + + last year's advanced + problems + { `for` } a good idea of the range of difficulty for all of our problems. +
++ The competition computers run on Ubuntu and have the following programs + installed: +
++ IDEs: IntelliJ IDEA, + PyCharm, + Eclipse, + CLion. +
++ Editors: Visual Studio Code, + IDLE, + emacs, + vim +
++ The actual competition will be open for 4 hours, from 10:00 AM until 2:00 PM Mountain Time. + There are opening and closing ceremonies lasting an hour at the start and end of the competition. +
++ Yes. However, we find that using less computers benefits team problem solving ability as + there is more opportunity for collaboration. + See the rules { `for` } details. +
++ Engage in exciting problems at the Mines HSPC! + if RegistrationEnabled(ctx) { + Registration is now open for the Spring 2024 competition. + } +
+ if RegistrationEnabled(ctx) { ++ + The registration deadline is April 13th +
+ + Register Now for In-Person Competition + + } else { ++ + Registration for this year's competition has closed. + +
+ } + ++ The CS@Mines High School Programming Competition is a competition for high school + students to write programs that solve problems. +
++ Our programming competition is modelled after well-known college-level competitions + such as ICPC, but we make an inviting set of problems + suitable for high school students. +
++ Starting in the 2024 competition, we will have three tracks, in the + form of in-person beginner and advanced competitions, and an open to + anyone, all difficulty virtual division. +
+Saturday, April 20th, 2024
++ Mines is a public research university focused on science and engineering, located in Golden, + Colorado. + HSPC is organized and run by Mines ACM volunteers with funding from CS@Mines. +
++ In the 2024 competition, we were pleased to host: +
+ The competition will be at the Center for Technology and Learning Media (CTLM): + 1650 Arapahoe St, Golden, CO 80401 +
++ We recommend parking at the CTLM Parking Lot on 18th St, and will have volunteers around the + building waiting. Parking is free since it is a weekend. Please arrive before 9:00am (ideally + 8:30am) so that you can check in. The competition ends at 3:00pm. +
++ Once at CTLM, enter to the left of the “Gordian Knot,” and we will direct you to the opening + ceremony room, CTLM102. We will have volunteers and signs in case you get lost. +
++ See the Mines campus map here. +
++ Students will receive a QR code in their email which will act as their ticket for the competition. + A volunteer will scan the student's QR code to make sure all of the necessary forms have been + signed. +
++ We'll send out details via email a week before the competition describing how to test in the + practice competition. +
++ Food will be provided around 11:30am on competition day. +
++ Please email support@mineshspc.com with any questions. +
++ Before the competition.. +
++ During the competition.. +
++ After the competition.. +
++ Please note that the registration deadline is for filling out teams. + You can sign forms past the registration deadline. +
++ Teachers must register teams for the competition. You may register as many + teams as you would like, however, we only guarantee that two teams per-school will be + able to participate in-person. +
+All registrations are subject to approval by the HSPC organizers.
+ if RegistrationEnabled(ctx) { + + Register A Team + + } ++ Students need to be signed up by their teachers. You should recieve an email + once your teacher has signed you up. +
++ We need to verify your email address is correct so that we can create your account on + Kattis. +
++ Students need to be signed up by their teachers. After your student has + been signed up, you may need to sign additional forms in order for your student to be + allowed to participate. +
++ If your student is not a minor, they may sign the forms for themselves. +
++ That email hasn't been confirmed yet. Please confirm your email before logging in. +
++ Lost your confirmation email? Send an email to + support@mineshspc.com. +
+} diff --git a/internal/templates/rules.templ b/internal/templates/rules.templ new file mode 100644 index 0000000..d758ee8 --- /dev/null +++ b/internal/templates/rules.templ @@ -0,0 +1,73 @@ +package templates + +templ Rules () { ++ Teams must have a minimum of two students and a maximum of four students. + Teams must have a faculty sponsor. If your school does not have a faculty sponsor, + please email us. +
++ Teams can be made up of students from different schools as long as all students + and faculty sponsors agree. +
++ This competition is open-internet. You may use + the internet for looking up language documentation or researching algorithms. You may + not use interactive online services such as chat rooms or forums to communicate with + anyone during the competition. You may not communicate with anyone outside of your team. +
++ The problems are written specifically for the competition, so looking up the problems will + not be useful. Additionally, the teams that do the best in the competition generally do not + use the internet extensively during the competition because they already are familiar with + their language's standard library and know a variety of algorithms that may be used to solve + the problems. +
++ Third party code generation is not allowed. + Any team that uses code generation AI/LLMs will be immediately disqualified and their school + will be ineligible from participating in HSPC in future years. All solutions will + be reviewed for AI use. We reserve the right to ask teams to explain the solution + in detail to judges and revoke credit for solutions if the judges feel the team + does not provide an adequate explanation. +
++ In-person teams will be provided a single computer { `for` } the competition. Teams may not + use more than one Mines-provided computer. Teams + + are allowed to bring and use additional + computers + { `if` } they wish. +
++ Using the computer provided is often an effective strategy as it allows team members to + collaborate on solutions rather than having to work on their own. +
++ In-person teams can win prizes by placing in the top three. We also + may award prizes for fun & creative solutions or quick completion of problems. +
+Rules and prizes are subject to change.
+- Since 2018, the CS@Mines High School Programming - Competition has provided high school students an opportunity to - demonstrate their programming and problem solving skills in a - competitive environment. For more information on all of our competitions - and past problems, view competition summaries. -
-{{ . }}
{{- end -}} -- {{ .School }} • {{ .Location }} -
-- Note: This page is hidden on the website, but accessible at - mineshspc.com/authors. -
-- The CS@Mines High School Programming Competition is a competition - for high school students to write programs that solve problems. - We model our programming competition off well-known college-level - competitions such as the - ACM ICPC, but bring an - inviting set of problems to the table suitable for high school - students. -
-- Mines students are encouraged join our problem writing process. - In the past, we have had many authors from ACM, DECtech, and the - CS@Mines department, as well as help from alumni. All of our - problems are reviewed by CS@Mines professors. -
-- See the sections below to get involved! - Problems are due December 31st, 2022. -
-- An outline of the problem process can be seen below. Once you - join our GitHub organization, a full contribution guide is available. - The bulk of the problem-making processes lies in making a well-defined - problem and writing a solution and test cases for it. -
-- -
-- Anyone can write and review problems! In the past, we have typically - always needed extra reviewers. Along with writing your own problem, you - can help us by writing solutions to other problems to ensure the best quality problems. -
-- We use GitHub to store and collaborate on all of the - problem code for HSPC. Due to the nature of the competition, this repository is private. -
-- Please join our Discord server and email erichards@mines.edu to get involved - in the problem writing process and to get access to the codebase. Problem writers are also encouraged to volunteer the day of the competition. -
-- The best way to practice is by solving the problems from previous - competitions. You can find this all on our archive page. - - We will use Kattis to run the competition, so all the problems will be given in a similar - input/output format to those seen in the Open Kattis Archive. -
-- Teams must have a minimum of two students and a maximum of four students. -
-- We allow for two teams per school to attend our in-person competition - (we may allow more based on registration time and available space). - - Any number of teams can join in the virtual open division. -
-- You can use C++, Python, Java or JavaScript. -
-- We recommend that everyone on your team know at least one of these languages in common. We - additionally recommend that teams practice submitting solutions on Kattis because the - input/output is done in a very specific manner that students may not be familiar with. -
-- Ensure that your team knows how to take in Kattis input before the competition day; - see the Kattis input tutorial for more details. - We will also send out a link to a practice competition to practice submitting code. -
-- Starting in the 2024 competition, we will only have one division for everyone. There will be 12-15 problems, - and about a third will be "beginner", a third "novice", and a third "advanced" difficulty. -
-- "Beginner" problem concepts include: arithmetic, expressions, conditionals, and lists. -
-- "Novice" problems concepts include: loops, nested loops, lists, and maybe some data structures. -
-- "Advanced" problems concepts include: data structures (maps, sets) and algorithms (sorting/BFS/DFS) -
-- View last year's beginner problems and - last year's advanced problems for a good idea of the range of difficulty for all of our problems. -
-- The competition computers run on Ubuntu and have the following programs installed: -
-- IDEs: IntelliJ IDEA, PyCharm, Eclipse, CLion. -
-- Editors: Visual Studio Code, - IDLE, - emacs, - vim -
-- The actual competition will be open for 4 hours, from 10:00 AM until 2:00 PM Mountain Time. - There are opening and closing ceremonies lasting an hour at the start and end of the competition. -
-- Yes. However, we find that using less computers benefits team problem solving ability as - there is more opportunity for collaboration. - - See the rules for details. -
-- Engage in exciting problems at the Mines HSPC! - {{ if .RegistrationEnabled }} - Registration is now open for the Spring 2024 competition. - {{ end }} -
- {{ if .RegistrationEnabled }} -- - The registration deadline is April 13th -
- - Register Now for In-Person Competition - - {{ else }} -- - Registration for this year's competition has closed. - -
- {{ end }} - -- The CS@Mines High School Programming Competition is a competition - for high school students to write programs that solve problems. -
-- Our programming competition is modelled after well-known college-level - competitions such as ICPC, but we - make an inviting set of problems suitable for high school students. -
-- Starting in the 2024 competition, we will have three tracks, - in the form of in-person beginner and advanced competitions, - and an open to anyone, all difficulty virtual division. -
-Saturday, April 20th, 2024
-
- Mines is a public research university focused on science and engineering, located in Golden, Colorado.
- HSPC is organized and run by Mines ACM volunteers with funding from CS@Mines.
-
- In the 2024 competition, we were pleased to host:
-
- The competition will be at the Center for Technology and Learning Media (CTLM): - 1650 Arapahoe St, Golden, CO 80401 -
-- We recommend parking at the CTLM Parking Lot on 18th St, and will have volunteers around the building waiting. Parking is free since it is a weekend. Please arrive before 9:00am (ideally 8:30am) so that you can check in. The competition ends at 3:00pm. -
-- Once at CTLM, enter to the left of the “Gordian Knot,” and we will direct you to the opening ceremony room, CTLM102. We will have volunteers and signs in case you get lost. -
-- See the Mines campus map here. -
-- Students will receive a QR code in their email which will act as their ticket for the competition. - A volunteer will scan the student's QR code to make sure all of the necessary forms have been signed. -
-- We'll send out details via email a week before the competition describing how to test in the practice competition. -
-- Food will be provided around 11:30am on competition day. -
-- Please email support@mineshspc.com with any questions. -
-- Before the competition.. -
-- During the competition.. -
-- After the competition.. -
-Please note that the registration deadline is for filling out teams. - You can sign forms past the registration deadline.
-- Teachers must register teams for the competition. You may register as many - teams as you would like, however, we only guarantee that two teams per-school will be - able to participate in-person. -
-All registrations are subject to approval by the HSPC organizers.
- {{ if .RegistrationEnabled }} - - Register A Team - - {{ end }} -- Students need to be signed up by their teachers. You should recieve an email - once your teacher has signed you up. -
-- We need to verify your email address is correct so that we can create your account on - Kattis. -
-- Students need to be signed up by their teachers. After your student has - been signed up, you may need to sign additional forms in order for your student to be - allowed to participate. -
-- If your student is not a minor, they may sign the forms for themselves. -
-- Teams must have a minimum of two students and a maximum of four students. - Teams must have a faculty sponsor. If your school does not have a faculty sponsor, - please email us. -
-- Teams can be made up of students from different schools as long as all students - and faculty sponsors agree. -
-- This competition is open-internet. You may use - the internet for looking up language documentation or researching algorithms. You may - not use interactive online services such as chat rooms or forums to communicate with - anyone during the competition. You may not communicate with anyone outside of your team. -
-- The problems are written specifically for the competition, so looking up the problems will - not be useful. Additionally, the teams that do the best in the competition generally do not - use the internet extensively during the competition because they already are familiar with - their language's standard library and know a variety of algorithms that may be used to solve - the problems. -
-- Third party code generation is not allowed. - Any team that uses code generation AI/LLMs will be immediately disqualified and their school - will be ineligible from participating in HSPC in future years. All solutions will - be reviewed for AI use. We reserve the right to ask teams to explain the solution - in detail to judges and revoke credit for solutions if the judges feel the team - does not provide an adequate explanation. -
-- In-person teams will be provided a single computer for the competition. Teams may not - use more than one Mines-provided computer. Teams are allowed to bring and use additional - computers if they wish. -
-- Using the computer provided is often an effective strategy as it allows team members to - collaborate on solutions rather than having to work on their own. -
-- In-person teams can win prizes by placing in the top three. We also - may award prizes for fun & creative solutions or quick completion of problems. -
-Rules and prizes are subject to change.
-