Files
breakpilot-lehrer/school-service/internal/handlers/timetable_constraints_subject_handlers.go
T
Benjamin Admin e958f88a2d Add timetable scheduler Phases 1 + 2 to school-service
Phase 1 — Stammdaten (7 tables):
  tt_class, tt_period, tt_room, tt_subject, tt_teacher,
  tt_curriculum, tt_assignment with CRUD endpoints.

Phase 2 — Constraints (15 typed tables):
  Teacher (6): unavailable_day, unavailable_window, max_hours_day,
    max_hours_week, excluded_subject, excluded_room
  Subject (5): min_day_gap, max_consecutive, contiguous_when_repeated,
    preferred_period, double_lesson
  Class (2): max_hours_day, no_gaps
  Room (2): requires_type, unavailable

Each constraint row carries is_hard / weight / active / note /
created_by_user_id; ownership enforced via WHERE EXISTS against the
parent tt_teacher/tt_class/tt_subject/tt_room row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:12:23 +02:00

251 lines
7.7 KiB
Go

package handlers
import (
"net/http"
"github.com/breakpilot/school-service/internal/models"
"github.com/gin-gonic/gin"
)
// Subject-constraint HTTP handlers.
// ---------- Subject Min Day Gap ----------
func (h *Handler) CreateSubjectMinDayGap(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
var req models.CreateSubjectMinDayGapRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
out, err := h.timetableService.CreateSubjectMinDayGap(c.Request.Context(), uid, &req)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to create constraint: "+err.Error())
return
}
respondCreated(c, out)
}
func (h *Handler) ListSubjectMinDayGaps(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
out, err := h.timetableService.ListSubjectMinDayGaps(c.Request.Context(), uid)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
return
}
respondSuccess(c, out)
}
func (h *Handler) DeleteSubjectMinDayGap(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
if err := h.timetableService.DeleteSubjectMinDayGap(c.Request.Context(), c.Param("id"), uid); err != nil {
respondError(c, http.StatusInternalServerError, "Failed to delete constraint: "+err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"message": "Constraint deleted"})
}
// ---------- Subject Max Consecutive ----------
func (h *Handler) CreateSubjectMaxConsecutive(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
var req models.CreateSubjectMaxConsecutiveRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
out, err := h.timetableService.CreateSubjectMaxConsecutive(c.Request.Context(), uid, &req)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to create constraint: "+err.Error())
return
}
respondCreated(c, out)
}
func (h *Handler) ListSubjectMaxConsecutives(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
out, err := h.timetableService.ListSubjectMaxConsecutives(c.Request.Context(), uid)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
return
}
respondSuccess(c, out)
}
func (h *Handler) DeleteSubjectMaxConsecutive(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
if err := h.timetableService.DeleteSubjectMaxConsecutive(c.Request.Context(), c.Param("id"), uid); err != nil {
respondError(c, http.StatusInternalServerError, "Failed to delete constraint: "+err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"message": "Constraint deleted"})
}
// ---------- Subject Contiguous When Repeated ----------
func (h *Handler) CreateSubjectContiguousWhenRepeated(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
var req models.CreateSubjectContiguousWhenRepeatedRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
out, err := h.timetableService.CreateSubjectContiguousWhenRepeated(c.Request.Context(), uid, &req)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to create constraint: "+err.Error())
return
}
respondCreated(c, out)
}
func (h *Handler) ListSubjectContiguousWhenRepeated(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
out, err := h.timetableService.ListSubjectContiguousWhenRepeated(c.Request.Context(), uid)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
return
}
respondSuccess(c, out)
}
func (h *Handler) DeleteSubjectContiguousWhenRepeated(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
if err := h.timetableService.DeleteSubjectContiguousWhenRepeated(c.Request.Context(), c.Param("id"), uid); err != nil {
respondError(c, http.StatusInternalServerError, "Failed to delete constraint: "+err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"message": "Constraint deleted"})
}
// ---------- Subject Preferred Period ----------
func (h *Handler) CreateSubjectPreferredPeriod(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
var req models.CreateSubjectPreferredPeriodRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
out, err := h.timetableService.CreateSubjectPreferredPeriod(c.Request.Context(), uid, &req)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to create constraint: "+err.Error())
return
}
respondCreated(c, out)
}
func (h *Handler) ListSubjectPreferredPeriods(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
out, err := h.timetableService.ListSubjectPreferredPeriods(c.Request.Context(), uid)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
return
}
respondSuccess(c, out)
}
func (h *Handler) DeleteSubjectPreferredPeriod(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
if err := h.timetableService.DeleteSubjectPreferredPeriod(c.Request.Context(), c.Param("id"), uid); err != nil {
respondError(c, http.StatusInternalServerError, "Failed to delete constraint: "+err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"message": "Constraint deleted"})
}
// ---------- Subject Double Lesson ----------
func (h *Handler) CreateSubjectDoubleLesson(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
var req models.CreateSubjectDoubleLessonRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
out, err := h.timetableService.CreateSubjectDoubleLesson(c.Request.Context(), uid, &req)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to create constraint: "+err.Error())
return
}
respondCreated(c, out)
}
func (h *Handler) ListSubjectDoubleLessons(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
out, err := h.timetableService.ListSubjectDoubleLessons(c.Request.Context(), uid)
if err != nil {
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
return
}
respondSuccess(c, out)
}
func (h *Handler) DeleteSubjectDoubleLesson(c *gin.Context) {
uid := getUserID(c)
if uid == "" {
respondError(c, http.StatusUnauthorized, "User not authenticated")
return
}
if err := h.timetableService.DeleteSubjectDoubleLesson(c.Request.Context(), c.Param("id"), uid); err != nil {
respondError(c, http.StatusInternalServerError, "Failed to delete constraint: "+err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"message": "Constraint deleted"})
}