package cmd
import (
"encoding/json"
"flag"
"fmt"
"os"
"skraak/tools"
)
// RunCalls handles the "calls" command
func RunCalls(args []string) {
if len(args) < 1 {
printCallsUsage()
os.Exit(1)
}
switch args[0] {
case "from-preds":
runCallsFromPreds(args[1:])
default:
fmt.Fprintf(os.Stderr, "Unknown calls subcommand: %s\n\n", args[0])
printCallsUsage()
os.Exit(1)
}
}
func printCallsUsage() {
fmt.Fprintf(os.Stderr, "Usage: skraak calls <subcommand> [options]\n\n")
fmt.Fprintf(os.Stderr, "Subcommands:\n")
fmt.Fprintf(os.Stderr, " from-preds Extract clustered calls from ML predictions CSV\n")
fmt.Fprintf(os.Stderr, "\nExamples:\n")
fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv predictions.csv > calls.json\n")
fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv preds.csv | jq '.calls[] | select(.ebird_code==\"kea1\")'\n")
}
// runCallsFromPreds handles the "calls from-preds" subcommand
func runCallsFromPreds(args []string) {
fs := flag.NewFlagSet("calls from-preds", flag.ExitOnError)
csvPath := fs.String("csv", "", "Path to predictions CSV file (required)")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: skraak calls from-preds [options]\n\n")
fmt.Fprintf(os.Stderr, "Extract clustered bird calls from ML predictions CSV.\n")
fmt.Fprintf(os.Stderr, "Reads prediction CSV with columns: file, start_time, end_time, <ebird_codes...>\n")
fmt.Fprintf(os.Stderr, "Each row is a clip with 1=present, 0=absent for each species.\n\n")
fmt.Fprintf(os.Stderr, "Options:\n")
fs.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nOutput:\n")
fmt.Fprintf(os.Stderr, " JSON with clustered calls (file, start_time, end_time, ebird_code, detections)\n")
fmt.Fprintf(os.Stderr, "\nExamples:\n")
fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv predictions.csv > calls.json\n")
fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv preds.csv | jq '.calls[] | select(.ebird_code==\"kea1\")'\n")
}
if err := fs.Parse(args); err != nil {
os.Exit(1)
}
// Validate required flags
if *csvPath == "" {
fmt.Fprintf(os.Stderr, "Error: --csv is required\n\n")
fs.Usage()
os.Exit(1)
}
input := tools.CallsFromPredsInput{
CSVPath: *csvPath,
}
fmt.Fprintf(os.Stderr, "Extracting calls from predictions: %s\n", *csvPath)
output, err := tools.CallsFromPreds(input)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Found %d clustered calls across %d species\n",
output.TotalCalls, len(output.SpeciesCount))
fmt.Fprintf(os.Stderr, "Clip duration: %.1fs, Gap threshold: %.1fs\n",
output.ClipDuration, output.GapThreshold)
// Output JSON to stdout
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(output)
}