OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
master @ 133 LINES
 
[ HISTORY ]  [ UP ]
 

package cmd

import (
	"fmt"

	"github.com/charmbracelet/log"
	"github.com/spf13/cobra"
	"ookstats/internal/database"
	"ookstats/internal/merge"
)

var mergeCmd = &cobra.Command{
	Use:   "merge",
	Short: "Merge duplicate player records",
	Long:  `Merge duplicate player records using a configuration file that maps old i
dentities to new ones.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		configPath, _ := cmd.Flags().GetString("config")
		if configPath == "" {
			return fmt.Errorf("--config flag is required")
		}

		dryRun, _ := cmd.Flags().GetBool("dry-run")

		log.Info("player merge tool", "config", configPath, "dry_run", dryRun)

		// Load config
		config, err := merge.LoadConfig(configPath)
		if err != nil {
			return fmt.Errorf("failed to load config: %w", err)
		}

		log.Info("loaded merge configuration", "entries", len(config.Merges))

		// Connect to database
		db, err := database.Connect()
		if err != nil {
			return fmt.Errorf("failed to connect to database: %w", err)
		}
		defer db.Close()

		dbService := database.NewDatabaseService(db)

		totalMerged := 0
		totalRuns := 0

		for i, entry := range config.Merges {
			log.Info("processing merge entry", "index", i+1, "total", len(config.Merges))

			// Look up "from" player
			fromID, err := dbService.GetPlayerByNameRealmRegion(
				entry.From.Name,
				entry.From.Realm,
				entry.From.Region,
			)
			if err != nil {
				log.Error("failed to find source player", "error", err, "entry", i+1)
				continue
			}

			// Look up "to" player
			toID, err := dbService.GetPlayerByNameRealmRegion(
				entry.To.Name,
				entry.To.Realm,
				entry.To.Region,
			)
			if err != nil {
				log.Error("failed to find target player", "error", err, "entry", i+1)
				continue
			}

			if fromID == toID {
				log.Warn("source and target are the same player, skipping", "player_id", fromI
D)
				continue
			}

			log.Info("merge plan",
				"from_player", fmt.Sprintf("%s-%s (%s)", entry.From.Name, entry.From.Realm, en
try.From.Region),
				"from_id", fromID,
				"to_player", fmt.Sprintf("%s-%s (%s)", entry.To.Name, entry.To.Realm, entry.To
.Region),
				"to_id", toID)

			if dryRun {
				log.Info("dry-run mode: skipping actual merge")
				continue
			}

			// Migrate runs
			runsMigrated, err := dbService.MigratePlayerRuns(fromID, toID)
			if err != nil {
				log.Error("failed to migrate runs", "error", err, "from", fromID, "to", toID)
				continue
			}

			// Invalidate target player's profile for rebuild
			if err := dbService.InvalidatePlayerProfile(toID); err != nil {
				log.Warn("failed to invalidate profile", "player_id", toID, "error", err)
			}

			// Mark source player as invalid
			if err := dbService.UpdatePlayerStatus(fromID, false, 0, nil); err != nil {
				log.Error("failed to mark source player invalid", "player_id", fromID, "error"
, err)
				continue
			}

			log.Info("merge complete",
				"from_id", fromID,
				"to_id", toID,
				"runs_migrated", runsMigrated)

			totalMerged++
			totalRuns += runsMigrated
		}

		log.Info("merge operation complete",
			"total_entries", len(config.Merges),
			"successful_merges", totalMerged,
			"total_runs_migrated", totalRuns)

		if !dryRun && totalMerged > 0 {
			log.Info("next step: run 'ookstats build profiles' to rebuild player rankings")
		}

		return nil
	},
}

func init() {
	rootCmd.AddCommand(mergeCmd)
	mergeCmd.Flags().String("config", "", "Path to merge configuration JSON file (req
uired)")
	mergeCmd.Flags().Bool("dry-run", false, "Show what would be merged without execut
ing")
	mergeCmd.MarkFlagRequired("config")
}

package cmd

import (
	"fmt"

	"github.com/charmbracelet/log"
	"github.com/spf13/cobra"
	"ookstats/internal/database"
	"ookstats/internal/merge"
)

var mergeCmd = &cobra.Command{
	Use:   "merge",
	Short: "Merge duplicate player records",
	Long:  `Merge duplicate player records usin
g a configuration file that maps old identit
ies to new ones.`,
	RunE: func(cmd *cobra.Command, args []strin
g) error {
		configPath, _ := cmd.Flags().GetString("co
nfig")
		if configPath == "" {
			return fmt.Errorf("--config flag is requi
red")
		}

		dryRun, _ := cmd.Flags().GetBool("dry-run"
)

		log.Info("player merge tool", "config", co
nfigPath, "dry_run", dryRun)

		// Load config
		config, err := merge.LoadConfig(configPath
)
		if err != nil {
			return fmt.Errorf("failed to load config:
 %w", err)
		}

		log.Info("loaded merge configuration", "en
tries", len(config.Merges))

		// Connect to database
		db, err := database.Connect()
		if err != nil {
			return fmt.Errorf("failed to connect to d
atabase: %w", err)
		}
		defer db.Close()

		dbService := database.NewDatabaseService(d
b)

		totalMerged := 0
		totalRuns := 0

		for i, entry := range config.Merges {
			log.Info("processing merge entry", "index
", i+1, "total", len(config.Merges))

			// Look up "from" player
			fromID, err := dbService.GetPlayerByNameR
ealmRegion(
				entry.From.Name,
				entry.From.Realm,
				entry.From.Region,
			)
			if err != nil {
				log.Error("failed to find source player"
, "error", err, "entry", i+1)
				continue
			}

			// Look up "to" player
			toID, err := dbService.GetPlayerByNameRea
lmRegion(
				entry.To.Name,
				entry.To.Realm,
				entry.To.Region,
			)
			if err != nil {
				log.Error("failed to find target player"
, "error", err, "entry", i+1)
				continue
			}

			if fromID == toID {
				log.Warn("source and target are the same
 player, skipping", "player_id", fromID)
				continue
			}

			log.Info("merge plan",
				"from_player", fmt.Sprintf("%s-%s (%s)",
 entry.From.Name, entry.From.Realm, entry.Fr
om.Region),
				"from_id", fromID,
				"to_player", fmt.Sprintf("%s-%s (%s)", e
ntry.To.Name, entry.To.Realm, entry.To.Regio
n),
				"to_id", toID)

			if dryRun {
				log.Info("dry-run mode: skipping actual 
merge")
				continue
			}

			// Migrate runs
			runsMigrated, err := dbService.MigratePla
yerRuns(fromID, toID)
			if err != nil {
				log.Error("failed to migrate runs", "err
or", err, "from", fromID, "to", toID)
				continue
			}

			// Invalidate target player's profile for
 rebuild
			if err := dbService.InvalidatePlayerProfi
le(toID); err != nil {
				log.Warn("failed to invalidate profile",
 "player_id", toID, "error", err)
			}

			// Mark source player as invalid
			if err := dbService.UpdatePlayerStatus(fr
omID, false, 0, nil); err != nil {
				log.Error("failed to mark source player 
invalid", "player_id", fromID, "error", err)
				continue
			}

			log.Info("merge complete",
				"from_id", fromID,
				"to_id", toID,
				"runs_migrated", runsMigrated)

			totalMerged++
			totalRuns += runsMigrated
		}

		log.Info("merge operation complete",
			"total_entries", len(config.Merges),
			"successful_merges", totalMerged,
			"total_runs_migrated", totalRuns)

		if !dryRun && totalMerged > 0 {
			log.Info("next step: run 'ookstats build 
profiles' to rebuild player rankings")
		}

		return nil
	},
}

func init() {
	rootCmd.AddCommand(mergeCmd)
	mergeCmd.Flags().String("config", "", "Path
 to merge configuration JSON file (required)
")
	mergeCmd.Flags().Bool("dry-run", false, "Sh
ow what would be merged without executing")
	mergeCmd.MarkFlagRequired("config")
}
 
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET