aboutsummaryrefslogtreecommitdiff
path: root/web/data_quality.go
diff options
context:
space:
mode:
Diffstat (limited to 'web/data_quality.go')
-rw-r--r--web/data_quality.go101
1 files changed, 101 insertions, 0 deletions
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)
+}