From be0f05450153170c0b9f1959bb1eef4f4f2725c0 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Mon, 28 Apr 2025 12:43:25 -0700 Subject: [PATCH 01/11] feat: add Exercise class --- ExerciseTracker.Call911plz/Exercise.cs | 8 +++++ .../ExerciseTracker.Call911plz.csproj | 10 ++++++ .../ExerciseTracker.Call911plz.sln | 34 +++++++++++++++++++ ExerciseTracker.Call911plz/Program.cs | 9 +++++ 4 files changed, 61 insertions(+) create mode 100644 ExerciseTracker.Call911plz/Exercise.cs create mode 100644 ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj create mode 100644 ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.sln create mode 100644 ExerciseTracker.Call911plz/Program.cs diff --git a/ExerciseTracker.Call911plz/Exercise.cs b/ExerciseTracker.Call911plz/Exercise.cs new file mode 100644 index 00000000..00947c92 --- /dev/null +++ b/ExerciseTracker.Call911plz/Exercise.cs @@ -0,0 +1,8 @@ +public class Exercise +{ + public int Id { get; set; } + public DateTime Start { get; set; } + public DateTime End { get; set; } + public TimeSpan Duration { get; set; } + public string Comments { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj new file mode 100644 index 00000000..fd4bd08d --- /dev/null +++ b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj @@ -0,0 +1,10 @@ + + + + Exe + net9.0 + enable + enable + + + diff --git a/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.sln b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.sln new file mode 100644 index 00000000..36ca3ddb --- /dev/null +++ b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExerciseTracker.Call911plz", "ExerciseTracker.Call911plz.csproj", "{2EC88879-AAFF-4268-8602-393DD1E114AB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Debug|x64.Build.0 = Debug|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Debug|x86.Build.0 = Debug|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Release|Any CPU.Build.0 = Release|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Release|x64.ActiveCfg = Release|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Release|x64.Build.0 = Release|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Release|x86.ActiveCfg = Release|Any CPU + {2EC88879-AAFF-4268-8602-393DD1E114AB}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs new file mode 100644 index 00000000..62b4707f --- /dev/null +++ b/ExerciseTracker.Call911plz/Program.cs @@ -0,0 +1,9 @@ +namespace ExerciseTracker.Call911plz; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } +} From 1990aa874584ec5edef90170c7292699dd1e134c Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Mon, 28 Apr 2025 13:01:29 -0700 Subject: [PATCH 02/11] feat: add menu selection --- ExerciseTracker.Call911plz/Enums.cs | 4 ++++ .../ExerciseTracker.Call911plz.csproj | 4 ++++ ExerciseTracker.Call911plz/Program.cs | 3 ++- ExerciseTracker.Call911plz/View/DislayMenu.cs | 22 +++++++++++++++++++ ExerciseTracker.Call911plz/View/UserInput.cs | 0 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 ExerciseTracker.Call911plz/Enums.cs create mode 100644 ExerciseTracker.Call911plz/View/DislayMenu.cs create mode 100644 ExerciseTracker.Call911plz/View/UserInput.cs diff --git a/ExerciseTracker.Call911plz/Enums.cs b/ExerciseTracker.Call911plz/Enums.cs new file mode 100644 index 00000000..3635c1e2 --- /dev/null +++ b/ExerciseTracker.Call911plz/Enums.cs @@ -0,0 +1,4 @@ +public class MenuEnums +{ + public enum Main { CREATE, READ, READALL, UPDATE, DELETE, EXIT } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj index fd4bd08d..85998ac5 100644 --- a/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj +++ b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs index 62b4707f..5e125f61 100644 --- a/ExerciseTracker.Call911plz/Program.cs +++ b/ExerciseTracker.Call911plz/Program.cs @@ -4,6 +4,7 @@ class Program { static void Main(string[] args) { - Console.WriteLine("Hello, World!"); + DisplayMenu displayMenu = new(); + displayMenu.MainMenu(); } } diff --git a/ExerciseTracker.Call911plz/View/DislayMenu.cs b/ExerciseTracker.Call911plz/View/DislayMenu.cs new file mode 100644 index 00000000..8dc46a5c --- /dev/null +++ b/ExerciseTracker.Call911plz/View/DislayMenu.cs @@ -0,0 +1,22 @@ +using Spectre.Console; + +public class DisplayMenu +{ + public MenuEnums.Main MainMenu() + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .AddChoices(Enum.GetValues()) + .UseConverter((input) => input switch + { + MenuEnums.Main.CREATE => "Create new exercise log", + MenuEnums.Main.READ => "Find all exercise logs", + MenuEnums.Main.READALL => "Find specific exercise log", + MenuEnums.Main.UPDATE => "Update exercise log", + MenuEnums.Main.DELETE => "Delete exercise log", + MenuEnums.Main.EXIT => "Exit program", + _ => throw new Exception("Selection somehow went wrong") + }) + ); + } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/View/UserInput.cs b/ExerciseTracker.Call911plz/View/UserInput.cs new file mode 100644 index 00000000..e69de29b From e473cb047d3b9d65950a0d5e83730dda834cfc11 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Mon, 28 Apr 2025 13:21:42 -0700 Subject: [PATCH 03/11] feat: add ef database context --- .../{ => Data}/Exercise.cs | 0 .../Data/ExerciseContext.cs | 17 ++++++ .../20250428201051_InitialCreate.Designer.cs | 55 +++++++++++++++++++ .../20250428201051_InitialCreate.cs | 38 +++++++++++++ .../ExerciseContextModelSnapshot.cs | 52 ++++++++++++++++++ .../ExerciseTracker.Call911plz.csproj | 6 ++ 6 files changed, 168 insertions(+) rename ExerciseTracker.Call911plz/{ => Data}/Exercise.cs (100%) create mode 100644 ExerciseTracker.Call911plz/Data/ExerciseContext.cs create mode 100644 ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.Designer.cs create mode 100644 ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.cs create mode 100644 ExerciseTracker.Call911plz/Data/Migrations/ExerciseContextModelSnapshot.cs diff --git a/ExerciseTracker.Call911plz/Exercise.cs b/ExerciseTracker.Call911plz/Data/Exercise.cs similarity index 100% rename from ExerciseTracker.Call911plz/Exercise.cs rename to ExerciseTracker.Call911plz/Data/Exercise.cs diff --git a/ExerciseTracker.Call911plz/Data/ExerciseContext.cs b/ExerciseTracker.Call911plz/Data/ExerciseContext.cs new file mode 100644 index 00000000..41241032 --- /dev/null +++ b/ExerciseTracker.Call911plz/Data/ExerciseContext.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; + +public class ExerciseContext : DbContext +{ + public DbSet Exercises { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(@" + Server=localhost; + Database=exercisetrackerdb; + User Id=sa; + Password=StrongP@ssword1; + TrustServerCertificate=True + "); + } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.Designer.cs b/ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.Designer.cs new file mode 100644 index 00000000..fb0fd77c --- /dev/null +++ b/ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.Designer.cs @@ -0,0 +1,55 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.Call911plz.Data.Migrations +{ + [DbContext(typeof(ExerciseContext))] + [Migration("20250428201051_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Exercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Comments") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Duration") + .HasColumnType("time"); + + b.Property("End") + .HasColumnType("datetime2"); + + b.Property("Start") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.cs b/ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.cs new file mode 100644 index 00000000..fc6c3c81 --- /dev/null +++ b/ExerciseTracker.Call911plz/Data/Migrations/20250428201051_InitialCreate.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.Call911plz.Data.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Exercises", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Start = table.Column(type: "datetime2", nullable: false), + End = table.Column(type: "datetime2", nullable: false), + Duration = table.Column(type: "time", nullable: false), + Comments = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Exercises", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Exercises"); + } + } +} diff --git a/ExerciseTracker.Call911plz/Data/Migrations/ExerciseContextModelSnapshot.cs b/ExerciseTracker.Call911plz/Data/Migrations/ExerciseContextModelSnapshot.cs new file mode 100644 index 00000000..0a85374c --- /dev/null +++ b/ExerciseTracker.Call911plz/Data/Migrations/ExerciseContextModelSnapshot.cs @@ -0,0 +1,52 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.Call911plz.Data.Migrations +{ + [DbContext(typeof(ExerciseContext))] + partial class ExerciseContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Exercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Comments") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Duration") + .HasColumnType("time"); + + b.Property("End") + .HasColumnType("datetime2"); + + b.Property("Start") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj index 85998ac5..492ed385 100644 --- a/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj +++ b/ExerciseTracker.Call911plz/ExerciseTracker.Call911plz.csproj @@ -8,6 +8,12 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + From b10517a8b764c15c44c3a61efa34b8c98cc09be7 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Tue, 29 Apr 2025 18:28:11 -0700 Subject: [PATCH 04/11] feat: add repo --- ExerciseTracker.Call911plz/.editorconfig | 4 ++ .../Model/ExerciseRepository.cs | 55 +++++++++++++++++++ ExerciseTracker.Call911plz/Program.cs | 10 ++-- 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 ExerciseTracker.Call911plz/.editorconfig create mode 100644 ExerciseTracker.Call911plz/Model/ExerciseRepository.cs diff --git a/ExerciseTracker.Call911plz/.editorconfig b/ExerciseTracker.Call911plz/.editorconfig new file mode 100644 index 00000000..e224bd4b --- /dev/null +++ b/ExerciseTracker.Call911plz/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS8603: Possible null reference return. +dotnet_diagnostic.CS8603.severity = suggestion diff --git a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs new file mode 100644 index 00000000..120bb939 --- /dev/null +++ b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; + +public interface IRepository +{ + public Task AddAsync(Exercise log); + public List? GetAll(); + public Task GetByIdAsync(int id); + public Task UpdateAsync(Exercise log); + public Task DeleteAsync(Exercise log); +} + +public class ExerciseRepository : IRepository +{ + private readonly ExerciseContext _exerciseContext; + + public ExerciseRepository(ExerciseContext exerciseContext) + { + _exerciseContext = exerciseContext; + } + + public async Task AddAsync(Exercise log) + { + var savedResult = await _exerciseContext.Exercises.AddAsync(log); + await _exerciseContext.SaveChangesAsync(); + return savedResult.Entity; + } + + public List? GetAll() + { + return _exerciseContext.Exercises.ToList(); + } + + public async Task GetByIdAsync(int id) + { + Exercise? result = await _exerciseContext.Exercises.FindAsync(id); + return result; + } + + public async Task UpdateAsync(Exercise log) + { + Exercise logInDb = await _exerciseContext.Exercises.FindAsync(log.Id) + ?? throw new Exception("Could not find log to update"); + logInDb = log; + var savedResult = await _exerciseContext.Exercises.AddAsync(logInDb); + await _exerciseContext.SaveChangesAsync(); + return savedResult.Entity; + } + + public async Task DeleteAsync(Exercise log) + { + var savedResult = _exerciseContext.Exercises.Remove(log); + await _exerciseContext.SaveChangesAsync(); + return savedResult.Entity; + } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs index 5e125f61..1a59af04 100644 --- a/ExerciseTracker.Call911plz/Program.cs +++ b/ExerciseTracker.Call911plz/Program.cs @@ -1,10 +1,12 @@ -namespace ExerciseTracker.Call911plz; +using System.Threading.Tasks; + +namespace ExerciseTracker.Call911plz; class Program { - static void Main(string[] args) + static async Task Main(string[] args) { - DisplayMenu displayMenu = new(); - displayMenu.MainMenu(); + ExerciseContext context = new(); + ExerciseRepository exerciseRepository = new(context); } } From b5f9ef4ebb91a2cfdc2ccafff4b77f728c60b61f Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Tue, 29 Apr 2025 18:30:07 -0700 Subject: [PATCH 05/11] refactor: rename and move DislayMenu to GetMenu --- ExerciseTracker.Call911plz/View/DislayMenu.cs | 22 ------------------- ExerciseTracker.Call911plz/View/UserInput.cs | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) delete mode 100644 ExerciseTracker.Call911plz/View/DislayMenu.cs diff --git a/ExerciseTracker.Call911plz/View/DislayMenu.cs b/ExerciseTracker.Call911plz/View/DislayMenu.cs deleted file mode 100644 index 8dc46a5c..00000000 --- a/ExerciseTracker.Call911plz/View/DislayMenu.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Spectre.Console; - -public class DisplayMenu -{ - public MenuEnums.Main MainMenu() - { - return AnsiConsole.Prompt( - new SelectionPrompt() - .AddChoices(Enum.GetValues()) - .UseConverter((input) => input switch - { - MenuEnums.Main.CREATE => "Create new exercise log", - MenuEnums.Main.READ => "Find all exercise logs", - MenuEnums.Main.READALL => "Find specific exercise log", - MenuEnums.Main.UPDATE => "Update exercise log", - MenuEnums.Main.DELETE => "Delete exercise log", - MenuEnums.Main.EXIT => "Exit program", - _ => throw new Exception("Selection somehow went wrong") - }) - ); - } -} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/View/UserInput.cs b/ExerciseTracker.Call911plz/View/UserInput.cs index e69de29b..60250e35 100644 --- a/ExerciseTracker.Call911plz/View/UserInput.cs +++ b/ExerciseTracker.Call911plz/View/UserInput.cs @@ -0,0 +1,22 @@ +using Spectre.Console; + +public class GetMenu +{ + public MenuEnums.Main MainMenu() + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .AddChoices(Enum.GetValues()) + .UseConverter((input) => input switch + { + MenuEnums.Main.CREATE => "Create new exercise log", + MenuEnums.Main.READ => "Find all exercise logs", + MenuEnums.Main.READALL => "Find specific exercise log", + MenuEnums.Main.UPDATE => "Update exercise log", + MenuEnums.Main.DELETE => "Delete exercise log", + MenuEnums.Main.EXIT => "Exit program", + _ => throw new Exception("Selection somehow went wrong") + }) + ); + } +} \ No newline at end of file From d6c49d8dd624f2c5d7f348b3bf7f21ab501bc139 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Fri, 2 May 2025 16:54:33 -0700 Subject: [PATCH 06/11] feat: add get Exercise Side note, I spent like a day and half trying to get a vim/google docs/nano like text editor but it ended up too complicated and frankly should be a different project. --- ExerciseTracker.Call911plz/Program.cs | 9 ++- ExerciseTracker.Call911plz/View/UserInput.cs | 61 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs index 1a59af04..87cdce04 100644 --- a/ExerciseTracker.Call911plz/Program.cs +++ b/ExerciseTracker.Call911plz/Program.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Numerics; +using System.Threading.Tasks; +using Spectre.Console; namespace ExerciseTracker.Call911plz; @@ -6,7 +8,8 @@ class Program { static async Task Main(string[] args) { - ExerciseContext context = new(); - ExerciseRepository exerciseRepository = new(context); + // ExerciseContext context = new(); + // ExerciseRepository exerciseRepository = new(context); + } } diff --git a/ExerciseTracker.Call911plz/View/UserInput.cs b/ExerciseTracker.Call911plz/View/UserInput.cs index 60250e35..2b6d3218 100644 --- a/ExerciseTracker.Call911plz/View/UserInput.cs +++ b/ExerciseTracker.Call911plz/View/UserInput.cs @@ -1,5 +1,66 @@ using Spectre.Console; +public static class GetData +{ + public static Exercise GetExercise() + { + Exercise exercise = new() + { + Id = default, + Start = GetDateTime("Enter start time: "), + End = GetDateTime("Enter end time: "), + Comments = GetComments(), + }; + exercise.Duration = exercise.End - exercise.Start; + + return exercise; + } + + public static Exercise UpdateExercise(Exercise existingExercise) + { + Exercise exercise = new() + { + Id = existingExercise.Id, + Start = GetDateTime("Enter updated start time: ", existingExercise.Start), + End = GetDateTime("Enter updated end time: ", existingExercise.End), + Comments = GetComments(existingExercise.Comments), + }; + exercise.Duration = exercise.End - exercise.Start; + + return exercise; + } + + private static DateTime GetDateTime(string stringPrompt, DateTime existingDateTime = default) + { + TextPrompt prompt = new($"[bold grey]{stringPrompt}[/]"); + + if (existingDateTime != default) + prompt.DefaultValue(existingDateTime.ToString()); + else + prompt.DefaultValue(DateTime.Now.ToString()); + + prompt.Validate( (input) => { + if (DateTime.TryParse(input, out var _)) + return ValidationResult.Success(); + return ValidationResult.Error("Invalid date time format"); + }); + + return DateTime.Parse(AnsiConsole.Prompt(prompt)); + } + + private static string GetComments(string? existingComments = default) + { + TextPrompt prompt = new("[bold grey](Optional) Additional comments[/]"); + + prompt.AllowEmpty(); + + if (existingComments != default) + prompt.DefaultValue(existingComments); + + return AnsiConsole.Prompt(prompt); + } +} + public class GetMenu { public MenuEnums.Main MainMenu() From 948cefe6355e00478d77580ed3952829ca35394e Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Fri, 2 May 2025 17:16:56 -0700 Subject: [PATCH 07/11] feat: add ExerciseService --- .../Model/ExerciseRepository.cs | 1 + .../Model/ExerciseService.cs | 39 +++++++++++++++++++ ExerciseTracker.Call911plz/Program.cs | 5 ++- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 ExerciseTracker.Call911plz/Model/ExerciseService.cs diff --git a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs index 120bb939..991fe282 100644 --- a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs +++ b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs @@ -41,6 +41,7 @@ public async Task UpdateAsync(Exercise log) Exercise logInDb = await _exerciseContext.Exercises.FindAsync(log.Id) ?? throw new Exception("Could not find log to update"); logInDb = log; + var savedResult = await _exerciseContext.Exercises.AddAsync(logInDb); await _exerciseContext.SaveChangesAsync(); return savedResult.Entity; diff --git a/ExerciseTracker.Call911plz/Model/ExerciseService.cs b/ExerciseTracker.Call911plz/Model/ExerciseService.cs new file mode 100644 index 00000000..f21f6ce6 --- /dev/null +++ b/ExerciseTracker.Call911plz/Model/ExerciseService.cs @@ -0,0 +1,39 @@ +public interface IService +{ + public Task AddAsync(Exercise log); + public List? GetAll(); + public Task GetByIdAsync(int id); + public Task UpdateAsync(Exercise log); + public Task DeleteAsync(Exercise log); +} + +public class ExerciseService(IRepository repo) : IService +{ + public async Task AddAsync(Exercise log) + { + Exercise exerciseAdded = await repo.AddAsync(log); + return exerciseAdded; + } + + public List? GetAll() + { + return repo.GetAll(); + } + + public async Task GetByIdAsync(int id) + { + return await repo.GetByIdAsync(id); + } + + public async Task UpdateAsync(Exercise log) + { + Exercise updatedExercise = await repo.UpdateAsync(log); + return updatedExercise; + } + + public async Task DeleteAsync(Exercise log) + { + Exercise deletedExercise = await repo.DeleteAsync(log); + return deletedExercise; + } +} diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs index 87cdce04..fb736f47 100644 --- a/ExerciseTracker.Call911plz/Program.cs +++ b/ExerciseTracker.Call911plz/Program.cs @@ -8,8 +8,9 @@ class Program { static async Task Main(string[] args) { - // ExerciseContext context = new(); - // ExerciseRepository exerciseRepository = new(context); + ExerciseContext context = new(); + ExerciseRepository exerciseRepository = new(context); + ExerciseService exerciseService = new(exerciseRepository); } } From c1e1c790b6bb1404a5d4a10d4a42407f3a023256 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Fri, 2 May 2025 18:29:09 -0700 Subject: [PATCH 08/11] feat: add controller --- .../Controller/ControllerBase.cs | 27 ++++++ .../Controller/ExerciseController.cs | 94 +++++++++++++++++++ ExerciseTracker.Call911plz/Program.cs | 4 +- .../View/DisplayData.cs | 21 +++++ ExerciseTracker.Call911plz/View/UserInput.cs | 26 ++++- 5 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 ExerciseTracker.Call911plz/Controller/ControllerBase.cs create mode 100644 ExerciseTracker.Call911plz/Controller/ExerciseController.cs create mode 100644 ExerciseTracker.Call911plz/View/DisplayData.cs diff --git a/ExerciseTracker.Call911plz/Controller/ControllerBase.cs b/ExerciseTracker.Call911plz/Controller/ControllerBase.cs new file mode 100644 index 00000000..053f187f --- /dev/null +++ b/ExerciseTracker.Call911plz/Controller/ControllerBase.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using Spectre.Console; + +public class ControllerBase +{ + internal virtual void OnStartOfLoop(){ Console.Clear(); } + internal virtual Task HandleUserInputAsync() { return Task.FromResult(false); } + public async Task StartAsync() + { + bool exit = false; + + while (exit == false) + { + try { OnStartOfLoop(); } + catch (Exception e) { AnsiConsole.MarkupLine($"[bold red]Error on Start Of Loop[/]\n{e}"); } + + try { exit = await HandleUserInputAsync(); } + catch (Exception e) { AnsiConsole.MarkupLine($"[bold red]Error on Handling User Input[/]\n{e}"); } + + if (exit == false) + { + AnsiConsole.MarkupLine("[bold yellow]Press Enter to continue[/]"); + Console.Read(); + } + } + } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/Controller/ExerciseController.cs b/ExerciseTracker.Call911plz/Controller/ExerciseController.cs new file mode 100644 index 00000000..6bc09e65 --- /dev/null +++ b/ExerciseTracker.Call911plz/Controller/ExerciseController.cs @@ -0,0 +1,94 @@ +using System.Threading.Tasks; +using Spectre.Console; + +public class ExerciseController(IService service) : ControllerBase +{ + IService _service = service; + internal override void OnStartOfLoop() + { + Console.Clear(); + AnsiConsole.Write + ( + new FigletText("Exercise Tracker") + .LeftJustified() + .Color(Color.Red) + ); + } + + internal override async Task HandleUserInputAsync() + { + MenuEnums.Main input = GetMenu.MainMenu(); + + switch (input) + { + case MenuEnums.Main.CREATE: + await CreateAsync(); + break; + case MenuEnums.Main.READ: + await ReadByIdAsync(); + break; + case MenuEnums.Main.READALL: + ReadAll(); + break; + case MenuEnums.Main.UPDATE: + await UpdateAsync(); + break; + case MenuEnums.Main.DELETE: + await DeleteAsync(); + break; + case MenuEnums.Main.EXIT: + return true; + } + + return false; + } + + private async Task CreateAsync() + { + Exercise exercise = GetData.GetExercise(); + Exercise createdExercise = await _service.AddAsync(exercise); + + AnsiConsole.MarkupLine($"[bold grey]Inserted new[/] [bold yellow]Exercise[/] [bold grey]to db:[/]"); + DisplayData.DisplayExercise([createdExercise]); + } + + private async Task ReadByIdAsync() + { + int id = GetData.GetId(); + Exercise? exercise = await _service.GetByIdAsync(id); + + DisplayData.DisplayExercise([exercise]); + } + + private void ReadAll() + { + List exercises = _service.GetAll() ?? []; + + DisplayData.DisplayExercise(exercises); + } + + private async Task UpdateAsync() + { + List exercises = _service.GetAll() + ?? throw new Exception("No exercises to update"); + Exercise exerciseToUpdate = GetData.GetExerciseFromList(exercises); + Exercise updateExercise = GetData.UpdateExercise(exerciseToUpdate); + + Exercise exerciseUpdated = await _service.UpdateAsync(updateExercise); + + AnsiConsole.MarkupLine("[bold grey]Original: [/]"); + DisplayData.DisplayExercise([exerciseToUpdate]); + AnsiConsole.MarkupLine("[bold grey]Updated: [/]"); + DisplayData.DisplayExercise([exerciseUpdated]); + } + + private async Task DeleteAsync() + { + List exercises = _service.GetAll() + ?? throw new Exception("No exercises to delete"); + Exercise exerciseToDelete = GetData.GetExerciseFromList(exercises); + await _service.DeleteAsync(exerciseToDelete); + + AnsiConsole.MarkupLine("[bold grey]Exercise[/] [bold red]deleted[/]"); + } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs index fb736f47..9e6f253c 100644 --- a/ExerciseTracker.Call911plz/Program.cs +++ b/ExerciseTracker.Call911plz/Program.cs @@ -10,7 +10,9 @@ static async Task Main(string[] args) { ExerciseContext context = new(); ExerciseRepository exerciseRepository = new(context); - ExerciseService exerciseService = new(exerciseRepository); + ExerciseController exerciseController = new(exerciseService); + + await exerciseController.StartAsync(); } } diff --git a/ExerciseTracker.Call911plz/View/DisplayData.cs b/ExerciseTracker.Call911plz/View/DisplayData.cs new file mode 100644 index 00000000..63643a93 --- /dev/null +++ b/ExerciseTracker.Call911plz/View/DisplayData.cs @@ -0,0 +1,21 @@ +using Spectre.Console; + +public static class DisplayData +{ + public static void DisplayExercise(List exercises) + { + Table table = new(); + table.AddColumns(["Id", "Start Date", "End Date", "Duration", "Additional Comments"]); + + foreach(Exercise exercise in exercises) + table.AddRow([ + exercise.Id.ToString(), + exercise.Start.ToString(), + exercise.End.ToString(), + exercise.Duration.ToString(), + exercise.Comments.ToString(), + ]); + + AnsiConsole.Write(table); + } +} \ No newline at end of file diff --git a/ExerciseTracker.Call911plz/View/UserInput.cs b/ExerciseTracker.Call911plz/View/UserInput.cs index 2b6d3218..de8d25ca 100644 --- a/ExerciseTracker.Call911plz/View/UserInput.cs +++ b/ExerciseTracker.Call911plz/View/UserInput.cs @@ -2,6 +2,24 @@ public static class GetData { + public static int GetId() + { + TextPrompt prompt = new("[bold grey]Enter id[/]"); + return AnsiConsole.Prompt(prompt); + } + + public static Exercise GetExerciseFromList(List exercises) + { + SelectionPrompt prompt = new(); + prompt.Title("[bold grey]Select from below[/]"); + prompt.AddChoices(exercises); + prompt.UseConverter((selection) => { + return $"{selection.Id} {selection.Start} {selection.End}"; + }); + + return AnsiConsole.Prompt(prompt); + } + public static Exercise GetExercise() { Exercise exercise = new() @@ -61,9 +79,9 @@ private static string GetComments(string? existingComments = default) } } -public class GetMenu +public static class GetMenu { - public MenuEnums.Main MainMenu() + public static MenuEnums.Main MainMenu() { return AnsiConsole.Prompt( new SelectionPrompt() @@ -71,8 +89,8 @@ public MenuEnums.Main MainMenu() .UseConverter((input) => input switch { MenuEnums.Main.CREATE => "Create new exercise log", - MenuEnums.Main.READ => "Find all exercise logs", - MenuEnums.Main.READALL => "Find specific exercise log", + MenuEnums.Main.READ => "Find specific exercise log ", + MenuEnums.Main.READALL => "Find all exercise logs", MenuEnums.Main.UPDATE => "Update exercise log", MenuEnums.Main.DELETE => "Delete exercise log", MenuEnums.Main.EXIT => "Exit program", From 9ef72e9887a26a2ce4d5fce19f78b1be62d52f69 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Fri, 2 May 2025 18:29:23 -0700 Subject: [PATCH 09/11] fix: read by id and read all menu msgs swapped --- ExerciseTracker.Call911plz/View/UserInput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExerciseTracker.Call911plz/View/UserInput.cs b/ExerciseTracker.Call911plz/View/UserInput.cs index de8d25ca..39295a40 100644 --- a/ExerciseTracker.Call911plz/View/UserInput.cs +++ b/ExerciseTracker.Call911plz/View/UserInput.cs @@ -89,7 +89,7 @@ public static MenuEnums.Main MainMenu() .UseConverter((input) => input switch { MenuEnums.Main.CREATE => "Create new exercise log", - MenuEnums.Main.READ => "Find specific exercise log ", + MenuEnums.Main.READ => "Find specific exercise log", MenuEnums.Main.READALL => "Find all exercise logs", MenuEnums.Main.UPDATE => "Update exercise log", MenuEnums.Main.DELETE => "Delete exercise log", From e71cce1d47cba7c495851b7f75349183311cb204 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Fri, 2 May 2025 18:46:28 -0700 Subject: [PATCH 10/11] fix: update exercise did not update values Updated the exercise gotten from db rather than the exercise IN db --- .../Controller/ExerciseController.cs | 12 +++++++----- .../Model/ExerciseRepository.cs | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ExerciseTracker.Call911plz/Controller/ExerciseController.cs b/ExerciseTracker.Call911plz/Controller/ExerciseController.cs index 6bc09e65..418d752e 100644 --- a/ExerciseTracker.Call911plz/Controller/ExerciseController.cs +++ b/ExerciseTracker.Call911plz/Controller/ExerciseController.cs @@ -55,7 +55,8 @@ private async Task CreateAsync() private async Task ReadByIdAsync() { int id = GetData.GetId(); - Exercise? exercise = await _service.GetByIdAsync(id); + Exercise exercise = await _service.GetByIdAsync(id) + ?? throw new Exception("[bold red]Could not find exercise with id[/]"); DisplayData.DisplayExercise([exercise]); } @@ -72,14 +73,15 @@ private async Task UpdateAsync() List exercises = _service.GetAll() ?? throw new Exception("No exercises to update"); Exercise exerciseToUpdate = GetData.GetExerciseFromList(exercises); - Exercise updateExercise = GetData.UpdateExercise(exerciseToUpdate); - Exercise exerciseUpdated = await _service.UpdateAsync(updateExercise); - AnsiConsole.MarkupLine("[bold grey]Original: [/]"); DisplayData.DisplayExercise([exerciseToUpdate]); + + Exercise updatedExercise = GetData.UpdateExercise(exerciseToUpdate); + Exercise updatedExerciseInDb = await _service.UpdateAsync(updatedExercise); + AnsiConsole.MarkupLine("[bold grey]Updated: [/]"); - DisplayData.DisplayExercise([exerciseUpdated]); + DisplayData.DisplayExercise([updatedExerciseInDb]); } private async Task DeleteAsync() diff --git a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs index 991fe282..306be109 100644 --- a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs +++ b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs @@ -40,11 +40,11 @@ public async Task UpdateAsync(Exercise log) { Exercise logInDb = await _exerciseContext.Exercises.FindAsync(log.Id) ?? throw new Exception("Could not find log to update"); - logInDb = log; - var savedResult = await _exerciseContext.Exercises.AddAsync(logInDb); + _exerciseContext.Entry(logInDb).CurrentValues.SetValues(log); + await _exerciseContext.SaveChangesAsync(); - return savedResult.Entity; + return _exerciseContext.Entry(logInDb).Entity; } public async Task DeleteAsync(Exercise log) From 92775f41d72ca2f80a392301d21f23252667a178 Mon Sep 17 00:00:00 2001 From: Hieu Truong Date: Fri, 2 May 2025 18:50:52 -0700 Subject: [PATCH 11/11] style: cleaned up spacing --- ExerciseTracker.Call911plz/Controller/ControllerBase.cs | 2 +- ExerciseTracker.Call911plz/Controller/ExerciseController.cs | 4 ++-- ExerciseTracker.Call911plz/Model/ExerciseRepository.cs | 2 -- ExerciseTracker.Call911plz/Program.cs | 6 +----- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/ExerciseTracker.Call911plz/Controller/ControllerBase.cs b/ExerciseTracker.Call911plz/Controller/ControllerBase.cs index 053f187f..41ca2db7 100644 --- a/ExerciseTracker.Call911plz/Controller/ControllerBase.cs +++ b/ExerciseTracker.Call911plz/Controller/ControllerBase.cs @@ -1,10 +1,10 @@ -using System.Threading.Tasks; using Spectre.Console; public class ControllerBase { internal virtual void OnStartOfLoop(){ Console.Clear(); } internal virtual Task HandleUserInputAsync() { return Task.FromResult(false); } + public async Task StartAsync() { bool exit = false; diff --git a/ExerciseTracker.Call911plz/Controller/ExerciseController.cs b/ExerciseTracker.Call911plz/Controller/ExerciseController.cs index 418d752e..bd05607d 100644 --- a/ExerciseTracker.Call911plz/Controller/ExerciseController.cs +++ b/ExerciseTracker.Call911plz/Controller/ExerciseController.cs @@ -1,9 +1,9 @@ -using System.Threading.Tasks; using Spectre.Console; public class ExerciseController(IService service) : ControllerBase { IService _service = service; + internal override void OnStartOfLoop() { Console.Clear(); @@ -39,7 +39,6 @@ internal override async Task HandleUserInputAsync() case MenuEnums.Main.EXIT: return true; } - return false; } @@ -88,6 +87,7 @@ private async Task DeleteAsync() { List exercises = _service.GetAll() ?? throw new Exception("No exercises to delete"); + Exercise exerciseToDelete = GetData.GetExerciseFromList(exercises); await _service.DeleteAsync(exerciseToDelete); diff --git a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs index 306be109..9cd9a0ff 100644 --- a/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs +++ b/ExerciseTracker.Call911plz/Model/ExerciseRepository.cs @@ -1,5 +1,3 @@ -using System.Threading.Tasks; - public interface IRepository { public Task AddAsync(Exercise log); diff --git a/ExerciseTracker.Call911plz/Program.cs b/ExerciseTracker.Call911plz/Program.cs index 9e6f253c..4c6176ec 100644 --- a/ExerciseTracker.Call911plz/Program.cs +++ b/ExerciseTracker.Call911plz/Program.cs @@ -1,8 +1,4 @@ -using System.Numerics; -using System.Threading.Tasks; -using Spectre.Console; - -namespace ExerciseTracker.Call911plz; +namespace ExerciseTracker.Call911plz; class Program {