aboutsummaryrefslogtreecommitdiff
path: root/web/data_quality.go
blob: 562ecf8ae94c9f48845ef990419b6108fcf5077e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

package web

import (
	"log/slog"
	"net/http"
	"strconv"

	"github.com/martinlehoux/biking_home/ride"
	"github.com/martinlehoux/kagamigo/kcore"
)

func (s *Server) handleDataQuality(w http.ResponseWriter, r *http.Request) {
	items, err := ride.ListUncategorised(s.db)
	if err != nil {
		slog.Error("Failed to load uncategorised rides", "error", err)
		http.Error(w, "failed to load rides", http.StatusInternalServerError)
		return
	}
	views := make([]RideView, 0, len(items))
	for _, item := range items {
		views = append(views, buildRideView(item))
	}
	total, err := ride.CountUncategorised(s.db)
	if err != nil {
		slog.Warn("Failed to count uncategorised rides", "error", err)
		total = len(items)
	}
	slog.Info("Loaded data quality page", "uncategorised", total, "displayed", len(views))
	kcore.RenderPage(r.Context(), DataQualityPage(DataQualityPageData{
		Items:  views,
		Total:  total,
		Notice: dataQualityNotice(r),
	}), w)
}

func (s *Server) handleBulkUpdateCategories(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		http.Error(w, "invalid form", http.StatusBadRequest)
		return
	}
	bikeType := ride.BikeType(r.FormValue("bike_type"))
	rideType := ride.RideType(r.FormValue("ride_type"))
	if !ride.IsValidBikeType(string(bikeType)) {
		http.Error(w, "invalid bike_type", http.StatusBadRequest)
		return
	}
	if !ride.IsValidRideType(string(rideType)) {
		http.Error(w, "invalid ride_type", http.StatusBadRequest)
		return
	}
	rawIDs := r.Form["ride_ids"]
	if len(rawIDs) == 0 {
		http.Error(w, "no rides selected", http.StatusBadRequest)
		return
	}
	ids := make([]int64, 0, len(rawIDs))
	for _, raw := range rawIDs {
		id, err := strconv.ParseInt(raw, 10, 64)
		if err != nil || id <= 0 {
			http.Error(w, "invalid ride id", http.StatusBadRequest)
			return
		}
		ids = append(ids, id)
	}
	affected, err := ride.BulkUpdateCategories(s.db, ids, bikeType, rideType)
	if err != nil {
		slog.Error("Failed to bulk update categories", "error", err)
		http.Error(w, "failed to update categories", http.StatusInternalServerError)
		return
	}
	slog.Info("Bulk updated ride categories", "count", affected, "bike_type", bikeType, "ride_type", rideType)
	http.Redirect(w, r, "/data-quality?bulk=updated&count="+strconv.FormatInt(affected, 10), http.StatusSeeOther)
}

func dataQualityNotice(r *http.Request) string {
	if r.URL.Query().Get("bulk") == "updated" {
		count := r.URL.Query().Get("count")
		if count != "" {
			return count + " rides categorised."
		}
		return "Rides categorised."
	}
	if r.URL.Query().Get("categories") == "updated" {
		return "Ride categories updated."
	}
	return ""
}

type DataQualityPageData struct {
	Items  []RideView
	Total  int
	Notice string
}

func formatRideID(id int64) string {
	return strconv.FormatInt(id, 10)
}