From d172407a94b14536f644fbbc98e08e655fa246b4 Mon Sep 17 00:00:00 2001 From: Dania Date: Fri, 13 Mar 2026 18:15:57 +0800 Subject: [PATCH] Finished and submit Coding Tracker for review --- Dania.CodingTracker/Assets/1.png | Bin 0 -> 13919 bytes Dania.CodingTracker/Assets/2.png | Bin 0 -> 16033 bytes Dania.CodingTracker/Assets/3.png | Bin 0 -> 19033 bytes .../CodingTracker/CodingTracker.csproj | 23 ++ .../CodingTracker/CodingTracker.slnx | 3 + .../CodingTracker/DatabaseManager.cs | 105 ++++++++ Dania.CodingTracker/CodingTracker/Enums.cs | 14 + Dania.CodingTracker/CodingTracker/Helpers.cs | 50 ++++ .../CodingTracker/Models/CodingSession.cs | 10 + Dania.CodingTracker/CodingTracker/Program.cs | 14 + .../CodingTracker/UserInterface.cs | 249 ++++++++++++++++++ .../CodingTracker/appsettings.json | 5 + Dania.CodingTracker/README.md | 32 +++ 13 files changed, 505 insertions(+) create mode 100644 Dania.CodingTracker/Assets/1.png create mode 100644 Dania.CodingTracker/Assets/2.png create mode 100644 Dania.CodingTracker/Assets/3.png create mode 100644 Dania.CodingTracker/CodingTracker/CodingTracker.csproj create mode 100644 Dania.CodingTracker/CodingTracker/CodingTracker.slnx create mode 100644 Dania.CodingTracker/CodingTracker/DatabaseManager.cs create mode 100644 Dania.CodingTracker/CodingTracker/Enums.cs create mode 100644 Dania.CodingTracker/CodingTracker/Helpers.cs create mode 100644 Dania.CodingTracker/CodingTracker/Models/CodingSession.cs create mode 100644 Dania.CodingTracker/CodingTracker/Program.cs create mode 100644 Dania.CodingTracker/CodingTracker/UserInterface.cs create mode 100644 Dania.CodingTracker/CodingTracker/appsettings.json create mode 100644 Dania.CodingTracker/README.md diff --git a/Dania.CodingTracker/Assets/1.png b/Dania.CodingTracker/Assets/1.png new file mode 100644 index 0000000000000000000000000000000000000000..1865b4797b0bc6f7a35163c700826760e2b40bd8 GIT binary patch literal 13919 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 literal 0 HcmV?d00001 diff --git a/Dania.CodingTracker/Assets/3.png b/Dania.CodingTracker/Assets/3.png new file mode 100644 index 0000000000000000000000000000000000000000..32d383ed5146a55b5685a5c14af58f1c1acec101 GIT binary patch literal 19033 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 B!I=O6 literal 0 HcmV?d00001 diff --git a/Dania.CodingTracker/CodingTracker/CodingTracker.csproj b/Dania.CodingTracker/CodingTracker/CodingTracker.csproj new file mode 100644 index 000000000..f559ad53d --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/CodingTracker.csproj @@ -0,0 +1,23 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + + PreserveNewest + + + + diff --git a/Dania.CodingTracker/CodingTracker/CodingTracker.slnx b/Dania.CodingTracker/CodingTracker/CodingTracker.slnx new file mode 100644 index 000000000..ba103e54f --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/CodingTracker.slnx @@ -0,0 +1,3 @@ + + + diff --git a/Dania.CodingTracker/CodingTracker/DatabaseManager.cs b/Dania.CodingTracker/CodingTracker/DatabaseManager.cs new file mode 100644 index 000000000..016570114 --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/DatabaseManager.cs @@ -0,0 +1,105 @@ +using CodingTracker.Models; +using Dapper; +using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Configuration; + +namespace CodingTracker +{ + internal class DatabaseManager + { + //Appsetting.json config connection + public string GetConnectionString() + { + IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + string connectionString = config.GetConnectionString("DefaultConnection"); + + return connectionString; + + } + + internal void CreateTable() + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = @"CREATE TABLE IF NOT EXISTS coding_tracker( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + StartTime TEXT, + EndTime TEXT, + Duration TEXT)"; + + connection.Execute(sql); //Dapper execute + } + } + + // Return rows affected checks if the execute succeeds or not + internal int Post(CodingSession session) + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = "INSERT INTO coding_tracker (StartTime, EndTime, Duration) VALUES (@StartTime, @EndTime, @Duration)"; + + var rowsAffected = connection.Execute(sql,session); //Dapper Execute + return rowsAffected; + } + } + + internal List Get() + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = "SELECT * FROM coding_tracker"; + + var sessions = connection.Query(sql); + + return sessions.ToList(); + } + } + + + internal void Delete(int id) + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = "DELETE FROM coding_tracker WHERE Id = @Id"; + + connection.Execute(sql, new {Id = id }); + } + } + + internal void Update(CodingSession session) + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = @"UPDATE coding_tracker + SET StartTime = @StartTime, + EndTime = @EndTime, + Duration = @Duration + WHERE Id = @Id"; + + connection.Execute(sql, session); + } + } + + internal bool CheckIdExists(int id) + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = "SELECT COUNT(*) FROM coding_tracker WHERE Id = @Id"; + + int count = connection.ExecuteScalar(sql, new { Id = id}); + + return count > 0; + } + } + + internal CodingSession GetSessionByID(int id) + { + using (var connection = new SqliteConnection(GetConnectionString())) + { + var sql = "SELECT * FROM coding_tracker where Id = @Id"; + return connection.QuerySingle(sql, new { Id = id }); + } + + } + } +} diff --git a/Dania.CodingTracker/CodingTracker/Enums.cs b/Dania.CodingTracker/CodingTracker/Enums.cs new file mode 100644 index 000000000..8f51c77c2 --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/Enums.cs @@ -0,0 +1,14 @@ +namespace CodingTracker +{ + internal class Enums + { + internal enum MenuOptions + { + ViewSessions, + InsertSessions, + UpdateSessions, + DeleteSessions, + CloseApp + } + } +} diff --git a/Dania.CodingTracker/CodingTracker/Helpers.cs b/Dania.CodingTracker/CodingTracker/Helpers.cs new file mode 100644 index 000000000..36a4b3bb9 --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/Helpers.cs @@ -0,0 +1,50 @@ +using Spectre.Console; +using System.Globalization; + +namespace CodingTracker +{ + internal class Helpers + { + CultureInfo enUS = new CultureInfo("en-US"); + + internal string CheckDateTime() + { + string date = ""; + bool isValid = false; + + do + { + date = AnsiConsole.Ask("Format is [green]dd-MM-yy HH:mm[/] or type t for today's date and time: "); + date = date.Trim().ToLower(); + + if (date == "t") + date = DateTime.Now.ToString("dd-MM-yy HH:mm"); + + if (date != "0") + isValid = DateTime.TryParseExact(date, "dd-MM-yy HH:mm", enUS, DateTimeStyles.None, out _); + else + isValid = true; + + if (!isValid) + { + AnsiConsole.Markup("[red]Please input the right date format![/]\n"); + Console.ReadLine(); + } + + } while (!isValid); + + return date; + } + + internal TimeSpan GetDuration(string startDateTime, string endDateTime) + { + DateTime start = DateTime.ParseExact(startDateTime, "dd-MM-yy HH:mm", null); + DateTime end = DateTime.ParseExact(endDateTime, "dd-MM-yy HH:mm", null); + + TimeSpan duration = end - start; + + return duration; + } + + } +} diff --git a/Dania.CodingTracker/CodingTracker/Models/CodingSession.cs b/Dania.CodingTracker/CodingTracker/Models/CodingSession.cs new file mode 100644 index 000000000..da4252518 --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/Models/CodingSession.cs @@ -0,0 +1,10 @@ +namespace CodingTracker.Models +{ + internal class CodingSession + { + public int Id { get; set; } + public string StartTime { get; set; } + public string EndTime { get; set; } + public string Duration { get; set; } + } +} diff --git a/Dania.CodingTracker/CodingTracker/Program.cs b/Dania.CodingTracker/CodingTracker/Program.cs new file mode 100644 index 000000000..0118c77f3 --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/Program.cs @@ -0,0 +1,14 @@ +namespace CodingTracker +{ + internal class Program + { + static void Main(string[] args) + { + DatabaseManager databaseManager = new DatabaseManager(); + UserInterface userInterface = new UserInterface(); + + databaseManager.CreateTable(); + userInterface.MainMenu(); + } + } +} diff --git a/Dania.CodingTracker/CodingTracker/UserInterface.cs b/Dania.CodingTracker/CodingTracker/UserInterface.cs new file mode 100644 index 000000000..77c65c56c --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/UserInterface.cs @@ -0,0 +1,249 @@ +using Spectre.Console; +using CodingTracker.Models; + +namespace CodingTracker +{ + internal class UserInterface + { + DatabaseManager databaseManager = new DatabaseManager(); + Helpers helpers = new Helpers(); + + internal void MainMenu() + { + bool isCloseApp = false; + while (!isCloseApp) + { + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[bold yellow]---Welcome to the Coding Tracker[/]---\n"); + + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("What would you like to do?") + .AddChoices(Enum.GetValues())); + + switch (choice) + { + case Enums.MenuOptions.ViewSessions: + ViewSessions(); + break; + case Enums.MenuOptions.InsertSessions: + InsertSessions(); + break; + case Enums.MenuOptions.UpdateSessions: + UpdateSessions(); + break; + case Enums.MenuOptions.DeleteSessions: + DeleteSessions(); + break; + case Enums.MenuOptions.CloseApp: + isCloseApp = true; + break; + + } + + } + } + + private void ViewSessions() + { + var sessions = databaseManager.Get(); + + if (sessions.Count == 0) + { + AnsiConsole.MarkupLine("[red]No sessions recorded.[/]\nPress enter key to go back to Main Menu."); + Console.ReadLine(); + return; + } + + DrawTable(sessions); + + AnsiConsole.MarkupLine("Press enter key to go back to Main Menu."); + Console.ReadLine(); + } + + private void InsertSessions() + { + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[bold yellow]---Insert Coding Sessions---[/]"); + + AnsiConsole.MarkupLine("Please insert the [bold green]start[/] date and time (or type 0 to go back to the Main Menu)."); + var startDateTime = helpers.CheckDateTime(); + if (startDateTime == "0") return; + + AnsiConsole.MarkupLine("\nPlease insert the [bold green]end[/] date and time (or type 0 to go back to the Main Menu)."); + var endDateTime = helpers.CheckDateTime(); + if (endDateTime == "0") return; + + var duration = helpers.GetDuration(startDateTime, endDateTime); + + if (duration < TimeSpan.Zero) + { + AnsiConsole.Markup("[red]End date cannot be before start date! Please try again.[/]\n"); + Console.ReadLine(); + + } + else + { + CodingSession session = new CodingSession(); + session.StartTime = startDateTime; + session.EndTime = endDateTime; + session.Duration = $"{(int)duration.TotalHours:D2}:{duration.Minutes:D2}"; + + int rows = databaseManager.Post(session); + + if (rows > 0) + { + AnsiConsole.MarkupLine("\n[green]Session added sucessfully![/]"); + Console.ReadLine(); + } + else + { + AnsiConsole.MarkupLine("\n[red]Session added failed! Try again later[/]"); + Console.ReadLine(); + } + + } + + } + + private void UpdateSessions() + { + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[bold yellow]---Update Coding Sessions---[/]"); + + var sessions = databaseManager.Get(); + + if (sessions.Count == 0) + { + AnsiConsole.MarkupLine("\n[red]No sessions recorded to update.[/]\nPress enter key to go back to Main Menu."); + Console.ReadLine(); + return; + } + + DrawTable(sessions); + + int inputID = AnsiConsole.Ask("Please insert the number ID to update the session (or type 0 to go back to the Main Menu). "); + + if (inputID == 0) return; + + if (!databaseManager.CheckIdExists(inputID)) + { + AnsiConsole.MarkupLine($"\n[red]Session with ID {inputID} doesn't exist.[/]"); + Console.ReadLine(); + UpdateSessions(); + } + else + { + var sessionChoice = databaseManager.GetSessionByID(inputID); + + bool isUpdating = true; + while (isUpdating) + { + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[bold yellow]---Update Coding Sessions---[/]"); + DrawTable(sessions); + + var choice = AnsiConsole.Prompt(new SelectionPrompt() + .Title($"What would you like to do with Session {inputID}?") + .AddChoices("Update Start Time","Update End Time","Save and Update Database","Return to Main Menu")); + + switch (choice) + { + case "Update Start Time": + AnsiConsole.MarkupLine("Please insert the [bold green]start[/] date and time."); + sessionChoice.StartTime = helpers.CheckDateTime(); + break; + case "Update End Time": + AnsiConsole.MarkupLine("\nPlease insert the [bold green]end[/] date and time."); + sessionChoice.EndTime = helpers.CheckDateTime(); + break; + case "Save and Update Database": + var duration = helpers.GetDuration(sessionChoice.StartTime, sessionChoice.EndTime); + if (duration < TimeSpan.Zero) + { + AnsiConsole.Markup("[red]End date cannot be before start date! Please try again.[/]\n"); + Console.ReadLine(); + } + else + { + sessionChoice.Duration = $"{(int)duration.TotalHours:D2}:{duration.Minutes:D2}"; + databaseManager.Update(sessionChoice); + AnsiConsole.Markup("[green]Session has been updated![/]\n"); + Console.ReadLine(); + isUpdating = false; + } + break; + case "Return to Main Menu": + isUpdating = false; + break; + + } + } + + } + } + + + private void DeleteSessions() + { + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[bold yellow]---Delete Coding Sessions---[/]"); + + var sessions = databaseManager.Get(); + + if (sessions.Count == 0) + { + AnsiConsole.MarkupLine("\n[red]No sessions recorded to delete.[/]\nPress enter key to go back to Main Menu."); + Console.ReadLine(); + return; + } + + DrawTable(sessions); + + int inputID = AnsiConsole.Ask("Please insert the number ID to delete the session (or type 0 to go back to the Main Menu). "); + + if (inputID == 0) return; + + if (!databaseManager.CheckIdExists(inputID)) + { + AnsiConsole.MarkupLine($"\n[red]Session with ID {inputID} doesn't exist.[/]"); + Console.ReadLine(); + DeleteSessions(); + } + else + { + if (AnsiConsole.Confirm("Are you sure you want to delete this session?")) + { + databaseManager.Delete(inputID); + AnsiConsole.MarkupLine($"\n[green]Session with ID {inputID} has been deleted![/]"); + Console.ReadLine(); + } + else + { + AnsiConsole.MarkupLine("[red]Session deletion cancelled[/]"); + Console.ReadLine(); + return; + } + } + } + + + private void DrawTable(List sessions) + { + var table = new Table(); + + table.AddColumn("[bold]ID[/]"); + table.AddColumn("[bold]Start Date[/]"); + table.AddColumn("[bold]End Date[/]"); + table.AddColumn("[bold]Duration[/]"); + + foreach (var session in sessions) + { + table.AddRow(session.Id.ToString(), $"{session.StartTime}", $"{session.EndTime}", $"{session.Duration}"); + } + + AnsiConsole.Write(table); + } + + } +} diff --git a/Dania.CodingTracker/CodingTracker/appsettings.json b/Dania.CodingTracker/CodingTracker/appsettings.json new file mode 100644 index 000000000..0908081bd --- /dev/null +++ b/Dania.CodingTracker/CodingTracker/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=coding-tracker.db" + } +} \ No newline at end of file diff --git a/Dania.CodingTracker/README.md b/Dania.CodingTracker/README.md new file mode 100644 index 000000000..0789a6ab7 --- /dev/null +++ b/Dania.CodingTracker/README.md @@ -0,0 +1,32 @@ +# Coding Tracker +Coding Tracker is the third green belt project from the C# Academy. It is similar to the Habit Logger on having CRUD functions but the difference is the app asks for the user's beginning coding date time and end coding date time to find the coding time duration. This is also an opportunity to learn Object-Oriented-Programming (OOP). I programmed using C#, SQlite, Dapper and Spectre.Console with Visual Studio 2026. + +## Requirements +- Same requirements as the habit logger in terms of database and CRUD. +- Have separate classes in different files. +- Create a configuration file called appsettings.json which contains the database path and connection strings. +- User should put their start time and end time in specific format and not their duration. Duration should be calculated based on the start time and end time. +- Data need to be shown through Spectre.Console library +- Need to use Dapper ORM for data access instead of ADO.NET. + +## Features +- Console based UI using the Spectre.Console library +![Image](/Assets/1.png) +- CRUD functions: + - Users insert their start date time and end date time in format dd/MM/yy HH:mm + - Users can read, update and delete their coding sessions by inputting the session id. + - Dates and numbers are validated to check if they're in the right format and check if the end date submission has to be after the start date. +![Image](/Assets/2.png) + +## Challenges +- Understanding what appsettings.json. This is the first hurdle of trying to understand what this script does. From my understanding it's a configuration file that stores data like the database connection string which is more secure than hardcoding it in the C# script. This was hardcoded in the habit log project. +- Learning Dapper ORM. I thought it would be another long code I had to learn, but it actually condensed the code from giving large command text to small sql strings to use Dapper's execute and Query(). It also teaches me to use @Parameters from SQL injection attacks and anonymous object to bridge the SQL parameters with the local paremeters. +- Spectre.Console is not that challenging to learn as they provide a very clear documentation on how to use them. The hard part is how to use them to separate the data and visuals in different classes from each other. +- Calculating the duration between two date times. I had to use 24 hour time to calculate the duration as it doesn't require to convert for calculations. The calculations is just simply subtract the end date with the start date with TimeSpan as a data type to represent total hours and minutes of the duration. + +## References +- https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Json +- https://stackoverflow.com/questions/39157781/the-configuration-file-appsettings-json-was-not-found-and-is-not-optional +- https://www.learndapper.com/non-query#dapper-execute +- https://learn.microsoft.com/en-us/dotnet/api/system.timespan?view=net-10.0 +- https://spectreconsole.net/console \ No newline at end of file