From 7f33c7c55773663a0c7fe30585e2099d4b5ba6d2 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Mon, 24 Aug 2026 09:06:37 +0200 Subject: Add data quality bulk categorisation page --- web/data_quality.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 web/data_quality.go (limited to 'web/data_quality.go') diff --git a/web/data_quality.go b/web/data_quality.go new file mode 100644 index 0000000..74e84af --- /dev/null +++ b/web/data_quality.go @@ -0,0 +1,101 @@ +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 "" +} + +func dataQualityURL() string { + return "/data-quality" +} + +type DataQualityPageData struct { + Items []RideView + Total int + Notice string +} + +func formatRideID(id int64) string { + return strconv.FormatInt(id, 10) +} -- cgit v1.3