https://github.com/obcode/plexams.go
Rewrite of obcode/plexams in Go
https://github.com/obcode/plexams.go
Last synced: about 23 hours ago
JSON representation
Rewrite of obcode/plexams in Go
- Host: GitHub
- URL: https://github.com/obcode/plexams.go
- Owner: obcode
- License: bsd-3-clause
- Created: 2022-06-18T15:19:16.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2026-06-25T06:47:15.000Z (4 days ago)
- Last Synced: 2026-06-25T07:14:32.629Z (4 days ago)
- Language: Go
- Size: 13 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://godoc.org/github.com/obcode/plexams.go)
[](https://goreportcard.com/report/github.com/obcode/plexams.go)
# plexams.go
`plexams.go` is a CLI tool and GraphQL server for planning university exams
(Prüfungsplanung) at HM (Hochschule München, FK07). It imports exam and teacher
data from the **ZPA** system and student registration/conflict data from
**Primuss**, connects them, and helps schedule exams into slots, assign rooms and
plan invigilations (Aufsichten).
It is the backend for [`plexams.gui`](https://github.com/obcode/plexams.gui).
Most domain terminology and log messages are in German.
> The work is gradually moving out of the CLI into the GraphQL API consumed by the
> GUI. New functionality (e.g. invigilation generation, email sending, room
> requests) is added as GraphQL queries/subscriptions; the CLI is kept in sync but
> is no longer the primary interface. It remains a single-user, local tool.
## Architecture
Four layers, each its own package, wired together in `cmd/`:
- **`cmd/`** — Cobra CLI, one file per top-level command. `root.go` loads config
and constructs the central `*plexams.Plexams` instance.
- **`plexams/`** — business logic. Almost all domain operations are methods on
`*Plexams`, grouped by concern across many files.
- **`db/`** — MongoDB persistence (`mongo-driver`). **Each semester is its own
MongoDB database** (named e.g. `2026-SS`); rooms and NTAs live in a shared
`plexams` database.
- **`zpa/`** — HTTP client for the external ZPA REST API.
- **`graph/`** — GraphQL API generated by [gqlgen](https://gqlgen.com).
See [CLAUDE.md](CLAUDE.md) for more detail on the internals.
## Prerequisites
- Go (see `go.mod` for the required version)
- A running MongoDB instance
- Access to the ZPA and Primuss systems (credentials in the config)
## Build, test, lint
```bash
go build -o plexams.go . # build the binary
go test ./... # run all tests
go vet ./...
golangci-lint-v2 run # the linter used in CI/pre-commit
go generate ./... # regenerate gqlgen code after editing *.graphqls
```
Install the pre-commit hooks once (`gofmt`, `go vet`, `golangci-lint-v2`,
gitleaks):
```bash
pre-commit install
```
## Configuration
Config is loaded via [viper](https://github.com/spf13/viper). A base file
`.plexams.yaml` (in `.` or `$HOME`) names the current `semester` (e.g. `2026-SS`)
and a `semester-path`. A second per-semester file `.yaml` is read from
that path and **merged** on top.
Key sections consumed by the code:
| Section | Purpose |
|---------|---------|
| `semester` | the active semester, e.g. `2026-SS` (required) |
| `db.uri`, `db.database` | MongoDB connection |
| `zpa.*` | `baseurl`, `username`, `password`/`token`, `fk07programs`, `oldprograms` |
| `smtp.*` | mail server + `testmail` (dry-run recipient) |
| `planer.*` | name/email of the planner |
| `semesterConfig.*` | `from`/`until`, `slots`, `forbiddenDays`, `goDay0`, `emails` |
| `goslots` | GO/GN slot configuration |
Global flags: `-v/--verbose`, `--db-uri`, `--semester`.
`plexams.go init ` interactively creates a semester config file.
`init` and `version` skip config loading.
> Secrets (ZPA token/password, SMTP password) live in the config file, not in the
> DB. Keep the file out of version control.
## Running
Run without a subcommand to start the **GraphQL server** (playground at `/`,
queries at `/query`; default CORS allows `localhost:5173/8080/3000`):
```bash
plexams.go # or: plexams.go server
```
Otherwise invoke a subcommand:
```bash
plexams.go [subcommand] [args] [--run]
```
## Planning workflow
A typical semester runs roughly in this order. Many of the later steps now also
have an equivalent in the GUI.
1. **Init** — `plexams.go init 2026-SS`, fill in the per-semester config.
2. **Import from ZPA** — `zpa exams`, `zpa teacher`, `zpa invigs`.
3. **Select exams** to plan and add constraints (in the GUI).
4. **Import from Primuss** and connect — `prepare connected-exams`
(`prepare connect-exam ` for leftovers), then
`prepare generated-exams` and `prepare studentregs`.
5. **Schedule** — place exams into slots (`plan move-to`, `plan pre-plan-exam`,
…), check with `validate conflicts` / `validate constraints`.
6. **Rooms** — `prepare rooms-for-exams` (it recomputes the allowed rooms per
slot first, so `prepare rooms-for-slots` is only needed to preview that
slot→rooms map on its own); request building-management rooms (see below).
7. **Validate & upload** — `validate all`, then `zpa upload-plan`; publish the
plan and send the announcement emails.
8. **Invigilations** — collect requirements, generate the invigilation plan,
validate, and send the published-invigilations emails.
### Building-management room requests
Some rooms must be requested externally: T-building rooms via **Anny**, the
others via the **Gebäudemanagement** (building management). The management
requests are managed in the DB (collection `room_requests`, per semester):
1. Dry run: `info request-rooms` (CLI) or the `roomRequestsPreview` query (GUI)
shows which rooms would be requested for which exams — read-only, changes
nothing.
2. Apply once (GUI, `applyRoomRequestsPreview`): writes the requests; it refuses
to overwrite existing ones unless forced, so an already-approved set is not
lost. Add extras with `addRoomRequest`, extend a request (e.g. for an NTA)
with `updateRoomRequestTime`.
3. Approve / deactivate individual requests as the management responds.
4. Send the request email: `email room-requests` (CLI) or the
`sendEmailRoomRequests` subscription (GUI). Requires
`semesterConfig.emails.roomManagement`.
Stored time ranges include a 15-minute buffer before and after the exam for
setup and teardown.
## Command reference
Top-level commands (run `plexams.go --help` for subcommands):
| Command | Purpose |
|---------|---------|
| `zpa` | fetch teacher/exams/invigs/students from ZPA, post studentregs, upload plan |
| `primuss` | handle Primuss data (add/fix ancodes, add/remove student regs) |
| `prepare` | connect exams, generate exams, prepare rooms/studentregs/invigilations |
| `plan` | manipulate the plan (move, pre-plan rooms, lock/unlock) |
| `rooms` | fetch room bookings (e.g. `rooms anny`) |
| `invigilation` | add invigilators / reserves to slots and rooms |
| `validate` | validate the plan (conflicts, constraints, rooms, ZPA, invigilations, …) |
| `email` | send the various planning emails (`--run` to really send) |
| `export`, `csv`, `pdf`, `ics` | generate exports in the respective formats |
| `info` | print info about the semester, exams, rooms, stats, students |
| `init` | interactively create a semester config |
| `server` | start the GraphQL server (also the default with no command) |
| `version` | print version info |
Email subcommands take a `--run` flag; without it they are a **dry run** that
only mails the `smtp.testmail` recipient.
## GraphQL / code generation
The GraphQL layer is generated by gqlgen (`gqlgen.yml`). Edit the schema in
`graph/*.graphqls`, then run `go generate ./...`. Do **not** hand-edit the
generated files (`graph/generated/generated.go`, `graph/model/models_gen.go`,
the resolver signatures in `graph/*.resolvers.go`). Hand-written model types live
alongside the generated ones in `graph/model/` and are autobound.
## License
BSD 3-Clause License — see [LICENSE](LICENSE).