From 8ad87468ad2884e96a0f4144a2b23c6386a5be90 Mon Sep 17 00:00:00 2001 From: CSharpAcademy-Learner Date: Fri, 24 Jul 2026 12:42:47 +0100 Subject: [PATCH] Finished project. One challenge attempted --- .../CodingTracker.CSharpAcademy-Learner.slnx | 3 + ...CodingTracker.CSharpAcademy-Learner.csproj | 24 +++ .../Configuration.cs | 17 ++ .../Controllers/CodingController.cs | 53 ++++++ .../DatabaseManager.cs | 23 +++ .../EnumExtensions.cs | 16 ++ .../Enums.cs | 29 +++ .../Models/CodingSession.cs | 14 ++ .../Program.cs | 7 + .../README.md | 28 +++ .../StopwatchService.cs | 58 ++++++ .../TableVisualisationEngine.cs | 34 ++++ .../UserInterface.cs | 165 ++++++++++++++++++ .../UserInterfaceHelpers.cs | 45 +++++ .../Validation.cs | 53 ++++++ .../appsettings.json | 5 + 16 files changed, 574 insertions(+) create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.slnx create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.csproj create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Configuration.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Controllers/CodingController.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/DatabaseManager.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/EnumExtensions.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Enums.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Models/CodingSession.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Program.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/README.md create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/StopwatchService.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/TableVisualisationEngine.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterface.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterfaceHelpers.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Validation.cs create mode 100644 CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/appsettings.json diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.slnx b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.slnx new file mode 100644 index 000000000..685fdbe04 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.slnx @@ -0,0 +1,3 @@ + + + diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.csproj b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.csproj new file mode 100644 index 000000000..e31d2b818 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.csproj @@ -0,0 +1,24 @@ + + + + Exe + net10.0 + CodingTracker.CSharpAcademy_Learner + enable + enable + + + + + + + + + + + + PreserveNewest + + + + diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Configuration.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Configuration.cs new file mode 100644 index 000000000..332fcdfb3 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Configuration.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner +{ + public static class Configuration + { + private static readonly IConfigurationRoot _config = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory).AddJsonFile("appsettings.json", false, true).Build(); + + public static string GetConnectionString() + { + return _config.GetConnectionString("DefaultConnection") ?? "Data Source=coding-tracker.db"; + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Controllers/CodingController.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Controllers/CodingController.cs new file mode 100644 index 000000000..c5b99ae8a --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Controllers/CodingController.cs @@ -0,0 +1,53 @@ +using CodingTracker.CSharpAcademy_Learner.Models; +using Dapper; +using Microsoft.Data.Sqlite; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner.Controllers +{ + internal class CodingController + { + internal List GetAllSessions() + { + using (var connection = new SqliteConnection(Configuration.GetConnectionString())) + { + var querySQL = "SELECT * FROM coding_sessions"; + var sessions = connection.Query(querySQL).ToList(); + + return sessions; + } + } + + internal void InsertSession(string startTime, string endTime, int duration) + { + using (var connection = new SqliteConnection(Configuration.GetConnectionString())) + { + var parameters = new { StartTime = startTime, EndTime = endTime, Duration = duration }; + var insertSQL = "INSERT INTO coding_sessions(StartTime, EndTime, Duration) VALUES (@StartTime, @EndTime, @Duration)"; + connection.Execute(insertSQL, parameters); + } + } + + internal void UpdateSession(int sessionId, string startTime, string endTime, int duration) + { + using (var connection = new SqliteConnection(Configuration.GetConnectionString())) + { + var parameters = new { Id = sessionId, StartTime = startTime, EndTime = endTime, Duration = duration }; + var updateSQL = "UPDATE coding_sessions SET StartTime = @StartTime, EndTime = @EndTime, Duration = @Duration WHERE Id = @Id"; + connection.Execute(updateSQL, parameters); + } + } + + internal void DeleteSession(int sessionId) + { + using (var connection = new SqliteConnection(Configuration.GetConnectionString())) + { + var parameters = new { Id = sessionId }; + var deleteSQL = "DELETE FROM coding_sessions WHERE Id = @Id"; + connection.Execute(deleteSQL, parameters); + } + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/DatabaseManager.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/DatabaseManager.cs new file mode 100644 index 000000000..7401e6947 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/DatabaseManager.cs @@ -0,0 +1,23 @@ +using Dapper; +using Microsoft.Data.Sqlite; + +namespace CodingTracker.CSharpAcademy_Learner +{ + public static class DatabaseManager + { + public static void InitializeDatabase() + { + var createTableSQL = @"CREATE TABLE IF NOT EXISTS coding_sessions ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + StartTime TEXT, + EndTime TEXT, + Duration INTEGER + )"; + + using (var connection = new SqliteConnection(Configuration.GetConnectionString())) + { + connection.Execute(createTableSQL); + } + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/EnumExtensions.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/EnumExtensions.cs new file mode 100644 index 000000000..9ebc122b8 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/EnumExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal static class EnumExtensions + { + internal static string GetDisplayName(this Enum enumValue) + { + return enumValue.GetType().GetMember(enumValue.ToString()).FirstOrDefault()?.GetCustomAttribute()?.GetName() ?? enumValue.ToString(); + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Enums.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Enums.cs new file mode 100644 index 000000000..35ea99f12 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Enums.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal class Enums + { + internal enum MenuAction + { + [Display(Name = "View Records")] + ViewRecords, + + [Display(Name = "Add Record")] + AddRecord, + + [Display(Name = "Update Record")] + UpdateRecord, + + [Display(Name = "Delete Record")] + DeleteRecord, + + [Display(Name = "Stopwatch")] + StopwatchService, + Exit + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Models/CodingSession.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Models/CodingSession.cs new file mode 100644 index 000000000..c50ba8487 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Models/CodingSession.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner.Models +{ + internal class CodingSession + { + public int Id { get; set; } + public string StartTime { get; set; } = string.Empty; + public string EndTime { get; set; } = string.Empty; + public int Duration { get; set; } = 0; + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Program.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Program.cs new file mode 100644 index 000000000..78a29e490 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Program.cs @@ -0,0 +1,7 @@ +using CodingTracker.CSharpAcademy_Learner; +using CodingTracker.CSharpAcademy_Learner.Controllers; + +DatabaseManager.InitializeDatabase(); +var codingController = new CodingController(); +var userInterface = new UserInterface(codingController); +userInterface.MainMenu(); \ No newline at end of file diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/README.md b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/README.md new file mode 100644 index 000000000..417baf68f --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/README.md @@ -0,0 +1,28 @@ +# Coding Tracker + +A console-based CRUD application to track coding sessions, written in C# and powered by SQLite. + +## How It Works + +The application connects to a local SQLite database using Dapper. Upon the very first execution, it automatically creates the coding_sessions table. +The user can then add, view, update and delete records via the console. + +## Challenges Completed + +1. **Stopwatch**: Added the functionality of tracking the coding time via a stopwatch so the user can track the session as it happens. + +### What Was Easy? + +A lot of the concepts were carried on from the Habit Tracker, although they had to be separated instead of it all being in one big Program.cs file. + +### What Was Hard + +This was the first project without following a video tutorial, so I had find things out by searching online and reading through documentation. There were also quite a few new concepts. + +### What I Learned + +I got a better grasp of separation of concerns and how code in different files can interact with each other. + +### Extra Notes + +I probably got a bit acrried away with some things. For example, I preferred to use Enums for the menu options instead of hardcoded strings, this introduced a new issue of having the menu options look good, as Enums can't have spaces. I managed to find a way to use the Display Attribute instead. Also, I tried to make the application what I think is a bit more user friendly by giving the user the option to Escape to the main menu after selecting a menu option, in case they selected the wrong option. diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/StopwatchService.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/StopwatchService.cs new file mode 100644 index 000000000..9202cf002 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/StopwatchService.cs @@ -0,0 +1,58 @@ +using CodingTracker.CSharpAcademy_Learner.Controllers; +using Spectre.Console; +using System.Diagnostics; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal class StopwatchService + { + private readonly CodingController _codingController; + public StopwatchService(CodingController codingController) + { + _codingController = codingController; + } + internal void TrackSession() + { + + if (!UserInterfaceHelpers.WaitForEnterOrEscape_CheckForEnter("Tracking live session")) + { + return; + } + + UserInterfaceHelpers.WaitForSpecificKey(ConsoleKey.Enter, "Press [green]ENTER[/] to start tracking."); + + var stopwatch = Stopwatch.StartNew(); + var startTime = DateTime.Now; + + AnsiConsole.Status() + .Start("Tracking session... Press [green]ENTER[/] to stop...", ctx => + { + while (!Console.KeyAvailable || Console.ReadKey(true).Key != ConsoleKey.Enter) + { + ctx.Status($"Tracking session... Elapsed time: {stopwatch.Elapsed:hh\\:mm\\:ss}... Press [green]ENTER[/] to stop..."); + Thread.Sleep(500); + } + }); + + stopwatch.Stop(); + var endTime = DateTime.Now; + + var formattedStart = startTime.ToString(Validation.DatabaseDateFormat); + var formattedEnd = endTime.ToString(Validation.DatabaseDateFormat); + var duration = Validation.CalculateDurationInSeconds(startTime, endTime); + + AnsiConsole.MarkupLine($"STOPPED. Start time: {formattedStart}. End time: {formattedEnd}. Duration: {duration} seconds"); + + if (AnsiConsole.Confirm("Do you want to save this session?")) + { + _codingController.InsertSession(formattedStart, formattedEnd, duration); + } + else + { + return; + } + + Console.ReadKey(); + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/TableVisualisationEngine.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/TableVisualisationEngine.cs new file mode 100644 index 000000000..a9a668b64 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/TableVisualisationEngine.cs @@ -0,0 +1,34 @@ +using CodingTracker.CSharpAcademy_Learner.Models; +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal static class TableVisualisationEngine + { + internal static void DisplaySessions(List sessions) + { + var table = new Table(); + table.Border(TableBorder.Rounded); + + table.AddColumn("ID"); + table.AddColumn("Start Date"); + table.AddColumn("End Date"); + table.AddColumn("Duration"); + + foreach(var session in sessions) + { + table.AddRow( + session.Id.ToString(), + session.StartTime, + session.EndTime, + Validation.ShowDurationInFriendlyFormat(session.Duration) + ); + } + + AnsiConsole.Write(table); + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterface.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterface.cs new file mode 100644 index 000000000..c45e70df9 --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterface.cs @@ -0,0 +1,165 @@ +using CodingTracker.CSharpAcademy_Learner.Controllers; +using CodingTracker.CSharpAcademy_Learner.Models; +using Spectre.Console; +using static CodingTracker.CSharpAcademy_Learner.Enums; +using static System.Collections.Specialized.BitVector32; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal class UserInterface + { + private readonly CodingController _codingController; + private readonly StopwatchService _stopwatchService; + + public UserInterface(CodingController codingController) + { + _codingController = codingController; + _stopwatchService = new StopwatchService(codingController); + } + + internal void MainMenu() + { + bool closeApp = false; + + while (!closeApp) + { + Console.Clear(); + + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[bold cyan]CODING TRACKER MENU[/]") + .UseConverter(choice => choice.GetDisplayName()) + .AddChoices(Enum.GetValues()) + ); + + switch (choice) + { + case MenuAction.ViewRecords: + ViewAllSessions(); + break; + case MenuAction.AddRecord: + AddRecord(); + break; + case MenuAction.UpdateRecord: + UpdateRecord(); + break; + case MenuAction.DeleteRecord: + DeleteRecord(); + break; + case MenuAction.StopwatchService: + _stopwatchService.TrackSession(); + break; + case MenuAction.Exit: + AnsiConsole.MarkupLine("[cyan]Goodbye :)[/]"); + closeApp = true; + Environment.Exit(0); + break; + } + } + } + + private void ViewAllSessions() + { + var sessions = _codingController.GetAllSessions(); + + TableVisualisationEngine.DisplaySessions(sessions); + + UserInterfaceHelpers.WaitForAnyKey(); + } + + private void AddRecord() + { + if(!UserInterfaceHelpers.WaitForEnterOrEscape_CheckForEnter("You are about to add a new record.")) { return; } + + var sessionInfo = GetSessionInfoFromUserInput(); + + _codingController.InsertSession(sessionInfo.startDate.ToString(Validation.DatabaseDateFormat), sessionInfo.endDate.ToString(Validation.DatabaseDateFormat), sessionInfo.duration); + + UserInterfaceHelpers.WaitForAnyKey(); + } + + private void UpdateRecord() + { + if (!UserInterfaceHelpers.WaitForEnterOrEscape_CheckForEnter("You are about to edit a record.")) { return; } + + var selectedSession = SessionSelector("Choose a session to edit"); + + var sessionInfo = GetSessionInfoFromUserInput(); + + _codingController.UpdateSession(selectedSession.Id, sessionInfo.startDate.ToString(Validation.DatabaseDateFormat), sessionInfo.endDate.ToString(Validation.DatabaseDateFormat), sessionInfo.duration); + + UserInterfaceHelpers.WaitForAnyKey(); + } + + private void DeleteRecord() + { + if (!UserInterfaceHelpers.WaitForEnterOrEscape_CheckForEnter("You are about to delete a record.")) { return; } + + var selectedSession = SessionSelector("Choose a session to delete:"); + + AnsiConsole.MarkupLine(DisplaySessionInfo(selectedSession)); + + if (AnsiConsole.Confirm("Are you sure you want to delete this session?")) + { + _codingController.DeleteSession(selectedSession.Id); + AnsiConsole.MarkupLine("\nSucessfully deleted!"); + } + else + { + AnsiConsole.MarkupLine("\nNOT DELETED"); + } + + UserInterfaceHelpers.WaitForAnyKey(); + } + + private DateTime GetDateInput(string prompt, DateTime? minDate = null) + { + while (true) + { + var dateInput = AnsiConsole.Ask($"[yellow]{prompt}[/]"); + + if (Validation.IsValidDate(dateInput, out DateTime parsedDate)) + { + if (minDate.HasValue && !Validation.IsValidDateRange(minDate.Value, parsedDate)) + { + AnsiConsole.MarkupLine("[red]Start date cannot be equal to or after the end date[/]"); + continue; + } + + return parsedDate; + } + + AnsiConsole.MarkupLine("[red]Invalid format. Please use exactly dd-MM-yyyy HH:mm (e.g., 25-10-2026 14:30).[/]"); + } + } + + private CodingSession SessionSelector(string prompt) + { + var sessions = _codingController.GetAllSessions(); + + var selectedSession = AnsiConsole.Prompt( + new SelectionPrompt() + .Title(prompt) + .UseConverter(s => DisplaySessionInfo(s)) + .AddChoices(sessions) + ); + + return selectedSession; + } + + private (DateTime startDate, DateTime endDate, int duration) GetSessionInfoFromUserInput() + { + var startDate = GetDateInput("Enter the start date (use format dd/MM/yyyy HH:mm): "); + var endDate = GetDateInput("Enter the end date (use format dd/MM/yyyy HH:mm): ", startDate); + + var duration = Validation.CalculateDurationInSeconds(startDate, endDate); + + return (startDate, endDate, duration); + } + + private string DisplaySessionInfo(CodingSession session) + { + return $"Start Time: {session.StartTime} - End Time: {session.EndTime} - Duration: {Validation.ShowDurationInFriendlyFormat(session.Duration)}"; + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterfaceHelpers.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterfaceHelpers.cs new file mode 100644 index 000000000..bab74d32b --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/UserInterfaceHelpers.cs @@ -0,0 +1,45 @@ +using Spectre.Console; +using SQLitePCL; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal static class UserInterfaceHelpers + { + internal static void WaitForAnyKey() + { + AnsiConsole.MarkupLine("[green]Press any key to continue...[/]"); + Console.ReadKey(); + } + + internal static void WaitForSpecificKey(ConsoleKey key, string message) + { + AnsiConsole.MarkupLine(message); + + while(Console.ReadKey(true).Key != key) { } + } + + internal static bool WaitForEnterOrEscape_CheckForEnter(string? initialMessage = null) + { + if(initialMessage != null) + { + AnsiConsole.MarkupLine(initialMessage); + } + + AnsiConsole.MarkupLine("Press [green]ENTER[/] to continue or [yellow]ESC[/] to go back to the main menu"); + + ConsoleKey key; + + do + { + key = Console.ReadKey(true).Key; + } + while (key != ConsoleKey.Enter && key != ConsoleKey.Escape); + + return key == ConsoleKey.Enter; + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Validation.cs b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Validation.cs new file mode 100644 index 000000000..24ed590ad --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/Validation.cs @@ -0,0 +1,53 @@ +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace CodingTracker.CSharpAcademy_Learner +{ + internal static class Validation + { + internal const string ViewingDateFormat = "dd/MM/yyyy HH:mm"; + internal const string DatabaseDateFormat = "dd/MM/yyyy HH:mm:ss"; + + internal static bool IsValidDate(string dateString, out DateTime parsedDate) + { + return DateTime.TryParseExact(dateString, ViewingDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate); + } + + internal static bool IsValidDateRange(DateTime startDate, DateTime endDate) + { + return endDate > startDate; + } + + internal static int CalculateDurationInSeconds(DateTime startDate, DateTime endDate) + { + return (int)(endDate - startDate).TotalSeconds; + } + + internal static string ShowDurationInFriendlyFormat(int durationInSeconds) + { + var timeSpan = TimeSpan.FromSeconds(durationInSeconds); + + var formattedDuration = ""; + + if (timeSpan.TotalHours >= 1) + { + int totalHours = (int)timeSpan.TotalHours; + formattedDuration += $"{totalHours} hour{(totalHours == 1 ? "" : "s")}, "; + } + + if (timeSpan.Minutes >= 1) + { + int minutes = timeSpan.Minutes; + formattedDuration += $"{minutes} minute{(minutes == 1 ? "" : "s")}, "; + } + + int seconds = timeSpan.Seconds; + formattedDuration += $"{seconds} second{(seconds == 1 ? "" : "s")}"; + + return formattedDuration; + } + } +} diff --git a/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/appsettings.json b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/appsettings.json new file mode 100644 index 000000000..0908081bd --- /dev/null +++ b/CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=coding-tracker.db" + } +} \ No newline at end of file