package cmd
import (
"encoding/json"
"flag"
"fmt"
"os"
"skraak/tools"
)
func runCallsPropagate(args []string) {
fs := flag.NewFlagSet("calls propagate", flag.ExitOnError)
file := fs.String("file", "", "Path to a single .data file (mutually exclusive with --folder)")
folder := fs.String("folder", "", "Path to folder containing .data files (mutually exclusive with --file)")
from := fs.String("from", "", "Source filter name (required)")
to := fs.String("to", "", "Target filter name (required)")
species := fs.String("species", "", "Species to propagate (required, e.g. Kiwi)")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: skraak calls propagate [options]\n\n")
fmt.Fprintf(os.Stderr, "Propagate verified classifications from one filter to another within a .data file\n")
fmt.Fprintf(os.Stderr, "or across every .data file in a folder.\n\n")
fmt.Fprintf(os.Stderr, "Only source labels with certainty=100 and matching --species are considered.\n")
fmt.Fprintf(os.Stderr, "Target labels (filter=--to) are updated when their certainty is 70 or 0.\n")
fmt.Fprintf(os.Stderr, "Updated target labels are set to certainty=90; file reviewer is set to \"Skraak\".\n")
fmt.Fprintf(os.Stderr, "Targets already at certainty=100 or 90 are left alone.\n")
fmt.Fprintf(os.Stderr, "Files that do not contain both --from and --to filter labels are skipped.\n\n")
fmt.Fprintf(os.Stderr, "Exactly one of --file or --folder is required.\n\n")
fmt.Fprintf(os.Stderr, "Options:\n")
fs.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nExamples:\n")
fmt.Fprintf(os.Stderr, " skraak calls propagate --file rec.wav.data \\\n")
fmt.Fprintf(os.Stderr, " --from opensoundscape-kiwi-1.2 --to opensoundscape-kiwi-1.5 --species Kiwi\n\n")
fmt.Fprintf(os.Stderr, " skraak calls propagate --folder ./recordings \\\n")
fmt.Fprintf(os.Stderr, " --from opensoundscape-kiwi-1.2 --to opensoundscape-kiwi-1.5 --species Kiwi\n")
}
if err := fs.Parse(args); err != nil {
os.Exit(1)
}
if (*file == "") == (*folder == "") {
fmt.Fprintf(os.Stderr, "Error: exactly one of --file or --folder is required\n\n")
fs.Usage()
os.Exit(1)
}
missing := []string{}
if *from == "" {
missing = append(missing, "--from")
}
if *to == "" {
missing = append(missing, "--to")
}
if *species == "" {
missing = append(missing, "--species")
}
if len(missing) > 0 {
fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)
fs.Usage()
os.Exit(1)
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if *file != "" {
result, err := tools.CallsPropagate(tools.CallsPropagateInput{
File: *file,
FromFilter: *from,
ToFilter: *to,
Species: *species,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", result.Error)
os.Exit(1)
}
if err := enc.Encode(result); err != nil {
fmt.Fprintf(os.Stderr, "Error encoding output: %v\n", err)
os.Exit(1)
}
return
}
result, err := tools.CallsPropagateFolder(tools.CallsPropagateFolderInput{
Folder: *folder,
FromFilter: *from,
ToFilter: *to,
Species: *species,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", result.Error)
os.Exit(1)
}
fmt.Fprintf(os.Stderr,
"Files: %d total, %d with both filters, %d skipped (missing filter), %d changed, %d errored\n",
result.FilesTotal, result.FilesWithBothFilters, result.FilesSkippedNoFilter,
result.FilesChanged, result.FilesErrored)
fmt.Fprintf(os.Stderr,
"Targets: %d examined, %d propagated, %d no-overlap, %d conflicts\n",
result.TargetsExamined, result.Propagated, result.SkippedNoOverlap, result.SkippedConflict)
if err := enc.Encode(result); err != nil {
fmt.Fprintf(os.Stderr, "Error encoding output: %v\n", err)
os.Exit(1)
}
}