package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strconv" "github.com/google/uuid" "github.com/xuri/excelize/v2" ) type CreateProtocolRequest struct { TemplateID string `json:"template_id"` FileID string `json:"file_id"` CellsToUpdate map[string]any `json:"cells_to_update"` PrintArea string `json:"print_area"` } func protocolHandler(backendURL string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Parse request body var req CreateProtocolRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest) return } // Fetch file binary from Python backend fileURL := fmt.Sprintf("%s/files/%s/content", backendURL, req.FileID) resp, err := http.Get(fileURL) if err != nil { http.Error(w, "failed to fetch file: "+err.Error(), http.StatusInternalServerError) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { http.Error(w, fmt.Sprintf("backend returned %d when fetching file", resp.StatusCode), http.StatusInternalServerError) return } fileBytes, err := io.ReadAll(resp.Body) if err != nil { http.Error(w, "failed to read file content: "+err.Error(), http.StatusInternalServerError) return } // Open xlsx with excelize f, err := excelize.OpenReader(bytes.NewReader(fileBytes)) if err != nil { http.Error(w, "failed to open xlsx: "+err.Error(), http.StatusInternalServerError) return } defer f.Close() // Get the first sheet sheetName := f.GetSheetName(0) if sheetName == "" { http.Error(w, "no sheets found in xlsx", http.StatusInternalServerError) return } // Fill cells for cellAddr, value := range req.CellsToUpdate { if err := setCellValue(f, sheetName, cellAddr, value); err != nil { http.Error(w, fmt.Sprintf("failed to set cell %s: %v", cellAddr, err), http.StatusInternalServerError) return } } // Write result to buffer var buf bytes.Buffer if err := f.Write(&buf); err != nil { http.Error(w, "failed to write xlsx: "+err.Error(), http.StatusInternalServerError) return } protocolID := uuid.New().String() w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="protocol_%s.xlsx"`, protocolID)) w.Header().Set("Content-Length", strconv.Itoa(buf.Len())) w.Write(buf.Bytes()) } } func setCellValue(f *excelize.File, sheet, cell string, value any) error { switch v := value.(type) { case float64: // JSON numbers are float64; check if it's actually an integer if v == float64(int64(v)) { return f.SetCellInt(sheet, cell, int64(v)) } return f.SetCellFloat(sheet, cell, v, -1, 64) case string: return f.SetCellStr(sheet, cell, v) case bool: return f.SetCellBool(sheet, cell, v) case nil: return f.SetCellStr(sheet, cell, "") default: return f.SetCellStr(sheet, cell, fmt.Sprintf("%v", v)) } }