munin/internal/web/handlers_nutzung.go

124 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package web
// Nutzungsübersicht je Projekt (AUSBAUSTUFEN.md 4.6).
//
// Für alle mit Zugang zum Projekt, auch mit Leserecht: Die Seite zeigt nur
// Zahlen und Namen von Zugängen, und die stehen ohnehin schon im Protokoll,
// das jede Rolle sehen darf.
import (
"net/http"
"time"
"munin/internal/store"
)
// Wie viele Einträge die Listen auf der Seite höchstens zeigen. Die Seite
// soll einen Überblick geben, keine vollständige Aufstellung — wer mehr
// braucht, findet sie über die Eintragsliste oder den Graph.
const (
nutzungListenLaenge = 15
nutzungKeysLaenge = 5
)
// usageData sind die Daten der Nutzungsseite.
type usageData struct {
Summary store.UsageSummary
WindowDays int
Untagged []store.EntryRef
Stale int // seit über einem Jahr unverändert (store.CountStaleEntries)
Missing []store.MissingLink
MissingMore int // wie viele Verweise ins Leere über die Liste hinaus
Keys []store.APIKey
// Kuratierte Einträge, die ein Agent seitdem geändert hat — zur Prüfung.
CuratedOverrides []store.CuratedOverride
// Suchen der Agenten über recall (Suchprotokoll, migrations/006).
Searches store.SearchStats
EmptySearches []store.EmptySearch
}
// handleProjectUsage zeigt die Nutzungsübersicht eines Projekts.
func (s *Server) handleProjectUsage(w http.ResponseWriter, r *http.Request) {
ws, _ := WorkspaceFrom(r)
project, _ := ProjectFrom(r)
ctx := r.Context()
alle, err := ws.Store.UsageSummaries(ctx, project.ID, time.Now())
if err != nil {
s.serverError(w, "Nutzung konnte nicht geladen werden", err)
return
}
ohneSchlagwort, err := ws.Store.UntaggedEntries(ctx, project.ID, nutzungListenLaenge)
if err != nil {
s.serverError(w, "Einträge ohne Schlagwort konnten nicht geladen werden", err)
return
}
insLeere, err := ws.Store.MissingLinks(ctx, project.ID)
if err != nil {
s.serverError(w, "Verweise konnten nicht geladen werden", err)
return
}
keys, err := ws.Store.RecentlyUsedKeys(ctx, project.ID, nutzungKeysLaenge)
if err != nil {
s.serverError(w, "Zugänge konnten nicht geladen werden", err)
return
}
zurPruefung, err := ws.Store.CuratedOverrides(ctx, project.ID, nutzungListenLaenge)
if err != nil {
s.serverError(w, "Geänderte kuratierte Einträge konnten nicht geladen werden", err)
return
}
suchen, err := ws.Store.SearchStatsFor(ctx, project.ID, time.Now())
if err != nil {
s.serverError(w, "Suchprotokoll konnte nicht geladen werden", err)
return
}
ohneTreffer, err := ws.Store.EmptySearches(ctx, project.ID, time.Now(), nutzungListenLaenge)
if err != nil {
s.serverError(w, "Suchen ohne Treffer konnten nicht geladen werden", err)
return
}
veraltet, err := ws.Store.CountStaleEntries(ctx, project.ID, time.Now())
if err != nil {
s.serverError(w, "veraltete Einträge konnten nicht gezählt werden", err)
return
}
daten := usageData{
Stale: veraltet,
// Ein Projekt ohne Einträge und Schreibvorgänge fehlt in der map —
// dann liefert alle[project.ID] den Nullwert, also überall 0.
Summary: alle[project.ID],
WindowDays: store.UsageWindowDays,
Untagged: ohneSchlagwort,
Missing: insLeere,
Keys: keys,
CuratedOverrides: zurPruefung,
Searches: suchen,
EmptySearches: ohneTreffer,
}
// Die Verweise ins Leere kommen vollständig (die Gesamtzahl ist selbst
// eine Aussage), angezeigt werden nur die meistgenannten.
if len(daten.Missing) > nutzungListenLaenge {
daten.MissingMore = len(daten.Missing) - nutzungListenLaenge
daten.Missing = daten.Missing[:nutzungListenLaenge]
}
s.render(w, r, "nutzung.html", http.StatusOK, pageData{
Title: "Nutzung – " + project.Name,
Bereich: "nutzung",
Data: daten,
})
}