feat: add flitsmeister utility for processing flitsmeister CSV files

This commit is contained in:
Menno van Leeuwen 2025-05-21 11:39:06 +02:00
parent 1e6bb83c21
commit 7dba7b00a7
Signed by: vleeuwenmenno
SSH Key Fingerprint: SHA256:OJFmjANpakwD3F2Rsws4GLtbdz1TJ5tkQF0RZmF0TRE
2 changed files with 130 additions and 3 deletions

View File

@ -0,0 +1,127 @@
package main
import (
"encoding/csv"
"fmt"
"os"
"sort"
"strconv"
"strings"
"time"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: flitsmeister <filename.csv>")
return
}
file, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
defer file.Close()
reader := csv.NewReader(file)
reader.Comma = ','
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
// Dutch to English month translation
dutchMonths := map[string]string{
"januari": "January", "februari": "February", "maart": "March",
"april": "April", "mei": "May", "juni": "June", "juli": "July",
"augustus": "August", "september": "September", "oktober": "October",
"november": "November", "december": "December",
}
monthlyTotals := make(map[string]float64)
monthlyCounts := make(map[string]int)
monthlyLongest := make(map[string]float64)
monthlyShortest := make(map[string]float64)
// Skip header row
for _, record := range records[1:] {
if len(record) < 8 {
continue
}
// Parse date with Dutch month names
dateParts := strings.Split(record[1], " ")
if len(dateParts) < 4 {
continue
}
// Translate Dutch month to English
dutchMonth := strings.ToLower(dateParts[1])
engMonth, ok := dutchMonths[dutchMonth]
if !ok {
fmt.Printf("Unknown month: %s\n", dutchMonth)
continue
}
// Rebuild date string with English month
dateStr := fmt.Sprintf("%s %s %s %s",
dateParts[0], engMonth, dateParts[2], dateParts[3])
// Parse date (single-digit days allowed)
tripDate, err := time.Parse("2 January 2006 15:04", dateStr)
if err != nil {
fmt.Printf("Error parsing date: %v\n", err)
continue
}
// Create month key (format: YYYY-MM)
monthKey := fmt.Sprintf("%d-%02d", tripDate.Year(), tripDate.Month())
// Parse distance
distance, err := strconv.ParseFloat(record[7], 64)
if err != nil {
fmt.Printf("Error parsing distance: %v\n", err)
continue
}
monthlyTotals[monthKey] += distance
monthlyCounts[monthKey]++
// Track longest and shortest trip
if _, exists := monthlyLongest[monthKey]; !exists {
monthlyLongest[monthKey] = distance
monthlyShortest[monthKey] = distance
} else {
if distance > monthlyLongest[monthKey] {
monthlyLongest[monthKey] = distance
}
if distance < monthlyShortest[monthKey] {
monthlyShortest[monthKey] = distance
}
}
}
// Sort months chronologically
var months []string
for k := range monthlyTotals {
months = append(months, k)
}
sort.Strings(months)
// Print table header
fmt.Printf("%-10s | %-12s | %-7s | %-12s | %-12s | %-12s\n", "Month", "Total KM", "Trips", "Avg KM/Trip", "Longest", "Shortest")
fmt.Println(strings.Repeat("-", 77))
// Print stats per month
for _, month := range months {
total := monthlyTotals[month]
trips := monthlyCounts[month]
avg := 0.0
if trips > 0 {
avg = total / float64(trips)
}
longest := monthlyLongest[month]
shortest := monthlyShortest[month]
fmt.Printf("%-10s | %-12.2f | %-7d | %-12.2f | %-12.2f | %-12.2f\n",
month, total, trips, avg, longest, shortest)
}
}

View File

@ -39,11 +39,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1747542820, "lastModified": 1747744144,
"narHash": "sha256-GaOZntlJ6gPPbbkTLjbd8BMWaDYafhuuYRNrxCGnPJw=", "narHash": "sha256-W7lqHp0qZiENCDwUZ5EX/lNhxjMdNapFnbErcbnP11Q=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "292fa7d4f6519c074f0a50394dbbe69859bb6043", "rev": "2795c506fe8fb7b03c36ccb51f75b6df0ab2553f",
"type": "github" "type": "github"
}, },
"original": { "original": {