From 23ed04f2a401ccb62cbe44fc5c7f920d7e15e848 Mon Sep 17 00:00:00 2001 From: Vincent <30174292+VibeNL@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:50:05 +0100 Subject: [PATCH 1/4] Update Sync logic --- .../Services/SyncGrpcService.cs | 130 +++++++++--------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs b/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs index f07c38b5d..c9a5517c5 100644 --- a/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs +++ b/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs @@ -9,70 +9,7 @@ namespace GhostfolioSidekick.PortfolioViewer.ApiService.Services public class SyncGrpcService(DatabaseContext dbContext, ILogger logger) : SyncService.SyncServiceBase { private readonly string[] _tablesToIgnore = ["sqlite_sequence", "__EFMigrationsHistory", "__EFMigrationsLock"]; - private readonly Dictionary _tablesWithDates = []; - private bool _tablesWithDatesInitialized; - - private async Task> GetTablesWithDatesAsync() - { - if (_tablesWithDatesInitialized) return _tablesWithDates; - - using var connection = dbContext.Database.GetDbConnection(); - await connection.OpenAsync(); - - var tableNames = new List(); - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table'"; - using var reader = await cmd.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - var name = reader.GetString(0); - if (!_tablesToIgnore.Contains(name)) tableNames.Add(name); - } - } - - foreach (var tableName in tableNames) - { - try - { - using var cmd = connection.CreateCommand(); - cmd.CommandText = $"PRAGMA table_info({tableName})"; - using var reader = await cmd.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - var columnName = reader.GetString(1); - var columnType = reader.GetString(2); - if (IsDateColumn(columnName, columnType)) - { - _tablesWithDates[tableName] = columnName; - logger.LogDebug("Found date column {ColumnName} in table {TableName}", columnName, tableName); - break; - } - } - } - catch (Exception ex) - { - logger.LogWarning(ex, "Failed to analyze columns for table {TableName}", tableName); - } - } - - _tablesWithDatesInitialized = true; - logger.LogInformation("Discovered {Count} tables with date columns: {Tables}", - _tablesWithDates.Count, string.Join(", ", _tablesWithDates.Select(kvp => $"{kvp.Key}({kvp.Value})"))); - - return _tablesWithDates; - } - - private static bool IsDateColumn(string columnName, string columnType) => - columnName.ToLower() switch - { - var name when name == "date" || name.EndsWith("date") || name.StartsWith("date") || name.Contains("timestamp") => true, - _ => columnType.ToLower() switch - { - var type when type.Contains("date") || type.Contains("datetime") || type.Contains("timestamp") => true, - _ => false - } - }; + private readonly Lazy>> _tablesWithDatesCache = new(() => LoadTablesWithDatesAsync(dbContext, logger)); public override async Task GetTableNames(GetTableNamesRequest request, ServerCallContext context) { @@ -171,6 +108,71 @@ public override async Task GetLatestDates(GetLatestDates } } + private async Task> GetTablesWithDatesAsync() => + await _tablesWithDatesCache.Value; + + private static async Task> LoadTablesWithDatesAsync(DatabaseContext dbContext, ILogger logger) + { + var tablesWithDates = new Dictionary(); + var tablesToIgnore = new[] { "sqlite_sequence", "__EFMigrationsHistory", "__EFMigrationsLock" }; + + using var connection = dbContext.Database.GetDbConnection(); + await connection.OpenAsync(); + + var tableNames = new List(); + using (var cmd = connection.CreateCommand()) + { + cmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table'"; + using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var name = reader.GetString(0); + if (!tablesToIgnore.Contains(name)) tableNames.Add(name); + } + } + + foreach (var tableName in tableNames) + { + try + { + using var cmd = connection.CreateCommand(); + cmd.CommandText = $"PRAGMA table_info({tableName})"; + using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var columnName = reader.GetString(1); + var columnType = reader.GetString(2); + if (IsDateColumn(columnName, columnType)) + { + tablesWithDates[tableName] = columnName; + logger.LogDebug("Found date column {ColumnName} in table {TableName}", columnName, tableName); + break; + } + } + } + catch (Exception ex) + { + logger.LogWarning(ex, "Failed to analyze columns for table {TableName}", tableName); + } + } + + logger.LogInformation("Discovered {Count} tables with date columns: {Tables}", + tablesWithDates.Count, string.Join(", ", tablesWithDates.Select(kvp => $"{kvp.Key}({kvp.Value})"))); + + return tablesWithDates; + } + + private static bool IsDateColumn(string columnName, string columnType) => + columnName.ToLower() switch + { + var name when name == "date" || name.EndsWith("date") || name.StartsWith("date") || name.Contains("timestamp") => true, + _ => columnType.ToLower() switch + { + var type when type.Contains("date") || type.Contains("datetime") || type.Contains("timestamp") => true, + _ => false + } + }; + private async Task GetEntityDataInternal(string entity, int page, int pageSize, string? sinceDate, IServerStreamWriter responseStream) { if (page <= 0 || pageSize <= 0) From fe7e0b4d6c9441e718b96d28164139d0f1cedf3c Mon Sep 17 00:00:00 2001 From: VibeNL <30174292+VibeNL@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:52:54 +0100 Subject: [PATCH 2/4] Update PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../PortfolioViewer.ApiService/Services/SyncGrpcService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs b/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs index c9a5517c5..42473e94a 100644 --- a/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs +++ b/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs @@ -9,8 +9,12 @@ namespace GhostfolioSidekick.PortfolioViewer.ApiService.Services public class SyncGrpcService(DatabaseContext dbContext, ILogger logger) : SyncService.SyncServiceBase { private readonly string[] _tablesToIgnore = ["sqlite_sequence", "__EFMigrationsHistory", "__EFMigrationsLock"]; - private readonly Lazy>> _tablesWithDatesCache = new(() => LoadTablesWithDatesAsync(dbContext, logger)); + private readonly Lazy>> _tablesWithDatesCache; + public SyncGrpcService + { + _tablesWithDatesCache = new(() => LoadTablesWithDatesAsync(dbContext, logger)); + } public override async Task GetTableNames(GetTableNamesRequest request, ServerCallContext context) { try From bdbb2f29d5ac771d62779692fc570e249da9db47 Mon Sep 17 00:00:00 2001 From: Vincent <30174292+VibeNL@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:56:55 +0100 Subject: [PATCH 3/4] 1 --- .../Services/SyncGrpcService.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs b/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs index 42473e94a..f9c2cefb4 100644 --- a/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs +++ b/PortfolioViewer/PortfolioViewer.ApiService/Services/SyncGrpcService.cs @@ -9,12 +9,11 @@ namespace GhostfolioSidekick.PortfolioViewer.ApiService.Services public class SyncGrpcService(DatabaseContext dbContext, ILogger logger) : SyncService.SyncServiceBase { private readonly string[] _tablesToIgnore = ["sqlite_sequence", "__EFMigrationsHistory", "__EFMigrationsLock"]; - private readonly Lazy>> _tablesWithDatesCache; + private Lazy>>? _tablesWithDatesCache; + + private Lazy>> TablesWithDatesCache => + _tablesWithDatesCache ??= new(() => LoadTablesWithDatesAsync(dbContext, logger, _tablesToIgnore)); - public SyncGrpcService - { - _tablesWithDatesCache = new(() => LoadTablesWithDatesAsync(dbContext, logger)); - } public override async Task GetTableNames(GetTableNamesRequest request, ServerCallContext context) { try @@ -113,15 +112,14 @@ public override async Task GetLatestDates(GetLatestDates } private async Task> GetTablesWithDatesAsync() => - await _tablesWithDatesCache.Value; + await TablesWithDatesCache.Value; - private static async Task> LoadTablesWithDatesAsync(DatabaseContext dbContext, ILogger logger) - { - var tablesWithDates = new Dictionary(); - var tablesToIgnore = new[] { "sqlite_sequence", "__EFMigrationsHistory", "__EFMigrationsLock" }; + private static async Task> LoadTablesWithDatesAsync(DatabaseContext dbContext, ILogger logger, string[] tablesToIgnore) + { + var tablesWithDates = new Dictionary(); - using var connection = dbContext.Database.GetDbConnection(); - await connection.OpenAsync(); + using var connection = dbContext.Database.GetDbConnection(); + await connection.OpenAsync(); var tableNames = new List(); using (var cmd = connection.CreateCommand()) From 8479419b6b6d0d476b5538154e55d59197f14583 Mon Sep 17 00:00:00 2001 From: Vincent <30174292+VibeNL@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:12:11 +0100 Subject: [PATCH 4/4] change order Upcoming Dividends --- .../PortfolioViewer.WASM/Pages/UpcomingDividends.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PortfolioViewer/PortfolioViewer.WASM/Pages/UpcomingDividends.razor b/PortfolioViewer/PortfolioViewer.WASM/Pages/UpcomingDividends.razor index 50a40f17d..9fe1794d4 100644 --- a/PortfolioViewer/PortfolioViewer.WASM/Pages/UpcomingDividends.razor +++ b/PortfolioViewer/PortfolioViewer.WASM/Pages/UpcomingDividends.razor @@ -48,7 +48,7 @@ - @foreach (var div in dividends.OrderBy(d => d.ExDate)) + @foreach (var div in dividends.OrderBy(d => d.PaymentDate)) { @div.Symbol @@ -65,7 +65,7 @@ } else { - + — } @div.Quantity.ToString("N2")