move towards auto-import
This commit is contained in:
		
							
								
								
									
										87
									
								
								Stars Assistant/CSV/PlanetLoader.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								Stars Assistant/CSV/PlanetLoader.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using CsvHelper.Configuration;
 | 
			
		||||
using CsvHelper;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using Splat;
 | 
			
		||||
 | 
			
		||||
namespace StarsAssistant.CSV;
 | 
			
		||||
 | 
			
		||||
public class PlanetLoader
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Reference to the game metadata, retrieved by DI
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected Services.Game Game = Locator.Current.GetService<Services.Game>()!;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// CSV Configuration to use for importing Planet files.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected CsvConfiguration CsvConfig;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Construction
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public PlanetLoader()
 | 
			
		||||
    {
 | 
			
		||||
        CreateConfig();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates the CSV configuration for planets.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MemberNotNull(nameof(CsvConfig))]
 | 
			
		||||
    protected void CreateConfig() 
 | 
			
		||||
    {
 | 
			
		||||
        CsvConfig = CsvConfiguration.FromAttributes<Planet>(CultureInfo.InvariantCulture);
 | 
			
		||||
        CsvConfig.Delimiter = "\t";
 | 
			
		||||
        CsvConfig.Escape = '\0';
 | 
			
		||||
        CsvConfig.MissingFieldFound = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Import the planet file for the given Race.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="race">Import File</param>
 | 
			
		||||
    public void ImportForRace(Model.Race race)
 | 
			
		||||
    {
 | 
			
		||||
        using (var db = Locator.Current.GetService<Model.StarsDatabase>()!)
 | 
			
		||||
        using (var reader = new StreamReader(Game.PlanetFileForRace(race), System.Text.Encoding.Latin1))
 | 
			
		||||
        using (var csv = new CsvReader(reader, CsvConfig))
 | 
			
		||||
        {
 | 
			
		||||
            List<CSV.Planet> records = csv.GetRecords<Planet>().ToList();
 | 
			
		||||
            var planetByPlayer = from csvp in records
 | 
			
		||||
                group csvp by csvp.Owner into byOwners
 | 
			
		||||
                select byOwners;
 | 
			
		||||
 | 
			
		||||
            foreach (var owner in planetByPlayer)
 | 
			
		||||
            {
 | 
			
		||||
                Model.Race? r;
 | 
			
		||||
                if (owner.Key == race.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    r = race;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    r = db.Race.Find(owner.Key);
 | 
			
		||||
                    if (r == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        r = new()
 | 
			
		||||
                        {
 | 
			
		||||
                            Name = owner.Key
 | 
			
		||||
                        };
 | 
			
		||||
                        db.Add(r);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach (Planet csvp in owner)
 | 
			
		||||
                {
 | 
			
		||||
                    Model.Planet p = new Model.Planet{ Name = csvp.Name };
 | 
			
		||||
                    csvp.UpdateDbPlanet(p);
 | 
			
		||||
                    db.Add(p);
 | 
			
		||||
                }
 | 
			
		||||
                db.SaveChanges();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -35,4 +35,23 @@ public static class RxExtensions
 | 
			
		||||
        return src.Buffer(zeroCrossings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Throttle a FileSystemObserver and eliminate all duplicates.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <param name="watcher">An observer created with <c>ObserveFileSystem</c></param>
 | 
			
		||||
    /// <param name="inactivitySeconds">Throttling window, defaults to 1 second.</param>
 | 
			
		||||
    /// <param name="scheduler">Scheduler to user for throttling, default is set by <c>Quiescent</c></param>
 | 
			
		||||
    /// <returns>A throttled observer with duplicate elimination.</returns>
 | 
			
		||||
    public static IObservable<IEnumerable<FileSystemEventArgs>> ThrottleAndDistinct (
 | 
			
		||||
        this IObservable<FileSystemEventArgs> watcher, 
 | 
			
		||||
        int inactivitySeconds = 1,
 | 
			
		||||
        IScheduler? scheduler = null)
 | 
			
		||||
    {
 | 
			
		||||
        return watcher
 | 
			
		||||
                .Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler)
 | 
			
		||||
                .Select(changes => changes.DistinctBy(x => (x.ChangeType, x.FullPath)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
 | 
			
		||||
namespace StarsAssistant.Model;
 | 
			
		||||
 | 
			
		||||
public class Game (string gamePath, string baseName)
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The base path in which all game files reside.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string GamePath { get; private set; } = gamePath;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The base name without extensions of your game, inside the GamePath folder.
 | 
			
		||||
    /// All dependant files are resolved using this name.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string BaseName { get; private set; } = baseName;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The number of the player, for example identifying the pxx planet file.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int PlayerId { get; private set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Combine into the DatabaseName
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string DatabaseName => Path.Combine(GamePath, $"{BaseName}.sqlite");
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Search for Planet files using this pattern, will give you all pxx files.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string PlanetFileSearchPattern => Path.Combine(GamePath, $"{BaseName}.p*");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,8 @@ public class Race
 | 
			
		||||
 | 
			
		||||
    public bool PlayerRace { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    public int? PlayerFileId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int? ColonistsPerResource { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int? GrowthRatePercent { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ public class StarsDatabase(string DbPath) : DbContext
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The record with all game metadata, will only contain a single line at all times.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DbSet<Game> Game { get; set; }
 | 
			
		||||
    // public DbSet<Game> Game { get; set; }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
}
 | 
			
		||||
@@ -21,11 +21,16 @@ sealed class Program
 | 
			
		||||
    public static void Main(string[] args) 
 | 
			
		||||
    {
 | 
			
		||||
        ModeDetector.OverrideModeDetector(Splat.ModeDetection.Mode.Run);
 | 
			
		||||
        
 | 
			
		||||
        Locator.CurrentMutable.Register(
 | 
			
		||||
            () => new StarsDatabase("stars.sqlite"),
 | 
			
		||||
            typeof(StarsDatabase)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Services.Game g = new()
 | 
			
		||||
        {
 | 
			
		||||
            BaseName = "GOINGTH",
 | 
			
		||||
            GamePath = "/home/torben/goingth/"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Locator.CurrentMutable.RegisterConstant(g, typeof(Services.Game));
 | 
			
		||||
        Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader));
 | 
			
		||||
        Locator.CurrentMutable.Register(() => new StarsDatabase(g.DatabaseFileName), typeof(StarsDatabase));
 | 
			
		||||
 | 
			
		||||
        __createTestData();
 | 
			
		||||
        
 | 
			
		||||
@@ -44,6 +49,7 @@ sealed class Program
 | 
			
		||||
 | 
			
		||||
    public static void __createTestData ()
 | 
			
		||||
    {
 | 
			
		||||
        Services.Game game = Locator.Current.GetService<Services.Game>()!;
 | 
			
		||||
        using var db = Locator.Current.GetService<StarsDatabase>()!;
 | 
			
		||||
 | 
			
		||||
        db.Database.EnsureDeleted();
 | 
			
		||||
@@ -56,6 +62,7 @@ sealed class Program
 | 
			
		||||
        {
 | 
			
		||||
            Name = "Atlantis",
 | 
			
		||||
            PlayerRace = true,
 | 
			
		||||
            PlayerFileId = 1,
 | 
			
		||||
            ColonistsPerResource = 1000,
 | 
			
		||||
            GrowthRatePercent = 19,
 | 
			
		||||
            PRT = PRT.Other,
 | 
			
		||||
@@ -72,40 +79,8 @@ sealed class Program
 | 
			
		||||
        db.SaveChanges();
 | 
			
		||||
        Race.Player = r;
 | 
			
		||||
 | 
			
		||||
        var config = CsvConfiguration.FromAttributes<Planet>(CultureInfo.InvariantCulture);
 | 
			
		||||
        config.Delimiter = "\t";
 | 
			
		||||
        config.Escape = '\0';
 | 
			
		||||
        config.MissingFieldFound = null;
 | 
			
		||||
 | 
			
		||||
        using (var reader = new StreamReader("/home/torben/goingth/GOINGTH.p1", System.Text.Encoding.Latin1))
 | 
			
		||||
        using (var csv = new CsvReader(reader, config))
 | 
			
		||||
        {
 | 
			
		||||
            List<CSV.Planet> records = csv.GetRecords<CSV.Planet>().ToList();
 | 
			
		||||
            var planetByPlayer = from csvp in records
 | 
			
		||||
                group csvp by csvp.Owner into byOwners
 | 
			
		||||
                select byOwners;
 | 
			
		||||
 | 
			
		||||
            foreach (var owner in planetByPlayer)
 | 
			
		||||
            {
 | 
			
		||||
                if (owner.Key != "Atlantis")
 | 
			
		||||
                {
 | 
			
		||||
                    r = new()
 | 
			
		||||
                    {
 | 
			
		||||
                        Name = owner.Key
 | 
			
		||||
                    };
 | 
			
		||||
                    db.Add(r);
 | 
			
		||||
                    db.SaveChanges();  
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach (CSV.Planet csvp in owner)
 | 
			
		||||
                {
 | 
			
		||||
                    Planet p = new Planet{ Name = csvp.Name };
 | 
			
		||||
                    csvp.UpdateDbPlanet(p);
 | 
			
		||||
                    db.Add(p);
 | 
			
		||||
                }
 | 
			
		||||
                db.SaveChanges();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        var loader = new CSV.PlanetLoader();
 | 
			
		||||
        loader.ImportForRace(Race.Player);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,41 @@
 | 
			
		||||
using System;
 | 
			
		||||
using CsvHelper.Configuration;
 | 
			
		||||
using CsvHelper;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Reactive.Linq;
 | 
			
		||||
using Splat;
 | 
			
		||||
using StarsAssistant.Helpers;
 | 
			
		||||
 | 
			
		||||
namespace StarsAssistant.Services;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class CSVDataLoader
 | 
			
		||||
public class CSVDataLoader 
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Reference to the game metadata, retrieved by DI
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected Model.Game game;
 | 
			
		||||
    protected Services.Game Game = Locator.Current.GetService<Services.Game>()!;
 | 
			
		||||
 | 
			
		||||
    public CSVDataLoader()
 | 
			
		||||
    public void CSVDataLoader() 
 | 
			
		||||
    {
 | 
			
		||||
        game = Locator.Current.GetService<Model.Game>()!;
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public void StartPlanetCSVWatcher() 
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: which scheduler for Throttle?
 | 
			
		||||
        var watcher = FsWatcher
 | 
			
		||||
            .ObserveFileSystem(Game.GamePath, new string[] { Game.PlanetFileSearchPattern });
 | 
			
		||||
            .ThrottleAndDistinct(2);
 | 
			
		||||
        
 | 
			
		||||
        /*
 | 
			
		||||
        watcher.Subscribe(x => 
 | 
			
		||||
        {
 | 
			
		||||
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()} got {x.Count()} events:");
 | 
			
		||||
 | 
			
		||||
            foreach (var fsEvent in x)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine($"{DateTime.Now.ToLongTimeString()} {i++} {fsEvent.FullPath} - {fsEvent.ChangeType}");
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        */
 | 
			
		||||
    }
 | 
			
		||||
}   
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								Stars Assistant/Services/Game.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Stars Assistant/Services/Game.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
namespace StarsAssistant.Services;
 | 
			
		||||
 | 
			
		||||
public class Game
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The base path in which all game files reside.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public required string GamePath { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The base name without extensions of your game, inside the GamePath folder.
 | 
			
		||||
    /// All dependant files are resolved using this name.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public required string BaseName { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Combine into the DatabaseName
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string DatabaseFileName => Path.Combine(GamePath, $"{BaseName}.sqlite");
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Search for Planet files using this pattern, will give you all pxx files.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string PlanetFileSearchPattern => $"{BaseName}.p*";
 | 
			
		||||
    
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get the name of a planet file for a given race.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="r">The race to load.</param>
 | 
			
		||||
    /// <returns>Fully qualified file path.</returns>
 | 
			
		||||
    public string PlanetFileForRace (Model.Race r) => Path.Combine(GamePath, $"{BaseName}.p{r.PlayerFileId}");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user