NKQAT3RE4IBIWXVMI5LJUINDPHTANNMORZ5N2JFA4AN6UUB72KGAC NAZQZRYQTXWVE2VFY65ONSD6O3EUMNRHARCDVH2D2HKM3YH4RGUAC GXVVTHNXT2IZPR4OB77VMU6GXFEA5TUFZ2MHMA5ASU2DSTFPLDLQC 7NS27QXZMVTZBK4VPMYL5IKGSTTAWR6NDG5SOVITNX44VNIRZPMAC IFVRAERTCCDICNTYTG3TX2WASB6RXQQEJWWXQMQZJSQDQ3HLE5OQC KS7LFF6M5Y6UGBBA7G63BJRR5XS4P4R3PSZPG752NSGZ3Z6GY72QC 2Y2ZW565SRONQ2UXPLX5SRP2HDFWMRF5KDXKSKVRCHBBGEGMTVIQC 2GJMZ6YA6OPHNS5KFFFI6POQ2BJ33SSS3NIPXYBFTJSN4BZBVEVAC 5LMYPB2QHNVDLYCRWLOMCPY35ZKHHPYVW5XHASE66L6PJZSOCXYQC fmt.Fprintf(os.Stderr, " dataset Manage datasets (create/update)\n")fmt.Fprintf(os.Stderr, " location Manage locations (create/update)\n")fmt.Fprintf(os.Stderr, " cluster Manage clusters (create/update)\n")fmt.Fprintf(os.Stderr, " pattern Manage recording patterns (create/update)\n")
package cmdimport ("context""encoding/json""flag""fmt""os""skraak/tools")// RunTime handles the "time" subcommandfunc RunTime(args []string) {fs := flag.NewFlagSet("time", flag.ExitOnError)fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak time\n\n")fmt.Fprintf(os.Stderr, "Get the current system time with timezone information.\n\n")fmt.Fprintf(os.Stderr, "Examples:\n")fmt.Fprintf(os.Stderr, " skraak time\n")fmt.Fprintf(os.Stderr, " skraak time | jq '.iso'\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Get current timeoutput, err := tools.GetCurrentTime(context.Background(), tools.GetCurrentTimeInput{})if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}// Output as JSONenc := json.NewEncoder(os.Stdout)enc.SetIndent("", " ")enc.Encode(output)}
package cmdimport ("context""flag""fmt""os""strconv""skraak/tools")// RunPattern handles the "pattern" subcommandfunc RunPattern(args []string) {if len(args) < 1 {printPatternUsage()os.Exit(1)}switch args[0] {case "create":runPatternCreate(args[1:])case "update":runPatternUpdate(args[1:])default:fmt.Fprintf(os.Stderr, "Unknown pattern subcommand: %s\n\n", args[0])printPatternUsage()os.Exit(1)}}func printPatternUsage() {fmt.Fprintf(os.Stderr, "Usage: skraak pattern <subcommand> [options]\n\n")fmt.Fprintf(os.Stderr, "Subcommands:\n")fmt.Fprintf(os.Stderr, " create Create a new recording pattern\n")fmt.Fprintf(os.Stderr, " update Update an existing recording pattern\n")fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak pattern create --db ./db/skraak.duckdb --record 60 --sleep 1740\n")fmt.Fprintf(os.Stderr, " skraak pattern update --db ./db/skraak.duckdb --id pattern123 --record 30\n")}func runPatternCreate(args []string) {fs := flag.NewFlagSet("pattern create", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")record := fs.Int("record", 0, "Record duration in seconds (required, must be positive)")sleep := fs.Int("sleep", 0, "Sleep duration in seconds (required, must be positive)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak pattern create [options]\n\n")fmt.Fprintf(os.Stderr, "Create a new cyclic recording pattern.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak pattern create --db ./db/skraak.duckdb --record 60 --sleep 1740\n")fmt.Fprintf(os.Stderr, " # Creates 60s record / 1740s sleep = 30 min cycle\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *record == 0 {missing = append(missing, "--record")}if *sleep == 0 {missing = append(missing, "--sleep")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.PatternInput{RecordSeconds: record,SleepSeconds: sleep,}output, err := tools.CreateOrUpdatePattern(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}func runPatternUpdate(args []string) {fs := flag.NewFlagSet("pattern update", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")id := fs.String("id", "", "Pattern ID (required)")recordStr := fs.String("record", "", "New record duration in seconds (optional)")sleepStr := fs.String("sleep", "", "New sleep duration in seconds (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak pattern update [options]\n\n")fmt.Fprintf(os.Stderr, "Update an existing recording pattern. Only provided fields are updated.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak pattern update --db ./db/skraak.duckdb --id pattern123 --record 30\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *id == "" {missing = append(missing, "--id")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}// Parse optional integersvar record, sleep *intif *recordStr != "" {r, err := strconv.Atoi(*recordStr)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid record: %v\n", err)os.Exit(1)}record = &r}if *sleepStr != "" {s, err := strconv.Atoi(*sleepStr)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid sleep: %v\n", err)os.Exit(1)}sleep = &s}tools.SetDBPath(*dbPath)// Build input - only set fields that were providedinput := tools.PatternInput{ID: id,}if record != nil {input.RecordSeconds = record}if sleep != nil {input.SleepSeconds = sleep}output, err := tools.CreateOrUpdatePattern(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}
package cmdimport ("context""flag""fmt""os""strconv""skraak/tools")// RunLocation handles the "location" subcommandfunc RunLocation(args []string) {if len(args) < 1 {printLocationUsage()os.Exit(1)}switch args[0] {case "create":runLocationCreate(args[1:])case "update":runLocationUpdate(args[1:])default:fmt.Fprintf(os.Stderr, "Unknown location subcommand: %s\n\n", args[0])printLocationUsage()os.Exit(1)}}func printLocationUsage() {fmt.Fprintf(os.Stderr, "Usage: skraak location <subcommand> [options]\n\n")fmt.Fprintf(os.Stderr, "Subcommands:\n")fmt.Fprintf(os.Stderr, " create Create a new location\n")fmt.Fprintf(os.Stderr, " update Update an existing location\n")fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak location create --db ./db/skraak.duckdb --dataset abc123 --name \"Site A\" --lat -36.85 --lon 174.76 --timezone Pacific/Auckland\n")fmt.Fprintf(os.Stderr, " skraak location update --db ./db/skraak.duckdb --id loc123 --name \"Updated Name\"\n")}func runLocationCreate(args []string) {fs := flag.NewFlagSet("location create", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")datasetID := fs.String("dataset", "", "Dataset ID (required)")name := fs.String("name", "", "Location name (required)")lat := fs.String("lat", "", "Latitude in decimal degrees (required)")lon := fs.String("lon", "", "Longitude in decimal degrees (required)")tz := fs.String("timezone", "", "IANA timezone ID (required, e.g. Pacific/Auckland)")description := fs.String("description", "", "Location description (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak location create [options]\n\n")fmt.Fprintf(os.Stderr, "Create a new location with GPS coordinates.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak location create --db ./db/skraak.duckdb --dataset abc123 --name \"Site A\" --lat -36.85 --lon 174.76 --timezone Pacific/Auckland\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *datasetID == "" {missing = append(missing, "--dataset")}if *name == "" {missing = append(missing, "--name")}if *lat == "" {missing = append(missing, "--lat")}if *lon == "" {missing = append(missing, "--lon")}if *tz == "" {missing = append(missing, "--timezone")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}// Parse floatslatitude, err := strconv.ParseFloat(*lat, 64)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid latitude: %v\n", err)os.Exit(1)}longitude, err := strconv.ParseFloat(*lon, 64)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid longitude: %v\n", err)os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.LocationInput{DatasetID: datasetID,Name: name,Latitude: &latitude,Longitude: &longitude,TimezoneID: tz,Description: description,}output, err := tools.CreateOrUpdateLocation(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}func runLocationUpdate(args []string) {fs := flag.NewFlagSet("location update", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")id := fs.String("id", "", "Location ID (required)")name := fs.String("name", "", "New location name (optional)")lat := fs.String("lat", "", "New latitude (optional)")lon := fs.String("lon", "", "New longitude (optional)")tz := fs.String("timezone", "", "New IANA timezone ID (optional)")description := fs.String("description", "", "New location description (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak location update [options]\n\n")fmt.Fprintf(os.Stderr, "Update an existing location. Only provided fields are updated.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak location update --db ./db/skraak.duckdb --id loc123 --name \"New Name\"\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *id == "" {missing = append(missing, "--id")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}// Parse optional floatsvar latitude, longitude *float64if *lat != "" {latVal, err := strconv.ParseFloat(*lat, 64)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid latitude: %v\n", err)os.Exit(1)}latitude = &latVal}if *lon != "" {lonVal, err := strconv.ParseFloat(*lon, 64)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid longitude: %v\n", err)os.Exit(1)}longitude = &lonVal}tools.SetDBPath(*dbPath)input := tools.LocationInput{ID: id,Name: name,Latitude: latitude,Longitude: longitude,TimezoneID: tz,Description: description,}output, err := tools.CreateOrUpdateLocation(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}
fmt.Fprintf(os.Stderr, " bulk Bulk import WAV files from CSV\n")
fmt.Fprintf(os.Stderr, " bulk Bulk import WAV files from CSV\n")fmt.Fprintf(os.Stderr, " file Import a single WAV file\n")fmt.Fprintf(os.Stderr, " folder Import all WAV files from a folder\n")fmt.Fprintf(os.Stderr, " selections Import ML selections from folder structure\n")
fmt.Fprintf(os.Stderr, " skraak import file --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/file.wav\n")fmt.Fprintf(os.Stderr, " skraak import folder --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/folder\n")fmt.Fprintf(os.Stderr, " skraak import selections --db ./db/skraak.duckdb --dataset abc123 --cluster clust789 --path /path/to/Clips_filter_date\n")
printJSON(output)}os.Exit(1)}printJSON(output)}func runImportFile(args []string) {fs := flag.NewFlagSet("import file", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")datasetID := fs.String("dataset", "", "Dataset ID (required)")locationID := fs.String("location", "", "Location ID (required)")clusterID := fs.String("cluster", "", "Cluster ID (required)")filePath := fs.String("path", "", "Path to WAV file (required)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak import file [options]\n\n")fmt.Fprintf(os.Stderr, "Import a single WAV file into the database.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak import file --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/file.wav\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *datasetID == "" {missing = append(missing, "--dataset")}if *locationID == "" {missing = append(missing, "--location")}if *clusterID == "" {missing = append(missing, "--cluster")}if *filePath == "" {missing = append(missing, "--path")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.ImportFileInput{FilePath: *filePath,DatasetID: *datasetID,LocationID: *locationID,ClusterID: *clusterID,}fmt.Fprintf(os.Stderr, "Importing file: %s\n", *filePath)output, err := tools.ImportFile(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}func runImportFolder(args []string) {fs := flag.NewFlagSet("import folder", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")datasetID := fs.String("dataset", "", "Dataset ID (required)")locationID := fs.String("location", "", "Location ID (required)")clusterID := fs.String("cluster", "", "Cluster ID (required)")folderPath := fs.String("path", "", "Path to folder containing WAV files (required)")recursive := fs.Bool("recursive", true, "Scan subfolders recursively (default: true)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak import folder [options]\n\n")fmt.Fprintf(os.Stderr, "Import all WAV files from a folder into the database.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak import folder --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/folder\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *datasetID == "" {missing = append(missing, "--dataset")}if *locationID == "" {missing = append(missing, "--location")}if *clusterID == "" {missing = append(missing, "--cluster")}if *folderPath == "" {missing = append(missing, "--path")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.ImportAudioFilesInput{FolderPath: *folderPath,DatasetID: *datasetID,LocationID: *locationID,ClusterID: *clusterID,Recursive: recursive,}fmt.Fprintf(os.Stderr, "Importing from folder: %s\n", *folderPath)if *recursive {fmt.Fprintf(os.Stderr, "Scanning recursively...\n")}output, err := tools.ImportAudioFiles(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)// Still print partial results if availableif len(output.FileIDs) > 0 {printJSON(output)}os.Exit(1)}printJSON(output)}func runImportSelections(args []string) {fs := flag.NewFlagSet("import selections", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")datasetID := fs.String("dataset", "", "Dataset ID (required)")clusterID := fs.String("cluster", "", "Cluster ID (required)")folderPath := fs.String("path", "", "Path to Clips_* folder (required)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak import selections [options]\n\n")fmt.Fprintf(os.Stderr, "Import ML-detected kiwi call selections from folder structure.\n")fmt.Fprintf(os.Stderr, "Expects: Clips_{filter}_{date}/Species/CallType/*.wav+.png\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak import selections --db ./db/skraak.duckdb --dataset abc123 --cluster clust789 --path /path/to/Clips_filter_2024-01-15\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *datasetID == "" {missing = append(missing, "--dataset")}if *clusterID == "" {missing = append(missing, "--cluster")}if *folderPath == "" {missing = append(missing, "--path")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.ImportMLSelectionsInput{FolderPath: *folderPath,DatasetID: *datasetID,ClusterID: *clusterID,}fmt.Fprintf(os.Stderr, "Importing selections from: %s\n", *folderPath)output, err := tools.ImportMLSelections(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)// Still print partial results if availableif len(output.SelectionIDs) > 0 {
package cmdimport ("context""flag""fmt""os""skraak/tools")// RunDataset handles the "dataset" subcommandfunc RunDataset(args []string) {if len(args) < 1 {printDatasetUsage()os.Exit(1)}switch args[0] {case "create":runDatasetCreate(args[1:])case "update":runDatasetUpdate(args[1:])default:fmt.Fprintf(os.Stderr, "Unknown dataset subcommand: %s\n\n", args[0])printDatasetUsage()os.Exit(1)}}func printDatasetUsage() {fmt.Fprintf(os.Stderr, "Usage: skraak dataset <subcommand> [options]\n\n")fmt.Fprintf(os.Stderr, "Subcommands:\n")fmt.Fprintf(os.Stderr, " create Create a new dataset\n")fmt.Fprintf(os.Stderr, " update Update an existing dataset\n")fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak dataset create --db ./db/skraak.duckdb --name \"Test Dataset\" --type unstructured\n")fmt.Fprintf(os.Stderr, " skraak dataset update --db ./db/skraak.duckdb --id abc123 --name \"Updated Name\"\n")}func runDatasetCreate(args []string) {fs := flag.NewFlagSet("dataset create", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")name := fs.String("name", "", "Dataset name (required)")dsType := fs.String("type", "", "Dataset type: structured, unstructured, test, train (required)")description := fs.String("description", "", "Dataset description (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak dataset create [options]\n\n")fmt.Fprintf(os.Stderr, "Create a new dataset.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak dataset create --db ./db/skraak.duckdb --name \"My Dataset\" --type unstructured\n")fmt.Fprintf(os.Stderr, " skraak dataset create --db ./db/skraak.duckdb --name \"Training Data\" --type train --description \"For ML training\"\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *name == "" {missing = append(missing, "--name")}if *dsType == "" {missing = append(missing, "--type")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.DatasetInput{Name: name,Type: dsType,Description: description,}output, err := tools.CreateOrUpdateDataset(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}func runDatasetUpdate(args []string) {fs := flag.NewFlagSet("dataset update", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")id := fs.String("id", "", "Dataset ID (required)")name := fs.String("name", "", "New dataset name (optional)")dsType := fs.String("type", "", "New dataset type: structured, unstructured, test, train (optional)")description := fs.String("description", "", "New dataset description (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak dataset update [options]\n\n")fmt.Fprintf(os.Stderr, "Update an existing dataset. Only provided fields are updated.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak dataset update --db ./db/skraak.duckdb --id abc123 --name \"New Name\"\n")fmt.Fprintf(os.Stderr, " skraak dataset update --db ./db/skraak.duckdb --id abc123 --type train\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *id == "" {missing = append(missing, "--id")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)input := tools.DatasetInput{ID: id,Name: name,Type: dsType,Description: description,}output, err := tools.CreateOrUpdateDataset(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}
package cmdimport ("context""flag""fmt""os""strconv""skraak/tools")// RunCluster handles the "cluster" subcommandfunc RunCluster(args []string) {if len(args) < 1 {printClusterUsage()os.Exit(1)}switch args[0] {case "create":runClusterCreate(args[1:])case "update":runClusterUpdate(args[1:])default:fmt.Fprintf(os.Stderr, "Unknown cluster subcommand: %s\n\n", args[0])printClusterUsage()os.Exit(1)}}func printClusterUsage() {fmt.Fprintf(os.Stderr, "Usage: skraak cluster <subcommand> [options]\n\n")fmt.Fprintf(os.Stderr, "Subcommands:\n")fmt.Fprintf(os.Stderr, " create Create a new cluster\n")fmt.Fprintf(os.Stderr, " update Update an existing cluster\n")fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak cluster create --db ./db/skraak.duckdb --dataset abc123 --location loc456 --name \"2024-01\" --sample-rate 250000\n")fmt.Fprintf(os.Stderr, " skraak cluster update --db ./db/skraak.duckdb --id cluster123 --name \"Updated Name\"\n")}func runClusterCreate(args []string) {fs := flag.NewFlagSet("cluster create", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")datasetID := fs.String("dataset", "", "Dataset ID (required)")locationID := fs.String("location", "", "Location ID (required)")name := fs.String("name", "", "Cluster name (required)")sampleRate := fs.Int("sample-rate", 0, "Sample rate in Hz (required, e.g. 250000)")path := fs.String("path", "", "Folder path (optional)")patternID := fs.String("pattern", "", "Cyclic recording pattern ID (optional)")description := fs.String("description", "", "Cluster description (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak cluster create [options]\n\n")fmt.Fprintf(os.Stderr, "Create a new cluster within a location.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak cluster create --db ./db/skraak.duckdb --dataset abc123 --location loc456 --name \"2024-01\" --sample-rate 250000\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *datasetID == "" {missing = append(missing, "--dataset")}if *locationID == "" {missing = append(missing, "--location")}if *name == "" {missing = append(missing, "--name")}if *sampleRate == 0 {missing = append(missing, "--sample-rate")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}tools.SetDBPath(*dbPath)// Build input with optional fields as nil when emptyinput := tools.ClusterInput{DatasetID: datasetID,LocationID: locationID,Name: name,SampleRate: sampleRate,}// Only set optional fields if they have valuesif *path != "" {input.Path = path}if *patternID != "" {input.CyclicRecordingPatternID = patternID}if *description != "" {input.Description = description}output, err := tools.CreateOrUpdateCluster(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}func runClusterUpdate(args []string) {fs := flag.NewFlagSet("cluster update", flag.ExitOnError)dbPath := fs.String("db", "", "Path to DuckDB database (required)")id := fs.String("id", "", "Cluster ID (required)")name := fs.String("name", "", "New cluster name (optional)")sampleRateStr := fs.String("sample-rate", "", "New sample rate in Hz (optional)")path := fs.String("path", "", "New folder path (optional)")patternID := fs.String("pattern", "", "New cyclic recording pattern ID (optional, use empty to clear)")description := fs.String("description", "", "New cluster description (optional)")fs.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: skraak cluster update [options]\n\n")fmt.Fprintf(os.Stderr, "Update an existing cluster. Only provided fields are updated.\n\n")fmt.Fprintf(os.Stderr, "Options:\n")fs.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExamples:\n")fmt.Fprintf(os.Stderr, " skraak cluster update --db ./db/skraak.duckdb --id cluster123 --name \"New Name\"\n")}if err := fs.Parse(args); err != nil {os.Exit(1)}// Validate required flagsmissing := []string{}if *dbPath == "" {missing = append(missing, "--db")}if *id == "" {missing = append(missing, "--id")}if len(missing) > 0 {fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)fs.Usage()os.Exit(1)}// Parse optional sample ratevar sampleRate *intif *sampleRateStr != "" {sr, err := strconv.Atoi(*sampleRateStr)if err != nil {fmt.Fprintf(os.Stderr, "Error: invalid sample-rate: %v\n", err)os.Exit(1)}sampleRate = &sr}tools.SetDBPath(*dbPath)// Build input - only set fields that were providedinput := tools.ClusterInput{ID: id,}if *name != "" {input.Name = name}if sampleRate != nil {input.SampleRate = sampleRate}if *path != "" {input.Path = path}if *patternID != "" {input.CyclicRecordingPatternID = patternID}if *description != "" {input.Description = description}output, err := tools.CreateOrUpdateCluster(context.Background(), input)if err != nil {fmt.Fprintf(os.Stderr, "Error: %v\n", err)os.Exit(1)}printJSON(output)}
# Bulk import from CSV
# Dataset management./skraak dataset create --db ./db/skraak.duckdb --name "My Dataset" --type unstructured./skraak dataset update --db ./db/skraak.duckdb --id abc123 --name "Updated Name"# Location management./skraak location create --db ./db/skraak.duckdb --dataset abc123 --name "Site A" --lat -36.85 --lon 174.76 --timezone Pacific/Auckland./skraak location update --db ./db/skraak.duckdb --id loc123 --name "Updated Name"# Cluster management./skraak cluster create --db ./db/skraak.duckdb --dataset abc123 --location loc456 --name "2024-01" --sample-rate 250000./skraak cluster update --db ./db/skraak.duckdb --id cluster123 --name "Updated Name"# Recording pattern management./skraak pattern create --db ./db/skraak.duckdb --record 60 --sleep 1740./skraak pattern update --db ./db/skraak.duckdb --id pattern123 --record 30# Import commands
./skraak import file --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/file.wav./skraak import folder --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/folder./skraak import selections --db ./db/skraak.duckdb --dataset abc123 --cluster clust789 --path /path/to/Clips_filter_date
- `time` - Get current time as JSON**Data Management:**- `dataset` - Manage datasets (create/update)- `location` - Manage locations (create/update)- `cluster` - Manage clusters (create/update)- `pattern` - Manage recording patterns (create/update)**Import:**
# Dataset management./skraak dataset create --db ./db/skraak.duckdb --name "My Dataset" --type unstructured./skraak dataset update --db ./db/skraak.duckdb --id abc123 --name "Updated Name"# Location management./skraak location create --db ./db/skraak.duckdb --dataset abc123 --name "Site A" --lat -36.85 --lon 174.76 --timezone Pacific/Auckland./skraak location update --db ./db/skraak.duckdb --id loc123 --name "Updated Name"
# Bulk import from CSV
# Cluster management./skraak cluster create --db ./db/skraak.duckdb --dataset abc123 --location loc456 --name "2024-01" --sample-rate 250000./skraak cluster update --db ./db/skraak.duckdb --id cluster123 --name "Updated Name"# Recording pattern management./skraak pattern create --db ./db/skraak.duckdb --record 60 --sleep 1740./skraak pattern update --db ./db/skraak.duckdb --id pattern123 --record 30# Import commands
./skraak import file --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/file.wav./skraak import folder --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --path /path/to/folder./skraak import selections --db ./db/skraak.duckdb --dataset abc123 --cluster clust789 --path /path/to/Clips_filter_date