A per-user jz_bm_material_pins table and PUT/DELETE /materials/{id}/pin
on the student API and its token twins: 204 for a material the caller
may see, idempotent, 404 otherwise. List rows, course rows and the
detail carry pinned. The plain list lists the caller's pins first
(newest pin first); with q the relevance order stays. A pin writes only
the pins table and never changes the material's revision.
- add jz_bm_materials.revision (default 1), read-only for GORM
- bump it only on content changes (models.ReviseMaterial)
- PUT /materials/{id} locks the row and answers 409 to a stale revision
- management answers carry revision and updated_at, the student detail
carries revision
The one-way migration 20261108140000_move_zjazd_tags_to_meetings gives
every material tagged zjazd-N without a meeting meeting N (the lowest
when it has several), never overwrites a meeting_id, logs tags naming a
missing meeting or disagreeing with meeting_id at Warn, and deletes the
exact zjazd-N tags; their links go through ON DELETE CASCADE. Rollback
is a no-op: the backup is the pg_dump taken before migrate.
- jz_bm_material_file_texts caches the pdftotext output per file
- the River job jz.bm.extract_material_text extracts and resyncs
- the management multipart attach queues it inside its transaction
- file_texts in the document and a file snippet with its name
A material may be private: only its author and the users it is shared
with see it. Every student read (list, detail, course detail and
counters, the checklist linked material, the token twins) goes through
one visibility fragment. Existing materials stay public with no author.
GET/PUT /_bm/api/v1/materials/{id}/access let the author, a material
manager, toggle the flag and pick the users; the first change of an
author-less material makes the caller its author.
Per-user checklists with items that may link a course, reading, deadline,
material or own presentation deck. An item with a reading shares the
reading's read mark. Student API routes with token twins, MCP tools,
migrations, tests and README.
The Pulpit hero lists what to read for the current or next zjazd, so
readings and materials get a nullable meeting_id (FK to jz_bm_meetings,
ON DELETE SET NULL, indexed). The reading admin form gets a "Na zjazd"
picker, the student reading JSON carries meeting_id, and the management
/readings records filter and validate it.
GET /_bm/api/v1/chat gives a chat-group member ogólny (the bm/1 group
conversation, adopted once as the system default channel under an
advisory lock), its unread and mention counts, the member's DMs newest
message first, the moderator flag and the composer limits. Every member
bootstrap syncs participants with the group: missing members join
ogólny, users no longer in the group lose their rows in every bm/1
conversation. A non-member gets only member:false and is pruned.
GET /_bm/api/v1/chat/members lists members (id, name, surname, handle,
is_bot from golem15.chatbots when active) and assigns missing handles;
non-members get 404.
The chatchannels access policy (ChatAccess) is installed from a
database hook: bm/1 for members only, nobody manages channels.
bm:chat-sync creates the groups when missing, syncs and assigns
handles. Migration 20261026130000_seed_jz_bm_chat_groups creates
studenci and moderatorzy without members; its rollback keeps them.
- new table jz_bm_reading_ownership and its migration
20261026120000_create_jz_bm_reading_ownership
- PUT and DELETE /_bm/api/v1/readings/{id}/owned, idempotent
- owned_at on every reading in /readings and /courses/{id}
- read semantics, progress routes and counts unchanged
The schedule showed a lecturer only where the session notes named one
(Globalizacja), so the student plan had nobody under the other courses.
The session JSON now carries the course lecturer in its course ref and an
optional per-session lecturer (new nullable column, admin field, seed key)
for courses shared between two people. The student app shows the
session's own lecturer or, failing that, the course's.
Datepickers on native time.Time / lagoon.Date fields, file uploads on
materials, readings and class sessions managed from the course form
(course_id made nullable).
Seven admin controllers in a "Studia BM" navigation group, guarded by
jz.bm.access_courses, jz.bm.access_schedule and jz.bm.access_materials,
with Polish and English labels. Date and time fields are checked by the
models before a save and answered as 422 on the field.