From aa15a51cd32b3497765ea35fb1f53a83f856487b Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:40:59 -0300 Subject: [PATCH 01/93] Add Project and solution --- .../CodingTracker.Kunikazu723.csproj | 10 ++++++++ .../CodingTracker.Kunikazu723.sln | 25 +++++++++++++++++++ CodingTracker.Kunikazu723/Program.cs | 10 ++++++++ 3 files changed, 45 insertions(+) create mode 100644 CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj create mode 100644 CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln create mode 100644 CodingTracker.Kunikazu723/Program.cs diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj new file mode 100644 index 000000000..fd4bd08da --- /dev/null +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -0,0 +1,10 @@ + + + + Exe + net9.0 + enable + enable + + + diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln new file mode 100644 index 000000000..0f4f29c6d --- /dev/null +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.37531.7 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.Kunikazu723", "CodingTracker.Kunikazu723.csproj", "{97EA1958-EE4A-4B55-AAB7-9E57F0A17958}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {178835FB-F730-48E9-ACB5-35AB7EE86672} + EndGlobalSection +EndGlobal diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs new file mode 100644 index 000000000..322545bb5 --- /dev/null +++ b/CodingTracker.Kunikazu723/Program.cs @@ -0,0 +1,10 @@ +namespace CodingTracker.Kunikazu723 +{ + internal class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } + } +} From 98e8169d00abf289f22f5ec6672ad5e7bee776c6 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:55:39 -0300 Subject: [PATCH 02/93] Create configuration file Create and use the Configuration package provided by Microsoft to handle configuration properties effectively (I don't really know why I should not use a simple JSON desserializer) --- .../CodingTracker.Kunikazu723.csproj | 12 ++++++++++++ CodingTracker.Kunikazu723/Program.cs | 10 ++++++++-- .../Properties/launchSettings.json | 8 ++++++++ CodingTracker.Kunikazu723/appsettings.json | 6 ++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 CodingTracker.Kunikazu723/Properties/launchSettings.json create mode 100644 CodingTracker.Kunikazu723/appsettings.json diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index fd4bd08da..cf7efdb45 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -7,4 +7,16 @@ enable + + + + + + + + + PreserveNewest + + + diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 322545bb5..b4641e5a7 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -1,10 +1,16 @@ -namespace CodingTracker.Kunikazu723 +using Microsoft.Extensions.Configuration; +namespace CodingTracker.Kunikazu723 { internal class Program { static void Main(string[] args) { - Console.WriteLine("Hello, World!"); + var configuration = new ConfigurationBuilder() + .SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json") + .Build(); + + } } } diff --git a/CodingTracker.Kunikazu723/Properties/launchSettings.json b/CodingTracker.Kunikazu723/Properties/launchSettings.json new file mode 100644 index 000000000..5c5dd003b --- /dev/null +++ b/CodingTracker.Kunikazu723/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "CodingTracker.Kunikazu723": { + "commandName": "Project", + "workingDirectory": "E:\\csharplearning\\CsharpAcademy\\CodeReviews.Console.CodingTracker\\CodingTracker.Kunikazu723" + } + } +} \ No newline at end of file diff --git a/CodingTracker.Kunikazu723/appsettings.json b/CodingTracker.Kunikazu723/appsettings.json new file mode 100644 index 000000000..e4e4f1327 --- /dev/null +++ b/CodingTracker.Kunikazu723/appsettings.json @@ -0,0 +1,6 @@ +{ + "DatabasePath": "coding-tracker.db", + "ConnectionStrings": { + "DefaultConnection": "Data Source=coding-tracker.db" + } +} \ No newline at end of file From 98bea93562474920af19f9bd4ac269c506102df6 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 17:05:39 -0300 Subject: [PATCH 03/93] Add addsettings to output directory --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index cf7efdb45..18ea580e5 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -12,11 +12,10 @@ - - - PreserveNewest - + + Always + From 532bcf838a82fc692acae23fa0d4093d26cc360a Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 19:52:57 -0300 Subject: [PATCH 04/93] Add format specifier to configuration file --- CodingTracker.Kunikazu723/Program.cs | 2 +- CodingTracker.Kunikazu723/appsettings.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index b4641e5a7..83de676f2 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -9,8 +9,8 @@ static void Main(string[] args) .SetBasePath(AppContext.BaseDirectory) .AddJsonFile("appsettings.json") .Build(); - + Console.WriteLine($"Date Format = {configuration["DateFormat"]}"); } } } diff --git a/CodingTracker.Kunikazu723/appsettings.json b/CodingTracker.Kunikazu723/appsettings.json index e4e4f1327..4ed2a60ae 100644 --- a/CodingTracker.Kunikazu723/appsettings.json +++ b/CodingTracker.Kunikazu723/appsettings.json @@ -1,6 +1,7 @@ { "DatabasePath": "coding-tracker.db", "ConnectionStrings": { - "DefaultConnection": "Data Source=coding-tracker.db" - } + "DefaultConnection": "Data Source=coding-tracker.db" + }, + "DateFormat": "dd-MM-yyyy" } \ No newline at end of file From 9c8f4f81fbb3919b4865fe2a81def72bc9b0a749 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:15:33 -0300 Subject: [PATCH 05/93] Add variables based on appsettings.json to Main --- CodingTracker.Kunikazu723/Program.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 83de676f2..1f7f7118c 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -7,10 +7,19 @@ static void Main(string[] args) { var configuration = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) - .AddJsonFile("appsettings.json") + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); - Console.WriteLine($"Date Format = {configuration["DateFormat"]}"); + string databasePath = configuration["DatabasePath"] + ?? throw new InvalidOperationException("DatabasePath is missing from appsettings.json"); + + string connectionString = configuration.GetConnectionString("DefaultConnection") + ?? throw new InvalidOperationException("Connection String not found in appsettings.json"); + + string dateTimeFormat = configuration["DateTimeFormat"] + ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); + + } } } From c81a61772290852f59f7afb65134002bc007d863 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:15:57 -0300 Subject: [PATCH 06/93] Add Dapper and SQLite provider packages --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index 18ea580e5..efc64aada 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -8,8 +8,10 @@ + + From e1ee56970bfc48a49feabb251c3d3a10ce66a07e Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:16:18 -0300 Subject: [PATCH 07/93] Add more specific DateTime Format values to appsettings.json --- CodingTracker.Kunikazu723/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/appsettings.json b/CodingTracker.Kunikazu723/appsettings.json index 4ed2a60ae..71ef1ae77 100644 --- a/CodingTracker.Kunikazu723/appsettings.json +++ b/CodingTracker.Kunikazu723/appsettings.json @@ -3,5 +3,5 @@ "ConnectionStrings": { "DefaultConnection": "Data Source=coding-tracker.db" }, - "DateFormat": "dd-MM-yyyy" + "DateTimeFormat": "dd-MM-yyyy-HH-mm-ss" } \ No newline at end of file From ff1d15a5b1c61e2541e94f4da985bee399585757 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:16:45 -0300 Subject: [PATCH 08/93] Add basic CodingSession model to map Dapper's queries --- .../Models/CodingSession.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 CodingTracker.Kunikazu723/Models/CodingSession.cs diff --git a/CodingTracker.Kunikazu723/Models/CodingSession.cs b/CodingTracker.Kunikazu723/Models/CodingSession.cs new file mode 100644 index 000000000..894f0f211 --- /dev/null +++ b/CodingTracker.Kunikazu723/Models/CodingSession.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Models +{ + public class CodingSession + { + //Id, StartTime, EndTime, Duration + public int Id { get; set; } + public string StartTime { get; set; } = string.Empty; + public string EndTime { get; set; } = string.Empty; + public float Duration { get; set; } + + } +} From 366e7fd1d922e17c5379dfcdd08449cb9d1155ed Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:19:15 -0300 Subject: [PATCH 09/93] Add Spectre Console Package to project --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index efc64aada..6ba8e1bd8 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -11,6 +11,7 @@ + From 45091ee2f2a4bae2b94b222cddd465d830b03296 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:19:35 -0300 Subject: [PATCH 10/93] Remove blank lines --- CodingTracker.Kunikazu723/Models/CodingSession.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CodingTracker.Kunikazu723/Models/CodingSession.cs b/CodingTracker.Kunikazu723/Models/CodingSession.cs index 894f0f211..cbf000202 100644 --- a/CodingTracker.Kunikazu723/Models/CodingSession.cs +++ b/CodingTracker.Kunikazu723/Models/CodingSession.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CodingTracker.Kunikazu723.Models +namespace CodingTracker.Kunikazu723.Models { public class CodingSession { @@ -13,6 +7,5 @@ public class CodingSession public string StartTime { get; set; } = string.Empty; public string EndTime { get; set; } = string.Empty; public float Duration { get; set; } - } } From 8ec1bbe9fe75036cd35943bce087642fb1d908e6 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:26:52 -0300 Subject: [PATCH 11/93] Add user input handling --- CodingTracker.Kunikazu723/Enums/MenuEnums.cs | 24 + .../FigletFonts/isometric3.flf | 1146 +++++++++++++++++ .../FigletFonts/larry3d.flf | 924 +++++++++++++ .../FigletFonts/smkeyboard.flf | 466 +++++++ CodingTracker.Kunikazu723/Program.cs | 3 +- CodingTracker.Kunikazu723/UserInput.cs | 23 + CodingTracker.Kunikazu723/UserInterface.cs | 32 + 7 files changed, 2617 insertions(+), 1 deletion(-) create mode 100644 CodingTracker.Kunikazu723/Enums/MenuEnums.cs create mode 100644 CodingTracker.Kunikazu723/FigletFonts/isometric3.flf create mode 100644 CodingTracker.Kunikazu723/FigletFonts/larry3d.flf create mode 100644 CodingTracker.Kunikazu723/FigletFonts/smkeyboard.flf create mode 100644 CodingTracker.Kunikazu723/UserInput.cs create mode 100644 CodingTracker.Kunikazu723/UserInterface.cs diff --git a/CodingTracker.Kunikazu723/Enums/MenuEnums.cs b/CodingTracker.Kunikazu723/Enums/MenuEnums.cs new file mode 100644 index 000000000..127d598e6 --- /dev/null +++ b/CodingTracker.Kunikazu723/Enums/MenuEnums.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Enums +{ + internal static class MenuEnums + { + public enum MainMenuOptions + { + ViewSessions, + LogSessions, + UpdateSession, + DeleteSession, + Exit + } + + public static string OptionToString(MainMenuOptions option) => Regex.Replace(option.ToString(), "([A-Z])", " $1").Trim(); + + } +} diff --git a/CodingTracker.Kunikazu723/FigletFonts/isometric3.flf b/CodingTracker.Kunikazu723/FigletFonts/isometric3.flf new file mode 100644 index 000000000..1ba5e5ca7 --- /dev/null +++ b/CodingTracker.Kunikazu723/FigletFonts/isometric3.flf @@ -0,0 +1,1146 @@ +flf2a$ 11 11 18 -1 23 +isometric3.flf + +Figlet conversion by Kent Nassen (kentn@cyberspace.org), 8-10-94, based +on the post by Lennert Stock: + +From: stock@fwi.uva.nl (Lennert Stock) +Date: 15 Jul 1994 00:04:25 GMT + +Here are some fonts. Non-figlet I'm afraid, if you wanna convert them, be +my guest. I posted the isometric fonts before. + +------------------------------------------------------------------------------ + + .x%%%%%%x. .x%%%%%%x. + ,%%%%%%%%%%. .%%%%%%%%%%. + ,%%%' )' \) :( `( `%%%. + ,%x%)________) --------- L e n n e r t S t o c k ( _ __ (%x%. + (%%%~^88P~88P| |~=> .=-~ %%%) + (%%::. .:,\ .' `. /,:. .::%%) + `;%:`\. `-' | | `-' ./':%:' + ``x`. -===.' stock@fwi.uva.nl -------- `.===- .'x'' + / `:`.__.; :.__.':' \ + .d8b. ..`. .'.. .d8b. +$ $@ +$ $@ +$ $@ +$ $@ +$ $@ +$ $@ +$ $@ +$ $@ +$ $@ +$ $@ +$ $@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/~/::\ @ + /__/:/ /:/\:\@ + \ \:\/:/__\/@ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + _____ @ + / /::\ @ + / /:/\:\ @ + / /:/~/::\ @ + /__/:/ /:/\:|@ + \ \:\/:/~/:/@ + \ \::/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/ @ + / /:/ @ + / /:/ ___ @ + /__/:/ / /\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + _____ @ + / /::\ @ + / /:/\:\ @ + / /:/ \:\ @ + /__/:/ \__\:|@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @ + @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\ @ + / /:/ /:/_ @ + /__/:/ /:/ /\@ + \ \:\/:/ /:/@ + \ \::/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\@ + / /:/ /:/@ + /__/:/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\ @ + / /:/_/::\ @ + /__/:/__\/\:\@ + \ \:\ /~~/:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + /__/\ @ + \ \:\ @ + \__\:\ @ + ___ / /::\ @ + /__/\ /:/\:\@ + \ \:\/:/__\/@ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + ___ @ + / /\ @ + / /:/ @ + /__/::\ @ + \__\/\:\__ @ + \ \:\/\@ + \__\::/@ + /__/:/ @ + \__\/ @ + @@ + ___ @ + / /\ @ + / /:/ @ + /__/::\ @ + \__\/\:\ @ + \ \:\ @ + \__\:\@ + / /:/@ + /__/:/ @ + \__\/ @ + @@ + ___ @ + /__/| @ + | |:| @ + | |:| @ + __| |:| @ + /__/\_|:|____@ + \ \:\/:::::/@ + \ \::/~~~~ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + @ + @ + ___ ___ @ + /__/\ / /\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @ + @@ + ___ @ + /__/\ @ + | |::\ @ + | |:|:\ @ + __|__|:|\:\ @ + /__/::::| \:\@ + \ \:\~~\__\/@ + \ \:\ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + /__/\ @ + \ \:\ @ + \ \:\ @ + _____\__\:\ @ + /__/::::::::\@ + \ \:\~~\~~\/@ + \ \:\ ~~~ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/ \:\ @ + /__/:/ \__\:\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\@ + / /:/~/:/@ + /__/:/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/~/::\ @ + /__/:/ /:/\:\@ + \ \:\/:/__\/@ + \ \::/ @ + \__\/ @ + @@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/~/:/ @ + /__/:/ /:/___@ + \ \:\/:::::/@ + \ \::/~~~~ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\ @ + / /:/ /::\ @ + /__/:/ /:/\:\@ + \ \:\/:/~/:/@ + \ \::/ /:/ @ + \__\/ /:/ @ + /__/:/ @ + \__\/ @@ + @ + ___ @ + / /\ @ + / /:/ @ + / /:/ @ + / /::\ @ + /__/:/\:\ @ + \__\/ \:\ @ + \ \:\@ + \__\/@ + @@ + ___ @ + /__/\ @ + \ \:\ @ + \ \:\ @ + ___ \ \:\ @ + /__/\ \__\:\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + @ + ___ @ + /__/\ @ + \ \:\ @ + \ \:\ @ + ___ \__\:\@ + /__/\ | |:|@ + \ \:\| |:|@ + \ \:\__|:|@ + \__\::::/ @ + ~~~~ @@ + ___ @ + /__/\ @ + _\_ \:\ @ + /__/\ \:\ @ + _\_ \:\ \:\ @ + /__/\ \:\ \:\@ + \ \:\ \:\/:/@ + \ \:\ \::/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + /__/| @ + | |:| @ + | |:| @ + __|__|:| @ + /__/::::\____@ + ~\~~\::::/@ + |~~|:|~~ @ + | |:| @ + | |:| @ + |__|/ @@ + @ + ___ @ + /__/| @ + | |:| @ + | |:| @ + __|__|:| @ + /__/::::\ @ + ~\~~\:\ @ + \ \:\@ + \__\/@ + @@ + ___ @ + / /\ @ + / /::| @ + / /:/:| @ + / /:/|:|__ @ + /__/:/ |:| /\@ + \__\/ |:|/:/@ + | |:/:/ @ + | |::/ @ + | |:/ @ + |__|/ @@ + ___ @ + / /\ @ + / /::\ @ + / /::::\ @ + / /::::::\ @ + /__/:::LS:::\@ + \ \::1994::/@ + \ \::::::/ @ + \ \::::/ @ + \ \::/ @ + \__\/ @@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/~/::\ @ + /__/:/ /:/\:\@ + \ \:\/:/__\/@ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + _____ @ + / /::\ @ + / /:/\:\ @ + / /:/~/::\ @ + /__/:/ /:/\:|@ + \ \:\/:/~/:/@ + \ \::/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/ @ + / /:/ @ + / /:/ ___ @ + /__/:/ / /\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + _____ @ + / /::\ @ + / /:/\:\ @ + / /:/ \:\ @ + /__/:/ \__\:|@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @ + @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\ @ + / /:/ /:/_ @ + /__/:/ /:/ /\@ + \ \:\/:/ /:/@ + \ \::/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\@ + / /:/ /:/@ + /__/:/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\ @ + / /:/_/::\ @ + /__/:/__\/\:\@ + \ \:\ /~~/:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + /__/\ @ + \ \:\ @ + \__\:\ @ + ___ / /::\ @ + /__/\ /:/\:\@ + \ \:\/:/__\/@ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + ___ @ + / /\ @ + / /:/ @ + /__/::\ @ + \__\/\:\__ @ + \ \:\/\@ + \__\::/@ + /__/:/ @ + \__\/ @ + @@ + ___ @ + / /\ @ + / /:/ @ + /__/::\ @ + \__\/\:\ @ + \ \:\ @ + \__\:\@ + / /:/@ + /__/:/ @ + \__\/ @ + @@ + ___ @ + /__/| @ + | |:| @ + | |:| @ + __| |:| @ + /__/\_|:|____@ + \ \:\/:::::/@ + \ \::/~~~~ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + @ + @ + ___ ___ @ + /__/\ / /\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @ + @@ + ___ @ + /__/\ @ + | |::\ @ + | |:|:\ @ + __|__|:|\:\ @ + /__/::::| \:\@ + \ \:\~~\__\/@ + \ \:\ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + /__/\ @ + \ \:\ @ + \ \:\ @ + _____\__\:\ @ + /__/::::::::\@ + \ \:\~~\~~\/@ + \ \:\ ~~~ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/ \:\ @ + /__/:/ \__\:\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\@ + / /:/~/:/@ + /__/:/ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + @ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/~/::\ @ + /__/:/ /:/\:\@ + \ \:\/:/__\/@ + \ \::/ @ + \__\/ @ + @@ + ___ @ + / /\ @ + / /::\ @ + / /:/\:\ @ + / /:/~/:/ @ + /__/:/ /:/___@ + \ \:\/:::::/@ + \ \::/~~~~ @ + \ \:\ @ + \ \:\ @ + \__\/ @@ + ___ @ + / /\ @ + / /:/_ @ + / /:/ /\ @ + / /:/ /::\ @ + /__/:/ /:/\:\@ + \ \:\/:/~/:/@ + \ \::/ /:/ @ + \__\/ /:/ @ + /__/:/ @ + \__\/ @@ + @ + ___ @ + / /\ @ + / /:/ @ + / /:/ @ + / /::\ @ + /__/:/\:\ @ + \__\/ \:\ @ + \ \:\@ + \__\/@ + @@ + ___ @ + /__/\ @ + \ \:\ @ + \ \:\ @ + ___ \ \:\ @ + /__/\ \__\:\@ + \ \:\ / /:/@ + \ \:\ /:/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + @ + ___ @ + /__/\ @ + \ \:\ @ + \ \:\ @ + ___ \__\:\@ + /__/\ | |:|@ + \ \:\| |:|@ + \ \:\__|:|@ + \__\::::/ @ + ~~~~ @@ + ___ @ + /__/\ @ + _\_ \:\ @ + /__/\ \:\ @ + _\_ \:\ \:\ @ + /__/\ \:\ \:\@ + \ \:\ \:\/:/@ + \ \:\ \::/ @ + \ \:\/:/ @ + \ \::/ @ + \__\/ @@ + ___ @ + /__/| @ + | |:| @ + | |:| @ + __|__|:| @ + /__/::::\____@ + ~\~~\::::/@ + |~~|:|~~ @ + | |:| @ + | |:| @ + |__|/ @@ + @ + ___ @ + /__/| @ + | |:| @ + | |:| @ + __|__|:| @ + /__/::::\ @ + ~\~~\:\ @ + \ \:\@ + \__\/@ + @@ + ___ @ + / /\ @ + / /::| @ + / /:/:| @ + / /:/|:|__ @ + /__/:/ |:| /\@ + \__\/ |:|/:/@ + | |:/:/ @ + | |::/ @ + | |:/ @ + |__|/ @@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@ +@@ diff --git a/CodingTracker.Kunikazu723/FigletFonts/larry3d.flf b/CodingTracker.Kunikazu723/FigletFonts/larry3d.flf new file mode 100644 index 000000000..2988d1874 --- /dev/null +++ b/CodingTracker.Kunikazu723/FigletFonts/larry3d.flf @@ -0,0 +1,924 @@ +flf2a$ 9 6 30 1 5 +larry3d.flf by Larry Gelberg (larryg@avs.com) +(stolen liberally from Juan Car's puffy.flf) +tweaked by Glenn Chappell +Version 1.2 2/24/94 + +$$$ @ + $$$ @ + $$$ @ + $$$ @ + $$$ @ + $$$ @ + $$$ @ + $$$ @ + $$$@@ + __ @ +/\ \ @ +\ \ \ @ + \ \ \ @ + \ \_\ @ + \/\_\@ + \/_/@ + @ + @@ + __ __ @ +/\ \\ \ @ +\ \_\\_\ @ + \/_//_/$ @ + $ $ @ + $ $@ + @ + @ + @@ + __ __ @ + _\ \\ \__ @ +/\__ _ _\ @ +\/_L\ \\ \L_ @ + /\_ _ _\@ + \/_/\_\\_\/@ + \/_//_/ @ + @ + @@ + __ @ +/\ \_ @ +\/'__`\ @ +/\ \_\_\ @ +\ \____ \ @ + \/\ \_\ \@ + \ `\_ _/@ + `\_/\_\@ + \/_/@@ + __ __ @ +/\_\ / / @ +\/_/ / / @ + / / @ + / / __ @ + /_/ /\_\@ + /_/ \/_/@ + @ + @@ + ____ @ +/| _ \ @ +|/\ | @ + \// __`\/\ @ + /| \L> <_@ + | \_____/\/@ + \/____/\/ @ + @ + @@ + __ @ +/\ \ @ +\ \/$ @ + \/ $ @ + $ $ @ + $ $@ + @ + @ + @@ + _ @ + /' \ @ +/\ ,/' @ +\ \ \ @ + \ \ `\ @ + \ `\__\@ + `\/_/ @ + @ + @@ + __ @ +/\ `\ @ +\`\ \ @ + `\`\ \ @ + `\/' \@ + /\__/@ + \/_/ @ + @ + @@ + __ @ + _\ \ _ @ +/\_` ' \ @ +\/_> <_ @ + /\_, ,_\@ + \/_/\_\/@ + \/_/ @ + @ + @@ + __ @ + /\ \ @ + \_\ \___ @ +/\___ __\@ +\/__/\ \_/@ + \ \_\ @ + \/_/ @ + @ + @@ + @ + @ + @ + @ + __ @ +/\ \@ +\ \/@ + \/ @ + @@ + @ + @ + @ + _______ @ +/\______\@ +\/______/@ + @ + @ + @@ + @ + @ + @ + @ + __ @ +/\_\@ +\/_/@ + @ + @@ + __@ + / /@ + / / @ + / / @ + / / @ + /_/ @ +/_/ @ + @ + @@ + __ @ + /'__`\ @ +/\ \/\ \ @ +\ \ \ \ \ @ + \ \ \_\ \@ + \ \____/@ + \/___/ @ + @ + @@ + _ @ + /' \ @ +/\_, \ @ +\/_/\ \ @ + \ \ \ @ + \ \_\@ + \/_/@ + @ + @@ + ___ @ + /'___`\ @ +/\_\ /\ \ @ +\/_/// /__ @ + // /_\ \@ + /\______/@ + \/_____/ @ + @ + @@ + __ @ + /'__`\ @ +/\_\L\ \ @ +\/_/_\_<_ @ + /\ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + __ __ @ +/\ \\ \ @ +\ \ \\ \ @ + \ \ \\ \_ @ + \ \__ ,__\@ + \/_/\_\_/@ + \/_/ @ + @ + @@ + ______ @ +/\ ___\ @ +\ \ \__/ @ + \ \___``\ @ + \/\ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + ____ @ + /'___\ @ +/\ \__/ @ +\ \ _``\ @ + \ \ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + ________ @ +/\_____ \@ +\/___//'/'@ + /' /' @ + /' /' @ + /\_/ @ + \// @ + @ + @@ + __ @ + /'_ `\ @ +/\ \L\ \ @ +\/_> _ <_ @ + /\ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + __ @ + /'_ `\ @ +/\ \L\ \ @ +\ \___, \ @ + \/__,/\ \ @ + \ \_\@ + \/_/@ + @ + @@ + @ + @ + __ @ +/\_\ @ +\/_/_ @ + /\_\@ + \/_/@ + @ + @@ + @ + @ + __ @ +/\_\ @ +\/_/_ @ + /\ \@ + \ \/@ + \/ @ + @@ + ___ @ + / / @ + / / @ +/< < @ +\ `\ `\ @ + `\ `\_|@ + `\// @ + @ + @@ + @ + _______ @ +/\______\ @ +\/______/_ @ + /\______\@ + \/______/@ + @ + @ + @@ + __ @ +/\ `\ @ +\ `\ `\ @ + `\ > >@ + / / @ + /\_/ @ + \// @ + @ + @@ + _ @ + /'_`\ @ +/\_\/\`\@ +\/_//'/'@ + /\_\ @ + \/\_\@ + \/_/@ + @ + @@ + @ + __ @ + /'_`\_ @ + /'/'_` \ @ +/\ \ \L\ \ @ +\ \ `\__,_\@ + \ `\_____\@ + `\/_____/@ + @@ + ______ @ +/\ _ \ @ +\ \ \L\ \ @ + \ \ __ \ @ + \ \ \/\ \ @ + \ \_\ \_\@ + \/_/\/_/@ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \L\ \ @ + \ \ _ <' @ + \ \ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \/\_\ @ + \ \ \/_/_ @ + \ \ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \/\ \ @ + \ \ \ \ \ @ + \ \ \_\ \@ + \ \____/@ + \/___/ @ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \L\_\ @ + \ \ _\L @ + \ \ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \L\_\@ + \ \ _\/@ + \ \ \/ @ + \ \_\ @ + \/_/ @ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \L\_\ @ + \ \ \L_L @ + \ \ \/, \@ + \ \____/@ + \/___/ @ + @ + @@ + __ __ @ +/\ \/\ \ @ +\ \ \_\ \ @ + \ \ _ \ @ + \ \ \ \ \ @ + \ \_\ \_\@ + \/_/\/_/@ + @ + @@ + ______ @ +/\__ _\ @ +\/_/\ \/ @ + \ \ \ @ + \_\ \__ @ + /\_____\@ + \/_____/@ + @ + @@ + _____ @ +/\___ \ @ +\/__/\ \ @ + _\ \ \ @ + /\ \_\ \@ + \ \____/@ + \/___/ @ + @ + @@ + __ __ @ +/\ \/\ \ @ +\ \ \/'/' @ + \ \ , < @ + \ \ \\`\ @ + \ \_\ \_\@ + \/_/\/_/@ + @ + @@ + __ @ +/\ \ @ +\ \ \ @ + \ \ \ __ @ + \ \ \L\ \@ + \ \____/@ + \/___/ @ + @ + @@ + @ + /'\_/`\ @ +/\ \ @ +\ \ \__\ \ @ + \ \ \_/\ \ @ + \ \_\\ \_\@ + \/_/ \/_/@ + @ + @@ + __ __ @ +/\ \/\ \ @ +\ \ `\\ \ @ + \ \ , ` \ @ + \ \ \`\ \ @ + \ \_\ \_\@ + \/_/\/_/@ + @ + @@ + _____ @ +/\ __`\ @ +\ \ \/\ \ @ + \ \ \ \ \ @ + \ \ \_\ \ @ + \ \_____\@ + \/_____/@ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \L\ \@ + \ \ ,__/@ + \ \ \/ @ + \ \_\ @ + \/_/ @ + @ + @@ + _____ @ +/\ __`\ @ +\ \ \/\ \ @ + \ \ \ \ \ @ + \ \ \\'\\ @ + \ \___\_\@ + \/__//_/@ + @ + @@ + ____ @ +/\ _`\ @ +\ \ \L\ \ @ + \ \ , / @ + \ \ \\ \ @ + \ \_\ \_\@ + \/_/\/ /@ + @ + @@ + ____ @ +/\ _`\ @ +\ \,\L\_\ @ + \/_\__ \ @ + /\ \L\ \ @ + \ `\____\@ + \/_____/@ + @ + @@ + ______ @ +/\__ _\ @ +\/_/\ \/ @ + \ \ \ @ + \ \ \ @ + \ \_\@ + \/_/@ + @ + @@ + __ __ @ +/\ \/\ \ @ +\ \ \ \ \ @ + \ \ \ \ \ @ + \ \ \_\ \ @ + \ \_____\@ + \/_____/@ + @ + @@ + __ __ @ +/\ \/\ \ @ +\ \ \ \ \ @ + \ \ \ \ \ @ + \ \ \_/ \@ + \ `\___/@ + `\/__/ @ + @ + @@ + __ __ @ +/\ \ __/\ \ @ +\ \ \/\ \ \ \ @ + \ \ \ \ \ \ \ @ + \ \ \_/ \_\ \@ + \ `\___x___/@ + '\/__//__/ @ + @ + @@ + __ __ @ +/\ \ /\ \ @ +\ `\`\/'/' @ + `\/ > < @ + \/'/\`\ @ + /\_\\ \_\@ + \/_/ \/_/@ + @ + @@ + __ __ @ +/\ \ /\ \@ +\ `\`\\/'/@ + `\ `\ /' @ + `\ \ \ @ + \ \_\@ + \/_/@ + @ + @@ + ________ @ +/\_____ \ @ +\/____//'/' @ + //'/' @ + //'/'___ @ + /\_______\@ + \/_______/@ + @ + @@ + ____ @ +/\ _\ @ +\ \ \/ @ + \ \ \ @ + \ \ \_ @ + \ \___\@ + \/___/@ + @ + @@ + __ @ +/\ `\ @ +\`\ `\ @ +`\`\ `\ @ + `\`\ `\ @ + `\`\__\@ + `\/__/@ + @ + @@ + ____ @ +/\__ \ @ +\/_/\ \ @ + \ \ \ @ + \_\ \ @ + /\___\@ + \/___/@ + @ + @@ + __ @ + / `\ @ +/\_/\_\ @ +\//\// $ @ + $ $ @ + $ $@ + @ + @ + @@ + @ + @ + @ + @ + @ +$ $ @ + $_______ @ + /\______\@ + \/______/@@ + __ @ +/\ \ @ +\ \\$ @ + \// $ @ + $ $ @ + $ $@ + @ + @ + @@ + @ + @ + __ @ + /'__`\ @ +/\ \L\.\_ @ +\ \__/.\_\@ + \/__/\/_/@ + @ + @@ + __ @ +/\ \ @ +\ \ \____ @ + \ \ '__`\ @ + \ \ \L\ \@ + \ \_,__/@ + \/___/ @ + @ + @@ + @ + @ + ___ @ + /'___\ @ +/\ \__/ @ +\ \____\@ + \/____/@ + @ + @@ + __ @ + /\ \ @ + \_\ \ @ + /'_` \ @ +/\ \L\ \ @ +\ \___,_\@ + \/__,_ /@ + @ + @@ + @ + @ + __ @ + /'__`\ @ +/\ __/ @ +\ \____\@ + \/____/@ + @ + @@ + ___ @ + /'___\ @ +/\ \__/ @ +\ \ ,__\@ + \ \ \_/@ + \ \_\ @ + \/_/ @ + @ + @@ + @ + @ + __ @ + /'_ `\ @ +/\ \L\ \ @ +\ \____ \ @ + \/___L\ \@ + /\____/@ + \_/__/ @@ + __ @ +/\ \ @ +\ \ \___ @ + \ \ _ `\ @ + \ \ \ \ \ @ + \ \_\ \_\@ + \/_/\/_/@ + @ + @@ + @ + __ @ +/\_\ @ +\/\ \ @ + \ \ \ @ + \ \_\@ + \/_/@ + @ + @@ + @ + __ @ +/\_\ @ +\/\ \ @ + \ \ \ @ + _\ \ \ @ +/\ \_\ \@ +\ \____/@ + \/___/ @@ + __ @ +/\ \ @ +\ \ \/'\ @ + \ \ , < @ + \ \ \\`\ @ + \ \_\ \_\@ + \/_/\/_/@ + @ + @@ + ___ @ +/\_ \ @ +\//\ \ @ + \ \ \ @ + \_\ \_ @ + /\____\@ + \/____/@ + @ + @@ + @ + @ + ___ ___ @ +/' __` __`\ @ +/\ \/\ \/\ \ @ +\ \_\ \_\ \_\@ + \/_/\/_/\/_/@ + @ + @@ + @ + @ + ___ @ +/' _ `\ @ +/\ \/\ \ @ +\ \_\ \_\@ + \/_/\/_/@ + @ + @@ + @ + @ + ___ @ + / __`\ @ +/\ \L\ \@ +\ \____/@ + \/___/ @ + @ + @@ + @ + @ + _____ @ +/\ '__`\ @ +\ \ \L\ \@ + \ \ ,__/@ + \ \ \/ @ + \ \_\ @ + \/_/ @@ + @ + @ + __ @ + /'__`\ @ +/\ \L\ \ @ +\ \___, \ @ + \/___/\ \ @ + \ \_\@ + \/_/@@ + @ + @ + _ __ @ +/\`'__\@ +\ \ \/ @ + \ \_\ @ + \/_/ @ + @ + @@ + @ + @ + ____ @ + /',__\ @ +/\__, `\@ +\/\____/@ + \/___/ @ + @ + @@ + __ @ +/\ \__ @ +\ \ ,_\ @ + \ \ \/ @ + \ \ \_ @ + \ \__\@ + \/__/@ + @ + @@ + @ + @ + __ __ @ +/\ \/\ \ @ +\ \ \_\ \@ + \ \____/@ + \/___/ @ + @ + @@ + @ + @ + __ __ @ +/\ \/\ \ @ +\ \ \_/ |@ + \ \___/ @ + \/__/ @ + @ + @@ + @ + @ + __ __ __ @ +/\ \/\ \/\ \ @ +\ \ \_/ \_/ \@ + \ \___x___/'@ + \/__//__/ @ + @ + @@ + @ + @ + __ _ @ +/\ \/'\ @ +\/> \@ + /\___/@ + \/__/ @@ + @ + @ + ____ @ +/\_ ,`\ @ +\/_/ /_ @ + /\____\@ + \/____/@ + @ + @@ + _ @ + /' \@ + \ ,/'@ + <' \ @ +< \ `\ @ + \`\__\@ + \/__/@ + @ + @@ + __ @ +/\ \ @ +\ \ \ @ + \ \ \ @ + \ \ \ @ + \ \ \ @ + \ \ \ @ + \ \_\@ + \/_/@@ + __ @ +/\ `\ @ +\`\ \ @ + \ \ `>@ + //' \ @ +/\__/' @ +\/_/ @ + @ + @@ + _ _ @ + /' \/' \ @ +/\_/\__//$ @ +\//\/__/ $ @ + $ $@ + @ + @ + @ + @@ + __ __ @ +/\_\/\_\ @ +\/\ _ \ @ + \ \ \L\ \ @ + \ \ __ \ @ + \ \_\/\_\@ + \/_/\/_/@ + @ + @@ + __ __ @ +/\_\/\_\ @ +\/\ __ \ @ + \ \ \/\ \ @ + \ \ \_\ \ @ + \ \_____\@ + \/_____/@ + @ + @@ + __ __ @ +/\_\/\_\ @ +\/\ \/\ \ @ + \ \ \ \ \ @ + \ \ \_\ \ @ + \ \_____\@ + \/_____/@ + @ + @@ + __ __ @ +/\_\/\_\ @ +\/_/\/_/_ @ + /'_` \ @ + /\ \L\ \ @ + \ `\__,_\@ + `\/_,__/@ + @ + @@ + __ __ @ +/\_\/\_\ @ +\/_/\/_/ @ + /'_`\ @ + /\ \L\ \@ + \ `\___/@ + `\/__/ @ + @ + @@ + __ __ @ +/\_\ \_\ @ +\/_/\/_/_ @ + /\ \/\ \ @ + \ \ \_\ \@ + \ `\___/@ + `\/__/ @ + @ + @@ + ______ @ +/\ __ \ @ +\ \ \/\ \ @ + \ \ \<_<_ @ + \ \ \ \ \@ + \ \ \\_/@ + \ \_\/ @ + \/_/ @ + @@ diff --git a/CodingTracker.Kunikazu723/FigletFonts/smkeyboard.flf b/CodingTracker.Kunikazu723/FigletFonts/smkeyboard.flf new file mode 100644 index 000000000..cefecc4ae --- /dev/null +++ b/CodingTracker.Kunikazu723/FigletFonts/smkeyboard.flf @@ -0,0 +1,466 @@ +flf2a$ 4 4 16 1 57 + +smkeyboard.flf + +Adapted for figlet by Kent Nassen, kentn@cyberspace.org, 11/22/94, based +on the idea: + +From: bas@damek.kth.se (Bas Meijer) +Newsgroups: rec.arts.ascii +Subject: Talk-line: keyboard.flf +Date: 22 Nov 1994 07:26:10 -0600 +Organization: Royal Institute of Technology +Lines: 41 +Sender: boba@gagme.wwa.com +Approved: boba@wwa.com +Message-ID: <3asrhi$con@gagme.wwa.com> +References: <3asltp$aog@gagme.wwa.com> +Reply-To: bas@damek.kth.se +NNTP-Posting-Host: gagme.wwa.com + + +In article aog@gagme.wwa.com, ssfiit@cs.umb.edu (SuperStreetFighter2Turbo) writes: +> +> I have composed a new figlet font! =) +> it is called keyboard.flf. It resemble the actual keyboard, well, kinda! =) +> anyway! here is a sample! =) +> _______ _______ _______ _______ _______ _______ +> |\ /|\ /|\ /|\ /|\ /|\ /| +> | +---+ | +---+ | +---+ | +---+ | +---+ | +---+ | +> | | | | | | | | | | | | | | | | | | | +> | |s | | |a | | |m | | |p | | |l | | |e | | +> | +---+ | +---+ | +---+ | +---+ | +---+ | +---+ | +> |/_____\|/_____\|/_____\|/_____\|/_____\|/_____\| + + ____ _________ ____ ____ ____ _________ ____ ____ ____ _________ +||A ||| |||B |||I |||T ||| |||T |||O |||O ||| || +||__|||_______|||__|||__|||__|||_______|||__|||__|||__|||_______|| +|/__\|/_______\|/__\|/__\|/__\|/_______\|/__\|/__\|/__\|/_______\|____ +||B |||I |||G ||| |||I |||F ||| |||Y |||O |||U ||| || +||__|||__|||__|||_______|||__|||__|||_______|||__|||__|||__|||_______|| +|/__\|/__\|/__\|/_______\|/__\|/__\|/_______\|/__\|/__\|/__\|/_______\| +||A |||S |||K ||| |||M |||E |||! ||| |||: |||) |||Return|| +||__|||__|||__|||_______|||__|||__|||__|||_______|||__|||__||| || +|/__\|/__\|/__\|/_______\|/__\|/__\|/__\|/_______\|/__\|/__\|/l || + l|_____|| +Bas. |/_____\| + + +email: bas@damek.kth.se ____ ____ KTH, Dept. of Mach. Design + _______ ___ ______ / \/ \ ____________________________ +( \/ \/ \ / \ ) ) ) ) \ + | | ) \ \___) / \ \__/\ /\ \ \__/ | ) + | ( | \_ \ ( ( ) ) )_ ( )_) \ )_| ( + | | ) _ )\ ) \ \__/ / / \/ \ / / \ \ \ +(_______/_/ \__/_____/ \ \/ /_______)___)___/______)__/\___) + \____/\____/ +tel.: +46-(0)8-790 6308 S-100 44 Stockholm, Sweden + + _________ @ +|| ||@ +||_______||@ +|/_______\|@@ + ____ @ +||! ||@ +||__||@ +|/__\|@@ + ____ @ +||" ||@ +||__||@ +|/__\|@@ + ____ @ +||# ||@ +||__||@ +|/__\|@@ + ____ @ +||$ ||@ +||__||@ +|/__\|@@ + ____ @ +||% ||@ +||__||@ +|/__\|@@ + ____ @ +||& ||@ +||__||@ +|/__\|@@ + ____ @ +||' ||@ +||__||@ +|/__\|@@ + ____ @ +||( ||@ +||__||@ +|/__\|@@ + ____ @ +||) ||@ +||__||@ +|/__\|@@ + ____ @ +||* ||@ +||__||@ +|/__\|@@ + ____ @ +||+ ||@ +||__||@ +|/__\|@@ + ____ @ +||, ||@ +||__||@ +|/__\|@@ + ____ @ +||- ||@ +||__||@ +|/__\|@@ + ____ @ +||. ||@ +||__||@ +|/__\|@@ + ____ @ +||/ ||@ +||__||@ +|/__\|@@ + ____ @ +||0 ||@ +||__||@ +|/__\|@@ + ____ @ +||1 ||@ +||__||@ +|/__\|@@ + ____ @ +||2 ||@ +||__||@ +|/__\|@@ + ____ @ +||3 ||@ +||__||@ +|/__\|@@ + ____ @ +||4 ||@ +||__||@ +|/__\|@@ + ____ @ +||5 ||@ +||__||@ +|/__\|@@ + ____ @ +||6 ||@ +||__||@ +|/__\|@@ + ____ @ +||7 ||@ +||__||@ +|/__\|@@ + ____ @ +||8 ||@ +||__||@ +|/__\|@@ + ____ @ +||9 ||@ +||__||@ +|/__\|@@ + ____ @ +||: ||@ +||__||@ +|/__\|@@ + ____ @ +||; ||@ +||__||@ +|/__\|@@ + ____ @ +||< ||@ +||__||@ +|/__\|@@ + ____ @ +||= ||@ +||__||@ +|/__\|@@ + ____ @ +||> ||@ +||__||@ +|/__\|@@ + ____ @ +||? ||@ +||__||@ +|/__\|@@ + ____ @ +||@ ||@ +||__||@ +|/__\|@@ + ____ @ +||A ||@ +||__||@ +|/__\|@@ + ____ @ +||B ||@ +||__||@ +|/__\|@@ + ____ @ +||C ||@ +||__||@ +|/__\|@@ + ____ @ +||D ||@ +||__||@ +|/__\|@@ + ____ @ +||E ||@ +||__||@ +|/__\|@@ + ____ @ +||F ||@ +||__||@ +|/__\|@@ + ____ @ +||G ||@ +||__||@ +|/__\|@@ + ____ @ +||H ||@ +||__||@ +|/__\|@@ + ____ @ +||I ||@ +||__||@ +|/__\|@@ + ____ @ +||J ||@ +||__||@ +|/__\|@@ + ____ @ +||K ||@ +||__||@ +|/__\|@@ + ____ @ +||L ||@ +||__||@ +|/__\|@@ + ____ @ +||M ||@ +||__||@ +|/__\|@@ + ____ @ +||N ||@ +||__||@ +|/__\|@@ + ____ @ +||O ||@ +||__||@ +|/__\|@@ + ____ @ +||P ||@ +||__||@ +|/__\|@@ + ____ @ +||Q ||@ +||__||@ +|/__\|@@ + ____ @ +||R ||@ +||__||@ +|/__\|@@ + ____ @ +||S ||@ +||__||@ +|/__\|@@ + ____ @ +||T ||@ +||__||@ +|/__\|@@ + ____ @ +||U ||@ +||__||@ +|/__\|@@ + ____ @ +||V ||@ +||__||@ +|/__\|@@ + ____ @ +||W ||@ +||__||@ +|/__\|@@ + ____ @ +||X ||@ +||__||@ +|/__\|@@ + ____ @ +||Y ||@ +||__||@ +|/__\|@@ + ____ @ +||Z ||@ +||__||@ +|/__\|@@ + ____ @ +||[ ||@ +||__||@ +|/__\|@@ + ____ @ +||\ ||@ +||__||@ +|/__\|@@ + ____ @ +||] ||@ +||__||@ +|/__\|@@ + ____ @ +||^ ||@ +||__||@ +|/__\|@@ + ____ @ +||_ ||@ +||__||@ +|/__\|@@ + ____ @ +||` ||@ +||__||@ +|/__\|@@ + ____ @ +||a ||@ +||__||@ +|/__\|@@ + ____ @ +||b ||@ +||__||@ +|/__\|@@ + ____ @ +||c ||@ +||__||@ +|/__\|@@ + ____ @ +||d ||@ +||__||@ +|/__\|@@ + ____ @ +||e ||@ +||__||@ +|/__\|@@ + ____ @ +||f ||@ +||__||@ +|/__\|@@ + ____ @ +||g ||@ +||__||@ +|/__\|@@ + ____ @ +||h ||@ +||__||@ +|/__\|@@ + ____ @ +||i ||@ +||__||@ +|/__\|@@ + ____ @ +||j ||@ +||__||@ +|/__\|@@ + ____ @ +||k ||@ +||__||@ +|/__\|@@ + ____ @ +||l ||@ +||__||@ +|/__\|@@ + ____ @ +||m ||@ +||__||@ +|/__\|@@ + ____ @ +||n ||@ +||__||@ +|/__\|@@ + ____ @ +||o ||@ +||__||@ +|/__\|@@ + ____ @ +||p ||@ +||__||@ +|/__\|@@ + ____ @ +||q ||@ +||__||@ +|/__\|@@ + ____ @ +||r ||@ +||__||@ +|/__\|@@ + ____ @ +||s ||@ +||__||@ +|/__\|@@ + ____ @ +||t ||@ +||__||@ +|/__\|@@ + ____ @ +||u ||@ +||__||@ +|/__\|@@ + ____ @ +||v ||@ +||__||@ +|/__\|@@ + ____ @ +||w ||@ +||__||@ +|/__\|@@ + ____ @ +||x ||@ +||__||@ +|/__\|@@ + ____ @ +||y ||@ +||__||@ +|/__\|@@ + ____ @ +||z ||@ +||__||@ +|/__\|@@ + ____ @ +||{ ||@ +||__||@ +|/__\|@@ + ____ @ +||| ||@ +||__||@ +|/__\|@@ + ____ @ +||} ||@ +||__||@ +|/__\|@@ + ____ @ +||~ ||@ +||__||@ +|/__\|@@ +@ +@ +@ +@@ +@ +@ +@ +@@ +@ +@ +@ +@@ +@ +@ +@ +@@ +@ +@ +@ +@@ +@ +@ +@ +@@ +@ +@ +@ +@@ diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 1f7f7118c..537aebebb 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -19,7 +19,8 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); - + var userInterface = new UserInterface(); + userInterface.MainMenu(); } } } diff --git a/CodingTracker.Kunikazu723/UserInput.cs b/CodingTracker.Kunikazu723/UserInput.cs new file mode 100644 index 000000000..06cd97678 --- /dev/null +++ b/CodingTracker.Kunikazu723/UserInput.cs @@ -0,0 +1,23 @@ +using CodingTracker.Kunikazu723.Enums; +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static CodingTracker.Kunikazu723.Enums.MenuEnums; + +namespace CodingTracker.Kunikazu723 +{ + internal class UserInput + { + public MainMenuOptions GetUserMainMenuOption() + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .Title("What do you want to do?") + .AddChoices(Enum.GetValues()) + .UseConverter(OptionToString)); + } + } +} diff --git a/CodingTracker.Kunikazu723/UserInterface.cs b/CodingTracker.Kunikazu723/UserInterface.cs new file mode 100644 index 000000000..3cadc81f2 --- /dev/null +++ b/CodingTracker.Kunikazu723/UserInterface.cs @@ -0,0 +1,32 @@ +using Spectre.Console; +using System.Reflection; + +namespace CodingTracker.Kunikazu723 +{ + public class UserInterface + { + public void MainMenu() + { + WriteBanner(); + // Main Loop + + + Console.ReadKey(); + } + public void WriteBanner(string bannerText = "CODING TRACKER") + { + var figletFont = FigletFont.Load("./FigletFonts/smkeyboard.flf"); + var appFiglet = new FigletText(figletFont, "CODING TRACKER") + .Color(Color.Blue); + + AnsiConsole.Write(new Rule().RuleStyle(Style.Parse("blue dim"))); + AnsiConsole.Write(appFiglet); + AnsiConsole.Write(new Rule().RuleStyle(Style.Parse("blue dim"))); + } + + public void PromptUserMenuOption() + { + + } + } +} From ba64a5cb72278eabc4b9ac5cf60e89349c5d621d Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:29:14 -0300 Subject: [PATCH 12/93] Abstract enum-string converting method to handle all types derived from enums --- CodingTracker.Kunikazu723/Enums/MenuEnums.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Enums/MenuEnums.cs b/CodingTracker.Kunikazu723/Enums/MenuEnums.cs index 127d598e6..fdf923d6b 100644 --- a/CodingTracker.Kunikazu723/Enums/MenuEnums.cs +++ b/CodingTracker.Kunikazu723/Enums/MenuEnums.cs @@ -18,7 +18,7 @@ public enum MainMenuOptions Exit } - public static string OptionToString(MainMenuOptions option) => Regex.Replace(option.ToString(), "([A-Z])", " $1").Trim(); + public static string OptionToString(T option) where T: struct, Enum => Regex.Replace(option.ToString(), "([A-Z])", " $1").Trim(); } } From acb8d8694ef20cbe9da38f8fd69afd518fe54dcb Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:30:51 -0300 Subject: [PATCH 13/93] Abstract Options prompt to accept any type of enum --- CodingTracker.Kunikazu723/UserInput.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserInput.cs b/CodingTracker.Kunikazu723/UserInput.cs index 06cd97678..e4bfa9178 100644 --- a/CodingTracker.Kunikazu723/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserInput.cs @@ -1,22 +1,16 @@ -using CodingTracker.Kunikazu723.Enums; -using Spectre.Console; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Spectre.Console; using static CodingTracker.Kunikazu723.Enums.MenuEnums; namespace CodingTracker.Kunikazu723 { internal class UserInput { - public MainMenuOptions GetUserMainMenuOption() + public T PromptUserEnumOption() where T: struct, Enum { return AnsiConsole.Prompt( - new SelectionPrompt() + new SelectionPrompt() .Title("What do you want to do?") - .AddChoices(Enum.GetValues()) + .AddChoices(Enum.GetValues()) .UseConverter(OptionToString)); } } From 5ef89ca99e5bff1a66ca5e711e934c03c8eadefe Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:37:28 -0300 Subject: [PATCH 14/93] Add UserInput dependency to UserInterface --- CodingTracker.Kunikazu723/Program.cs | 4 ++-- CodingTracker.Kunikazu723/UserInterface.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 537aebebb..4bed0ed94 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -18,8 +18,8 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); - - var userInterface = new UserInterface(); + var userInput = new UserInput(); + var userInterface = new UserInterface(userInput); userInterface.MainMenu(); } } diff --git a/CodingTracker.Kunikazu723/UserInterface.cs b/CodingTracker.Kunikazu723/UserInterface.cs index 3cadc81f2..547369d27 100644 --- a/CodingTracker.Kunikazu723/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserInterface.cs @@ -5,6 +5,11 @@ namespace CodingTracker.Kunikazu723 { public class UserInterface { + private readonly UserInput _userInput; + public UserInterface(UserInput userInput) + { + _userInput = userInput; + } public void MainMenu() { WriteBanner(); From c99ac86bca443f3ea8d05e66cb4323b7ab15c9ae Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:39:53 -0300 Subject: [PATCH 15/93] Correct access modifier for UserInput UserInput is a dependency of UserInterface, the latter being public. Allowing access of an internal member in a public member breaks the contract. So it needs to be corrected by either reducing UserInterface's access modifer permission or elevating UserInput's access modifier. The last one being the choice I made --- CodingTracker.Kunikazu723/UserInput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserInput.cs b/CodingTracker.Kunikazu723/UserInput.cs index e4bfa9178..472f0bf87 100644 --- a/CodingTracker.Kunikazu723/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserInput.cs @@ -3,7 +3,7 @@ namespace CodingTracker.Kunikazu723 { - internal class UserInput + public class UserInput { public T PromptUserEnumOption() where T: struct, Enum { From 095371686b4acde143e508694d2fb6b7fbb73cee Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:40:20 -0300 Subject: [PATCH 16/93] Add selection prompt for main menu options in UserInterface --- CodingTracker.Kunikazu723/UserInterface.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserInterface.cs b/CodingTracker.Kunikazu723/UserInterface.cs index 547369d27..5630ab063 100644 --- a/CodingTracker.Kunikazu723/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserInterface.cs @@ -1,5 +1,6 @@ using Spectre.Console; using System.Reflection; +using static CodingTracker.Kunikazu723.Enums.MenuEnums; namespace CodingTracker.Kunikazu723 { @@ -14,10 +15,11 @@ public void MainMenu() { WriteBanner(); // Main Loop - + MainMenuOptions option = _userInput.PromptUserEnumOption(); Console.ReadKey(); } + public void WriteBanner(string bannerText = "CODING TRACKER") { var figletFont = FigletFont.Load("./FigletFonts/smkeyboard.flf"); From 740162d667189845cf3f4dda1570a3f042ae9fba Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 22:06:05 -0300 Subject: [PATCH 17/93] Add services that will talk to the Database --- .../Services/CodeSessionService.cs | 31 +++++++++++++++++++ .../Services/IService.cs | 16 ++++++++++ 2 files changed, 47 insertions(+) create mode 100644 CodingTracker.Kunikazu723/Services/CodeSessionService.cs create mode 100644 CodingTracker.Kunikazu723/Services/IService.cs diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs new file mode 100644 index 000000000..ebdaeeb09 --- /dev/null +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Services +{ + internal class CodeSessionService : IService + { + public void AddItem() + { + throw new NotImplementedException(); + } + + public void DeleteItem() + { + throw new NotImplementedException(); + } + + public void UpdateItem() + { + throw new NotImplementedException(); + } + + public void ViewAllItems() + { + throw new NotImplementedException(); + } + } +} diff --git a/CodingTracker.Kunikazu723/Services/IService.cs b/CodingTracker.Kunikazu723/Services/IService.cs new file mode 100644 index 000000000..ccce98dea --- /dev/null +++ b/CodingTracker.Kunikazu723/Services/IService.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Services +{ + public interface IService + { + void ViewAllItems(); + void AddItem(); + void UpdateItem(); + void DeleteItem(); + } +} From a40ebd596af648dc56f9e95dc0df7fac1eb50c95 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 22:06:41 -0300 Subject: [PATCH 18/93] Add Service dependency to UserInterface --- CodingTracker.Kunikazu723/Program.cs | 7 +++++-- CodingTracker.Kunikazu723/UserInterface.cs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 4bed0ed94..6dead0d87 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using CodingTracker.Kunikazu723.Services; +using Microsoft.Extensions.Configuration; namespace CodingTracker.Kunikazu723 { internal class Program @@ -18,8 +19,10 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); + var userInput = new UserInput(); - var userInterface = new UserInterface(userInput); + var codeSessionService = new CodeSessionService(); + var userInterface = new UserInterface(userInput, codeSessionService); userInterface.MainMenu(); } } diff --git a/CodingTracker.Kunikazu723/UserInterface.cs b/CodingTracker.Kunikazu723/UserInterface.cs index 5630ab063..ccd6af06b 100644 --- a/CodingTracker.Kunikazu723/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserInterface.cs @@ -7,9 +7,11 @@ namespace CodingTracker.Kunikazu723 public class UserInterface { private readonly UserInput _userInput; - public UserInterface(UserInput userInput) + private readonly IService _service; + public UserInterface(UserInput userInput, IService service) { _userInput = userInput; + _service = service; } public void MainMenu() { From 90ae78560e98c7953c777c1cec9d4250867727e8 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Tue, 6 Oct 2026 22:07:05 -0300 Subject: [PATCH 19/93] Add main loop with options leading to the service --- CodingTracker.Kunikazu723/UserInterface.cs | 33 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserInterface.cs b/CodingTracker.Kunikazu723/UserInterface.cs index ccd6af06b..56ea73549 100644 --- a/CodingTracker.Kunikazu723/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserInterface.cs @@ -1,4 +1,5 @@ -using Spectre.Console; +using CodingTracker.Kunikazu723.Services; +using Spectre.Console; using System.Reflection; using static CodingTracker.Kunikazu723.Enums.MenuEnums; @@ -17,9 +18,35 @@ public void MainMenu() { WriteBanner(); // Main Loop - MainMenuOptions option = _userInput.PromptUserEnumOption(); + bool isRunning = true; + while (isRunning) + { + MainMenuOptions option = _userInput.PromptUserEnumOption(); + switch (option) + { + case MainMenuOptions.ViewSessions: + _service.ViewAllItems(); + break; + case MainMenuOptions.LogSessions: + _service.AddItem(); + break; + case MainMenuOptions.UpdateSession: + _service.UpdateItem(); + break; + case MainMenuOptions.DeleteSession: + _service.DeleteItem(); + break; + case MainMenuOptions.Exit: + AnsiConsole.MarkupLine("Exiting [bold cyan]Coding Tracker[/]..."); + isRunning = false; + break; + default: + break; + } - Console.ReadKey(); + Console.ReadKey(); + } + } public void WriteBanner(string bannerText = "CODING TRACKER") From 13491c055374e340902bd378297543bbc3efc839 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Wed, 7 Oct 2026 11:34:23 -0300 Subject: [PATCH 20/93] Lazy commit I know I should focus on making atomic commits. But now I'm too burned out for it and for that I am sorry. --- .../Dao/CodingSessionDao.cs | 27 ++++++++++++ CodingTracker.Kunikazu723/Dao/IDao.cs | 17 ++++++++ CodingTracker.Kunikazu723/Program.cs | 11 +++-- .../Services/CodeSessionService.cs | 13 +++++- CodingTracker.Kunikazu723/UserInput.cs | 17 -------- .../UserLand/UserInput.cs | 41 +++++++++++++++++++ .../{ => UserLand}/UserInterface.cs | 8 +--- .../UserLand/Validation.cs | 10 +++++ 8 files changed, 117 insertions(+), 27 deletions(-) create mode 100644 CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs create mode 100644 CodingTracker.Kunikazu723/Dao/IDao.cs delete mode 100644 CodingTracker.Kunikazu723/UserInput.cs create mode 100644 CodingTracker.Kunikazu723/UserLand/UserInput.cs rename CodingTracker.Kunikazu723/{ => UserLand}/UserInterface.cs (95%) create mode 100644 CodingTracker.Kunikazu723/UserLand/Validation.cs diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs new file mode 100644 index 000000000..fd3287951 --- /dev/null +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -0,0 +1,27 @@ +using CodingTracker.Kunikazu723.Models; + +namespace CodingTracker.Kunikazu723.Dao +{ + internal class CodingSessionDao : IDao + { + public void DeleteItemById(int id) + { + throw new NotImplementedException(); + } + + public List GetAllItems() + { + throw new NotImplementedException(); + } + + public void InsertItem(CodingSession item) + { + throw new NotImplementedException(); + } + + public void UpdateItemById(int id) + { + throw new NotImplementedException(); + } + } +} diff --git a/CodingTracker.Kunikazu723/Dao/IDao.cs b/CodingTracker.Kunikazu723/Dao/IDao.cs new file mode 100644 index 000000000..d9c5d144f --- /dev/null +++ b/CodingTracker.Kunikazu723/Dao/IDao.cs @@ -0,0 +1,17 @@ +using CodingTracker.Kunikazu723.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Dao +{ + internal interface IDao where T : class + { + List GetAllItems(); + void InsertItem(T item); + void UpdateItemById(int id); + void DeleteItemById(int id); + } +} diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 6dead0d87..44c4046c8 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -1,4 +1,7 @@ -using CodingTracker.Kunikazu723.Services; +using CodingTracker.Kunikazu723.Dao; +using CodingTracker.Kunikazu723.Models; +using CodingTracker.Kunikazu723.Services; +using CodingTracker.Kunikazu723.UserLand; using Microsoft.Extensions.Configuration; namespace CodingTracker.Kunikazu723 { @@ -20,8 +23,10 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); - var userInput = new UserInput(); - var codeSessionService = new CodeSessionService(); + var validation = new Validation(); + var userInput = new UserInput(dateTimeFormat, validation); + IDao codingSessionDao = new CodingSessionDao(); + var codeSessionService = new CodeSessionService(codingSessionDao, userInput); var userInterface = new UserInterface(userInput, codeSessionService); userInterface.MainMenu(); } diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index ebdaeeb09..dd5754e88 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -1,4 +1,7 @@ -using System; +using CodingTracker.Kunikazu723.Dao; +using CodingTracker.Kunikazu723.Models; +using CodingTracker.Kunikazu723.UserLand; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,8 +11,16 @@ namespace CodingTracker.Kunikazu723.Services { internal class CodeSessionService : IService { + private readonly IDao _sessionDao; + private readonly UserInput _userInput; + public CodeSessionService(IDao sessionDao, UserInput userInput) + { + _sessionDao = sessionDao; + _userInput = userInput; + } public void AddItem() { + throw new NotImplementedException(); } diff --git a/CodingTracker.Kunikazu723/UserInput.cs b/CodingTracker.Kunikazu723/UserInput.cs deleted file mode 100644 index 472f0bf87..000000000 --- a/CodingTracker.Kunikazu723/UserInput.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Spectre.Console; -using static CodingTracker.Kunikazu723.Enums.MenuEnums; - -namespace CodingTracker.Kunikazu723 -{ - public class UserInput - { - public T PromptUserEnumOption() where T: struct, Enum - { - return AnsiConsole.Prompt( - new SelectionPrompt() - .Title("What do you want to do?") - .AddChoices(Enum.GetValues()) - .UseConverter(OptionToString)); - } - } -} diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs new file mode 100644 index 000000000..cdce1f541 --- /dev/null +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -0,0 +1,41 @@ +using CodingTracker.Kunikazu723.Models; +using Spectre.Console; +using static CodingTracker.Kunikazu723.Enums.MenuEnums; + +namespace CodingTracker.Kunikazu723.UserLand +{ + public class UserInput + { + private readonly string _dateTimeFormat; + private readonly Validation _validation; + public UserInput(string dateTimeFormat, Validation validation) + { + _dateTimeFormat = dateTimeFormat; + _validation = validation; + } + public T PromptUserEnumOption() where T: struct, Enum + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .Title("What do you want to do?") + .AddChoices(Enum.GetValues()) + .UseConverter(OptionToString)); + } + public CodingSession GetUserCodingSession() + { + return new CodingSession(); + } + + public string? GetDateTime() + { + string date = AnsiConsole.Ask("Enter the date in the format [bold yellow]dd-MM-yyyy[/]"); + string time = AnsiConsole.Ask("Enter the time in the format HH-mm-ss"); + string userDateTime = date + '-' + time; + if (_validation.IsDateTimeValid(userDateTime, _dateTimeFormat)) + { + return userDateTime; + } + return null; + } + } +} diff --git a/CodingTracker.Kunikazu723/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs similarity index 95% rename from CodingTracker.Kunikazu723/UserInterface.cs rename to CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 56ea73549..e927a5a85 100644 --- a/CodingTracker.Kunikazu723/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -3,7 +3,7 @@ using System.Reflection; using static CodingTracker.Kunikazu723.Enums.MenuEnums; -namespace CodingTracker.Kunikazu723 +namespace CodingTracker.Kunikazu723.UserLand { public class UserInterface { @@ -21,6 +21,7 @@ public void MainMenu() bool isRunning = true; while (isRunning) { + Console.Clear(); MainMenuOptions option = _userInput.PromptUserEnumOption(); switch (option) { @@ -59,10 +60,5 @@ public void WriteBanner(string bannerText = "CODING TRACKER") AnsiConsole.Write(appFiglet); AnsiConsole.Write(new Rule().RuleStyle(Style.Parse("blue dim"))); } - - public void PromptUserMenuOption() - { - - } } } diff --git a/CodingTracker.Kunikazu723/UserLand/Validation.cs b/CodingTracker.Kunikazu723/UserLand/Validation.cs new file mode 100644 index 000000000..b91591fca --- /dev/null +++ b/CodingTracker.Kunikazu723/UserLand/Validation.cs @@ -0,0 +1,10 @@ +using System.Globalization; + +namespace CodingTracker.Kunikazu723.UserLand +{ + public class Validation + { + public bool IsDateTimeValid(string date, string format) => DateTime.TryParseExact(date, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out _); + + } +} From 5b509874192a61fae0d7f06d1c3140887e12c8e3 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:07:28 -0300 Subject: [PATCH 21/93] Make date format a dependency of validation instead of user input --- CodingTracker.Kunikazu723/Program.cs | 4 +- .../UserLand/UserInput.cs | 49 +++++++++++++++---- .../UserLand/Validation.cs | 2 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 44c4046c8..45e8d77c2 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -23,8 +23,8 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); - var validation = new Validation(); - var userInput = new UserInput(dateTimeFormat, validation); + var validation = new Validation(dateTimeFormat); + var userInput = new UserInput(validation); IDao codingSessionDao = new CodingSessionDao(); var codeSessionService = new CodeSessionService(codingSessionDao, userInput); var userInterface = new UserInterface(userInput, codeSessionService); diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index cdce1f541..aa2cad27f 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -6,11 +6,9 @@ namespace CodingTracker.Kunikazu723.UserLand { public class UserInput { - private readonly string _dateTimeFormat; private readonly Validation _validation; - public UserInput(string dateTimeFormat, Validation validation) + public UserInput(Validation validation) { - _dateTimeFormat = dateTimeFormat; _validation = validation; } public T PromptUserEnumOption() where T: struct, Enum @@ -26,16 +24,47 @@ public CodingSession GetUserCodingSession() return new CodingSession(); } - public string? GetDateTime() + public string GetDateTime() { - string date = AnsiConsole.Ask("Enter the date in the format [bold yellow]dd-MM-yyyy[/]"); - string time = AnsiConsole.Ask("Enter the time in the format HH-mm-ss"); - string userDateTime = date + '-' + time; - if (_validation.IsDateTimeValid(userDateTime, _dateTimeFormat)) + // The service should make validation decisions, not the UseriNput class. Since the Service is the business logic and will decide what goes where and how. UserInput should be limited to atomic methods. + while (true) { - return userDateTime; + string date = AnsiConsole.Ask("Enter the date in the format [bold yellow]dd-MM-yyyy[/]"); + string time = AnsiConsole.Ask("Enter the time in the format HH-mm-ss"); + string userDateTime = date + '-' + time; + if (_validation.IsDateTimeValidFormat(userDateTime, out _)) + { + return userDateTime; + } + else + { + AnsiConsole.MarkupLine("[bold red]Invalid Input[/]"); + } + } + } + + public (string StartTime, string EndTime) GetStartEndDate() + { + while (true) + { + AnsiConsole.MarkupLine("[bold yellow]Start Time[/]"); + string startTime = GetDateTime(); + + + Console.WriteLine(); + + AnsiConsole.MarkupLine("[bold yellow]End Time[/]"); + string endTime = GetDateTime(); + + if (_validation.AreDatesChronological(startTime, endTime)) + { + return (startTime, endTime); + } + else + { + AnsiConsole.MarkupLine("[bold red]End Time is earlier than Start Time"); + } } - return null; } } } diff --git a/CodingTracker.Kunikazu723/UserLand/Validation.cs b/CodingTracker.Kunikazu723/UserLand/Validation.cs index b91591fca..d0d86a11b 100644 --- a/CodingTracker.Kunikazu723/UserLand/Validation.cs +++ b/CodingTracker.Kunikazu723/UserLand/Validation.cs @@ -4,7 +4,7 @@ namespace CodingTracker.Kunikazu723.UserLand { public class Validation { - public bool IsDateTimeValid(string date, string format) => DateTime.TryParseExact(date, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out _); + private readonly string _dateFormat; } } From 1962f8f5b085001cc3866b7390f6f76a4238f273 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:08:50 -0300 Subject: [PATCH 22/93] Add data chronological validation The user must input a valid end and start date. So the need of a method that can inform if a start date happened earlier than an end date emerges. --- .../UserLand/Validation.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/Validation.cs b/CodingTracker.Kunikazu723/UserLand/Validation.cs index d0d86a11b..e5c90f72d 100644 --- a/CodingTracker.Kunikazu723/UserLand/Validation.cs +++ b/CodingTracker.Kunikazu723/UserLand/Validation.cs @@ -5,6 +5,21 @@ namespace CodingTracker.Kunikazu723.UserLand public class Validation { private readonly string _dateFormat; - + public Validation(string dateFormat) + { + _dateFormat = dateFormat; + } + public bool IsDateTimeValidFormat(string date, out DateTime parsedDate) => DateTime.TryParseExact(date, _dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate); + public bool AreDatesChronological(string startTime, string endTime) + { + DateTime parsedStartTime; + DateTime parsedEndTime; + if (!IsDateTimeValidFormat(startTime, out parsedStartTime) || !IsDateTimeValidFormat(endTime, out parsedEndTime)) + { + return false; + } + + return parsedEndTime > parsedStartTime; + } } } From 6bf51bee59300dc0bd15522e0d68ad4e4892c4b2 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:09:35 -0300 Subject: [PATCH 23/93] Add a single method that incorporates other user inputs methods na single line for better modularity and debugging --- .../Services/CodeSessionService.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index dd5754e88..f2f69c212 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -1,6 +1,7 @@ using CodingTracker.Kunikazu723.Dao; using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.UserLand; +using Spectre.Console; using System; using System.Collections.Generic; using System.Linq; @@ -20,8 +21,15 @@ public CodeSessionService(IDao sessionDao, UserInput userInput) } public void AddItem() { + (string startTime, string endTime) = _userInput.GetStartEndDate(); - throw new NotImplementedException(); + var codingSession = new CodingSession() + { + StartTime = startTime, + EndTime = endTime + }; + + _sessionDao.InsertItem(codingSession); } public void DeleteItem() From 79cb9f87da8f4434f2bf23db63824c6e9447e63d Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:12:28 -0300 Subject: [PATCH 24/93] Fix unclosed markup tag --- CodingTracker.Kunikazu723/UserLand/UserInput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index aa2cad27f..e95ced8b8 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -62,7 +62,7 @@ public string GetDateTime() } else { - AnsiConsole.MarkupLine("[bold red]End Time is earlier than Start Time"); + AnsiConsole.MarkupLine("[bold red]Invalid Input: End Time is earlier than Start Time[/]"); } } } From dd0dfd7b9d07a344659579989163e9da22c28e37 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:31:54 -0300 Subject: [PATCH 25/93] Move interface to proper directory --- CodingTracker.Kunikazu723/Dao/{ => Interfaces}/IDao.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename CodingTracker.Kunikazu723/Dao/{ => Interfaces}/IDao.cs (79%) diff --git a/CodingTracker.Kunikazu723/Dao/IDao.cs b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs similarity index 79% rename from CodingTracker.Kunikazu723/Dao/IDao.cs rename to CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs index d9c5d144f..3ad359c9f 100644 --- a/CodingTracker.Kunikazu723/Dao/IDao.cs +++ b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; -namespace CodingTracker.Kunikazu723.Dao +namespace CodingTracker.Kunikazu723.Dao.Interfaces { internal interface IDao where T : class { @@ -13,5 +13,6 @@ internal interface IDao where T : class void InsertItem(T item); void UpdateItemById(int id); void DeleteItemById(int id); + void InsertMany(List items); } } From 1a27e12b261ec59519feb6156f348ff216c780c7 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:32:15 -0300 Subject: [PATCH 26/93] Add InsertMany interface memeber --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index fd3287951..8ebc66fa7 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -1,4 +1,5 @@ -using CodingTracker.Kunikazu723.Models; +using CodingTracker.Kunikazu723.Dao.Interfaces; +using CodingTracker.Kunikazu723.Models; namespace CodingTracker.Kunikazu723.Dao { @@ -19,6 +20,11 @@ public void InsertItem(CodingSession item) throw new NotImplementedException(); } + public void InsertMany(List collection) + { + throw new NotImplementedException(); + } + public void UpdateItemById(int id) { throw new NotImplementedException(); From 0eca0d3b0aaaf0a47aa69eb83a18b84bf3d9984c Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:32:30 -0300 Subject: [PATCH 27/93] Correct namespaces --- CodingTracker.Kunikazu723/Services/CodeSessionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index f2f69c212..4fa019b8a 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -1,4 +1,4 @@ -using CodingTracker.Kunikazu723.Dao; +using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.UserLand; using Spectre.Console; From c99cbcd02d053003595b8c61f7fbc86e44e4f2ab Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:33:19 -0300 Subject: [PATCH 28/93] Implement Seeder members --- .../Seeders/CodingSessionSeeder.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs diff --git a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs new file mode 100644 index 000000000..e761b5001 --- /dev/null +++ b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs @@ -0,0 +1,73 @@ +using CodingTracker.Kunikazu723.Dao; +using CodingTracker.Kunikazu723.Dao.Interfaces; +using CodingTracker.Kunikazu723.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using static CodingTracker.Kunikazu723.Enums.DateEnums; + +namespace CodingTracker.Kunikazu723.Seeders +{ + internal class CodingSessionSeeder + { + private readonly IDao _dao; + private readonly string _dateFormat; + private readonly Random _rng = new Random(); + private readonly List _unavailableDates = new List(); + public CodingSessionSeeder(IDao dao, string dateFormat) + { + _dao = dao; + _dateFormat = dateFormat; + } + + public void SeedIfEmpty() + { + if (_dao.GetAllItems().Count == 0) + { + _dao.InsertMany(GenerateData()); + } + } + + private List GenerateData() + { + var data = new List(); + DateTime sessionDate = new DateTime(2025, 01, 01); + for (int i = 0; i < 100; i++) + { + DateTime startTime = GenerateDate(sessionDate, 12, TimeType.Hour); + DateTime endTime = GenerateDate(startTime, 5, TimeType.Hour); + + data.Add( + new CodingSession() + { + StartTime = startTime.ToString(_dateFormat), + EndTime = endTime.ToString(_dateFormat), + }); + + sessionDate = GenerateDate(endTime, 7, TimeType.Day); + } + + return data; + } + + private DateTime GenerateDate(DateTime startDate, int range, TimeType rangeType) + { + if (range <= 0) + { + throw new ArgumentException("range cannot be lesser than 1"); + } + + return rangeType switch + { + TimeType.Day => startDate.AddDays(_rng.Next(range)), + TimeType.Hour => startDate.AddHours(_rng.Next(range)), + TimeType.Minute => startDate.AddMinutes(_rng.Next(range)), + TimeType.Second => startDate.AddSeconds(_rng.Next(range)), + _ => throw new NotImplementedException() + }; + } + } +} From 8d270ed065723116d1c07599fcffa78ed37d9255 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:33:38 -0300 Subject: [PATCH 29/93] Create enum to handle random date generation methods --- CodingTracker.Kunikazu723/Enums/DateEnums.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 CodingTracker.Kunikazu723/Enums/DateEnums.cs diff --git a/CodingTracker.Kunikazu723/Enums/DateEnums.cs b/CodingTracker.Kunikazu723/Enums/DateEnums.cs new file mode 100644 index 000000000..724a773b1 --- /dev/null +++ b/CodingTracker.Kunikazu723/Enums/DateEnums.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Enums +{ + public class DateEnums + { + public enum TimeType + { + Day, + Hour, + Minute, + Second + } + } +} From 4d35c756726056c755c0a14f437021bbbc8accf5 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:34:39 -0300 Subject: [PATCH 30/93] Correct namespace for moved Interface --- CodingTracker.Kunikazu723/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 45e8d77c2..6e38c25ba 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -1,4 +1,5 @@ using CodingTracker.Kunikazu723.Dao; +using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.Services; using CodingTracker.Kunikazu723.UserLand; From 5b7d938f0e1b7509abbf0c62d6f69be7ad4f5848 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:59:27 -0300 Subject: [PATCH 31/93] Add seeder to app's entry point. --- CodingTracker.Kunikazu723/Program.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 6e38c25ba..0237266f2 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -1,6 +1,7 @@ using CodingTracker.Kunikazu723.Dao; using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; +using CodingTracker.Kunikazu723.Seeders; using CodingTracker.Kunikazu723.Services; using CodingTracker.Kunikazu723.UserLand; using Microsoft.Extensions.Configuration; @@ -26,7 +27,11 @@ static void Main(string[] args) var validation = new Validation(dateTimeFormat); var userInput = new UserInput(validation); + IDao codingSessionDao = new CodingSessionDao(); + var codingSessionSeeder = new CodingSessionSeeder(codingSessionDao, dateTimeFormat); + codingSessionSeeder.SeedIfEmpty(); + var codeSessionService = new CodeSessionService(codingSessionDao, userInput); var userInterface = new UserInterface(userInput, codeSessionService); userInterface.MainMenu(); From 5ae6035347feeb06cb56322f4a16bb67a30dfed0 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:59:14 -0300 Subject: [PATCH 32/93] Implement Database Intializer --- .../DatabaseInitializer.cs | 31 +++++++++++++++++++ CodingTracker.Kunikazu723/Program.cs | 4 +-- 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 CodingTracker.Kunikazu723/DatabaseInitializer.cs diff --git a/CodingTracker.Kunikazu723/DatabaseInitializer.cs b/CodingTracker.Kunikazu723/DatabaseInitializer.cs new file mode 100644 index 000000000..9a03d83da --- /dev/null +++ b/CodingTracker.Kunikazu723/DatabaseInitializer.cs @@ -0,0 +1,31 @@ +using Dapper; +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723 +{ + public class DatabaseInitializer + { + private readonly string _connectionString; + public DatabaseInitializer(string connectionString) + { + _connectionString = connectionString; + } + + public void Initialize() + { + using var connection = new SQLiteConnection(_connectionString); + connection.Execute(""" + CREATE TABLE IF NOT EXISTS CodingSessions ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + StartTime TEXT UNIQUE NOT NULL, + EndTime TEXT UNIQUE NOT NULL + ) + """); + } + } +} diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 0237266f2..59fdbc8cb 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -25,8 +25,8 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); - var validation = new Validation(dateTimeFormat); - var userInput = new UserInput(validation); + var dbInitializer = new DatabaseInitializer(connectionString); + dbInitializer.Initialize(); IDao codingSessionDao = new CodingSessionDao(); var codingSessionSeeder = new CodingSessionSeeder(codingSessionDao, dateTimeFormat); From 74b271b7dde21c0a674a1fd0f86b90674a841f1a Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:59:40 -0300 Subject: [PATCH 33/93] Link overall dependencies properly --- CodingTracker.Kunikazu723/Program.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 59fdbc8cb..61e85f902 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -28,11 +28,14 @@ static void Main(string[] args) var dbInitializer = new DatabaseInitializer(connectionString); dbInitializer.Initialize(); - IDao codingSessionDao = new CodingSessionDao(); + IDao codingSessionDao = new CodingSessionDao(connectionString); var codingSessionSeeder = new CodingSessionSeeder(codingSessionDao, dateTimeFormat); codingSessionSeeder.SeedIfEmpty(); - var codeSessionService = new CodeSessionService(codingSessionDao, userInput); + var validation = new Validation(dateTimeFormat); + var userInput = new UserInput(validation); + + var codeSessionService = new CodeSessionService(codingSessionDao, userInput, dateTimeFormat); var userInterface = new UserInterface(userInput, codeSessionService); userInterface.MainMenu(); } From 7e4b99087bf221720d90af7b261f87c641a7b6db Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:00:20 -0300 Subject: [PATCH 34/93] Implement InsertMany --- .../Dao/CodingSessionDao.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 8ebc66fa7..067ab9cf5 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -1,10 +1,20 @@ using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; +using CodingTracker.Kunikazu723.Seeders; +using Dapper; +using System.Data.SQLite; namespace CodingTracker.Kunikazu723.Dao { internal class CodingSessionDao : IDao { + private readonly string _connectionString; + + public CodingSessionDao(string connectionString) + { + _connectionString = connectionString; + } + public void DeleteItemById(int id) { throw new NotImplementedException(); @@ -22,7 +32,15 @@ public void InsertItem(CodingSession item) public void InsertMany(List collection) { - throw new NotImplementedException(); + using var connection = new SQLiteConnection(_connectionString); + connection.Open(); + + using var transaction = connection.BeginTransaction(); + connection.Execute(""" + INSERT INTO CodingSessions (StartTime, EndTime) + VALUES (@StartTime, @EndTime) + """, collection); + transaction.Commit(); } public void UpdateItemById(int id) From c48e77dfd54d661cad33f7859dd94bc7305630ec Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:00:32 -0300 Subject: [PATCH 35/93] Implement GetAllItems --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 067ab9cf5..a7d5c26f9 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -22,7 +22,11 @@ public void DeleteItemById(int id) public List GetAllItems() { - throw new NotImplementedException(); + using var connection = new SQLiteConnection(_connectionString); + string sql = """ + SELECT * FROM CodingSessions + """; + return connection.Query(sql).ToList(); } public void InsertItem(CodingSession item) From a414aa60373ea5863fa525de17acfdb3e323eafd Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:01:05 -0300 Subject: [PATCH 36/93] Fix Seeder generating repeated dates --- .../Seeders/CodingSessionSeeder.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs index e761b5001..46e2fb807 100644 --- a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs +++ b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs @@ -59,13 +59,13 @@ private DateTime GenerateDate(DateTime startDate, int range, TimeType rangeType) { throw new ArgumentException("range cannot be lesser than 1"); } - + int randomNumber = _rng.Next(1, range); return rangeType switch { - TimeType.Day => startDate.AddDays(_rng.Next(range)), - TimeType.Hour => startDate.AddHours(_rng.Next(range)), - TimeType.Minute => startDate.AddMinutes(_rng.Next(range)), - TimeType.Second => startDate.AddSeconds(_rng.Next(range)), + TimeType.Day => startDate.AddDays(randomNumber), + TimeType.Hour => startDate.AddHours(randomNumber), + TimeType.Minute => startDate.AddMinutes(randomNumber), + TimeType.Second => startDate.AddSeconds(randomNumber), _ => throw new NotImplementedException() }; } From 6f382504b7d7cd1c452e03dfd7296bb0d1f42f4f Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:01:21 -0300 Subject: [PATCH 37/93] Implement ViewAllItems --- .../Services/CodeSessionService.cs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index 4fa019b8a..dc78c4605 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -4,6 +4,7 @@ using Spectre.Console; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -14,10 +15,12 @@ internal class CodeSessionService : IService { private readonly IDao _sessionDao; private readonly UserInput _userInput; - public CodeSessionService(IDao sessionDao, UserInput userInput) + private readonly string _dateFormat; + public CodeSessionService(IDao sessionDao, UserInput userInput, string dateFormat) { _sessionDao = sessionDao; _userInput = userInput; + _dateFormat = dateFormat; } public void AddItem() { @@ -44,7 +47,33 @@ public void UpdateItem() public void ViewAllItems() { - throw new NotImplementedException(); + List codingSessions = _sessionDao.GetAllItems(); + // TODO: Instead of displaying it here in the service. Make the service create a table that will be presented at the UserInterface. + foreach (CodingSession codingSession in codingSessions) + { + try + { + DateTime parsedStartTime = ParseStringTime(codingSession.StartTime); + DateTime parsedEndTime = ParseStringTime(codingSession.EndTime); + TimeSpan duration = (parsedEndTime - parsedStartTime); + Console.WriteLine($"ID: {codingSession.Id} Start: {codingSession.StartTime} End: {codingSession.EndTime} Duration: {duration.Hours} hours {duration.Minutes} minutes and {duration.Seconds} seconds"); + + } catch (ArgumentException ex) + { + AnsiConsole.MarkupLine($"[bold red]WARNING: \t{ex.Message}"); + } + + } + } + + private DateTime ParseStringTime(string time) + { + DateTime result; + if (!DateTime.TryParseExact(time, _dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) + { + throw new ArgumentException($"Time String {time} does not follow proper date format {_dateFormat}"); + } + return result; } } } From 9aafad6d471d56ffac26f1822a935cdd2c0cda53 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:03:50 -0300 Subject: [PATCH 38/93] Update .gitignore file to ignore databases --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 49976e115..449d9c285 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +# Databse files +*.db # User-specific files *.rsuser From 425b9d8f8b8652166d243cc0bbf3fb8f4ece280f Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:10:57 -0300 Subject: [PATCH 39/93] Add more information to exception --- CodingTracker.Kunikazu723/Services/CodeSessionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index dc78c4605..3db3472f1 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -60,7 +60,7 @@ public void ViewAllItems() } catch (ArgumentException ex) { - AnsiConsole.MarkupLine($"[bold red]WARNING: \t{ex.Message}"); + AnsiConsole.MarkupLine($"[bold red]WARNING: \t{ex.Message} at session with ID {codingSession.Id}[/]"); } } From 0158505fdf3f88fe594dd0264b4e408d4a695a79 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:42:57 -0300 Subject: [PATCH 40/93] Make Dao interface public to properly follow pre-estabilished pattern --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 2 +- CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index a7d5c26f9..17a12e74f 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -6,7 +6,7 @@ namespace CodingTracker.Kunikazu723.Dao { - internal class CodingSessionDao : IDao + public class CodingSessionDao : IDao { private readonly string _connectionString; diff --git a/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs index 3ad359c9f..d2aee5868 100644 --- a/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs +++ b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs @@ -7,7 +7,7 @@ namespace CodingTracker.Kunikazu723.Dao.Interfaces { - internal interface IDao where T : class + public interface IDao where T : class { List GetAllItems(); void InsertItem(T item); From 72147a2187b01132b23d18699af6257819cd4397 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:48:07 -0300 Subject: [PATCH 41/93] Remove CRUD methods from Service class and delegate them to the User Interface By following the separation of concerns priciple, dealing with CRUD operations inside the Service class became redundant. If user interaction (prompts and display of iformations) should be at the User Interface layer, then having them at the Service breaks the principle. Not having any user interaction with the Service class makes interaction with the data layer redundant since validation occurs at the user interface layer. One specific use case for the service class is the generation of the UI table that is responsible for displaying coding sessions information. --- CodingTracker.Kunikazu723/Program.cs | 2 +- .../Services/CodeSessionService.cs | 40 ++++---------- .../Services/IService.cs | 16 ------ .../UserLand/UserInterface.cs | 52 ++++++++++++++++--- 4 files changed, 55 insertions(+), 55 deletions(-) delete mode 100644 CodingTracker.Kunikazu723/Services/IService.cs diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 61e85f902..0097a4b93 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -36,7 +36,7 @@ static void Main(string[] args) var userInput = new UserInput(validation); var codeSessionService = new CodeSessionService(codingSessionDao, userInput, dateTimeFormat); - var userInterface = new UserInterface(userInput, codeSessionService); + var userInterface = new UserInterface(userInput, codeSessionService, codingSessionDao); userInterface.MainMenu(); } } diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index 3db3472f1..1fb405817 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -11,7 +11,7 @@ namespace CodingTracker.Kunikazu723.Services { - internal class CodeSessionService : IService + public class CodeSessionService { private readonly IDao _sessionDao; private readonly UserInput _userInput; @@ -22,33 +22,11 @@ public CodeSessionService(IDao sessionDao, UserInput userInput, s _userInput = userInput; _dateFormat = dateFormat; } - public void AddItem() - { - (string startTime, string endTime) = _userInput.GetStartEndDate(); - - var codingSession = new CodingSession() - { - StartTime = startTime, - EndTime = endTime - }; - _sessionDao.InsertItem(codingSession); - } - - public void DeleteItem() + public Table GenerateCodingSessionsTable(List codingSessions) { - throw new NotImplementedException(); - } - - public void UpdateItem() - { - throw new NotImplementedException(); - } - - public void ViewAllItems() - { - List codingSessions = _sessionDao.GetAllItems(); - // TODO: Instead of displaying it here in the service. Make the service create a table that will be presented at the UserInterface. + var table = new Table().Title("Coding Sessions"); + table.AddColumns("Id", "Start Time", "End Time", "Duration (hh:mm:ss)"); foreach (CodingSession codingSession in codingSessions) { try @@ -56,14 +34,16 @@ public void ViewAllItems() DateTime parsedStartTime = ParseStringTime(codingSession.StartTime); DateTime parsedEndTime = ParseStringTime(codingSession.EndTime); TimeSpan duration = (parsedEndTime - parsedStartTime); - Console.WriteLine($"ID: {codingSession.Id} Start: {codingSession.StartTime} End: {codingSession.EndTime} Duration: {duration.Hours} hours {duration.Minutes} minutes and {duration.Seconds} seconds"); + table.AddRow(codingSession.Id.ToString(), codingSession.StartTime, codingSession.EndTime, duration.ToString(@"hh\:mm\:ss")); - } catch (ArgumentException ex) + } + catch (ArgumentException ex) { - AnsiConsole.MarkupLine($"[bold red]WARNING: \t{ex.Message} at session with ID {codingSession.Id}[/]"); + AnsiConsole.MarkupLine($"[bold red]WARNING: session with ID: {codingSession.Id} \t{ex.Message}[/]"); } - + } + return table; } private DateTime ParseStringTime(string time) diff --git a/CodingTracker.Kunikazu723/Services/IService.cs b/CodingTracker.Kunikazu723/Services/IService.cs deleted file mode 100644 index ccce98dea..000000000 --- a/CodingTracker.Kunikazu723/Services/IService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CodingTracker.Kunikazu723.Services -{ - public interface IService - { - void ViewAllItems(); - void AddItem(); - void UpdateItem(); - void DeleteItem(); - } -} diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index e927a5a85..e607511a3 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -1,5 +1,9 @@ -using CodingTracker.Kunikazu723.Services; +using CodingTracker.Kunikazu723.Dao.Interfaces; +using CodingTracker.Kunikazu723.Models; +using CodingTracker.Kunikazu723.Services; +using Microsoft.VisualBasic; using Spectre.Console; +using System.Globalization; using System.Reflection; using static CodingTracker.Kunikazu723.Enums.MenuEnums; @@ -8,11 +12,14 @@ namespace CodingTracker.Kunikazu723.UserLand public class UserInterface { private readonly UserInput _userInput; - private readonly IService _service; - public UserInterface(UserInput userInput, IService service) + private readonly CodeSessionService _service; + private readonly IDao _dao; + + public UserInterface(UserInput userInput, CodeSessionService service, IDao dao) { _userInput = userInput; _service = service; + _dao = dao; } public void MainMenu() { @@ -26,16 +33,16 @@ public void MainMenu() switch (option) { case MainMenuOptions.ViewSessions: - _service.ViewAllItems(); + ViewSessions(); break; case MainMenuOptions.LogSessions: - _service.AddItem(); + LogSession(); break; case MainMenuOptions.UpdateSession: - _service.UpdateItem(); + UpdateSession(); break; case MainMenuOptions.DeleteSession: - _service.DeleteItem(); + DeleteSession(); break; case MainMenuOptions.Exit: AnsiConsole.MarkupLine("Exiting [bold cyan]Coding Tracker[/]..."); @@ -44,12 +51,41 @@ public void MainMenu() default: break; } - + AnsiConsole.MarkupLine("[bold grey]Press Any Key To Continue[/]"); Console.ReadKey(); } } + private void DeleteSession() + { + throw new NotImplementedException(); + } + + private void UpdateSession() + { + throw new NotImplementedException(); + } + + private void LogSession() + { + (string startTime, string endTime) = _userInput.GetStartEndDate(); + + var codingSession = new CodingSession() + { + StartTime = startTime, + EndTime = endTime + }; + + _dao.InsertItem(codingSession); + } + + private void ViewSessions() + { + var sessionsTable = _service.GenerateCodingSessionsTable(_dao.GetAllItems()); + AnsiConsole.Write(sessionsTable); + } + public void WriteBanner(string bannerText = "CODING TRACKER") { var figletFont = FigletFont.Load("./FigletFonts/smkeyboard.flf"); From 871af8f3fb7a13c3bae24d5a96dfb38393f7b53f Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:48:23 -0300 Subject: [PATCH 42/93] Remove whitespace and unnecessary comments --- CodingTracker.Kunikazu723/UserLand/UserInput.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index e95ced8b8..247f493f4 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -26,7 +26,6 @@ public CodingSession GetUserCodingSession() public string GetDateTime() { - // The service should make validation decisions, not the UseriNput class. Since the Service is the business logic and will decide what goes where and how. UserInput should be limited to atomic methods. while (true) { string date = AnsiConsole.Ask("Enter the date in the format [bold yellow]dd-MM-yyyy[/]"); @@ -50,7 +49,6 @@ public string GetDateTime() AnsiConsole.MarkupLine("[bold yellow]Start Time[/]"); string startTime = GetDateTime(); - Console.WriteLine(); AnsiConsole.MarkupLine("[bold yellow]End Time[/]"); From 4be2fc7d0e7a217dc62242315fa0d5d389b6b56e Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:49:50 -0300 Subject: [PATCH 43/93] Apply little DRY principle --- CodingTracker.Kunikazu723/Services/CodeSessionService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index 1fb405817..cf327149f 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -25,8 +25,9 @@ public CodeSessionService(IDao sessionDao, UserInput userInput, s public Table GenerateCodingSessionsTable(List codingSessions) { + string durationFormat = @"hh\:mm\:ss"; var table = new Table().Title("Coding Sessions"); - table.AddColumns("Id", "Start Time", "End Time", "Duration (hh:mm:ss)"); + table.AddColumns("Id", "Start Time", "End Time", $"Duration ({durationFormat})"); foreach (CodingSession codingSession in codingSessions) { try @@ -34,7 +35,7 @@ public Table GenerateCodingSessionsTable(List codingSessions) DateTime parsedStartTime = ParseStringTime(codingSession.StartTime); DateTime parsedEndTime = ParseStringTime(codingSession.EndTime); TimeSpan duration = (parsedEndTime - parsedStartTime); - table.AddRow(codingSession.Id.ToString(), codingSession.StartTime, codingSession.EndTime, duration.ToString(@"hh\:mm\:ss")); + table.AddRow(codingSession.Id.ToString(), codingSession.StartTime, codingSession.EndTime, duration.ToString(durationFormat)); } catch (ArgumentException ex) From a7e24481bf1291cc9bb3f6f44353f7b54deb23e4 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:55:37 -0300 Subject: [PATCH 44/93] Change user input get date method to be consistent with defined date format from appsettings.json --- CodingTracker.Kunikazu723/UserLand/UserInput.cs | 8 +++----- CodingTracker.Kunikazu723/UserLand/Validation.cs | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index 247f493f4..7fd8b468c 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -28,12 +28,10 @@ public string GetDateTime() { while (true) { - string date = AnsiConsole.Ask("Enter the date in the format [bold yellow]dd-MM-yyyy[/]"); - string time = AnsiConsole.Ask("Enter the time in the format HH-mm-ss"); - string userDateTime = date + '-' + time; - if (_validation.IsDateTimeValidFormat(userDateTime, out _)) + string date = AnsiConsole.Ask($"Enter the date in the format [bold yellow]{_validation.DateFormat}[/]"); + if (_validation.IsDateTimeValidFormat(date, out _)) { - return userDateTime; + return date; } else { diff --git a/CodingTracker.Kunikazu723/UserLand/Validation.cs b/CodingTracker.Kunikazu723/UserLand/Validation.cs index e5c90f72d..192e54f91 100644 --- a/CodingTracker.Kunikazu723/UserLand/Validation.cs +++ b/CodingTracker.Kunikazu723/UserLand/Validation.cs @@ -4,12 +4,12 @@ namespace CodingTracker.Kunikazu723.UserLand { public class Validation { - private readonly string _dateFormat; + public readonly string DateFormat; public Validation(string dateFormat) { - _dateFormat = dateFormat; + DateFormat = dateFormat; } - public bool IsDateTimeValidFormat(string date, out DateTime parsedDate) => DateTime.TryParseExact(date, _dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate); + public bool IsDateTimeValidFormat(string date, out DateTime parsedDate) => DateTime.TryParseExact(date, DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate); public bool AreDatesChronological(string startTime, string endTime) { DateTime parsedStartTime; From 24478e54c81b275552e41120d81d931dd88bdbc5 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:00:17 -0300 Subject: [PATCH 45/93] Implement InsertItem --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 17a12e74f..9d69f19a5 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -31,7 +31,11 @@ public List GetAllItems() public void InsertItem(CodingSession item) { - throw new NotImplementedException(); + using var connection = new SQLiteConnection(_connectionString); + var sql = """ + INSERT INTO CodingSessions (StartTime, EndTime) VALUES (@StartTime, @EndTime) + """; + connection.Execute(sql, item); } public void InsertMany(List collection) From 830cf2a1135e50d1aaf1ebaac2a2f009e1ca2c1c Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:19:45 -0300 Subject: [PATCH 46/93] Remove unnecessary comment --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index e607511a3..b36b665fe 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -23,12 +23,11 @@ public UserInterface(UserInput userInput, CodeSessionService service, IDao(); switch (option) { From 14906533f101201deff04cb4ac1331efb894b406 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:57:27 -0300 Subject: [PATCH 47/93] Implement Delete and Update --- .../Services/CodeSessionService.cs | 7 +++- .../UserLand/UserInput.cs | 11 +++++ .../UserLand/UserInterface.cs | 40 ++++++++++++++----- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index cf327149f..651f3cdc4 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -23,10 +23,13 @@ public CodeSessionService(IDao sessionDao, UserInput userInput, s _dateFormat = dateFormat; } - public Table GenerateCodingSessionsTable(List codingSessions) + public Table GenerateCodingSessionsTable(List codingSessions, string? tableTitle = null) { string durationFormat = @"hh\:mm\:ss"; - var table = new Table().Title("Coding Sessions"); + var table = new Table(); + if (tableTitle is not null) + table.Title(tableTitle); + table.AddColumns("Id", "Start Time", "End Time", $"Duration ({durationFormat})"); foreach (CodingSession codingSession in codingSessions) { diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index 7fd8b468c..ea0ca643f 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -11,6 +11,17 @@ public UserInput(Validation validation) { _validation = validation; } + + public int AskUserForPositiveInteger(string prompt) + { + while (true) + { + int input = AnsiConsole.Ask(prompt); + if (input > 0) + return input; + } + } + public T PromptUserEnumOption() where T: struct, Enum { return AnsiConsole.Prompt( diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index b36b665fe..9b776cab0 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -3,6 +3,7 @@ using CodingTracker.Kunikazu723.Services; using Microsoft.VisualBasic; using Spectre.Console; +using System.Data.SQLite; using System.Globalization; using System.Reflection; using static CodingTracker.Kunikazu723.Enums.MenuEnums; @@ -27,8 +28,9 @@ public void MainMenu() while (isRunning) { Console.Clear(); - WriteBanner(); + WriteBanner(Color.Blue); MainMenuOptions option = _userInput.PromptUserEnumOption(); + Console.Clear(); switch (option) { case MainMenuOptions.ViewSessions: @@ -56,14 +58,28 @@ public void MainMenu() } + private void ExecuteOption(MainMenuOptions option) + { + + } + + private int PromptForSessionId(Color bannerColor, string bannerTitle) + { + WriteBanner(Color.Red, "Delete Session"); + ViewSessions(); + return _userInput.AskUserForPositiveInteger("Enter the Session's ID"); + } + private void DeleteSession() { - throw new NotImplementedException(); + int id = PromptForSessionId(Color.Red, "Delete Session"); + _dao.DeleteItemById(id); } private void UpdateSession() { - throw new NotImplementedException(); + int id = PromptForSessionId(Color.Cyan, "Update Session"); + _dao.UpdateItemById(id); } private void LogSession() @@ -81,19 +97,25 @@ private void LogSession() private void ViewSessions() { - var sessionsTable = _service.GenerateCodingSessionsTable(_dao.GetAllItems()); + var sessionsTable = _service.GenerateCodingSessionsTable(_dao.GetAllItems(), "Coding Sessions"); AnsiConsole.Write(sessionsTable); } - public void WriteBanner(string bannerText = "CODING TRACKER") + public void WriteBanner(Color color, string bannerText = "CODING TRACKER") { var figletFont = FigletFont.Load("./FigletFonts/smkeyboard.flf"); - var appFiglet = new FigletText(figletFont, "CODING TRACKER") - .Color(Color.Blue); + var appFiglet = new FigletText(figletFont, bannerText) + .Color(color); - AnsiConsole.Write(new Rule().RuleStyle(Style.Parse("blue dim"))); + var ruleStyle = Style.Parse(color.ToMarkup()); + AnsiConsole.Write(new Rule().RuleStyle(ruleStyle)); AnsiConsole.Write(appFiglet); - AnsiConsole.Write(new Rule().RuleStyle(Style.Parse("blue dim"))); + AnsiConsole.Write(new Rule().RuleStyle(ruleStyle)); + } + + void DisplayMessage(string message, Color color) + { + AnsiConsole.MarkupLine($"[{color.ToMarkup()}]{Markup.Escape(message)}[/]"); } } } From 42f626b2a28987e0868e821f2a07b67647ed51b5 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 02:02:34 -0300 Subject: [PATCH 48/93] Refactor code to make it easier to handle possible database errors --- .../UserLand/UserInterface.cs | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 9b776cab0..bebf2844c 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -31,27 +31,17 @@ public void MainMenu() WriteBanner(Color.Blue); MainMenuOptions option = _userInput.PromptUserEnumOption(); Console.Clear(); - switch (option) + try { - case MainMenuOptions.ViewSessions: - ViewSessions(); - break; - case MainMenuOptions.LogSessions: - LogSession(); - break; - case MainMenuOptions.UpdateSession: - UpdateSession(); - break; - case MainMenuOptions.DeleteSession: - DeleteSession(); - break; - case MainMenuOptions.Exit: - AnsiConsole.MarkupLine("Exiting [bold cyan]Coding Tracker[/]..."); - isRunning = false; - break; - default: - break; + ExecuteOption(option); + } catch (SQLiteException ex) + { + // TODO: Make specific messages for error code (Like 'invalid ID' for a specific error code) + DisplayMessage($"DATABASE ERROR: \n\t{ex.Message}", Color.Red); } + if (option == MainMenuOptions.Exit) + isRunning = false; + AnsiConsole.MarkupLine("[bold grey]Press Any Key To Continue[/]"); Console.ReadKey(); } @@ -60,7 +50,26 @@ public void MainMenu() private void ExecuteOption(MainMenuOptions option) { - + switch (option) + { + case MainMenuOptions.ViewSessions: + ViewSessions(); + break; + case MainMenuOptions.LogSessions: + LogSession(); + break; + case MainMenuOptions.UpdateSession: + UpdateSession(); + break; + case MainMenuOptions.DeleteSession: + DeleteSession(); + break; + case MainMenuOptions.Exit: + AnsiConsole.MarkupLine("Exiting [bold cyan]Coding Tracker[/]..."); + break; + default: + break; + } } private int PromptForSessionId(Color bannerColor, string bannerTitle) From fc466be36a8a875e02b561f8fecc3c3c8e05cf71 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:40:16 -0300 Subject: [PATCH 49/93] Remove Banner presentation where it didn't belong --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index bebf2844c..53b5b9e0e 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -72,7 +72,7 @@ private void ExecuteOption(MainMenuOptions option) } } - private int PromptForSessionId(Color bannerColor, string bannerTitle) + private int PromptForSessionId() { WriteBanner(Color.Red, "Delete Session"); ViewSessions(); From 62241ae91064db3e798ae34a965c2edefd4f2951 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:41:07 -0300 Subject: [PATCH 50/93] Get new coding session in a single method, avoiding future repetition --- CodingTracker.Kunikazu723/UserLand/UserInput.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index ea0ca643f..1abad9b34 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -32,7 +32,14 @@ public T PromptUserEnumOption() where T: struct, Enum } public CodingSession GetUserCodingSession() { - return new CodingSession(); + (string startTime, string endTime) = GetStartEndDate(); + + var codingSession = new CodingSession() + { + StartTime = startTime, + EndTime = endTime + }; + return codingSession; } public string GetDateTime() From 3a42647d6cd331624badad5f188c61ebcaf6a007 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:42:02 -0300 Subject: [PATCH 51/93] Apply GetUserCodingSession in Insertion --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 53b5b9e0e..2294d46cf 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -93,13 +93,8 @@ private void UpdateSession() private void LogSession() { - (string startTime, string endTime) = _userInput.GetStartEndDate(); - - var codingSession = new CodingSession() - { - StartTime = startTime, - EndTime = endTime - }; + WriteBanner(Color.Yellow, "Log Session"); + var codingSession = _userInput.GetUserCodingSession(); _dao.InsertItem(codingSession); } From d1726fce62be3fa9e3dc037c07727a0453c2f7d1 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:42:57 -0300 Subject: [PATCH 52/93] Change Interface contract to align with ORM paradigms --- CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs index d2aee5868..df597594b 100644 --- a/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs +++ b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs @@ -10,9 +10,10 @@ namespace CodingTracker.Kunikazu723.Dao.Interfaces public interface IDao where T : class { List GetAllItems(); + T? GetItemById(int id); void InsertItem(T item); - void UpdateItemById(int id); - void DeleteItemById(int id); + void UpdateItemById(T newItem); + void DeleteItemById(T item); void InsertMany(List items); } } From 3623af294bc2547984f64a7601d5a6c6421408be Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:43:29 -0300 Subject: [PATCH 53/93] Apply Interface change in derived class --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 9d69f19a5..e26aaaa95 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -15,7 +15,7 @@ public CodingSessionDao(string connectionString) _connectionString = connectionString; } - public void DeleteItemById(int id) + public void DeleteItemById(CodingSession codingSession) { throw new NotImplementedException(); } @@ -51,7 +51,7 @@ INSERT INTO CodingSessions (StartTime, EndTime) transaction.Commit(); } - public void UpdateItemById(int id) + public void UpdateItemById(CodingSession newItem) { throw new NotImplementedException(); } From 8cac8320b2c20727ab8d0bb43fd18b866ece48f9 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:43:46 -0300 Subject: [PATCH 54/93] Implement session deletion --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index e26aaaa95..1cde900b5 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -17,7 +17,11 @@ public CodingSessionDao(string connectionString) public void DeleteItemById(CodingSession codingSession) { - throw new NotImplementedException(); + using var connection = new SQLiteConnection(_connectionString); + var sql = """ + DELETE FROM CodingSessions WHERE Id = @Id + """; + connection.Execute(sql, codingSession); } public List GetAllItems() From b9dfab769dedf473087597ae821bc8b49d7e21b0 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:44:22 -0300 Subject: [PATCH 55/93] Implement remaining Interface member --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 1cde900b5..04bb41542 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -33,6 +33,15 @@ public List GetAllItems() return connection.Query(sql).ToList(); } + public CodingSession? GetItemById(int id) + { + using var connection = new SQLiteConnection(_connectionString); + var sql = """ + SELECT * FROM CodingSessions WHERE Id = @itemId + """; + return connection.QuerySingleOrDefault(sql, new { itemId = id }); + } + public void InsertItem(CodingSession item) { using var connection = new SQLiteConnection(_connectionString); From d514827862681074eb2d6957b1119fdbab57fb73 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:44:41 -0300 Subject: [PATCH 56/93] Implement session Update --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 04bb41542..bd3dbb37c 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -66,7 +66,13 @@ INSERT INTO CodingSessions (StartTime, EndTime) public void UpdateItemById(CodingSession newItem) { - throw new NotImplementedException(); + using var connection = new SQLiteConnection(_connectionString); + var sql = """ + UPDATE CodingSessions + SET StartTime = @StartTime, EndTime = @EndTime + WHERE Id = @Id + """; + connection.Execute(sql, newItem); } } } From 0135b6acdecbc557d61bceb3dd384eab567b0c4a Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:44:54 -0300 Subject: [PATCH 57/93] Remove unnecessary comment --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 2294d46cf..b47180552 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -36,7 +36,6 @@ public void MainMenu() ExecuteOption(option); } catch (SQLiteException ex) { - // TODO: Make specific messages for error code (Like 'invalid ID' for a specific error code) DisplayMessage($"DATABASE ERROR: \n\t{ex.Message}", Color.Red); } if (option == MainMenuOptions.Exit) From b39d4f6c696e9213ee0a5361b09aa776e07a961a Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:46:31 -0300 Subject: [PATCH 58/93] Create method to prompt user for an existing coding session This method avoids code repetition where an already existing coding session is needed to perform certain operations. E.g. At deletion and update, the user needs to inform a valid session, failing to do so will result in a null coding session object. --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index b47180552..847b08853 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -78,6 +78,17 @@ private int PromptForSessionId() return _userInput.AskUserForPositiveInteger("Enter the Session's ID"); } + private CodingSession? PromptForExistingSession() + { + int id = PromptForSessionId(); + var codingSession = _dao.GetItemById(id); + if (codingSession is null) + { + DisplayMessage($"Coding Session With ID {id} not found\nAborting process", Color.Red); + } + return codingSession; + } + private void DeleteSession() { int id = PromptForSessionId(Color.Red, "Delete Session"); From 780a7fb5c4a83afb226755b3b8ab208c4df08188 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:46:48 -0300 Subject: [PATCH 59/93] Implement DeleteSession --- .../UserLand/UserInterface.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 847b08853..7ee749b4b 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -91,8 +91,23 @@ private int PromptForSessionId() private void DeleteSession() { - int id = PromptForSessionId(Color.Red, "Delete Session"); - _dao.DeleteItemById(id); + WriteBanner(Color.Red, "Delete Session"); + var codingSession = PromptForExistingSession(); + if (codingSession is null) return; + + Table sessionTable = _service.GenerateCodingSessionsTable(new List {codingSession}, "To be Deleted"); + AnsiConsole.Write(sessionTable); + + bool confirmDeletion = AnsiConsole.Confirm("Are you sure you wish to delete permanently this session?"); + if (confirmDeletion) + { + _dao.DeleteItemById(codingSession); + DisplayMessage("Session Deleted successfully", Color.Green); + } + else + { + DisplayMessage("Deletion aborted", Color.Green); + } } private void UpdateSession() From 6f953f342c3c586f21e28b8b9a61bddcf36a29db Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:47:10 -0300 Subject: [PATCH 60/93] Remove misplaced banner --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 7ee749b4b..728f3512c 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -73,7 +73,6 @@ private void ExecuteOption(MainMenuOptions option) private int PromptForSessionId() { - WriteBanner(Color.Red, "Delete Session"); ViewSessions(); return _userInput.AskUserForPositiveInteger("Enter the Session's ID"); } From d968fe9dc0862f2b8fce20780f3ef9a274ca7cae Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:47:19 -0300 Subject: [PATCH 61/93] Implement UpdateSession --- .../UserLand/UserInterface.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 728f3512c..6d0ac3fce 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -111,8 +111,32 @@ private void DeleteSession() private void UpdateSession() { - int id = PromptForSessionId(Color.Cyan, "Update Session"); - _dao.UpdateItemById(id); + WriteBanner(Color.Cyan, "Update Session"); + var currentCodingSession = PromptForExistingSession(); + + if (currentCodingSession is null) return; + + Table currentItemTable = _service.GenerateCodingSessionsTable(new List { currentCodingSession }, "Current Coding Session"); + AnsiConsole.Write(currentItemTable); + + AnsiConsole.MarkupLine("[bold yellow]Enter new Coding Sessino Data[/]"); + var newCodingSession = _userInput.GetUserCodingSession(); + newCodingSession.Id = currentCodingSession.Id; + + Table newItemTable = _service.GenerateCodingSessionsTable(new List { newCodingSession }, "Updated Coding Session"); + AnsiConsole.Write(newItemTable); + + bool confirmUpdate = AnsiConsole.Confirm("Do you wish to apply the Update?", false); + + if (confirmUpdate) + { + _dao.UpdateItemById(newCodingSession); + DisplayMessage("Coding session updated successfuly", Color.Green); + } + else + { + DisplayMessage("Update Aborted. No changes made.", Color.Green); + } } private void LogSession() From faa3b68254f8c70bf368e9aa5c9409f7389d53d1 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:52:44 -0300 Subject: [PATCH 62/93] Add user friendly messages and confirmations --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 6d0ac3fce..55cfb3261 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -97,7 +97,7 @@ private void DeleteSession() Table sessionTable = _service.GenerateCodingSessionsTable(new List {codingSession}, "To be Deleted"); AnsiConsole.Write(sessionTable); - bool confirmDeletion = AnsiConsole.Confirm("Are you sure you wish to delete permanently this session?"); + bool confirmDeletion = AnsiConsole.Confirm("Are you sure you wish to delete permanently this session?", false); if (confirmDeletion) { _dao.DeleteItemById(codingSession); @@ -145,6 +145,7 @@ private void LogSession() var codingSession = _userInput.GetUserCodingSession(); _dao.InsertItem(codingSession); + DisplayMessage("New coding session Successfuly Logged", Color.Green); } private void ViewSessions() From 15807311f0a3b27d7bd786bfb52e2dd26f3c5765 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:02:37 -0300 Subject: [PATCH 63/93] Remove user input dependency from service to porperly align with separation of concerns --- CodingTracker.Kunikazu723/Program.cs | 2 +- CodingTracker.Kunikazu723/Services/CodeSessionService.cs | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 0097a4b93..1cd62da0b 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -35,7 +35,7 @@ static void Main(string[] args) var validation = new Validation(dateTimeFormat); var userInput = new UserInput(validation); - var codeSessionService = new CodeSessionService(codingSessionDao, userInput, dateTimeFormat); + var codeSessionService = new CodeSessionService(codingSessionDao, dateTimeFormat); var userInterface = new UserInterface(userInput, codeSessionService, codingSessionDao); userInterface.MainMenu(); } diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index 651f3cdc4..72c3a3ba3 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -2,24 +2,17 @@ using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.UserLand; using Spectre.Console; -using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace CodingTracker.Kunikazu723.Services { public class CodeSessionService { private readonly IDao _sessionDao; - private readonly UserInput _userInput; private readonly string _dateFormat; - public CodeSessionService(IDao sessionDao, UserInput userInput, string dateFormat) + public CodeSessionService(IDao sessionDao, string dateFormat) { _sessionDao = sessionDao; - _userInput = userInput; _dateFormat = dateFormat; } From 25285ed43a88ee63feb5334d79d4f3db9a2df272 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:28:31 -0300 Subject: [PATCH 64/93] Remove whitespace --- CodingTracker.Kunikazu723/Services/CodeSessionService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index 72c3a3ba3..f5f61cd59 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -43,6 +43,7 @@ public Table GenerateCodingSessionsTable(List codingSessions, str return table; } + private DateTime ParseStringTime(string time) { DateTime result; From 14bd9f9d45b81799addeaeda3f87bcf2c4d11698 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:29:00 -0300 Subject: [PATCH 65/93] Display invalid option if user somehow selects an invalid Enum --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 55cfb3261..28ddbb3ec 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -46,7 +46,6 @@ public void MainMenu() } } - private void ExecuteOption(MainMenuOptions option) { switch (option) @@ -67,6 +66,7 @@ private void ExecuteOption(MainMenuOptions option) AnsiConsole.MarkupLine("Exiting [bold cyan]Coding Tracker[/]..."); break; default: + DisplayMessage("Invalid Option", Color.Red); break; } } From 4e7e185d2fb038f234019e7793923b175880364b Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:31:09 -0300 Subject: [PATCH 66/93] Rewove Dao from Service Since the DAO is now handled by the UserInterface, having it at the Service is wasteful. This ensures that the Service only handles logic and nothing more. --- CodingTracker.Kunikazu723/Program.cs | 2 +- CodingTracker.Kunikazu723/Services/CodeSessionService.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 1cd62da0b..9f4092d69 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -35,7 +35,7 @@ static void Main(string[] args) var validation = new Validation(dateTimeFormat); var userInput = new UserInput(validation); - var codeSessionService = new CodeSessionService(codingSessionDao, dateTimeFormat); + var codeSessionService = new CodeSessionService(dateTimeFormat); var userInterface = new UserInterface(userInput, codeSessionService, codingSessionDao); userInterface.MainMenu(); } diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs index f5f61cd59..ff6b770e0 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/CodeSessionService.cs @@ -8,11 +8,9 @@ namespace CodingTracker.Kunikazu723.Services { public class CodeSessionService { - private readonly IDao _sessionDao; private readonly string _dateFormat; - public CodeSessionService(IDao sessionDao, string dateFormat) + public CodeSessionService(string dateFormat) { - _sessionDao = sessionDao; _dateFormat = dateFormat; } @@ -43,7 +41,6 @@ public Table GenerateCodingSessionsTable(List codingSessions, str return table; } - private DateTime ParseStringTime(string time) { DateTime result; From ccc59973c7f2c881d161dc70bf93c4978dd53259 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 12 Oct 2026 16:12:13 -0300 Subject: [PATCH 67/93] Renaming service to Table Service is too broad and can mean a lot of things depending on which source you base it on. Since the only thing the class is performing now is the build of a readable table(consequence of letting the UI talk directly to the DAO), naming it so makes it clear to what it actually does. --- CodingTracker.Kunikazu723/Program.cs | 4 ++-- .../{CodeSessionService.cs => TableEngine.cs} | 4 ++-- .../UserLand/UserInterface.cs | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) rename CodingTracker.Kunikazu723/Services/{CodeSessionService.cs => TableEngine.cs} (95%) diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index 9f4092d69..bfa6802b1 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -35,8 +35,8 @@ static void Main(string[] args) var validation = new Validation(dateTimeFormat); var userInput = new UserInput(validation); - var codeSessionService = new CodeSessionService(dateTimeFormat); - var userInterface = new UserInterface(userInput, codeSessionService, codingSessionDao); + var tableEngine = new TableEngine(dateTimeFormat); + var userInterface = new UserInterface(userInput, tableEngine, codingSessionDao); userInterface.MainMenu(); } } diff --git a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs b/CodingTracker.Kunikazu723/Services/TableEngine.cs similarity index 95% rename from CodingTracker.Kunikazu723/Services/CodeSessionService.cs rename to CodingTracker.Kunikazu723/Services/TableEngine.cs index ff6b770e0..76e532f3e 100644 --- a/CodingTracker.Kunikazu723/Services/CodeSessionService.cs +++ b/CodingTracker.Kunikazu723/Services/TableEngine.cs @@ -6,10 +6,10 @@ namespace CodingTracker.Kunikazu723.Services { - public class CodeSessionService + public class TableEngine { private readonly string _dateFormat; - public CodeSessionService(string dateFormat) + public TableEngine(string dateFormat) { _dateFormat = dateFormat; } diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 28ddbb3ec..4e719fb7c 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -13,13 +13,13 @@ namespace CodingTracker.Kunikazu723.UserLand public class UserInterface { private readonly UserInput _userInput; - private readonly CodeSessionService _service; + private readonly TableEngine _tableEngine; private readonly IDao _dao; - public UserInterface(UserInput userInput, CodeSessionService service, IDao dao) + public UserInterface(UserInput userInput, TableEngine engine, IDao dao) { _userInput = userInput; - _service = service; + _tableEngine = engine; _dao = dao; } public void MainMenu() @@ -94,7 +94,7 @@ private void DeleteSession() var codingSession = PromptForExistingSession(); if (codingSession is null) return; - Table sessionTable = _service.GenerateCodingSessionsTable(new List {codingSession}, "To be Deleted"); + Table sessionTable = _tableEngine.GenerateCodingSessionsTable(new List {codingSession}, "To be Deleted"); AnsiConsole.Write(sessionTable); bool confirmDeletion = AnsiConsole.Confirm("Are you sure you wish to delete permanently this session?", false); @@ -116,14 +116,14 @@ private void UpdateSession() if (currentCodingSession is null) return; - Table currentItemTable = _service.GenerateCodingSessionsTable(new List { currentCodingSession }, "Current Coding Session"); + Table currentItemTable = _tableEngine.GenerateCodingSessionsTable(new List { currentCodingSession }, "Current Coding Session"); AnsiConsole.Write(currentItemTable); AnsiConsole.MarkupLine("[bold yellow]Enter new Coding Sessino Data[/]"); var newCodingSession = _userInput.GetUserCodingSession(); newCodingSession.Id = currentCodingSession.Id; - Table newItemTable = _service.GenerateCodingSessionsTable(new List { newCodingSession }, "Updated Coding Session"); + Table newItemTable = _tableEngine.GenerateCodingSessionsTable(new List { newCodingSession }, "Updated Coding Session"); AnsiConsole.Write(newItemTable); bool confirmUpdate = AnsiConsole.Confirm("Do you wish to apply the Update?", false); @@ -150,7 +150,7 @@ private void LogSession() private void ViewSessions() { - var sessionsTable = _service.GenerateCodingSessionsTable(_dao.GetAllItems(), "Coding Sessions"); + var sessionsTable = _tableEngine.GenerateCodingSessionsTable(_dao.GetAllItems(), "Coding Sessions"); AnsiConsole.Write(sessionsTable); } From d26bd970c043d4daea436a93d39c3769afefe40a Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 19 Oct 2026 09:55:44 -0300 Subject: [PATCH 68/93] Remove unnecessary 'usings' --- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 1 - CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs | 9 +-------- CodingTracker.Kunikazu723/DatabaseInitializer.cs | 5 ----- CodingTracker.Kunikazu723/Enums/DateEnums.cs | 8 +------- CodingTracker.Kunikazu723/Enums/MenuEnums.cs | 7 +------ CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs | 9 +-------- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 3 --- 7 files changed, 4 insertions(+), 38 deletions(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index bd3dbb37c..24436bfe9 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -1,6 +1,5 @@ using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; -using CodingTracker.Kunikazu723.Seeders; using Dapper; using System.Data.SQLite; diff --git a/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs index df597594b..5bd3fd8ee 100644 --- a/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs +++ b/CodingTracker.Kunikazu723/Dao/Interfaces/IDao.cs @@ -1,11 +1,4 @@ -using CodingTracker.Kunikazu723.Services; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CodingTracker.Kunikazu723.Dao.Interfaces +namespace CodingTracker.Kunikazu723.Dao.Interfaces { public interface IDao where T : class { diff --git a/CodingTracker.Kunikazu723/DatabaseInitializer.cs b/CodingTracker.Kunikazu723/DatabaseInitializer.cs index 9a03d83da..81d19e720 100644 --- a/CodingTracker.Kunikazu723/DatabaseInitializer.cs +++ b/CodingTracker.Kunikazu723/DatabaseInitializer.cs @@ -1,10 +1,5 @@ using Dapper; -using System; -using System.Collections.Generic; using System.Data.SQLite; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace CodingTracker.Kunikazu723 { diff --git a/CodingTracker.Kunikazu723/Enums/DateEnums.cs b/CodingTracker.Kunikazu723/Enums/DateEnums.cs index 724a773b1..f4a80cda0 100644 --- a/CodingTracker.Kunikazu723/Enums/DateEnums.cs +++ b/CodingTracker.Kunikazu723/Enums/DateEnums.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CodingTracker.Kunikazu723.Enums +namespace CodingTracker.Kunikazu723.Enums { public class DateEnums { diff --git a/CodingTracker.Kunikazu723/Enums/MenuEnums.cs b/CodingTracker.Kunikazu723/Enums/MenuEnums.cs index fdf923d6b..663555abe 100644 --- a/CodingTracker.Kunikazu723/Enums/MenuEnums.cs +++ b/CodingTracker.Kunikazu723/Enums/MenuEnums.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; +using System.Text.RegularExpressions; namespace CodingTracker.Kunikazu723.Enums { diff --git a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs index 46e2fb807..d7e3d4193 100644 --- a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs +++ b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs @@ -1,12 +1,5 @@ -using CodingTracker.Kunikazu723.Dao; -using CodingTracker.Kunikazu723.Dao.Interfaces; +using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; using static CodingTracker.Kunikazu723.Enums.DateEnums; namespace CodingTracker.Kunikazu723.Seeders diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 4e719fb7c..fc550cd6e 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -1,11 +1,8 @@ using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.Services; -using Microsoft.VisualBasic; using Spectre.Console; using System.Data.SQLite; -using System.Globalization; -using System.Reflection; using static CodingTracker.Kunikazu723.Enums.MenuEnums; namespace CodingTracker.Kunikazu723.UserLand From 44eacf4332d6712674eb8b2bda9f37e061eb11c6 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:38:28 -0300 Subject: [PATCH 69/93] Add unit tests project --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln index 0f4f29c6d..abd1b03e0 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.14.37531.7 d17.14 +VisualStudioVersion = 17.14.37531.7 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.Kunikazu723", "CodingTracker.Kunikazu723.csproj", "{97EA1958-EE4A-4B55-AAB7-9E57F0A17958}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.UnitTests", "..\CodingTracker.UnitTests\CodingTracker.UnitTests.csproj", "{8DE33EA7-572A-4D3D-A47F-097B04F53AA5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Debug|Any CPU.Build.0 = Debug|Any CPU {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Release|Any CPU.ActiveCfg = Release|Any CPU {97EA1958-EE4A-4B55-AAB7-9E57F0A17958}.Release|Any CPU.Build.0 = Release|Any CPU + {8DE33EA7-572A-4D3D-A47F-097B04F53AA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DE33EA7-572A-4D3D-A47F-097B04F53AA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DE33EA7-572A-4D3D-A47F-097B04F53AA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DE33EA7-572A-4D3D-A47F-097B04F53AA5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 1782d1de764452fdcba07c24b9812f00ee2c6653 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:40:03 -0300 Subject: [PATCH 70/93] Add Project reference to unit tests --- .../CodingTracker.UnitTests.csproj | 23 +++++++++++++++++++ CodingTracker.UnitTests/UnitTest1.cs | 16 +++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 CodingTracker.UnitTests/CodingTracker.UnitTests.csproj create mode 100644 CodingTracker.UnitTests/UnitTest1.cs diff --git a/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj b/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj new file mode 100644 index 000000000..2055aa088 --- /dev/null +++ b/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj @@ -0,0 +1,23 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + diff --git a/CodingTracker.UnitTests/UnitTest1.cs b/CodingTracker.UnitTests/UnitTest1.cs new file mode 100644 index 000000000..e3f430efe --- /dev/null +++ b/CodingTracker.UnitTests/UnitTest1.cs @@ -0,0 +1,16 @@ +namespace CodingTracker.UnitTests +{ + public class Tests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + Assert.Pass(); + } + } +} From 49b7117f778fa7248a0065c43275f859010e8cca Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:22:50 -0300 Subject: [PATCH 71/93] Implement Validation Unit Tests --- .../CodingTracker.UnitTests.csproj | 4 + .../DefaultValidationTests.cs | 171 ++++++++++++++++++ CodingTracker.UnitTests/UnitTest1.cs | 16 -- 3 files changed, 175 insertions(+), 16 deletions(-) create mode 100644 CodingTracker.UnitTests/DefaultValidationTests.cs delete mode 100644 CodingTracker.UnitTests/UnitTest1.cs diff --git a/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj b/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj index 2055aa088..557e16dc5 100644 --- a/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj +++ b/CodingTracker.UnitTests/CodingTracker.UnitTests.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/CodingTracker.UnitTests/DefaultValidationTests.cs b/CodingTracker.UnitTests/DefaultValidationTests.cs new file mode 100644 index 000000000..f3ee6f646 --- /dev/null +++ b/CodingTracker.UnitTests/DefaultValidationTests.cs @@ -0,0 +1,171 @@ +using CodingTracker.Kunikazu723.UserLand; + +namespace CodingTracker.UnitTests +{ + public class DefaultValidationTests + { + private Validation _validation = null!; + + [SetUp] + public void Setup() + { + _validation = new Validation("dd-MM-yyyy-HH-mm-ss"); + } + + [TestCase("22-10-2026-14-30-45", TestName = "Typical date")] + [TestCase("01-01-2026-00-00-00", TestName = "Start of year, midnight")] + [TestCase("31-12-2026-23-59-59", TestName = "End of year, last second")] + [TestCase("29-02-2024-12-00-00", TestName = "Leap day, leap year")] + [TestCase("28-02-2023-12-00-00", TestName = "Last Feb day, non-leap year")] + [TestCase("30-04-2026-10-10-10", TestName = "30-day month, day 30")] + public void IsDateTimeValidFormat_CorrectFormat_ReturnsTrue(string inputDate) + { + + bool isValid = _validation.IsDateTimeValidFormat(inputDate, out _); + Assert.That(isValid, Is.True); + } + + // Out-of-range values + [TestCase("29-02-2023-12-00-00", TestName = "Feb 29 in non-leap year")] + [TestCase("31-04-2026-12-00-00", TestName = "Day 31 in 30-day month")] + [TestCase("32-01-2026-12-00-00", TestName = "Day 32")] + [TestCase("00-01-2026-12-00-00", TestName = "Day zero")] + [TestCase("15-13-2026-12-00-00", TestName = "Month 13")] + [TestCase("15-00-2026-12-00-00", TestName = "Month zero")] + [TestCase("15-06-2026-24-00-00", TestName = "Hour 24")] + [TestCase("15-06-2026-12-60-00", TestName = "Minute 60")] + [TestCase("15-06-2026-12-00-60", TestName = "Second 60")] + [TestCase("15-06-0000-12-00-00", TestName = "Year zero")] + // Wrong shape + [TestCase("22/10/2026/14/30/45", TestName = "Wrong separator")] + [TestCase("22-10-2026 14:30:45", TestName = "Mixed separators")] + [TestCase("22-10-2026-14-30", TestName = "Missing seconds")] + [TestCase("22-10-2026-14-30-45-99", TestName = "Extra part")] + [TestCase("2026-10-22-14-30-45", TestName = "Year first")] + [TestCase("22-10-26-14-30-45", TestName = "Two-digit year")] + [TestCase("2-10-2026-14-30-45", TestName = "Single-digit day")] + [TestCase("22-10-2026-4-30-45", TestName = "Single-digit hour")] + // Garbage and edge input + [TestCase("ab-cd-efgh-ij-kl-mn", TestName = "Letters")] + [TestCase("22-10-2026-14-30-4x", TestName = "One bad character")] + [TestCase(" 22-10-2026-14-30-45", TestName = "Leading space")] + [TestCase("22-10-2026-14-30-45 ", TestName = "Trailing space")] + [TestCase("", TestName = "Empty string")] + [TestCase(" ", TestName = "Whitespace only")] + [TestCase(null, TestName = "Null")] + public void IsDateTimeValidFormat_InvalidInput_ReturnsFalse(string? input) + { + bool isValid = _validation.IsDateTimeValidFormat(input!, out _); + + Assert.That(isValid, Is.False); + } + + [TestCase("22-10-2026-14-00-00", "22-10-2026-15-30-00", TestName = "Same day, end later")] + [TestCase("22-10-2026-23-00-00", "23-10-2026-01-00-00", TestName = "Crosses midnight")] + [TestCase("31-12-2026-23-59-59", "01-01-2027-00-00-01", TestName = "Crosses year boundary")] + public void AreDatesChronological_ValidInput_ReturnsTrue(string start, string end) + { + Assert.That(_validation.AreDatesChronological(start, end), Is.True); + } + + [TestCase("22-10-2026-15-30-00", "22-10-2026-14-00-00", TestName = "End before start")] + [TestCase("23-10-2026-01-00-00", "22-10-2026-23-00-00", TestName = "End on previous day")] + [TestCase("22-10-2026-14-00-00", "22-10-2026-14-00-00", TestName = "Equal times")] + public void AreDatesChronological_EndNotAfterStart_ReturnsFalse(string start, string end) + { + Assert.That(_validation.AreDatesChronological(start, end), Is.False); + } + + [TestCase("garbage", "22-10-2026-15-00-00", TestName = "Invalid start")] + [TestCase("22-10-2026-14-00-00", "garbage", TestName = "Invalid end")] + [TestCase("", "", TestName = "Both empty")] + public void AreDatesChronological_InvalidFormat_ReturnsFalse(string start, string end) + { + Assert.That(_validation.AreDatesChronological(start, end), Is.False); + } + } + + public class IsoValidationTests + { + private Validation _validation = null!; + [SetUp] + public void SetUp() + { + _validation = new Validation("yyyy-MM-dd'T'HH:mm:ss"); + } + + // Valid inputs + [TestCase("2026-10-22T14:30:45", TestName = "Typical date")] + [TestCase("2026-01-01T00:00:00", TestName = "Start of year")] + [TestCase("2026-12-31T23:59:59", TestName = "End of year")] + [TestCase("2024-02-29T12:00:00", TestName = "Leap day, leap year")] + [TestCase("2023-02-28T12:00:00", TestName = "Last Feb day, non-leap year")] + [TestCase("2026-04-30T10:10:10", TestName = "30-day month, day 30")] + public void IsDateTimeValidFormat_ValidInput_ReturnsTrue(string input) + { + bool isValid = _validation.IsDateTimeValidFormat(input, out _); + + Assert.That(isValid, Is.True); + } + + // Invalid inputs + [TestCase("2023-02-29T12:00:00", TestName = "Feb 29 in non-leap year")] + [TestCase("2026-04-31T12:00:00", TestName = "Day 31 in 30-day month")] + [TestCase("2026-01-32T12:00:00", TestName = "Day 32")] + [TestCase("2026-01-00T12:00:00", TestName = "Day zero")] + [TestCase("2026-13-15T12:00:00", TestName = "Month 13")] + [TestCase("2026-00-15T12:00:00", TestName = "Month zero")] + [TestCase("2026-06-15T24:00:00", TestName = "Hour 24")] + [TestCase("2026-06-15T12:60:00", TestName = "Minute 60")] + [TestCase("2026-06-15T12:00:60", TestName = "Second 60")] + [TestCase("2026-10-22 14:30:45", TestName = "Space instead of T")] + [TestCase("2026-10-22t14:30:45", TestName = "Lowercase t")] + [TestCase("2026/10/22T14:30:45", TestName = "Wrong date separator")] + [TestCase("2026-10-22T14-30-45", TestName = "Wrong time separator")] + [TestCase("22-10-2026T14:30:45", TestName = "Day first")] + [TestCase("26-10-22T14:30:45", TestName = "Two-digit year")] + [TestCase("2026-10-22T14:30", TestName = "Missing seconds")] + [TestCase("2026-10-22", TestName = "Date only")] + [TestCase("2026-10-22T14:30:45Z", TestName = "Trailing Z")] + [TestCase("2026-10-22T14:30:45.123", TestName = "Fractional seconds")] + [TestCase("2026-10-22T14:30:45+03:00", TestName = "Timezone offset")] + [TestCase("2026-1-2T14:30:45", TestName = "Single-digit month and day")] + [TestCase("abcd-ef-ghTij:kl:mn", TestName = "Letters")] + [TestCase(" 2026-10-22T14:30:45", TestName = "Leading space")] + [TestCase("2026-10-22T14:30:45 ", TestName = "Trailing space")] + [TestCase("", TestName = "Empty string")] + [TestCase(" ", TestName = "Whitespace only")] + [TestCase(null, TestName = "Null")] + public void IsDateTimeValidFormat_InvalidInput_ReturnsFalse(string? input) + { + bool isValid = _validation.IsDateTimeValidFormat(input!, out _); + + Assert.That(isValid, Is.False); + } + + + [TestCase("2026-10-22T14:00:00", "2026-10-22T15:30:00", TestName = "Same day, end later")] + [TestCase("2026-10-22T23:00:00", "2026-10-23T01:00:00", TestName = "Crosses midnight")] + [TestCase("2026-12-31T23:59:59", "2027-01-01T00:00:01", TestName = "Crosses year boundary")] + public void AreDatesChronological_ValidInput_ReturnsTrue(string start, string end) + { + Assert.That(_validation.AreDatesChronological(start, end), Is.True); + } + + [TestCase("2026-10-22T15:30:00", "2026-10-22T14:00:00", TestName = "End before start")] + [TestCase("2026-10-23T01:00:00", "2026-10-22T23:00:00", TestName = "End on previous day")] + [TestCase("2026-10-22T14:00:00", "2026-10-22T14:00:00", TestName = "Equal times")] + public void AreDatesChronological_EndNotAfterStart_ReturnsFalse(string start, string end) + { + Assert.That(_validation.AreDatesChronological(start, end), Is.False); + } + + [TestCase("garbage", "2026-10-22T15:00:00", TestName = "Invalid start")] + [TestCase("2026-10-22T14:00:00", "garbage", TestName = "Invalid end")] + [TestCase("", "", TestName = "Both empty")] + public void AreDatesChronological_InvalidFormat_ReturnsFalse(string start, string end) + { + Assert.That(_validation.AreDatesChronological(start, end), Is.False); + } + } +} diff --git a/CodingTracker.UnitTests/UnitTest1.cs b/CodingTracker.UnitTests/UnitTest1.cs deleted file mode 100644 index e3f430efe..000000000 --- a/CodingTracker.UnitTests/UnitTest1.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace CodingTracker.UnitTests -{ - public class Tests - { - [SetUp] - public void Setup() - { - } - - [Test] - public void Test1() - { - Assert.Pass(); - } - } -} From 3824f0b009960b754097167ae8961fae49946a9f Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:54:55 -0300 Subject: [PATCH 72/93] Add stopwatch based method that will be consumed by the UserInterface --- CodingTracker.Kunikazu723/UserLand/UserInput.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index 1abad9b34..3b2830dff 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -1,5 +1,6 @@ using CodingTracker.Kunikazu723.Models; using Spectre.Console; +using System.Diagnostics; using static CodingTracker.Kunikazu723.Enums.MenuEnums; namespace CodingTracker.Kunikazu723.UserLand @@ -80,5 +81,21 @@ public string GetDateTime() } } } + + public (string StartTime, string EndTime) GetStartEndDateViaStopwatch() + { + + var stopwatch = new Stopwatch(); + + stopwatch.Start(); + + DateTime startTime = DateTime.Now; + AnsiConsole.MarkupLine("[bold yellow]Stopwatch Initiated[/]\n[grey]Press Any Key to stop it[/]"); + Console.ReadKey(); + + stopwatch.Stop(); + + return (startTime.ToString(_validation.DateFormat), (startTime + stopwatch.Elapsed).ToString(_validation.DateFormat)); + } } } From 7fdf0aa471ae643ddb60e231e642d966f3e73902 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:17:59 -0300 Subject: [PATCH 73/93] Make GetUserCodingSession depend on where it should use a stopwatch or not This is a neat way to add the new stopwatch funcitonality since it follows the same pattern as GetStartEndDate manually. This glues them toghether a little bit but it is readable and a nice way to solve it --- CodingTracker.Kunikazu723/UserLand/UserInput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index 3b2830dff..11cfee27a 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -31,9 +31,9 @@ public T PromptUserEnumOption() where T: struct, Enum .AddChoices(Enum.GetValues()) .UseConverter(OptionToString)); } - public CodingSession GetUserCodingSession() + public CodingSession GetUserCodingSession(bool useStopwatch = false) { - (string startTime, string endTime) = GetStartEndDate(); + (string startTime, string endTime) = useStopwatch ? GetStartEndDateViaStopwatch() : GetStartEndDate(); var codingSession = new CodingSession() { From 80042a5982f23ea121ee72c2784982466e951229 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:18:49 -0300 Subject: [PATCH 74/93] Add Unique constraint violation message that informs the user that he inserted a value that already exists in the database --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index fc550cd6e..d51a4c605 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -33,7 +33,14 @@ public void MainMenu() ExecuteOption(option); } catch (SQLiteException ex) { - DisplayMessage($"DATABASE ERROR: \n\t{ex.Message}", Color.Red); + if (ex.ErrorCode == 19) + { + DisplayMessage($"ERROR: Date is already exists in database. Insertion aborted", Color.Red); + } + else + { + DisplayMessage($"DATABASE ERROR: \n\t{ex.Message}\nERROR CODE: {ex.ErrorCode}", Color.Red); + } } if (option == MainMenuOptions.Exit) isRunning = false; From 6ba2ae170e7ce9d1d52112febadbedfe5fb5dc19 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:20:07 -0300 Subject: [PATCH 75/93] Implement stopwatch at the LogSession method while also adding visual feedback into what the user is currently inserting --- .../UserLand/UserInterface.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index d51a4c605..b52689b62 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -36,7 +36,7 @@ public void MainMenu() if (ex.ErrorCode == 19) { DisplayMessage($"ERROR: Date is already exists in database. Insertion aborted", Color.Red); - } + } else { DisplayMessage($"DATABASE ERROR: \n\t{ex.Message}\nERROR CODE: {ex.ErrorCode}", Color.Red); @@ -146,10 +146,25 @@ private void UpdateSession() private void LogSession() { WriteBanner(Color.Yellow, "Log Session"); - var codingSession = _userInput.GetUserCodingSession(); - _dao.InsertItem(codingSession); - DisplayMessage("New coding session Successfuly Logged", Color.Green); + bool useStopwatch = AnsiConsole.Confirm("Use stopwatch to track current session in real time?"); + + var codingSession = _userInput.GetUserCodingSession(useStopwatch); + + var toBeInsertedDisplay = _tableEngine.GenerateCodingSessionsTable(new List() { codingSession }, "To Be Inserted"); + AnsiConsole.Write(toBeInsertedDisplay); + + bool confirmInsertion = AnsiConsole.Confirm("[bold yellow]Do you wish to Log the current session?[/]"); + if (confirmInsertion) + { + _dao.InsertItem(codingSession); + DisplayMessage("New coding session Successfuly Logged", Color.Green); + } + else + { + DisplayMessage("Insertion aborted, no changes made", Color.Green); + } + } private void ViewSessions() From b47d290dca13aa559921b94c303109f6b9e09c09 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Wed, 28 Oct 2026 00:54:54 -0300 Subject: [PATCH 76/93] Change date time implementation from string to actual DateTime objects in client code allowing for default ISO format to be stored Previously the date stored in the database followed the date format defined in the appsetting.json file. This brought many limitations and akward code, for example: Changing dateFormat in appsettings after the seeded database would make the database store more than one format; TryParseExact already offers an out variable that can be returned directly at UserInput.GetDateTime, no need to make it a string and then parse it as datetime later on. This implementation also takes advantage of System.Data.Sqlite driver that maps DateTime objects directly to ISO standard TEXT types when storing it into the database. Overall this makes the code cleaner and avoid breaking default storage format --- .../Dao/CodingSessionDao.cs | 2 +- .../Models/CodingSession.cs | 6 +-- .../Seeders/CodingSessionSeeder.cs | 11 +++-- .../Services/TableEngine.cs | 6 +-- .../UserLand/UserInput.cs | 19 +++++---- .../UserLand/Validation.cs | 12 +----- .../DefaultValidationTests.cs | 41 ++++++++----------- 7 files changed, 40 insertions(+), 57 deletions(-) diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 24436bfe9..248f07507 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -59,7 +59,7 @@ public void InsertMany(List collection) connection.Execute(""" INSERT INTO CodingSessions (StartTime, EndTime) VALUES (@StartTime, @EndTime) - """, collection); + """, collection, transaction); transaction.Commit(); } diff --git a/CodingTracker.Kunikazu723/Models/CodingSession.cs b/CodingTracker.Kunikazu723/Models/CodingSession.cs index cbf000202..65c15ce84 100644 --- a/CodingTracker.Kunikazu723/Models/CodingSession.cs +++ b/CodingTracker.Kunikazu723/Models/CodingSession.cs @@ -4,8 +4,8 @@ public class CodingSession { //Id, StartTime, EndTime, Duration public int Id { get; set; } - public string StartTime { get; set; } = string.Empty; - public string EndTime { get; set; } = string.Empty; - public float Duration { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public TimeSpan Duration { get; set; } } } diff --git a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs index d7e3d4193..f52f718b1 100644 --- a/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs +++ b/CodingTracker.Kunikazu723/Seeders/CodingSessionSeeder.cs @@ -9,7 +9,6 @@ internal class CodingSessionSeeder private readonly IDao _dao; private readonly string _dateFormat; private readonly Random _rng = new Random(); - private readonly List _unavailableDates = new List(); public CodingSessionSeeder(IDao dao, string dateFormat) { _dao = dao; @@ -27,20 +26,20 @@ public void SeedIfEmpty() private List GenerateData() { var data = new List(); - DateTime sessionDate = new DateTime(2025, 01, 01); + DateTime initialDate = new DateTime(2025, 01, 01); for (int i = 0; i < 100; i++) { - DateTime startTime = GenerateDate(sessionDate, 12, TimeType.Hour); + DateTime startTime = GenerateDate(initialDate, 12, TimeType.Hour); DateTime endTime = GenerateDate(startTime, 5, TimeType.Hour); data.Add( new CodingSession() { - StartTime = startTime.ToString(_dateFormat), - EndTime = endTime.ToString(_dateFormat), + StartTime = startTime, + EndTime = endTime, }); - sessionDate = GenerateDate(endTime, 7, TimeType.Day); + initialDate = GenerateDate(endTime, 7, TimeType.Day); } return data; diff --git a/CodingTracker.Kunikazu723/Services/TableEngine.cs b/CodingTracker.Kunikazu723/Services/TableEngine.cs index 76e532f3e..725a52eb7 100644 --- a/CodingTracker.Kunikazu723/Services/TableEngine.cs +++ b/CodingTracker.Kunikazu723/Services/TableEngine.cs @@ -26,10 +26,8 @@ public Table GenerateCodingSessionsTable(List codingSessions, str { try { - DateTime parsedStartTime = ParseStringTime(codingSession.StartTime); - DateTime parsedEndTime = ParseStringTime(codingSession.EndTime); - TimeSpan duration = (parsedEndTime - parsedStartTime); - table.AddRow(codingSession.Id.ToString(), codingSession.StartTime, codingSession.EndTime, duration.ToString(durationFormat)); + TimeSpan duration = (codingSession.EndTime - codingSession.StartTime); + table.AddRow(codingSession.Id.ToString(), codingSession.StartTime.ToString(_dateFormat), codingSession.EndTime.ToString(_dateFormat), duration.ToString(durationFormat)); } catch (ArgumentException ex) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index 11cfee27a..cea30ad1b 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -33,7 +33,7 @@ public T PromptUserEnumOption() where T: struct, Enum } public CodingSession GetUserCodingSession(bool useStopwatch = false) { - (string startTime, string endTime) = useStopwatch ? GetStartEndDateViaStopwatch() : GetStartEndDate(); + (DateTime startTime, DateTime endTime) = useStopwatch ? GetStartEndDateViaStopwatch() : GetStartEndDate(); var codingSession = new CodingSession() { @@ -43,14 +43,15 @@ public CodingSession GetUserCodingSession(bool useStopwatch = false) return codingSession; } - public string GetDateTime() + public DateTime GetDateTime() { while (true) { string date = AnsiConsole.Ask($"Enter the date in the format [bold yellow]{_validation.DateFormat}[/]"); - if (_validation.IsDateTimeValidFormat(date, out _)) + DateTime parsedDate; + if (_validation.IsDateTimeValidFormat(date, out parsedDate)) { - return date; + return parsedDate; } else { @@ -59,17 +60,17 @@ public string GetDateTime() } } - public (string StartTime, string EndTime) GetStartEndDate() + public (DateTime StartTime, DateTime EndTime) GetStartEndDate() { while (true) { AnsiConsole.MarkupLine("[bold yellow]Start Time[/]"); - string startTime = GetDateTime(); + DateTime startTime = GetDateTime(); Console.WriteLine(); AnsiConsole.MarkupLine("[bold yellow]End Time[/]"); - string endTime = GetDateTime(); + DateTime endTime = GetDateTime(); if (_validation.AreDatesChronological(startTime, endTime)) { @@ -82,7 +83,7 @@ public string GetDateTime() } } - public (string StartTime, string EndTime) GetStartEndDateViaStopwatch() + public (DateTime StartTime, DateTime EndTime) GetStartEndDateViaStopwatch() { var stopwatch = new Stopwatch(); @@ -95,7 +96,7 @@ public string GetDateTime() stopwatch.Stop(); - return (startTime.ToString(_validation.DateFormat), (startTime + stopwatch.Elapsed).ToString(_validation.DateFormat)); + return (startTime, (startTime + stopwatch.Elapsed)); } } } diff --git a/CodingTracker.Kunikazu723/UserLand/Validation.cs b/CodingTracker.Kunikazu723/UserLand/Validation.cs index 192e54f91..220ecbf56 100644 --- a/CodingTracker.Kunikazu723/UserLand/Validation.cs +++ b/CodingTracker.Kunikazu723/UserLand/Validation.cs @@ -10,16 +10,6 @@ public Validation(string dateFormat) DateFormat = dateFormat; } public bool IsDateTimeValidFormat(string date, out DateTime parsedDate) => DateTime.TryParseExact(date, DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate); - public bool AreDatesChronological(string startTime, string endTime) - { - DateTime parsedStartTime; - DateTime parsedEndTime; - if (!IsDateTimeValidFormat(startTime, out parsedStartTime) || !IsDateTimeValidFormat(endTime, out parsedEndTime)) - { - return false; - } - - return parsedEndTime > parsedStartTime; - } + public bool AreDatesChronological(DateTime startTime, DateTime endTime) => endTime > startTime; } } diff --git a/CodingTracker.UnitTests/DefaultValidationTests.cs b/CodingTracker.UnitTests/DefaultValidationTests.cs index f3ee6f646..d301ab573 100644 --- a/CodingTracker.UnitTests/DefaultValidationTests.cs +++ b/CodingTracker.UnitTests/DefaultValidationTests.cs @@ -12,6 +12,12 @@ public void Setup() _validation = new Validation("dd-MM-yyyy-HH-mm-ss"); } + private DateTime Parse(string input) + { + Assert.That(_validation.IsDateTimeValidFormat(input, out var date), Is.True, $"Test data '{input}' should be valid"); + return date; + } + [TestCase("22-10-2026-14-30-45", TestName = "Typical date")] [TestCase("01-01-2026-00-00-00", TestName = "Start of year, midnight")] [TestCase("31-12-2026-23-59-59", TestName = "End of year, last second")] @@ -20,7 +26,6 @@ public void Setup() [TestCase("30-04-2026-10-10-10", TestName = "30-day month, day 30")] public void IsDateTimeValidFormat_CorrectFormat_ReturnsTrue(string inputDate) { - bool isValid = _validation.IsDateTimeValidFormat(inputDate, out _); Assert.That(isValid, Is.True); } @@ -65,7 +70,7 @@ public void IsDateTimeValidFormat_InvalidInput_ReturnsFalse(string? input) [TestCase("31-12-2026-23-59-59", "01-01-2027-00-00-01", TestName = "Crosses year boundary")] public void AreDatesChronological_ValidInput_ReturnsTrue(string start, string end) { - Assert.That(_validation.AreDatesChronological(start, end), Is.True); + Assert.That(_validation.AreDatesChronological(Parse(start), Parse(end)), Is.True); } [TestCase("22-10-2026-15-30-00", "22-10-2026-14-00-00", TestName = "End before start")] @@ -73,27 +78,26 @@ public void AreDatesChronological_ValidInput_ReturnsTrue(string start, string en [TestCase("22-10-2026-14-00-00", "22-10-2026-14-00-00", TestName = "Equal times")] public void AreDatesChronological_EndNotAfterStart_ReturnsFalse(string start, string end) { - Assert.That(_validation.AreDatesChronological(start, end), Is.False); - } - - [TestCase("garbage", "22-10-2026-15-00-00", TestName = "Invalid start")] - [TestCase("22-10-2026-14-00-00", "garbage", TestName = "Invalid end")] - [TestCase("", "", TestName = "Both empty")] - public void AreDatesChronological_InvalidFormat_ReturnsFalse(string start, string end) - { - Assert.That(_validation.AreDatesChronological(start, end), Is.False); + Assert.That(_validation.AreDatesChronological(Parse(start), Parse(end)), Is.False); } } public class IsoValidationTests { private Validation _validation = null!; + [SetUp] public void SetUp() { _validation = new Validation("yyyy-MM-dd'T'HH:mm:ss"); } + private DateTime Parse(string input) + { + Assert.That(_validation.IsDateTimeValidFormat(input, out var date), Is.True, $"Test data '{input}' should be valid"); + return date; + } + // Valid inputs [TestCase("2026-10-22T14:30:45", TestName = "Typical date")] [TestCase("2026-01-01T00:00:00", TestName = "Start of year")] @@ -143,13 +147,12 @@ public void IsDateTimeValidFormat_InvalidInput_ReturnsFalse(string? input) Assert.That(isValid, Is.False); } - [TestCase("2026-10-22T14:00:00", "2026-10-22T15:30:00", TestName = "Same day, end later")] [TestCase("2026-10-22T23:00:00", "2026-10-23T01:00:00", TestName = "Crosses midnight")] [TestCase("2026-12-31T23:59:59", "2027-01-01T00:00:01", TestName = "Crosses year boundary")] public void AreDatesChronological_ValidInput_ReturnsTrue(string start, string end) { - Assert.That(_validation.AreDatesChronological(start, end), Is.True); + Assert.That(_validation.AreDatesChronological(Parse(start), Parse(end)), Is.True); } [TestCase("2026-10-22T15:30:00", "2026-10-22T14:00:00", TestName = "End before start")] @@ -157,15 +160,7 @@ public void AreDatesChronological_ValidInput_ReturnsTrue(string start, string en [TestCase("2026-10-22T14:00:00", "2026-10-22T14:00:00", TestName = "Equal times")] public void AreDatesChronological_EndNotAfterStart_ReturnsFalse(string start, string end) { - Assert.That(_validation.AreDatesChronological(start, end), Is.False); - } - - [TestCase("garbage", "2026-10-22T15:00:00", TestName = "Invalid start")] - [TestCase("2026-10-22T14:00:00", "garbage", TestName = "Invalid end")] - [TestCase("", "", TestName = "Both empty")] - public void AreDatesChronological_InvalidFormat_ReturnsFalse(string start, string end) - { - Assert.That(_validation.AreDatesChronological(start, end), Is.False); + Assert.That(_validation.AreDatesChronological(Parse(start), Parse(end)), Is.False); } } -} +} \ No newline at end of file From 09ccad61b0f27257334446f2c23012efbd452766 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:35:49 -0300 Subject: [PATCH 77/93] Remove deprecated method --- CodingTracker.Kunikazu723/Services/TableEngine.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CodingTracker.Kunikazu723/Services/TableEngine.cs b/CodingTracker.Kunikazu723/Services/TableEngine.cs index 725a52eb7..51684af07 100644 --- a/CodingTracker.Kunikazu723/Services/TableEngine.cs +++ b/CodingTracker.Kunikazu723/Services/TableEngine.cs @@ -38,15 +38,5 @@ public Table GenerateCodingSessionsTable(List codingSessions, str } return table; } - - private DateTime ParseStringTime(string time) - { - DateTime result; - if (!DateTime.TryParseExact(time, _dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) - { - throw new ArgumentException($"Time String {time} does not follow proper date format {_dateFormat}"); - } - return result; - } } } From 04c8426eed8a7b36689e8f700db07fac2e653bfb Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Fri, 30 Oct 2026 01:55:00 -0300 Subject: [PATCH 78/93] Add filter feature Not an atomic commit I know. Unfortunately I fell in this rabbit hole after midnight and I'm kind of burned out. Sorry --- CodingTracker.Kunikazu723/Enums/Filters.cs | 25 +++++++++ CodingTracker.Kunikazu723/Program.cs | 2 +- .../UserLand/UserInput.cs | 4 +- .../UserLand/UserInterface.cs | 56 +++++++++++++++++-- 4 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 CodingTracker.Kunikazu723/Enums/Filters.cs diff --git a/CodingTracker.Kunikazu723/Enums/Filters.cs b/CodingTracker.Kunikazu723/Enums/Filters.cs new file mode 100644 index 000000000..3006815e3 --- /dev/null +++ b/CodingTracker.Kunikazu723/Enums/Filters.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodingTracker.Kunikazu723.Enums +{ + public static class Filters + { + public enum FilterType + { + Days, + Weeks, + Months, + Years, + } + + public enum FilterOrder + { + Ascending, + Descending, + } + } +} diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index bfa6802b1..d1b11cd4c 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -36,7 +36,7 @@ static void Main(string[] args) var userInput = new UserInput(validation); var tableEngine = new TableEngine(dateTimeFormat); - var userInterface = new UserInterface(userInput, tableEngine, codingSessionDao); + var userInterface = new UserInterface(userInput, tableEngine, codingSessionDao, dateTimeFormat); userInterface.MainMenu(); } } diff --git a/CodingTracker.Kunikazu723/UserLand/UserInput.cs b/CodingTracker.Kunikazu723/UserLand/UserInput.cs index cea30ad1b..7e13aefc1 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInput.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInput.cs @@ -23,11 +23,11 @@ public int AskUserForPositiveInteger(string prompt) } } - public T PromptUserEnumOption() where T: struct, Enum + public T PromptUserEnumOption(string prompt) where T: struct, Enum { return AnsiConsole.Prompt( new SelectionPrompt() - .Title("What do you want to do?") + .Title($"[bold yellow]{prompt}[/]") .AddChoices(Enum.GetValues()) .UseConverter(OptionToString)); } diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index b52689b62..389bc79d2 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -4,6 +4,7 @@ using Spectre.Console; using System.Data.SQLite; using static CodingTracker.Kunikazu723.Enums.MenuEnums; +using static CodingTracker.Kunikazu723.Enums.Filters; namespace CodingTracker.Kunikazu723.UserLand { @@ -12,12 +13,14 @@ public class UserInterface private readonly UserInput _userInput; private readonly TableEngine _tableEngine; private readonly IDao _dao; + private readonly string _dateFormat = string.Empty; - public UserInterface(UserInput userInput, TableEngine engine, IDao dao) + public UserInterface(UserInput userInput, TableEngine engine, IDao dao, string dateFormat) { _userInput = userInput; _tableEngine = engine; _dao = dao; + _dateFormat = dateFormat; } public void MainMenu() { @@ -26,12 +29,13 @@ public void MainMenu() { Console.Clear(); WriteBanner(Color.Blue); - MainMenuOptions option = _userInput.PromptUserEnumOption(); + MainMenuOptions option = _userInput.PromptUserEnumOption("What do you want to do?"); Console.Clear(); try { ExecuteOption(option); - } catch (SQLiteException ex) + } + catch (SQLiteException ex) { if (ex.ErrorCode == 19) { @@ -48,7 +52,7 @@ public void MainMenu() AnsiConsole.MarkupLine("[bold grey]Press Any Key To Continue[/]"); Console.ReadKey(); } - + } private void ExecuteOption(MainMenuOptions option) { @@ -98,7 +102,7 @@ private void DeleteSession() var codingSession = PromptForExistingSession(); if (codingSession is null) return; - Table sessionTable = _tableEngine.GenerateCodingSessionsTable(new List {codingSession}, "To be Deleted"); + Table sessionTable = _tableEngine.GenerateCodingSessionsTable(new List { codingSession }, "To be Deleted"); AnsiConsole.Write(sessionTable); bool confirmDeletion = AnsiConsole.Confirm("Are you sure you wish to delete permanently this session?", false); @@ -169,8 +173,48 @@ private void LogSession() private void ViewSessions() { - var sessionsTable = _tableEngine.GenerateCodingSessionsTable(_dao.GetAllItems(), "Coding Sessions"); + var doApplyFilter = AnsiConsole.Confirm("[bold yellow]Apply Filter?[/]", false); + var codingSessions = _dao.GetAllItems().OrderBy(codingSession => codingSession.StartTime).ToList(); + if (doApplyFilter) + { + codingSessions = FilterCodingSessions(codingSessions); + } + + var sessionsTable = _tableEngine.GenerateCodingSessionsTable(codingSessions, "Coding Sessions"); AnsiConsole.Write(sessionsTable); + + } + + private List FilterCodingSessions(List codingSessions) + { + var filterType = _userInput.PromptUserEnumOption("[bold yellow]Select Paremeter[/]"); + + var amount = _userInput.AskUserForPositiveInteger($"[bold yellow]Enter how many {filterType} before today {DateTime.Today.ToString(_dateFormat)}[/]"); + + var today = DateTime.Today; + + var lowestDate = filterType switch + { + FilterType.Days => today.AddDays(-amount), + FilterType.Weeks => today.AddDays(-amount * 7), + FilterType.Months => today.AddMonths(-amount), + FilterType.Years => today.AddYears(-amount), + _ => throw new NotImplementedException() + }; + + var filteredCodingSessions = codingSessions + .Where(codingSession => codingSession.StartTime >= lowestDate); + + var filterOrder = _userInput.PromptUserEnumOption("[bold yellow]Select Order[/]"); + + filteredCodingSessions = filterOrder switch + { + FilterOrder.Ascending => filteredCodingSessions.OrderBy(codingSession => codingSession.StartTime), + FilterOrder.Descending => filteredCodingSessions.OrderByDescending(codingSession => codingSession.StartTime), + _ => throw new NotImplementedException() + }; + + return filteredCodingSessions.ToList(); } public void WriteBanner(Color color, string bannerText = "CODING TRACKER") From 7d87b73e69195bc57aa5b2d88e215d5ea4849ce2 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 02:49:16 -0300 Subject: [PATCH 79/93] Add QoL where if a filter returns an empty list, the user don't need to type select the Order in which the filter should be displayed --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 389bc79d2..557cf05d2 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -173,13 +173,21 @@ private void LogSession() private void ViewSessions() { - var doApplyFilter = AnsiConsole.Confirm("[bold yellow]Apply Filter?[/]", false); + var doApplyFilter = AnsiConsole.Confirm("[bold yellow]Apply Filter to view all coding sessions?[/]", false); var codingSessions = _dao.GetAllItems().OrderBy(codingSession => codingSession.StartTime).ToList(); if (doApplyFilter) { codingSessions = FilterCodingSessions(codingSessions); } + if (codingSessions.Count == 0) + { + AnsiConsole.Write(new Panel("[bold red]No Coding Sessions available[/]") + .RoundedBorder() + .BorderColor(Color.Yellow)); + return; + } + var sessionsTable = _tableEngine.GenerateCodingSessionsTable(codingSessions, "Coding Sessions"); AnsiConsole.Write(sessionsTable); @@ -205,6 +213,9 @@ private List FilterCodingSessions(List codingSessi var filteredCodingSessions = codingSessions .Where(codingSession => codingSession.StartTime >= lowestDate); + if (filteredCodingSessions.Any() == false) + return filteredCodingSessions.ToList(); + var filterOrder = _userInput.PromptUserEnumOption("[bold yellow]Select Order[/]"); filteredCodingSessions = filterOrder switch From 3e837998aee9cb3be520e65e51774773d457faca Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 02:57:20 -0300 Subject: [PATCH 80/93] Create PromptForNewSession method to allign with PromptForExistingSession at Update --- .../UserLand/UserInterface.cs | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 557cf05d2..644c20f55 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -79,22 +79,7 @@ private void ExecuteOption(MainMenuOptions option) } } - private int PromptForSessionId() - { - ViewSessions(); - return _userInput.AskUserForPositiveInteger("Enter the Session's ID"); - } - - private CodingSession? PromptForExistingSession() - { - int id = PromptForSessionId(); - var codingSession = _dao.GetItemById(id); - if (codingSession is null) - { - DisplayMessage($"Coding Session With ID {id} not found\nAborting process", Color.Red); - } - return codingSession; - } + private void DeleteSession() { @@ -117,6 +102,30 @@ private void DeleteSession() } } + private int PromptForSessionId() + { + ViewSessions(); + return _userInput.AskUserForPositiveInteger("Enter the Session's ID"); + } + + private CodingSession? PromptForExistingSession() + { + int id = PromptForSessionId(); + var codingSession = _dao.GetItemById(id); + if (codingSession is null) + { + DisplayMessage($"Coding Session With ID {id} not found\nAborting process", Color.Red); + } + return codingSession; + } + + private CodingSession PromptForNewSession() + { + bool useStopwatch = AnsiConsole.Confirm("Use stopwatch to track current session in real time?"); + var codingSession = _userInput.GetUserCodingSession(useStopwatch); + return codingSession; + } + private void UpdateSession() { WriteBanner(Color.Cyan, "Update Session"); @@ -151,9 +160,7 @@ private void LogSession() { WriteBanner(Color.Yellow, "Log Session"); - bool useStopwatch = AnsiConsole.Confirm("Use stopwatch to track current session in real time?"); - - var codingSession = _userInput.GetUserCodingSession(useStopwatch); + var codingSession = PromptForNewSession(); var toBeInsertedDisplay = _tableEngine.GenerateCodingSessionsTable(new List() { codingSession }, "To Be Inserted"); AnsiConsole.Write(toBeInsertedDisplay); From 38507cf19fb14734ef3db6bd02109cf8e30a3051 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 16:50:51 -0300 Subject: [PATCH 81/93] Fix solution file location --- ...ngTracker.Kunikazu723.sln => CodingTracker.Kunikazu723.sln | 4 ++-- CodingTracker.Kunikazu723/Program.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) rename CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln => CodingTracker.Kunikazu723.sln (86%) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln b/CodingTracker.Kunikazu723.sln similarity index 86% rename from CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln rename to CodingTracker.Kunikazu723.sln index abd1b03e0..447bd548c 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.sln +++ b/CodingTracker.Kunikazu723.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.14.37531.7 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.Kunikazu723", "CodingTracker.Kunikazu723.csproj", "{97EA1958-EE4A-4B55-AAB7-9E57F0A17958}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.Kunikazu723", "CodingTracker.Kunikazu723\CodingTracker.Kunikazu723.csproj", "{97EA1958-EE4A-4B55-AAB7-9E57F0A17958}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.UnitTests", "..\CodingTracker.UnitTests\CodingTracker.UnitTests.csproj", "{8DE33EA7-572A-4D3D-A47F-097B04F53AA5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.UnitTests", "CodingTracker.UnitTests\CodingTracker.UnitTests.csproj", "{8DE33EA7-572A-4D3D-A47F-097B04F53AA5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index d1b11cd4c..d76a9322d 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -1,4 +1,5 @@ -using CodingTracker.Kunikazu723.Dao; + +using CodingTracker.Kunikazu723.Dao; using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.Seeders; From 4f4e426fce457ae84e96ee849b9e9a793e2dbd48 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 16:54:48 -0300 Subject: [PATCH 82/93] Add figlet font to output directory --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index 6ba8e1bd8..4189c49b1 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -19,6 +19,9 @@ Always + + PreserveNewest + From a6bbe0894c80c0723c1052fb05f6b406c6f2c0ee Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 16:56:20 -0300 Subject: [PATCH 83/93] Change appsettings.json property to PreserveNewest --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index 4189c49b1..e029b8525 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -17,7 +17,7 @@ - Always + PreserveNewest PreserveNewest From 17b0b74a829de77c7757bb9968e935ce628f6dc9 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:00:32 -0300 Subject: [PATCH 84/93] Fix figletFont path to use output folder correctly --- CodingTracker.Kunikazu723/UserLand/UserInterface.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 644c20f55..50d9d7f18 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -237,7 +237,9 @@ private List FilterCodingSessions(List codingSessi public void WriteBanner(Color color, string bannerText = "CODING TRACKER") { - var figletFont = FigletFont.Load("./FigletFonts/smkeyboard.flf"); + var fontPath = Path.Combine(AppContext.BaseDirectory, "FigletFonts", "smkeyboard.flf"); + var figletFont = FigletFont.Load(fontPath); + var appFiglet = new FigletText(figletFont, bannerText) .Color(color); From a41f5cc59bffe1ad50abe4d153d6d5b3cbc61322 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:34:51 -0300 Subject: [PATCH 85/93] Add README.md draft to be completed --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..2563d8f9c --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +![.NET Vesrion](https://img.shields.io/badge/.NET-9.0-512BD4) +![Language](https://img.shields.io/badge/Language-C%23-blue) +![Database](https://img.shields.io/badge/Database-SQLite-green) +## :mag_right: About +Tracking Coding Sessions is a good way for the user to evaluate how often they code. **Coding Tracker** is a [TUI](https://en.wikipedia.org/wiki/Text-based_user_interface) application that offers simple [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations to track the user's Coding Sessions by either manually typing the start and end of a coding session, or by using a built-in Stopwatch to track the session in real time. + +## How to run locally +The app has not been published, so it needs to run locally by .NET's runtime. +> [!IMPORTANT] Make sure [**.NET SDK 9**](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or higher is installed in order to run this project. + +Clone the repository: +``` +git clone https://github.com/Kunikazu723/TCSA.HabitTracker.git +cd CodeReviews.Console.CodingTracker +``` + +Restore dependencies: +``` +dotnet restore +``` + +Run the project: +``` +dotnet run --poject CodingTracker.Kunikazu723 +``` + +To change databasePath, connection strings and date time format, refer to the [App Settings]() section. + +## 💾 How it works +First the Database is generated with 100 random Coding Sessions. Then the user is presented with 5 options tha can be selected using the keyboard's arrows, 4 of them are CRUD operations. +Each Coding Session needs a Start Date and End Date, both are stored in the database with a default date-time format. The duration is calculated during data presentation. + +## :pencil2: Architectural Choices + +TODO + +## Reflection + +TODO \ No newline at end of file From ffcf5405aad7134b92d54a537d4c28d986dfe711 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 02:48:30 -0300 Subject: [PATCH 86/93] Create SeedDatabase option to allow the choice of seeding the database --- CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj | 5 +++-- CodingTracker.Kunikazu723/Program.cs | 7 ++++++- CodingTracker.Kunikazu723/appsettings.json | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index e029b8525..4e5896020 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -9,8 +9,9 @@ - - + + + diff --git a/CodingTracker.Kunikazu723/Program.cs b/CodingTracker.Kunikazu723/Program.cs index d76a9322d..886dababf 100644 --- a/CodingTracker.Kunikazu723/Program.cs +++ b/CodingTracker.Kunikazu723/Program.cs @@ -25,13 +25,18 @@ static void Main(string[] args) string dateTimeFormat = configuration["DateTimeFormat"] ?? throw new InvalidOperationException("DateTime Format not found in appsettings.json"); + bool seedDatabase = configuration.GetValue("SeedDatabase") + ?? throw new InvalidOperationException("SeedDatabase not found in appsettings.json"); + var dbInitializer = new DatabaseInitializer(connectionString); dbInitializer.Initialize(); IDao codingSessionDao = new CodingSessionDao(connectionString); var codingSessionSeeder = new CodingSessionSeeder(codingSessionDao, dateTimeFormat); - codingSessionSeeder.SeedIfEmpty(); + + if (seedDatabase) + codingSessionSeeder.SeedIfEmpty(); var validation = new Validation(dateTimeFormat); var userInput = new UserInput(validation); diff --git a/CodingTracker.Kunikazu723/appsettings.json b/CodingTracker.Kunikazu723/appsettings.json index 71ef1ae77..01616a8d8 100644 --- a/CodingTracker.Kunikazu723/appsettings.json +++ b/CodingTracker.Kunikazu723/appsettings.json @@ -3,5 +3,6 @@ "ConnectionStrings": { "DefaultConnection": "Data Source=coding-tracker.db" }, - "DateTimeFormat": "dd-MM-yyyy-HH-mm-ss" + "DateTimeFormat": "dd-MM-yyyy-HH-mm-ss", + "SeedDatabase" : true } \ No newline at end of file From 44445241791facabdb50cb64f0afa3d97b84f004 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:16:31 -0300 Subject: [PATCH 87/93] Fix wrong sqlite driver package System.Data.Sqlite.Core is an older legacy driver. It is advisable and good practice to use the newer Microsof.Data.Sqlite driver --- .../CodingTracker.Kunikazu723.csproj | 2 +- CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs | 14 +++++++------- CodingTracker.Kunikazu723/DatabaseInitializer.cs | 4 ++-- .../UserLand/UserInterface.cs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj index 4e5896020..be84a9e44 100644 --- a/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj +++ b/CodingTracker.Kunikazu723/CodingTracker.Kunikazu723.csproj @@ -9,11 +9,11 @@ + - diff --git a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs index 248f07507..8240c4c6e 100644 --- a/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs +++ b/CodingTracker.Kunikazu723/Dao/CodingSessionDao.cs @@ -1,7 +1,7 @@ using CodingTracker.Kunikazu723.Dao.Interfaces; using CodingTracker.Kunikazu723.Models; using Dapper; -using System.Data.SQLite; +using Microsoft.Data.Sqlite; namespace CodingTracker.Kunikazu723.Dao { @@ -16,7 +16,7 @@ public CodingSessionDao(string connectionString) public void DeleteItemById(CodingSession codingSession) { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); var sql = """ DELETE FROM CodingSessions WHERE Id = @Id """; @@ -25,7 +25,7 @@ public void DeleteItemById(CodingSession codingSession) public List GetAllItems() { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); string sql = """ SELECT * FROM CodingSessions """; @@ -34,7 +34,7 @@ public List GetAllItems() public CodingSession? GetItemById(int id) { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); var sql = """ SELECT * FROM CodingSessions WHERE Id = @itemId """; @@ -43,7 +43,7 @@ public List GetAllItems() public void InsertItem(CodingSession item) { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); var sql = """ INSERT INTO CodingSessions (StartTime, EndTime) VALUES (@StartTime, @EndTime) """; @@ -52,7 +52,7 @@ INSERT INTO CodingSessions (StartTime, EndTime) VALUES (@StartTime, @EndTime) public void InsertMany(List collection) { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); connection.Open(); using var transaction = connection.BeginTransaction(); @@ -65,7 +65,7 @@ INSERT INTO CodingSessions (StartTime, EndTime) public void UpdateItemById(CodingSession newItem) { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); var sql = """ UPDATE CodingSessions SET StartTime = @StartTime, EndTime = @EndTime diff --git a/CodingTracker.Kunikazu723/DatabaseInitializer.cs b/CodingTracker.Kunikazu723/DatabaseInitializer.cs index 81d19e720..9edab0b16 100644 --- a/CodingTracker.Kunikazu723/DatabaseInitializer.cs +++ b/CodingTracker.Kunikazu723/DatabaseInitializer.cs @@ -1,5 +1,5 @@ using Dapper; -using System.Data.SQLite; +using Microsoft.Data.Sqlite; namespace CodingTracker.Kunikazu723 { @@ -13,7 +13,7 @@ public DatabaseInitializer(string connectionString) public void Initialize() { - using var connection = new SQLiteConnection(_connectionString); + using var connection = new SqliteConnection(_connectionString); connection.Execute(""" CREATE TABLE IF NOT EXISTS CodingSessions ( Id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs index 50d9d7f18..3e304b9bd 100644 --- a/CodingTracker.Kunikazu723/UserLand/UserInterface.cs +++ b/CodingTracker.Kunikazu723/UserLand/UserInterface.cs @@ -2,7 +2,7 @@ using CodingTracker.Kunikazu723.Models; using CodingTracker.Kunikazu723.Services; using Spectre.Console; -using System.Data.SQLite; +using Microsoft.Data.Sqlite; using static CodingTracker.Kunikazu723.Enums.MenuEnums; using static CodingTracker.Kunikazu723.Enums.Filters; @@ -35,7 +35,7 @@ public void MainMenu() { ExecuteOption(option); } - catch (SQLiteException ex) + catch (SqliteException ex) { if (ex.ErrorCode == 19) { From 00ea3c4ddc0485106532f51c70ad733104abb184 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:22:30 -0300 Subject: [PATCH 88/93] Add README.md --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2563d8f9c..1398103b8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,16 @@ ![.NET Vesrion](https://img.shields.io/badge/.NET-9.0-512BD4) ![Language](https://img.shields.io/badge/Language-C%23-blue) -![Database](https://img.shields.io/badge/Database-SQLite-green) +![Database](https://img.shields.io/badge/Database-SQLite_+_Dapper-green) + ## :mag_right: About Tracking Coding Sessions is a good way for the user to evaluate how often they code. **Coding Tracker** is a [TUI](https://en.wikipedia.org/wiki/Text-based_user_interface) application that offers simple [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations to track the user's Coding Sessions by either manually typing the start and end of a coding session, or by using a built-in Stopwatch to track the session in real time. +**Packages**: +- [Dapper](https://github.com/dapperlib/dapper) +- [ADO.NET SQLite provider](https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/?tabs=net-cli) +- [Spectre Console](https://spectreconsole.net/) +- [Microsoft Configuration Extensions for .NET](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration) + ## How to run locally The app has not been published, so it needs to run locally by .NET's runtime. > [!IMPORTANT] Make sure [**.NET SDK 9**](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or higher is installed in order to run this project. @@ -24,16 +31,47 @@ Run the project: dotnet run --poject CodingTracker.Kunikazu723 ``` -To change databasePath, connection strings and date time format, refer to the [App Settings]() section. +To change databasePath, connection strings and date time format, refer to the [App Settings](#app-settings-file) section. ## 💾 How it works First the Database is generated with 100 random Coding Sessions. Then the user is presented with 5 options tha can be selected using the keyboard's arrows, 4 of them are CRUD operations. Each Coding Session needs a Start Date and End Date, both are stored in the database with a default date-time format. The duration is calculated during data presentation. ## :pencil2: Architectural Choices +### Database Structure +The database has a Coding Sessions table that contains Id, Start Time and End Time. Both Start and End times follow one strict storage format (yyyy-MM-dd hh:mm:ss). + +### Data Access +Database access is made through a Data Access Object (DAO) to make client code esaily communicate with the database through simple method calls. +Usually I would use a service class as an intermediate between the DAO and the User Interface, but for a project this size I thought it was a bit overkill. I uderstand that an intermediate would be valuable to make sure any data entering the Database is clean and follows the specified pattern. But for simplicity sake I chose to make the UI communicate directly to the DAO. +> [!NOTE] One thing worth noticing is that DateTime objects are stored in the database using Dapper calls directly without any conversion made by my code, it uses ADO.NET drivers implicit conversion that follows DateTime's ISO string format -TODO +### User Interface +As usual the UI is projected as a TUI by using [Spectre Console](https://spectreconsole.net/) package, allowing for intuitive, pretty and smooth user experience while also making it far easier to design it. + +### App Settings File +The following is an example containing the required settings that the application uses to run properly. +```{ + "DatabasePath": "coding-tracker.db", + "ConnectionStrings": { + "DefaultConnection": "Data Source=coding-tracker.db" + }, + "DateTimeFormat": "dd-MM-yyyy-HH-mm-ss", + "SeedDatabase" : true +} +``` +**DatabasePath and ConnectionStrings**: Sets up the relative database path that will be outputed in de working directory. Its available, but its use is not fully fleshed out. Use Path builder and connection builder would be better but they're not currently implemented. + +**DateTimeFormat**: This will define the format that will be **presented** to the user. This setting does not affect the way DateTime strings are stored into the database, it only changes presentation. + +**SeedDatabase**: If set to true, the Seeding algorithm will run and the database will be populated with 100 random Coding Sessions. ## Reflection +Even though the use of Dapper was required, it turned out to be quite simple, even simpler than using raw ADO.NET. There were few differences in the overall project structure from my [previous project](https://github.com/Kunikazu723/TCSA.HabitTracker). The only noticeable differences were the use of Dapper micro ORM, configuration files and DateTime logic. + +It was an interesting project. I wish to improve on it when I gather more experience. Maybe adding a GUI later on. + +### Challenges +By far the most difficult challenge was desiging a good filter for displaying Coding Sessions. Setting up the date rolling window was tricky and displayed incorrect or unexpected results. Luckily after banging my head against the wall for a bit I managed to make a predictable rolling window. -TODO \ No newline at end of file +Another suprising challenge was properly accessing the [App Settings File](#app-settings-file). The task is seemingly simple but I found myself deep into the rabbit hole of configuration files and how to set them up properly. After reading some articles I found the Extension Configuration package from microsoft. \ No newline at end of file From e89c2fc5bf78663825273d1ca4c5c2f4656cc822 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:26:19 -0300 Subject: [PATCH 89/93] Add emoji to Reflection section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1398103b8..a73d6ae06 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The following is an example containing the required settings that the applicatio **SeedDatabase**: If set to true, the Seeding algorithm will run and the database will be populated with 100 random Coding Sessions. -## Reflection +## :mirror: Reflection Even though the use of Dapper was required, it turned out to be quite simple, even simpler than using raw ADO.NET. There were few differences in the overall project structure from my [previous project](https://github.com/Kunikazu723/TCSA.HabitTracker). The only noticeable differences were the use of Dapper micro ORM, configuration files and DateTime logic. It was an interesting project. I wish to improve on it when I gather more experience. Maybe adding a GUI later on. From 1fec921b8b436590b6cd937c4f248190f315b56c Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:27:36 -0300 Subject: [PATCH 90/93] quote test --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a73d6ae06..c0e98414d 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ The database has a Coding Sessions table that contains Id, Start Time and End Ti ### Data Access Database access is made through a Data Access Object (DAO) to make client code esaily communicate with the database through simple method calls. Usually I would use a service class as an intermediate between the DAO and the User Interface, but for a project this size I thought it was a bit overkill. I uderstand that an intermediate would be valuable to make sure any data entering the Database is clean and follows the specified pattern. But for simplicity sake I chose to make the UI communicate directly to the DAO. -> [!NOTE] One thing worth noticing is that DateTime objects are stored in the database using Dapper calls directly without any conversion made by my code, it uses ADO.NET drivers implicit conversion that follows DateTime's ISO string format +> [!NOTE] +> One thing worth noticing is that DateTime objects are stored in the database using Dapper calls directly without any conversion made by my code, it uses ADO.NET drivers implicit conversion that follows DateTime's ISO string format ### User Interface As usual the UI is projected as a TUI by using [Spectre Console](https://spectreconsole.net/) package, allowing for intuitive, pretty and smooth user experience while also making it far easier to design it. From c08bddd06849e0d0aaebe61605d41a16d3806a32 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:28:17 -0300 Subject: [PATCH 91/93] Fix Important box --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c0e98414d..35bc3edfe 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Tracking Coding Sessions is a good way for the user to evaluate how often they c ## How to run locally The app has not been published, so it needs to run locally by .NET's runtime. -> [!IMPORTANT] Make sure [**.NET SDK 9**](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or higher is installed in order to run this project. +> [!IMPORTANT] +> Make sure [**.NET SDK 9**](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or higher is installed in order to run this project. Clone the repository: ``` From 5bf3dd5a99044caa31b5fe13e1c2378b1ecbbce1 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:32:02 -0300 Subject: [PATCH 92/93] Fix typo in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 35bc3edfe..eb2ed3fcf 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,6 @@ Even though the use of Dapper was required, it turned out to be quite simple, ev It was an interesting project. I wish to improve on it when I gather more experience. Maybe adding a GUI later on. ### Challenges -By far the most difficult challenge was desiging a good filter for displaying Coding Sessions. Setting up the date rolling window was tricky and displayed incorrect or unexpected results. Luckily after banging my head against the wall for a bit I managed to make a predictable rolling window. +By far the most difficult challenge was designing a good filter for displaying Coding Sessions. Setting up the date rolling window was tricky and displayed incorrect or unexpected results. Luckily after banging my head against the wall for a bit I managed to make a predictable rolling window. -Another suprising challenge was properly accessing the [App Settings File](#app-settings-file). The task is seemingly simple but I found myself deep into the rabbit hole of configuration files and how to set them up properly. After reading some articles I found the Extension Configuration package from microsoft. \ No newline at end of file +Another surprising challenge was properly accessing the [App Settings File](#app-settings-file). The task is seemingly simple but I found myself deep into the rabbit hole of configuration files and how to set them up properly. After reading some articles I found the Extension Configuration package from microsoft. \ No newline at end of file From bc7589e779854685a44052f191afd8fc2fb1dcf0 Mon Sep 17 00:00:00 2001 From: Kunikazu723 <71518707+Kunikazu723@users.noreply.github.com> Date: Mon, 2 Nov 2026 13:48:50 -0300 Subject: [PATCH 93/93] Add useful information to README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index eb2ed3fcf..6506ce543 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ dotnet run --poject CodingTracker.Kunikazu723 To change databasePath, connection strings and date time format, refer to the [App Settings](#app-settings-file) section. ## 💾 How it works -First the Database is generated with 100 random Coding Sessions. Then the user is presented with 5 options tha can be selected using the keyboard's arrows, 4 of them are CRUD operations. +First the Database is generated with 100 random Coding Sessions (Database seeding can be configured using [App Settings](#app-settings-file)). Then the user is presented with 5 options tha can be selected using the keyboard's arrows, 4 of them are CRUD operations the last one is to exit the app. Each Coding Session needs a Start Date and End Date, both are stored in the database with a default date-time format. The duration is calculated during data presentation. ## :pencil2: Architectural Choices @@ -46,7 +46,7 @@ The database has a Coding Sessions table that contains Id, Start Time and End Ti Database access is made through a Data Access Object (DAO) to make client code esaily communicate with the database through simple method calls. Usually I would use a service class as an intermediate between the DAO and the User Interface, but for a project this size I thought it was a bit overkill. I uderstand that an intermediate would be valuable to make sure any data entering the Database is clean and follows the specified pattern. But for simplicity sake I chose to make the UI communicate directly to the DAO. > [!NOTE] -> One thing worth noticing is that DateTime objects are stored in the database using Dapper calls directly without any conversion made by my code, it uses ADO.NET drivers implicit conversion that follows DateTime's ISO string format +> One thing worth noticing is that DateTime objects are stored in the database using Dapper calls directly without any conversion made by my code, it uses [Microsoft's ADO.NET Sqlite provider implicit conversion](https://learn.microsoft.com/pt-br/dotnet/standard/data/sqlite/types) that follows DateTime's ISO string format. ### User Interface As usual the UI is projected as a TUI by using [Spectre Console](https://spectreconsole.net/) package, allowing for intuitive, pretty and smooth user experience while also making it far easier to design it. @@ -69,7 +69,7 @@ The following is an example containing the required settings that the applicatio **SeedDatabase**: If set to true, the Seeding algorithm will run and the database will be populated with 100 random Coding Sessions. ## :mirror: Reflection -Even though the use of Dapper was required, it turned out to be quite simple, even simpler than using raw ADO.NET. There were few differences in the overall project structure from my [previous project](https://github.com/Kunikazu723/TCSA.HabitTracker). The only noticeable differences were the use of Dapper micro ORM, configuration files and DateTime logic. +Even though the use of Dapper was required, it turned out to be quite simple, even simpler than using raw ADO.NET. There were few differences in the overall project structure compared to my [previous project](https://github.com/Kunikazu723/TCSA.HabitTracker). The only noticeable differences were the use of Dapper micro ORM, configuration files and DateTime logic. It was an interesting project. I wish to improve on it when I gather more experience. Maybe adding a GUI later on.