{"id":42536308,"url":"https://github.com/obcode/plexams.go","last_synced_at":"2026-06-28T09:01:25.737Z","repository":{"id":51799587,"uuid":"504878234","full_name":"obcode/plexams.go","owner":"obcode","description":"Rewrite of obcode/plexams in Go","archived":false,"fork":false,"pushed_at":"2026-06-25T06:47:15.000Z","size":13657,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-25T07:14:32.629Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obcode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-06-18T15:19:16.000Z","updated_at":"2026-06-25T06:46:39.000Z","dependencies_parsed_at":"2026-01-27T14:01:11.926Z","dependency_job_id":null,"html_url":"https://github.com/obcode/plexams.go","commit_stats":{"total_commits":276,"total_committers":2,"mean_commits":138.0,"dds":"0.0036231884057971175","last_synced_commit":"edef700ca9a92fe04cddf675f20867a19f1296c0"},"previous_names":[],"tags_count":222,"template":false,"template_full_name":null,"purl":"pkg:github/obcode/plexams.go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fplexams.go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fplexams.go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fplexams.go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fplexams.go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obcode","download_url":"https://codeload.github.com/obcode/plexams.go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fplexams.go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34882751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-28T17:04:14.353Z","updated_at":"2026-06-28T09:01:25.731Z","avatar_url":"https://github.com/obcode.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GoDoc](https://godoc.org/github.com/obcode/plexams.go?status.svg)](https://godoc.org/github.com/obcode/plexams.go)\n[![Go Report Card](https://goreportcard.com/badge/github.com/obcode/plexams.go)](https://goreportcard.com/report/github.com/obcode/plexams.go)\n\n# plexams.go\n\n`plexams.go` is a CLI tool and GraphQL server for planning university exams\n(Prüfungsplanung) at HM (Hochschule München, FK07). It imports exam and teacher\ndata from the **ZPA** system and student registration/conflict data from\n**Primuss**, connects them, and helps schedule exams into slots, assign rooms and\nplan invigilations (Aufsichten).\n\nIt is the backend for [`plexams.gui`](https://github.com/obcode/plexams.gui).\nMost domain terminology and log messages are in German.\n\n\u003e The work is gradually moving out of the CLI into the GraphQL API consumed by the\n\u003e GUI. New functionality (e.g. invigilation generation, email sending, room\n\u003e requests) is added as GraphQL queries/subscriptions; the CLI is kept in sync but\n\u003e is no longer the primary interface. It remains a single-user, local tool.\n\n## Architecture\n\nFour layers, each its own package, wired together in `cmd/`:\n\n- **`cmd/`** — Cobra CLI, one file per top-level command. `root.go` loads config\n  and constructs the central `*plexams.Plexams` instance.\n- **`plexams/`** — business logic. Almost all domain operations are methods on\n  `*Plexams`, grouped by concern across many files.\n- **`db/`** — MongoDB persistence (`mongo-driver`). **Each semester is its own\n  MongoDB database** (named e.g. `2026-SS`); rooms and NTAs live in a shared\n  `plexams` database.\n- **`zpa/`** — HTTP client for the external ZPA REST API.\n- **`graph/`** — GraphQL API generated by [gqlgen](https://gqlgen.com).\n\nSee [CLAUDE.md](CLAUDE.md) for more detail on the internals.\n\n## Prerequisites\n\n- Go (see `go.mod` for the required version)\n- A running MongoDB instance\n- Access to the ZPA and Primuss systems (credentials in the config)\n\n## Build, test, lint\n\n```bash\ngo build -o plexams.go .         # build the binary\ngo test ./...                    # run all tests\ngo vet ./...\ngolangci-lint-v2 run             # the linter used in CI/pre-commit\ngo generate ./...                # regenerate gqlgen code after editing *.graphqls\n```\n\nInstall the pre-commit hooks once (`gofmt`, `go vet`, `golangci-lint-v2`,\ngitleaks):\n\n```bash\npre-commit install\n```\n\n## Configuration\n\nConfig is loaded via [viper](https://github.com/spf13/viper). A base file\n`.plexams.yaml` (in `.` or `$HOME`) names the current `semester` (e.g. `2026-SS`)\nand a `semester-path`. A second per-semester file `\u003csemester\u003e.yaml` is read from\nthat path and **merged** on top.\n\nKey sections consumed by the code:\n\n| Section | Purpose |\n|---------|---------|\n| `semester` | the active semester, e.g. `2026-SS` (required) |\n| `db.uri`, `db.database` | MongoDB connection |\n| `zpa.*` | `baseurl`, `username`, `password`/`token`, `fk07programs`, `oldprograms` |\n| `smtp.*` | mail server + `testmail` (dry-run recipient) |\n| `planer.*` | name/email of the planner |\n| `semesterConfig.*` | `from`/`until`, `slots`, `forbiddenDays`, `goDay0`, `emails` |\n| `goslots` | GO/GN slot configuration |\n\nGlobal flags: `-v/--verbose`, `--db-uri`, `--semester`.\n\n`plexams.go init \u003csemester\u003e` interactively creates a semester config file.\n`init` and `version` skip config loading.\n\n\u003e Secrets (ZPA token/password, SMTP password) live in the config file, not in the\n\u003e DB. Keep the file out of version control.\n\n## Running\n\nRun without a subcommand to start the **GraphQL server** (playground at `/`,\nqueries at `/query`; default CORS allows `localhost:5173/8080/3000`):\n\n```bash\nplexams.go            # or: plexams.go server\n```\n\nOtherwise invoke a subcommand:\n\n```bash\nplexams.go \u003ccommand\u003e [subcommand] [args] [--run]\n```\n\n## Planning workflow\n\nA typical semester runs roughly in this order. Many of the later steps now also\nhave an equivalent in the GUI.\n\n1. **Init** — `plexams.go init 2026-SS`, fill in the per-semester config.\n2. **Import from ZPA** — `zpa exams`, `zpa teacher`, `zpa invigs`.\n3. **Select exams** to plan and add constraints (in the GUI).\n4. **Import from Primuss** and connect — `prepare connected-exams`\n   (`prepare connect-exam \u003cancode\u003e \u003cprogram\u003e` for leftovers), then\n   `prepare generated-exams` and `prepare studentregs`.\n5. **Schedule** — place exams into slots (`plan move-to`, `plan pre-plan-exam`,\n   …), check with `validate conflicts` / `validate constraints`.\n6. **Rooms** — `prepare rooms-for-exams` (it recomputes the allowed rooms per\n   slot first, so `prepare rooms-for-slots` is only needed to preview that\n   slot→rooms map on its own); request building-management rooms (see below).\n7. **Validate \u0026 upload** — `validate all`, then `zpa upload-plan`; publish the\n   plan and send the announcement emails.\n8. **Invigilations** — collect requirements, generate the invigilation plan,\n   validate, and send the published-invigilations emails.\n\n### Building-management room requests\n\nSome rooms must be requested externally: T-building rooms via **Anny**, the\nothers via the **Gebäudemanagement** (building management). The management\nrequests are managed in the DB (collection `room_requests`, per semester):\n\n1. Dry run: `info request-rooms` (CLI) or the `roomRequestsPreview` query (GUI)\n   shows which rooms would be requested for which exams — read-only, changes\n   nothing.\n2. Apply once (GUI, `applyRoomRequestsPreview`): writes the requests; it refuses\n   to overwrite existing ones unless forced, so an already-approved set is not\n   lost. Add extras with `addRoomRequest`, extend a request (e.g. for an NTA)\n   with `updateRoomRequestTime`.\n3. Approve / deactivate individual requests as the management responds.\n4. Send the request email: `email room-requests` (CLI) or the\n   `sendEmailRoomRequests` subscription (GUI). Requires\n   `semesterConfig.emails.roomManagement`.\n\nStored time ranges include a 15-minute buffer before and after the exam for\nsetup and teardown.\n\n## Command reference\n\nTop-level commands (run `plexams.go \u003ccommand\u003e --help` for subcommands):\n\n| Command | Purpose |\n|---------|---------|\n| `zpa` | fetch teacher/exams/invigs/students from ZPA, post studentregs, upload plan |\n| `primuss` | handle Primuss data (add/fix ancodes, add/remove student regs) |\n| `prepare` | connect exams, generate exams, prepare rooms/studentregs/invigilations |\n| `plan` | manipulate the plan (move, pre-plan rooms, lock/unlock) |\n| `rooms` | fetch room bookings (e.g. `rooms anny`) |\n| `invigilation` | add invigilators / reserves to slots and rooms |\n| `validate` | validate the plan (conflicts, constraints, rooms, ZPA, invigilations, …) |\n| `email` | send the various planning emails (`--run` to really send) |\n| `export`, `csv`, `pdf`, `ics` | generate exports in the respective formats |\n| `info` | print info about the semester, exams, rooms, stats, students |\n| `init` | interactively create a semester config |\n| `server` | start the GraphQL server (also the default with no command) |\n| `version` | print version info |\n\nEmail subcommands take a `--run` flag; without it they are a **dry run** that\nonly mails the `smtp.testmail` recipient.\n\n## GraphQL / code generation\n\nThe GraphQL layer is generated by gqlgen (`gqlgen.yml`). Edit the schema in\n`graph/*.graphqls`, then run `go generate ./...`. Do **not** hand-edit the\ngenerated files (`graph/generated/generated.go`, `graph/model/models_gen.go`,\nthe resolver signatures in `graph/*.resolvers.go`). Hand-written model types live\nalongside the generated ones in `graph/model/` and are autobound.\n\n## License\n\nBSD 3-Clause License — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobcode%2Fplexams.go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobcode%2Fplexams.go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobcode%2Fplexams.go/lists"}