727 lines
18 KiB
Go
727 lines
18 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/google/uuid"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// --- Helpers ---
|
||
|
||
func parseUUID(c *gin.Context, param string) (uuid.UUID, bool) {
|
||
id, err := uuid.Parse(c.Param(param))
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"detail": "invalid uuid"})
|
||
return uuid.Nil, false
|
||
}
|
||
return id, true
|
||
}
|
||
|
||
func templateAttributeDetails(db *gorm.DB, templateID uuid.UUID) []gin.H {
|
||
var allAttrs []Attribute
|
||
db.Find(&allAttrs)
|
||
|
||
var tas []TemplateAttribute
|
||
db.Where("template_id = ?", templateID).Preload("Attribute").Find(&tas)
|
||
|
||
taMap := map[uuid.UUID]*TemplateAttribute{}
|
||
for i := range tas {
|
||
taMap[tas[i].AttributeID] = &tas[i]
|
||
}
|
||
|
||
result := []gin.H{}
|
||
for _, a := range allAttrs {
|
||
detail := gin.H{
|
||
"attribute_id": a.ID,
|
||
"attribute_name": a.Name,
|
||
"is_required": a.IsRequired,
|
||
"value": nil,
|
||
}
|
||
if ta, ok := taMap[a.ID]; ok {
|
||
detail["value"] = ta.Value
|
||
}
|
||
result = append(result, detail)
|
||
}
|
||
return result
|
||
}
|
||
|
||
func templateWithAttrs(db *gorm.DB, t Template) gin.H {
|
||
return gin.H{
|
||
"id": t.ID,
|
||
"name": t.Name,
|
||
"description": t.Description,
|
||
"elements": json.RawMessage(t.Elements),
|
||
"group_id": t.GroupID,
|
||
"attributes": templateAttributeDetails(db, t.ID),
|
||
"order": t.Order,
|
||
"created_at": t.CreatedAt,
|
||
"updated_at": t.UpdatedAt,
|
||
"deleted_at": t.DeletedAt,
|
||
}
|
||
}
|
||
|
||
// --- Templates ---
|
||
|
||
func getTemplates(c *gin.Context, db *gorm.DB) {
|
||
var templates []Template
|
||
db.Find(&templates)
|
||
|
||
result := []gin.H{}
|
||
for _, t := range templates {
|
||
result = append(result, templateWithAttrs(db, t))
|
||
}
|
||
c.JSON(200, gin.H{"templates": result})
|
||
}
|
||
|
||
func getTemplate(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var t Template
|
||
if db.First(&t, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"template": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"template": templateWithAttrs(db, t)})
|
||
}
|
||
|
||
func createTemplate(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
Name string `json:"name"`
|
||
Description string `json:"description"`
|
||
Elements json.RawMessage `json:"elements"`
|
||
Attributes []setTemplateAttrInput `json:"attributes"`
|
||
GroupID *uuid.UUID `json:"group_id"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
|
||
t := Template{Name: input.Name, Description: &input.Description, Elements: JSON(input.Elements), GroupID: input.GroupID}
|
||
db.Create(&t)
|
||
|
||
for _, a := range input.Attributes {
|
||
db.Create(&TemplateAttribute{TemplateID: t.ID, AttributeID: a.AttributeID, Value: a.Value})
|
||
}
|
||
c.JSON(200, gin.H{"id": t.ID})
|
||
}
|
||
|
||
type setTemplateAttrInput struct {
|
||
AttributeID uuid.UUID `json:"attribute_id"`
|
||
Value *string `json:"value"`
|
||
}
|
||
|
||
func patchTemplate(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var t Template
|
||
if db.First(&t, "id = ?", id).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Template not found"})
|
||
return
|
||
}
|
||
|
||
var input struct {
|
||
Name *string `json:"name"`
|
||
Description *string `json:"description"`
|
||
Elements *json.RawMessage `json:"elements"`
|
||
Attributes []setTemplateAttrInput `json:"attributes"`
|
||
GroupID interface{} `json:"group_id"`
|
||
Order *float64 `json:"order"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
|
||
updates := map[string]interface{}{}
|
||
if input.Name != nil {
|
||
updates["name"] = *input.Name
|
||
}
|
||
if input.Description != nil {
|
||
updates["description"] = *input.Description
|
||
}
|
||
if input.Elements != nil {
|
||
updates["elements"] = JSON(*input.Elements)
|
||
}
|
||
if input.Order != nil {
|
||
updates["order"] = *input.Order
|
||
}
|
||
if input.GroupID != nil {
|
||
switch v := input.GroupID.(type) {
|
||
case string:
|
||
if v == "ungroup" {
|
||
updates["group_id"] = nil
|
||
} else {
|
||
if uid, err := uuid.Parse(v); err == nil {
|
||
updates["group_id"] = uid
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(updates) > 0 {
|
||
db.Model(&t).Updates(updates)
|
||
}
|
||
|
||
for _, a := range input.Attributes {
|
||
res := db.Model(&TemplateAttribute{}).Where("template_id = ? AND attribute_id = ?", id, a.AttributeID).Update("value", a.Value)
|
||
if res.RowsAffected == 0 {
|
||
db.Create(&TemplateAttribute{TemplateID: id, AttributeID: a.AttributeID, Value: a.Value})
|
||
}
|
||
}
|
||
|
||
c.JSON(200, gin.H{"id": t.ID})
|
||
}
|
||
|
||
func deleteTemplate(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
res := db.Delete(&Template{}, "id = ?", id)
|
||
if res.RowsAffected == 0 {
|
||
c.JSON(404, gin.H{"detail": "Template not found"})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"success": true, "template_id": id})
|
||
}
|
||
|
||
func copyTemplate(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
TemplateID uuid.UUID `json:"template_id"`
|
||
NewName string `json:"new_name"`
|
||
NewDescription *string `json:"new_description"`
|
||
NewAttributes []setTemplateAttrInput `json:"new_attributes"`
|
||
GroupID *uuid.UUID `json:"group_id"`
|
||
CopyFirstSheetEmpty bool `json:"copy_first_sheet_empty"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
|
||
var orig Template
|
||
if db.First(&orig, "id = ?", input.TemplateID).Error != nil {
|
||
c.JSON(400, gin.H{"detail": "Template not found"})
|
||
return
|
||
}
|
||
|
||
// Update element IDs
|
||
newElements := updateElementIDs(orig.Elements)
|
||
|
||
newT := Template{Name: input.NewName, Description: input.NewDescription, Elements: newElements, GroupID: input.GroupID}
|
||
db.Create(&newT)
|
||
|
||
// Copy files
|
||
var files []File
|
||
db.Where("template_id = ?", input.TemplateID).Find(&files)
|
||
for _, f := range files {
|
||
newF := File{Name: f.Name, Content: f.Content, Cells: f.Cells, TemplateID: &newT.ID}
|
||
db.Create(&newF)
|
||
}
|
||
|
||
// Copy sheets
|
||
var sheets []Sheet
|
||
db.Where("template_id = ?", input.TemplateID).Find(&sheets)
|
||
for _, s := range sheets {
|
||
cells := s.Cells
|
||
if s.Name == "L" && input.CopyFirstSheetEmpty {
|
||
cells = JSON("{}")
|
||
}
|
||
newS := Sheet{Name: s.Name, Cells: cells, TemplateID: newT.ID}
|
||
db.Create(&newS)
|
||
}
|
||
|
||
// Copy or set attributes
|
||
if len(input.NewAttributes) > 0 {
|
||
for _, a := range input.NewAttributes {
|
||
db.Create(&TemplateAttribute{TemplateID: newT.ID, AttributeID: a.AttributeID, Value: a.Value})
|
||
}
|
||
} else {
|
||
var origAttrs []TemplateAttribute
|
||
db.Where("template_id = ?", input.TemplateID).Find(&origAttrs)
|
||
for _, a := range origAttrs {
|
||
db.Create(&TemplateAttribute{TemplateID: newT.ID, AttributeID: a.AttributeID, Value: a.Value})
|
||
}
|
||
}
|
||
|
||
c.JSON(200, gin.H{"new_template_id": newT.ID})
|
||
}
|
||
|
||
func updateElementIDs(elements JSON) JSON {
|
||
var parsed map[string]interface{}
|
||
if json.Unmarshal(elements, &parsed) != nil || parsed == nil {
|
||
return elements
|
||
}
|
||
newElements := map[string]interface{}{}
|
||
for _, data := range parsed {
|
||
newID := uuid.New().String()
|
||
if m, ok := data.(map[string]interface{}); ok {
|
||
m["id"] = newID
|
||
}
|
||
newElements[newID] = data
|
||
}
|
||
result, _ := json.Marshal(newElements)
|
||
return JSON(result)
|
||
}
|
||
|
||
// --- Files ---
|
||
|
||
func getFiles(c *gin.Context, db *gorm.DB) {
|
||
var files []File
|
||
q := db.Model(&File{})
|
||
if tid := c.Query("template_id"); tid != "" {
|
||
q = q.Where("template_id = ?", tid)
|
||
}
|
||
q.Find(&files)
|
||
c.JSON(200, gin.H{"files": files})
|
||
}
|
||
|
||
func getFile(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var f File
|
||
if db.First(&f, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"file": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"file": f})
|
||
}
|
||
|
||
func uploadFile(c *gin.Context, db *gorm.DB) {
|
||
templateID, err := uuid.Parse(c.Query("template_id"))
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"detail": "invalid template_id"})
|
||
return
|
||
}
|
||
|
||
fileHeader, err := c.FormFile("file")
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"detail": "file required"})
|
||
return
|
||
}
|
||
|
||
f, _ := fileHeader.Open()
|
||
defer f.Close()
|
||
content, _ := io.ReadAll(f)
|
||
|
||
cellsData, err := ReadCells(content)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"detail": fmt.Sprintf("failed to read cells: %v", err)})
|
||
return
|
||
}
|
||
|
||
cellsJSON, _ := json.Marshal(cellsData)
|
||
file := File{Name: fileHeader.Filename, Content: content, Cells: JSON(cellsJSON), TemplateID: &templateID}
|
||
db.Create(&file)
|
||
|
||
c.JSON(200, gin.H{"file_id": file.ID, "cells": cellsData})
|
||
}
|
||
|
||
// --- Sheets ---
|
||
|
||
func getSheets(c *gin.Context, db *gorm.DB) {
|
||
var sheets []Sheet
|
||
q := db.Model(&Sheet{})
|
||
if tid := c.Query("template_id"); tid != "" {
|
||
q = q.Where("template_id = ?", tid)
|
||
}
|
||
q.Find(&sheets)
|
||
c.JSON(200, gin.H{"sheets": sheets})
|
||
}
|
||
|
||
func getSheet(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var s Sheet
|
||
if db.First(&s, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"sheet": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"sheet": s})
|
||
}
|
||
|
||
func createSheet(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
Name string `json:"name"`
|
||
Cells json.RawMessage `json:"cells"`
|
||
TemplateID uuid.UUID `json:"template_id"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
s := Sheet{Name: input.Name, Cells: JSON(input.Cells), TemplateID: input.TemplateID}
|
||
db.Create(&s)
|
||
c.JSON(200, gin.H{"id": s.ID})
|
||
}
|
||
|
||
func patchSheet(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var s Sheet
|
||
if db.First(&s, "id = ?", id).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Sheet not found"})
|
||
return
|
||
}
|
||
var input struct {
|
||
Name *string `json:"name"`
|
||
Cells *json.RawMessage `json:"cells"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
updates := map[string]interface{}{}
|
||
if input.Name != nil {
|
||
updates["name"] = *input.Name
|
||
}
|
||
if input.Cells != nil {
|
||
updates["cells"] = JSON(*input.Cells)
|
||
}
|
||
if len(updates) > 0 {
|
||
db.Model(&s).Updates(updates)
|
||
}
|
||
c.JSON(200, gin.H{"id": s.ID})
|
||
}
|
||
|
||
// --- Attributes ---
|
||
|
||
func getAttributes(c *gin.Context, db *gorm.DB) {
|
||
var attrs []Attribute
|
||
db.Find(&attrs)
|
||
c.JSON(200, gin.H{"attributes": attrs})
|
||
}
|
||
|
||
func createAttribute(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
Name string `json:"name"`
|
||
IsRequired bool `json:"is_required"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
var existing Attribute
|
||
if db.Where("name = ?", input.Name).First(&existing).Error == nil {
|
||
c.JSON(400, gin.H{"detail": fmt.Sprintf("Атрибут с именем '%s' уже существует", input.Name)})
|
||
return
|
||
}
|
||
a := Attribute{Name: input.Name, IsRequired: input.IsRequired}
|
||
db.Create(&a)
|
||
c.JSON(200, gin.H{"id": a.ID})
|
||
}
|
||
|
||
func getAttributeValues(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var attr Attribute
|
||
if db.First(&attr, "id = ?", id).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Attribute not found"})
|
||
return
|
||
}
|
||
|
||
type valCount struct {
|
||
Value string `json:"value"`
|
||
UsageCount int `json:"usage_count"`
|
||
}
|
||
var values []valCount
|
||
db.Model(&TemplateAttribute{}).
|
||
Select("value, count(template_id) as usage_count").
|
||
Where("attribute_id = ? AND value IS NOT NULL", id).
|
||
Group("value").
|
||
Order("usage_count DESC").
|
||
Find(&values)
|
||
|
||
c.JSON(200, gin.H{
|
||
"attribute_id": attr.ID,
|
||
"attribute_name": attr.Name,
|
||
"values": values,
|
||
"total_count": len(values),
|
||
})
|
||
}
|
||
|
||
// --- Standards ---
|
||
|
||
func getStandards(c *gin.Context, db *gorm.DB) {
|
||
var standards []Standard
|
||
db.Find(&standards)
|
||
c.JSON(200, gin.H{"standards": standards})
|
||
}
|
||
|
||
func getStandard(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var s Standard
|
||
if db.First(&s, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"standard": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"standard": s})
|
||
}
|
||
|
||
func createStandard(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
Name string `json:"name"`
|
||
ProtocolName string `json:"protocol_name"`
|
||
RegistryNumber string `json:"registry_number"`
|
||
Range string `json:"range"`
|
||
Accuracy string `json:"accuracy"`
|
||
ValidUntil string `json:"valid_until"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
validUntil, _ := time.Parse("2006-01-02", input.ValidUntil)
|
||
s := Standard{
|
||
Name: input.Name, ProtocolName: input.ProtocolName, RegistryNumber: input.RegistryNumber,
|
||
Range: input.Range, Accuracy: input.Accuracy, ValidUntil: validUntil,
|
||
}
|
||
db.Create(&s)
|
||
c.JSON(200, gin.H{"id": s.ID})
|
||
}
|
||
|
||
func patchStandard(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var s Standard
|
||
if db.First(&s, "id = ?", id).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Standard not found"})
|
||
return
|
||
}
|
||
var raw map[string]interface{}
|
||
if c.BindJSON(&raw) != nil {
|
||
return
|
||
}
|
||
db.Model(&s).Updates(raw)
|
||
c.JSON(200, gin.H{"id": s.ID})
|
||
}
|
||
|
||
func deleteStandard(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
res := db.Delete(&Standard{}, "id = ?", id)
|
||
if res.RowsAffected == 0 {
|
||
c.JSON(404, gin.H{"detail": "Standard not found"})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"message": "Standard deleted successfully"})
|
||
}
|
||
|
||
// --- Laboratories ---
|
||
|
||
func getLaboratories(c *gin.Context, db *gorm.DB) {
|
||
var labs []Laboratory
|
||
db.Preload("ConditionTypes").Find(&labs)
|
||
c.JSON(200, gin.H{"laboratories": labs})
|
||
}
|
||
|
||
func getLaboratory(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var lab Laboratory
|
||
if db.Preload("ConditionTypes").First(&lab, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"laboratory": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"laboratory": lab})
|
||
}
|
||
|
||
// --- Daily Conditions ---
|
||
|
||
func getDailyConditions(c *gin.Context, db *gorm.DB) {
|
||
q := db.Model(&DailyCondition{})
|
||
if lid := c.Query("laboratory_id"); lid != "" {
|
||
q = q.Where("laboratory_id = ?", lid)
|
||
}
|
||
if sd := c.Query("start_date"); sd != "" {
|
||
q = q.Where("measurement_date >= ?", sd)
|
||
}
|
||
if ed := c.Query("end_date"); ed != "" {
|
||
q = q.Where("measurement_date <= ?", ed)
|
||
}
|
||
var dcs []DailyCondition
|
||
q.Find(&dcs)
|
||
c.JSON(200, gin.H{"daily_conditions": dcs})
|
||
}
|
||
|
||
func getDailyCondition(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var dc DailyCondition
|
||
if db.First(&dc, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"daily_condition": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"daily_condition": dc})
|
||
}
|
||
|
||
func createDailyCondition(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
LaboratoryID uuid.UUID `json:"laboratory_id"`
|
||
MeasurementDate string `json:"measurement_date"`
|
||
Conditions json.RawMessage `json:"conditions"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
var lab Laboratory
|
||
if db.First(&lab, "id = ?", input.LaboratoryID).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Laboratory not found"})
|
||
return
|
||
}
|
||
md, _ := time.Parse("2006-01-02", input.MeasurementDate)
|
||
dc := DailyCondition{LaboratoryID: input.LaboratoryID, MeasurementDate: md, Conditions: JSON(input.Conditions)}
|
||
db.Create(&dc)
|
||
c.JSON(200, gin.H{"id": dc.ID})
|
||
}
|
||
|
||
func updateDailyCondition(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var dc DailyCondition
|
||
if db.First(&dc, "id = ?", id).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Daily condition not found"})
|
||
return
|
||
}
|
||
var raw map[string]interface{}
|
||
if c.BindJSON(&raw) != nil {
|
||
return
|
||
}
|
||
db.Model(&dc).Updates(raw)
|
||
c.JSON(200, gin.H{"id": dc.ID})
|
||
}
|
||
|
||
func deleteDailyCondition(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
res := db.Delete(&DailyCondition{}, "id = ?", id)
|
||
if res.RowsAffected == 0 {
|
||
c.JSON(404, gin.H{"detail": "Daily condition not found"})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"message": "Daily condition deleted successfully"})
|
||
}
|
||
|
||
// --- Groups ---
|
||
|
||
func getGroups(c *gin.Context, db *gorm.DB) {
|
||
var groups []Group
|
||
db.Order("\"order\", name").Find(&groups)
|
||
c.JSON(200, gin.H{"groups": groups})
|
||
}
|
||
|
||
func getGroup(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var g Group
|
||
if db.First(&g, "id = ?", id).Error != nil {
|
||
c.JSON(200, gin.H{"group": nil})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"group": g})
|
||
}
|
||
|
||
func createGroup(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
Name string `json:"name"`
|
||
Order float64 `json:"order"`
|
||
IsVisible *bool `json:"is_visible"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
isVisible := true
|
||
if input.IsVisible != nil {
|
||
isVisible = *input.IsVisible
|
||
}
|
||
g := Group{Name: input.Name, Order: input.Order, IsVisible: isVisible}
|
||
db.Create(&g)
|
||
c.JSON(200, gin.H{"id": g.ID})
|
||
}
|
||
|
||
func patchGroup(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var g Group
|
||
if db.First(&g, "id = ?", id).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "Group not found"})
|
||
return
|
||
}
|
||
var raw map[string]interface{}
|
||
if c.BindJSON(&raw) != nil {
|
||
return
|
||
}
|
||
db.Model(&g).Updates(raw)
|
||
db.First(&g, "id = ?", id) // reload
|
||
c.JSON(200, g)
|
||
}
|
||
|
||
func deleteGroup(c *gin.Context, db *gorm.DB) {
|
||
id, ok := parseUUID(c, "id")
|
||
if !ok {
|
||
return
|
||
}
|
||
res := db.Delete(&Group{}, "id = ?", id)
|
||
c.JSON(200, gin.H{"success": res.RowsAffected > 0})
|
||
}
|
||
|
||
// --- Protocols ---
|
||
|
||
func createProtocol(c *gin.Context, db *gorm.DB) {
|
||
var input struct {
|
||
TemplateID uuid.UUID `json:"template_id"`
|
||
FileID uuid.UUID `json:"file_id"`
|
||
CellsToUpdate map[string]interface{} `json:"cells_to_update"`
|
||
PrintArea string `json:"print_area"`
|
||
}
|
||
if c.BindJSON(&input) != nil {
|
||
return
|
||
}
|
||
|
||
var file File
|
||
if db.First(&file, "id = ?", input.FileID).Error != nil {
|
||
c.JSON(404, gin.H{"detail": "File not found"})
|
||
return
|
||
}
|
||
|
||
modified, err := FillCells(input.CellsToUpdate, file.Content, input.PrintArea)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"detail": fmt.Sprintf("Excel error: %v", err)})
|
||
return
|
||
}
|
||
|
||
protocolID := uuid.New()
|
||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=protocol_%s.xlsx", protocolID))
|
||
c.Data(200, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", modified)
|
||
}
|