package main import ( "encoding/json" "net/http" "sort" "strings" "inou/lib" ) func handleSeries(w http.ResponseWriter, r *http.Request) { ctx := getAccessContextOrFail(w, r) if ctx == nil { return } dossierID := r.URL.Query().Get("dossier") if dossierID == "" { dossierID = r.URL.Query().Get("token") } studyID := r.URL.Query().Get("study") orientation := strings.ToUpper(r.URL.Query().Get("orientation")) if dossierID == "" || studyID == "" { http.Error(w, "dossier and study required", http.StatusBadRequest) return } // RBAC: Check read access to dossier if !requireDossierAccess(w, ctx, dossierID) { return } entries, err := lib.EntryChildrenByType(dossierID, studyID, "series") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var series []map[string]interface{} for _, e := range entries { desc := e.Tags descUpper := strings.ToUpper(desc) // Filter by orientation if specified if orientation != "" { if !strings.HasPrefix(descUpper, orientation+" ") && !strings.HasPrefix(descUpper, orientation+"_") { continue } } // Count slices for this series (Type="slice") slices, _ := lib.EntryChildrenByType(dossierID, e.EntryID, "slice") sliceCount := len(slices) // Skip series with no slices if sliceCount == 0 { continue } series = append(series, map[string]interface{}{ "id": e.EntryID, "series_number": e.Ordinal, "series_desc": desc, "modality": e.Type, "slice_count": sliceCount, }) } // Sort by priority: T2(+) first, then T1(+), then others if orientation != "" && len(series) > 0 { sort.Slice(series, func(i, j int) bool { return seriesPriority(series[i]["series_desc"].(string)) < seriesPriority(series[j]["series_desc"].(string)) }) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(series) } func seriesPriority(desc string) int { d := strings.ToUpper(desc) // T2 variants (prefer non-contrast first, then contrast) if strings.Contains(d, "T2") && !strings.Contains(d, "+") { return 1 } if strings.Contains(d, "T2") && strings.Contains(d, "+") { return 2 } // T1 variants if strings.Contains(d, "T1") && !strings.Contains(d, "+") { return 3 } if strings.Contains(d, "T1") && strings.Contains(d, "+") { return 4 } // FLAIR if strings.Contains(d, "FLAIR") && !strings.Contains(d, "+") { return 5 } if strings.Contains(d, "FLAIR") && strings.Contains(d, "+") { return 6 } return 99 }