inou/api/api_series.go

121 lines
3.1 KiB
Go

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 {
var d struct {
SeriesDesc string `json:"series_desc"`
Modality string `json:"modality"`
ViewportLeft *float64 `json:"viewport_left"`
ViewportTop *float64 `json:"viewport_top"`
ViewportRight *float64 `json:"viewport_right"`
ViewportBottom *float64 `json:"viewport_bottom"`
}
json.Unmarshal([]byte(e.Data), &d)
desc := d.SeriesDesc
modality := d.Modality
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
}
s := map[string]interface{}{
"id": e.EntryID,
"series_number": e.Ordinal,
"series_desc": desc,
"modality": modality,
"slice_count": sliceCount,
}
if d.ViewportLeft != nil && d.ViewportTop != nil && d.ViewportRight != nil && d.ViewportBottom != nil {
s["viewport"] = []float64{*d.ViewportLeft, *d.ViewportTop, *d.ViewportRight, *d.ViewportBottom}
}
series = append(series, s)
}
// 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
}