e958f88a2d
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>
301 lines
9.4 KiB
Go
301 lines
9.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/breakpilot/school-service/internal/models"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Teacher-constraint HTTP handlers. They share the same auth + JSON-bind
|
|
// shape as the existing timetable handlers; per-table the only thing that
|
|
// differs is the request DTO type and the service method invoked.
|
|
|
|
// ---------- Teacher Unavailable Day ----------
|
|
|
|
func (h *Handler) CreateTeacherUnavailableDay(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
var req models.CreateTeacherUnavailableDayRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
out, err := h.timetableService.CreateTeacherUnavailableDay(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) ListTeacherUnavailableDays(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
out, err := h.timetableService.ListTeacherUnavailableDays(c.Request.Context(), uid)
|
|
if err != nil {
|
|
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
|
|
return
|
|
}
|
|
respondSuccess(c, out)
|
|
}
|
|
|
|
func (h *Handler) DeleteTeacherUnavailableDay(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
if err := h.timetableService.DeleteTeacherUnavailableDay(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"})
|
|
}
|
|
|
|
// ---------- Teacher Unavailable Window ----------
|
|
|
|
func (h *Handler) CreateTeacherUnavailableWindow(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
var req models.CreateTeacherUnavailableWindowRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
out, err := h.timetableService.CreateTeacherUnavailableWindow(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) ListTeacherUnavailableWindows(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
out, err := h.timetableService.ListTeacherUnavailableWindows(c.Request.Context(), uid)
|
|
if err != nil {
|
|
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
|
|
return
|
|
}
|
|
respondSuccess(c, out)
|
|
}
|
|
|
|
func (h *Handler) DeleteTeacherUnavailableWindow(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
if err := h.timetableService.DeleteTeacherUnavailableWindow(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"})
|
|
}
|
|
|
|
// ---------- Teacher Max Hours / Day ----------
|
|
|
|
func (h *Handler) CreateTeacherMaxHoursDay(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
var req models.CreateTeacherMaxHoursDayRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
out, err := h.timetableService.CreateTeacherMaxHoursDay(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) ListTeacherMaxHoursDay(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
out, err := h.timetableService.ListTeacherMaxHoursDay(c.Request.Context(), uid)
|
|
if err != nil {
|
|
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
|
|
return
|
|
}
|
|
respondSuccess(c, out)
|
|
}
|
|
|
|
func (h *Handler) DeleteTeacherMaxHoursDay(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
if err := h.timetableService.DeleteTeacherMaxHoursDay(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"})
|
|
}
|
|
|
|
// ---------- Teacher Max Hours / Week ----------
|
|
|
|
func (h *Handler) CreateTeacherMaxHoursWeek(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
var req models.CreateTeacherMaxHoursWeekRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
out, err := h.timetableService.CreateTeacherMaxHoursWeek(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) ListTeacherMaxHoursWeek(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
out, err := h.timetableService.ListTeacherMaxHoursWeek(c.Request.Context(), uid)
|
|
if err != nil {
|
|
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
|
|
return
|
|
}
|
|
respondSuccess(c, out)
|
|
}
|
|
|
|
func (h *Handler) DeleteTeacherMaxHoursWeek(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
if err := h.timetableService.DeleteTeacherMaxHoursWeek(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"})
|
|
}
|
|
|
|
// ---------- Teacher Excluded Subject ----------
|
|
|
|
func (h *Handler) CreateTeacherExcludedSubject(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
var req models.CreateTeacherExcludedSubjectRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
out, err := h.timetableService.CreateTeacherExcludedSubject(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) ListTeacherExcludedSubjects(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
out, err := h.timetableService.ListTeacherExcludedSubjects(c.Request.Context(), uid)
|
|
if err != nil {
|
|
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
|
|
return
|
|
}
|
|
respondSuccess(c, out)
|
|
}
|
|
|
|
func (h *Handler) DeleteTeacherExcludedSubject(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
if err := h.timetableService.DeleteTeacherExcludedSubject(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"})
|
|
}
|
|
|
|
// ---------- Teacher Excluded Room ----------
|
|
|
|
func (h *Handler) CreateTeacherExcludedRoom(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
var req models.CreateTeacherExcludedRoomRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
out, err := h.timetableService.CreateTeacherExcludedRoom(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) ListTeacherExcludedRooms(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
out, err := h.timetableService.ListTeacherExcludedRooms(c.Request.Context(), uid)
|
|
if err != nil {
|
|
respondError(c, http.StatusInternalServerError, "Failed to list constraints: "+err.Error())
|
|
return
|
|
}
|
|
respondSuccess(c, out)
|
|
}
|
|
|
|
func (h *Handler) DeleteTeacherExcludedRoom(c *gin.Context) {
|
|
uid := getUserID(c)
|
|
if uid == "" {
|
|
respondError(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
if err := h.timetableService.DeleteTeacherExcludedRoom(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"})
|
|
}
|