From 3cbcb73add5532800632f56b4d13c1af9e934eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicholas=20Pe=C3=A7anha?= Date: Mon, 24 Mar 2025 23:15:02 -0300 Subject: [PATCH 1/4] added initial setup, repository, service, controller and start menu --- .../ExerciseTracker.selnoom.sln | 25 +++++ .../Controllers/WeightExerciseController.cs | 39 ++++++++ .../Data/ExerciseDbContext.cs | 15 +++ .../Data/WeightExerciseRepository.cs | 56 +++++++++++ .../ExerciseTracker.selnoom.csproj | 30 ++++++ .../ExerciseTracker.selnoom/Menu/Menu.cs | 87 ++++++++++++++++++ .../20250324212905_InitialCreate.Designer.cs | 56 +++++++++++ .../20250324212905_InitialCreate.cs | 40 ++++++++ ...0250324223334_ExerciseDuration.Designer.cs | 52 +++++++++++ .../20250324223334_ExerciseDuration.cs | 30 ++++++ ...250324223712_ExerciseDuration2.Designer.cs | 52 +++++++++++ .../20250324223712_ExerciseDuration2.cs | 22 +++++ ...20250325020803_UpdateMigration.Designer.cs | 62 +++++++++++++ .../20250325020803_UpdateMigration.cs | 52 +++++++++++ .../ExerciseDbContextModelSnapshot.cs | 59 ++++++++++++ .../Models/WeightExercise.cs | 14 +++ .../ExerciseTracker.selnoom/Program.cs | 42 +++++++++ .../Services/WeightExerciseService.cs | 49 ++++++++++ .../ExerciseTracker.selnoom/appsettings.json | 5 + .../ExerciseTracker.selnoom/exercise.db | Bin 0 -> 28672 bytes 20 files changed, 787 insertions(+) create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom.sln create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Controllers/WeightExerciseController.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/ExerciseDbContext.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/WeightExerciseRepository.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/ExerciseTracker.selnoom.csproj create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.Designer.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.Designer.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.Designer.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.Designer.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Program.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/appsettings.json create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom.sln b/ExerciseTracker.selnoom/ExerciseTracker.selnoom.sln new file mode 100644 index 00000000..8b3f5d96 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35818.85 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExerciseTracker.selnoom", "ExerciseTracker.selnoom\ExerciseTracker.selnoom.csproj", "{A48F8834-F068-46C0-9489-D572B2F26B8B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A48F8834-F068-46C0-9489-D572B2F26B8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A48F8834-F068-46C0-9489-D572B2F26B8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A48F8834-F068-46C0-9489-D572B2F26B8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A48F8834-F068-46C0-9489-D572B2F26B8B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {96C93C50-9C5E-48D5-BD19-1A396D9A4806} + EndGlobalSection +EndGlobal diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Controllers/WeightExerciseController.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Controllers/WeightExerciseController.cs new file mode 100644 index 00000000..6f1ccd33 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Controllers/WeightExerciseController.cs @@ -0,0 +1,39 @@ +using ExerciseTracker.selnoom.Models; +using ExerciseTracker.selnoom.Services; + +namespace ExerciseTracker.selnoom.Controllers; + +public class WeightExerciseController +{ + private readonly WeightExerciseService _weightExerciseService; + + public WeightExerciseController(WeightExerciseService weightExerciseService) + { + _weightExerciseService = weightExerciseService; + } + + public async Task GetExerciseByIdAsync(int id) + { + return await _weightExerciseService.GetByIdAsync(id); + } + + public async Task> GetExercisesAsync() + { + return await _weightExerciseService.GetExercisesAsync(); + } + + public async Task CreateExerciseAsync(WeightExercise exercise) + { + return await _weightExerciseService.CreateExerciseAsync(exercise); + } + + public async Task UpdateExerciseAsync(WeightExercise exercise) + { + return await _weightExerciseService.UpdateExerciseAsync(exercise); + } + + public async Task DeleteExerciseAsync(int id) + { + return await _weightExerciseService.DeleteExerciseAsync(id); + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/ExerciseDbContext.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/ExerciseDbContext.cs new file mode 100644 index 00000000..bb8c0684 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/ExerciseDbContext.cs @@ -0,0 +1,15 @@ +using ExerciseTracker.selnoom.Models; +using Microsoft.EntityFrameworkCore; + +namespace ExerciseTracker.Data +{ + public class ExerciseDbContext : DbContext + { + public ExerciseDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet Exercises { get; set; } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/WeightExerciseRepository.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/WeightExerciseRepository.cs new file mode 100644 index 00000000..e7c17a18 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Data/WeightExerciseRepository.cs @@ -0,0 +1,56 @@ +using ExerciseTracker.Data; +using ExerciseTracker.selnoom.Models; +using Microsoft.EntityFrameworkCore; + +namespace ExerciseTracker.selnoom.Data; + +public class WeightExerciseRepository +{ + private readonly ExerciseDbContext _exerciseDbContext; + + public WeightExerciseRepository(ExerciseDbContext exerciseDbContext) + { + _exerciseDbContext = exerciseDbContext; + } + + public async Task GetExerciseByIdAsync(int id) + { + return await _exerciseDbContext.Exercises.FindAsync(id); + } + + public async Task> GetExercisesAsync() + { + return await _exerciseDbContext.Exercises.ToListAsync(); + } + + public async Task CreateExerciseAsync(WeightExercise exercise) + { + var createdExercise = await _exerciseDbContext.Exercises.AddAsync(exercise); + await _exerciseDbContext.SaveChangesAsync(); + return createdExercise.Entity; + } + + public async Task UpdateExerciseAsync(WeightExercise exercise) + { + WeightExercise? savedExercise = await _exerciseDbContext.Exercises.FindAsync(exercise.Id); + + if (savedExercise == null) return null; + + _exerciseDbContext.Entry(savedExercise).CurrentValues.SetValues(exercise); + await _exerciseDbContext.SaveChangesAsync(); + + return savedExercise; + } + + public async Task DeleteExerciseAsync(int id) + { + WeightExercise? savedExercise = await _exerciseDbContext.Exercises.FindAsync(id); + + if (savedExercise == null) return null; + + _exerciseDbContext.Exercises.Remove(savedExercise); + await _exerciseDbContext.SaveChangesAsync(); + + return $"Exercise with Id {id} deleted successfully."; + } +} \ No newline at end of file diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/ExerciseTracker.selnoom.csproj b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/ExerciseTracker.selnoom.csproj new file mode 100644 index 00000000..a00a633f --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/ExerciseTracker.selnoom.csproj @@ -0,0 +1,30 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + PreserveNewest + + + + diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs new file mode 100644 index 00000000..14bfb1d7 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs @@ -0,0 +1,87 @@ +using ExerciseTracker.selnoom.Controllers; +using Spectre.Console; + +namespace ExerciseTracker.selnoom.Menu; + +public class Menu +{ + private readonly WeightExerciseController _weightExerciseController; + + public Menu(WeightExerciseController weightExerciseController) + { + _weightExerciseController = weightExerciseController; + } + + public async Task ShowMenu() + { + while (true) + { + AnsiConsole.Clear(); + AnsiConsole.Markup("[bold underline]Exercise Tracker[/]\n\n"); + AnsiConsole.WriteLine("Select an option:"); + var menuChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Please select an option:") + .AddChoices(Enum.GetValues()) + ); + + switch (menuChoice) + { + case MainMenuChoices.View: + await ViewExercises(); + break; + case MainMenuChoices.Create: + //await CreateExercise(); + break; + case MainMenuChoices.Edit: + //await EditExercise(); + break; + case MainMenuChoices.Delete: + //await DeleteExercise(); + break; + case MainMenuChoices.Exit: + return; + } + } + } + + private async Task ViewExercises() + { + AnsiConsole.Clear(); + try + { + var exercises = await _weightExerciseController.GetExercisesAsync(); + if (!exercises.Any()) + { + AnsiConsole.MarkupLine("[red]No exercises registered.[/]"); + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + return; + } + + var sortedExercises = exercises.OrderBy(ex => ex.DateStart).ToList(); + + AnsiConsole.MarkupLine("[blue bold underline]Exercises:[/]"); + foreach (var exercise in sortedExercises) + { + AnsiConsole.WriteLine($"Date Start: {exercise.DateStart}\tName: {exercise.Name}\tDuration: {exercise.Duration}\tSets: {exercise.Sets}\tRepetitions: {exercise.Repetitions}\tComments: {exercise.Comments}"); + } + } + catch (Exception e) + { + AnsiConsole.MarkupLine("[red]There was an error while retrieving the exercises! Please try again later.[/]"); + } + + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + } + + public enum MainMenuChoices + { + View, + Create, + Edit, + Delete, + Exit + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.Designer.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.Designer.cs new file mode 100644 index 00000000..c1eb3080 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.Designer.cs @@ -0,0 +1,56 @@ +// +using System; +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + [Migration("20250324212905_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comments") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.cs new file mode 100644 index 00000000..3665a2a3 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324212905_InitialCreate.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Exercises", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Date = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Weight = table.Column(type: "REAL", nullable: false), + Sets = table.Column(type: "INTEGER", nullable: false), + Repetitions = table.Column(type: "INTEGER", nullable: false), + Comments = table.Column(type: "TEXT", 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.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.Designer.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.Designer.cs new file mode 100644 index 00000000..76554d44 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.Designer.cs @@ -0,0 +1,52 @@ +// +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + [Migration("20250324223334_ExerciseDuration")] + partial class ExerciseDuration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comments") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.cs new file mode 100644 index 00000000..2575bd1d --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223334_ExerciseDuration.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + /// + public partial class ExerciseDuration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Date", + table: "Exercises"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Date", + table: "Exercises", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.Designer.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.Designer.cs new file mode 100644 index 00000000..9b1d30cc --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.Designer.cs @@ -0,0 +1,52 @@ +// +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + [Migration("20250324223712_ExerciseDuration2")] + partial class ExerciseDuration2 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comments") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.cs new file mode 100644 index 00000000..ca07b670 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250324223712_ExerciseDuration2.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + /// + public partial class ExerciseDuration2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.Designer.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.Designer.cs new file mode 100644 index 00000000..83183828 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.Designer.cs @@ -0,0 +1,62 @@ +// +using System; +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + [Migration("20250325020803_UpdateMigration")] + partial class UpdateMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comments") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateEnd") + .HasColumnType("TEXT"); + + b.Property("DateStart") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.cs new file mode 100644 index 00000000..a5149d20 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325020803_UpdateMigration.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + /// + public partial class UpdateMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateEnd", + table: "Exercises", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "DateStart", + table: "Exercises", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "Duration", + table: "Exercises", + type: "TEXT", + nullable: false, + defaultValue: new TimeSpan(0, 0, 0, 0, 0)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateEnd", + table: "Exercises"); + + migrationBuilder.DropColumn( + name: "DateStart", + table: "Exercises"); + + migrationBuilder.DropColumn( + name: "Duration", + table: "Exercises"); + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs new file mode 100644 index 00000000..84214669 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs @@ -0,0 +1,59 @@ +// +using System; +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + partial class ExerciseDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comments") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateEnd") + .HasColumnType("TEXT"); + + b.Property("DateStart") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs new file mode 100644 index 00000000..bf72cbeb --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs @@ -0,0 +1,14 @@ +namespace ExerciseTracker.selnoom.Models; + +public class WeightExercise +{ + public int Id { get; set; } + public DateTime DateStart { get; set; } + public DateTime DateEnd { get; set; } + public TimeSpan Duration { get; set; } + public string Name { get; set; } = string.Empty; + public double Weight { get; set; } + public int Sets { get; set; } + public int Repetitions { get; set; } + public string Comments { get; set; } = string.Empty; +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Program.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Program.cs new file mode 100644 index 00000000..f2c568c4 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Program.cs @@ -0,0 +1,42 @@ +using ExerciseTracker.Data; +using ExerciseTracker.selnoom.Controllers; +using ExerciseTracker.selnoom.Data; +using ExerciseTracker.selnoom.Menu; +using ExerciseTracker.selnoom.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +var host = Host.CreateDefaultBuilder(args) + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.AddConsole(); + logging.SetMinimumLevel(LogLevel.Warning); + }) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((context, services) => + { + var connectionString = context.Configuration.GetConnectionString("DefaultConnection"); + services.AddDbContext(options => + options.UseSqlite(connectionString)); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + }) + .Build(); + +using (var scope = host.Services.CreateScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + context.Database.Migrate(); + + var menu = scope.ServiceProvider.GetRequiredService(); + await menu.ShowMenu(); +} \ No newline at end of file diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs new file mode 100644 index 00000000..4a0405e5 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs @@ -0,0 +1,49 @@ +using ExerciseTracker.selnoom.Data; +using ExerciseTracker.selnoom.Models; + +namespace ExerciseTracker.selnoom.Services; + +public class WeightExerciseService +{ + private readonly WeightExerciseRepository _weightExerciseRepository; + + public WeightExerciseService(WeightExerciseRepository weightExerciseRepository) + { + _weightExerciseRepository = weightExerciseRepository; + } + + public async Task GetByIdAsync(int id) + { + return await _weightExerciseRepository.GetExerciseByIdAsync(id); + } + + public async Task> GetExercisesAsync() + { + return await _weightExerciseRepository.GetExercisesAsync(); + } + + public async Task CreateExerciseAsync(WeightExercise exercise) + { + if (exercise.DateEnd < exercise.DateStart) + { + throw new ArgumentException("Start date must be before the end date."); + } + + return await _weightExerciseRepository.CreateExerciseAsync(exercise); + } + + public async Task UpdateExerciseAsync(WeightExercise exercise) + { + if (exercise.DateEnd < exercise.DateStart) + { + throw new ArgumentException("Start date must be before the end date."); + } + + return await _weightExerciseRepository.UpdateExerciseAsync(exercise); + } + + public async Task DeleteExerciseAsync(int id) + { + return await _weightExerciseRepository.DeleteExerciseAsync(id); + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/appsettings.json b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/appsettings.json new file mode 100644 index 00000000..38dcd8ee --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=exercise.db" + } +} \ No newline at end of file diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db new file mode 100644 index 0000000000000000000000000000000000000000..e5be1511db0f4a8c82f70cf6682af460065dd48c GIT binary patch literal 28672 zcmeI)-%ry(00;1P9UDwWc`#Xg;!;r{{Oa0G81ca|iZ1Im97W;5G!qVNWE-qq0=_Yc z{t5maCOrBd_#gP>tFQhAUh7ua*gz57%?NqfB?y-)9!Tyxp_OWkrP*{#(pWrwKT zC>IED&j{f-PGB+2;sA?0i$g31S!@S4?Ybava~}?)!{51q$QdU-9T^$H%e)iPNcP*d7;=^DVtB1wn$FfBIymI zm@Tjt@>;=|Y1NmhRVuXV*b*_cO;%m2%J%I-xkCHgD$xV#SdLYzvclbu)GohH-KzWD zeNC;scaB6@r**&SXk>;+D`kgjRo2&zry?ubYI;LA$b>A*@mcR6vb^LS6Rl#*w$AFj zVZlbd>~`gfa;;XT)&@ec>1lq;dxiGlK3j#DHa%?6>Nf4V40XKDuA7rpW;?@5PKgOV z;|`0{qSH*W5i&o|pFZ%ar_D!yM@;M5f3yAxMvSgUBU2}vfpBbLfo~Q)H`CNs^VZ(q zcWS?|Y^PQ~?wKfbydtS*p7Yt~*Xd5f&Jor%2m4fXVLrB}$XdO&)7W-i(Yk$k0J5%S zg;4Cx0?%319eUInL*<53^RCUF?U?bN$?h6>(ETksN%P@YJkB>~duZvk?f0F<-ITyp zPHu*Cq<{H|y4-+7de|6Nh1yQJa?qvCSfXx6MmbLW;hrEs00Izz00bZa0SG_<0uX=z z1R!v?1jc!RD=M;*oR!sCB}rsuNnTu%Q*&`yO{AVAr@ha8?)pC{nr!_qeic8n69foA z00Izz00bZa0SG_<0uX=z1a2zuI4ERe?jl@PSybdjSv5BfcG$KYe-A`zPM%XI1tH(g zmrxXSA+GrQk5*cnY7{T$L_o-0%&Dr0OF6xaZ2hmDXN)T;Ica9AY^PCqKU1gd3A|hp z)}x^4aNF7ry!e}A z2Lc2j009U<00Izz00bZa0SG_<0(V~EJ}=A<(p__y7sdwtKfY}L|KFYO9y$O42tWV= U5P$##AOHafKmY;|fIwe?Ke>ze3IG5A literal 0 HcmV?d00001 From a925463d4b8bee3eca51c6d9cd28d70e249be924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicholas=20Pe=C3=A7anha?= Date: Tue, 25 Mar 2025 10:30:51 -0300 Subject: [PATCH 2/4] Finished menu and validation --- .../Helpers/Validation.cs | 103 ++++++++++++++++++ .../ExerciseTracker.selnoom/Menu/Menu.cs | 89 ++++++++++++++- ...25133021_RemovedWeightComments.Designer.cs | 58 ++++++++++ .../20250325133021_RemovedWeightComments.cs | 29 +++++ .../ExerciseDbContextModelSnapshot.cs | 4 - .../Models/WeightExercise.cs | 1 - .../Services/WeightExerciseService.cs | 30 +++++ .../ExerciseTracker.selnoom/exercise.db | Bin 28672 -> 28672 bytes 8 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Helpers/Validation.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.Designer.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.cs diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Helpers/Validation.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Helpers/Validation.cs new file mode 100644 index 00000000..54f42503 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Helpers/Validation.cs @@ -0,0 +1,103 @@ +using Spectre.Console; + +namespace ExerciseTracker.selnoom.Helpers; + +class Validation +{ + public static string? ValidateTimeInput() + { + string timeInput = AnsiConsole.Ask("Enter date (yyyy-MM-dd HH:mm) or 0 to return to menu:"); + + if (timeInput == "0") + { + return null; + } + + DateTime parsedDate; + while (!DateTime.TryParseExact(timeInput, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out parsedDate)) + { + AnsiConsole.MarkupLine("[bold red]Invalid input. Please try again.[/]\n"); + timeInput = AnsiConsole.Ask("Enter date (yyyy-MM-dd HH:mm) or 0 to return to menu:"); + + if (timeInput == "0") + { + return null; + } + } + + return parsedDate.ToString("yyyy-MM-dd HH:mm"); + } + + internal static string? ValidateEndTimeInput(string? startTime) + { + if (startTime == null) + { + return null; + } + + while (true) + { + string? endTime = ValidateTimeInput(); + + if (endTime == null) + { + return null; + } + + if (!DateTime.TryParseExact(startTime, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out DateTime parsedStart)) + { + AnsiConsole.MarkupLine("[bold red]Invalid start time format.[/]\n"); + return null; + } + + if (!DateTime.TryParseExact(endTime, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out DateTime parsedEnd)) + { + AnsiConsole.MarkupLine("[bold red]Invalid end time format. Please try again.[/]\n"); + continue; + } + + if (parsedEnd < parsedStart) + { + AnsiConsole.MarkupLine("[bold red]The end time cannot be before the start time. Please try again.[/]\n"); + } + else + { + return endTime; + } + } + } + + public static double ValidatePositiveDouble(string prompt) + { + double value; + while (true) + { + var input = AnsiConsole.Ask(prompt); + if (double.TryParse(input, out value) && value >= 0) + { + return value; + } + else + { + AnsiConsole.MarkupLine("[red]Please enter a positive number or 0 to return to the menu:[/]"); + } + } + } + + public static int ValidatePositiveInt(string prompt) + { + int value; + while (true) + { + var input = AnsiConsole.Ask(prompt); + if (int.TryParse(input, out value) && value >= 0) + { + return value; + } + else + { + AnsiConsole.MarkupLine("[red]Please enter positive integer or 0 to return:[/]"); + } + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs index 14bfb1d7..182a698d 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs @@ -1,4 +1,6 @@ using ExerciseTracker.selnoom.Controllers; +using ExerciseTracker.selnoom.Helpers; +using ExerciseTracker.selnoom.Models; using Spectre.Console; namespace ExerciseTracker.selnoom.Menu; @@ -31,7 +33,7 @@ public async Task ShowMenu() await ViewExercises(); break; case MainMenuChoices.Create: - //await CreateExercise(); + await CreateExercise(); break; case MainMenuChoices.Edit: //await EditExercise(); @@ -48,6 +50,65 @@ public async Task ShowMenu() private async Task ViewExercises() { AnsiConsole.Clear(); + + await DisplayExercises(); + + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + } + + public async Task CreateExercise() + { + AnsiConsole.Clear(); + + string employeeName = AnsiConsole.Prompt(new TextPrompt("Enter the exercise name or 0 to return:")); + if (employeeName == "0") return; + + double weight = Validation.ValidatePositiveDouble("Enter the weight used in the exercise or 0 to return:"); + if (weight == 0) return; + + int sets = Validation.ValidatePositiveInt("Enter the number of sets done or 0 to return::"); + if (sets == 0) return; + + int repetitions = Validation.ValidatePositiveInt("Enter the number of repetitions done in each set or 0 to return::"); + if (repetitions == 0) return; + + (string?, string?) times = GetStartAndEndTimes(); + if (times.Item1 == null || times.Item2 == null) return; + + WeightExercise newExercise = new WeightExercise + { + Name = employeeName, + Weight = weight, + Sets = sets, + Repetitions = repetitions, + DateStart = DateTime.Parse(times.Item1), + DateEnd = DateTime.Parse(times.Item2) + }; + + try + { + var createdExercise = await _weightExerciseController.CreateExerciseAsync(newExercise); + if (createdExercise == null) + { + AnsiConsole.MarkupLine("[red]Exercise creation failed.[/]"); + } + else + { + AnsiConsole.MarkupLine("[green]Exercise created successfully![/]"); + } + } + catch (Exception e) + { + AnsiConsole.MarkupLine("[red]There was an error while creating the exercise! Please try again later.[/]"); + } + + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + } + + public async Task DisplayExercises() + { try { var exercises = await _weightExerciseController.GetExercisesAsync(); @@ -64,18 +125,38 @@ private async Task ViewExercises() AnsiConsole.MarkupLine("[blue bold underline]Exercises:[/]"); foreach (var exercise in sortedExercises) { - AnsiConsole.WriteLine($"Date Start: {exercise.DateStart}\tName: {exercise.Name}\tDuration: {exercise.Duration}\tSets: {exercise.Sets}\tRepetitions: {exercise.Repetitions}\tComments: {exercise.Comments}"); + AnsiConsole.WriteLine($"Date Start: {exercise.DateStart}\tName: {exercise.Name}\tDuration: {exercise.Duration}\tSets: {exercise.Sets}\tRepetitions: {exercise.Repetitions}"); } } catch (Exception e) { AnsiConsole.MarkupLine("[red]There was an error while retrieving the exercises! Please try again later.[/]"); } + } - AnsiConsole.MarkupLine("\nPress enter to continue"); - AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + internal static (string?, string?) GetStartAndEndTimes() + { + string? startTime; + string? endTime; + + startTime = Validation.ValidateTimeInput(); + if (startTime == null) + { + return (null, null); + } + + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[bold]Now, type the ending time of your exercise or 0 to return[/]"); + endTime = Validation.ValidateEndTimeInput(startTime); + if (endTime == null) + { + return (null, null); + } + + return (startTime, endTime); } + public enum MainMenuChoices { View, diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.Designer.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.Designer.cs new file mode 100644 index 00000000..d6624e22 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.Designer.cs @@ -0,0 +1,58 @@ +// +using System; +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + [Migration("20250325133021_RemovedWeightComments")] + partial class RemovedWeightComments + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateEnd") + .HasColumnType("TEXT"); + + b.Property("DateStart") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.cs new file mode 100644 index 00000000..05eade1b --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325133021_RemovedWeightComments.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + /// + public partial class RemovedWeightComments : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Comments", + table: "Exercises"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Comments", + table: "Exercises", + type: "TEXT", + nullable: false, + defaultValue: ""); + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs index 84214669..38341173 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs @@ -23,10 +23,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("Comments") - .IsRequired() - .HasColumnType("TEXT"); - b.Property("DateEnd") .HasColumnType("TEXT"); diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs index bf72cbeb..d4b04159 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs @@ -10,5 +10,4 @@ public class WeightExercise public double Weight { get; set; } public int Sets { get; set; } public int Repetitions { get; set; } - public string Comments { get; set; } = string.Empty; } diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs index 4a0405e5..380f7bfa 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Services/WeightExerciseService.cs @@ -29,6 +29,21 @@ public async Task> GetExercisesAsync() throw new ArgumentException("Start date must be before the end date."); } + if (exercise.Weight <= 0) + { + throw new ArgumentException("Weight must be greater than 0."); + } + + if (exercise.Sets <= 0) + { + throw new ArgumentException("Sets must be greater than 0."); + } + + if (exercise.Repetitions <= 0) + { + throw new ArgumentException("Repetitions must be greater than 0."); + } + return await _weightExerciseRepository.CreateExerciseAsync(exercise); } @@ -39,6 +54,21 @@ public async Task> GetExercisesAsync() throw new ArgumentException("Start date must be before the end date."); } + if (exercise.Weight <= 0) + { + throw new ArgumentException("Weight must be greater than 0."); + } + + if (exercise.Sets <= 0) + { + throw new ArgumentException("Sets must be greater than 0."); + } + + if (exercise.Repetitions <= 0) + { + throw new ArgumentException("Repetitions must be greater than 0."); + } + return await _weightExerciseRepository.UpdateExerciseAsync(exercise); } diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db index e5be1511db0f4a8c82f70cf6682af460065dd48c..fc98f96894b8bf166e3b17d3883adcd4a1473907 100644 GIT binary patch delta 501 zcmZp8z}WDBae}lU3j+fKI}|elX`YEX#*!=ydR2wIJf|60xrG_{ZTR^3Uh-6O3v-%n zY~05Y*i_BVF0QZ7*s5BRn3R*6nigM@np+U>T9I0moLQV&j3L149OUX4;;Inh=;Y(7 zpoAemc>=%QWM5vL$@jR$CQsny;B-kWNewPZEGn7Y$SXeCk4GFRTF1q6n(?T(ygXxD z@Z=9Xu2N0KP-lojoY9mFH Mi~v>s=VxR90PNR|F#rGn delta 305 zcmZp8z}WDBae}lUGXnzyI}o!1F(VLjPSi1$WMgE{g6QZDQU|?XV z3q}eC23BCCt~2>BrxZaovRsnF$mT#bZ?53!W1PH(i(@k%-yK0U10yQ~3o8RlJwpRy z6H7BwZLk%*Ku@sn$20KX;@`;M&L0nSvOoXic>Se3Ec_)5{15o|@K5J20g9&ZPcE_V u+02;mpMUayf9}nK4iEUnL>QU%Ia1T&c^H}HIb17Ji;^>oQ;Qcl2mk=LZb*Ru From 5eafd7d18ff6cebc7211b1436998f183a691c259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicholas=20Pe=C3=A7anha?= Date: Tue, 25 Mar 2025 10:32:57 -0300 Subject: [PATCH 3/4] Removed double semicolon --- .../ExerciseTracker.selnoom/Menu/Menu.cs | 153 +++++++++++++++++- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs index 182a698d..af36efd5 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs @@ -36,10 +36,10 @@ public async Task ShowMenu() await CreateExercise(); break; case MainMenuChoices.Edit: - //await EditExercise(); + await UpdateExercise(); break; case MainMenuChoices.Delete: - //await DeleteExercise(); + await DeleteExercise(); break; case MainMenuChoices.Exit: return; @@ -67,10 +67,10 @@ public async Task CreateExercise() double weight = Validation.ValidatePositiveDouble("Enter the weight used in the exercise or 0 to return:"); if (weight == 0) return; - int sets = Validation.ValidatePositiveInt("Enter the number of sets done or 0 to return::"); + int sets = Validation.ValidatePositiveInt("Enter the number of sets done or 0 to return:"); if (sets == 0) return; - int repetitions = Validation.ValidatePositiveInt("Enter the number of repetitions done in each set or 0 to return::"); + int repetitions = Validation.ValidatePositiveInt("Enter the number of repetitions done in each set or 0 to return:"); if (repetitions == 0) return; (string?, string?) times = GetStartAndEndTimes(); @@ -107,6 +107,151 @@ public async Task CreateExercise() AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); } + public async Task UpdateExercise() + { + AnsiConsole.Clear(); + + List exercises = await _weightExerciseController.GetExercisesAsync(); + + if (!exercises.Any()) + { + AnsiConsole.MarkupLine("[red]No exercises registered.[/]"); + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + return; + } + + var selectedExercise = ChooseExercise(exercises); + if (selectedExercise == null) return; + + string employeeName = AnsiConsole.Prompt(new TextPrompt("Enter the new exercise name or 0 to return:")); + if (employeeName == "0") return; + + double weight = Validation.ValidatePositiveDouble("Enter the new weight used in the exercise or 0 to return:"); + if (weight == 0) return; + + int sets = Validation.ValidatePositiveInt("Enter the new number of sets done or 0 to return:"); + if (sets == 0) return; + + int repetitions = Validation.ValidatePositiveInt("Enter the new number of repetitions done in each set or 0 to return:"); + if (repetitions == 0) return; + + (string?, string?) times = GetStartAndEndTimes(); + if (times.Item1 == null || times.Item2 == null) return; + + WeightExercise newExercise = new WeightExercise + { + Id = selectedExercise.Id, + Name = employeeName, + Weight = weight, + Sets = sets, + Repetitions = repetitions, + DateStart = DateTime.Parse(times.Item1), + DateEnd = DateTime.Parse(times.Item2) + }; + + try + { + var updatedExercise = await _weightExerciseController.UpdateExerciseAsync(newExercise); + if (updatedExercise == null) + { + AnsiConsole.MarkupLine("[red]Exercise update failed.[/]"); + } + else + { + AnsiConsole.MarkupLine("[green]Exercise updated successfully![/]"); + } + } + catch (Exception e) + { + AnsiConsole.MarkupLine("[red]There was an error while updating the exercise! Please try again later.[/]"); + } + + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + } + + public async Task DeleteExercise() + { + AnsiConsole.Clear(); + + List exercises = await _weightExerciseController.GetExercisesAsync(); + if (!exercises.Any()) + { + AnsiConsole.MarkupLine("[red]No exercises registered.[/]"); + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + return; + } + + var selectedExercise = ChooseExercise(exercises); + if (selectedExercise == null) return; + + try + { + var deletedExercise = await _weightExerciseController.DeleteExerciseAsync(selectedExercise.Id); + if (deletedExercise == null) + { + AnsiConsole.MarkupLine("[red]Exercise deletion failed.[/]"); + } + else + { + AnsiConsole.MarkupLine("[green]Exercise deleted successfully![/]"); + } + } + catch (Exception e) + { + AnsiConsole.MarkupLine("[red]There was an error while deleting the exercise! Please try again later.[/]"); + } + + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + } + + public static WeightExercise? ChooseExercise(List exercises) + { + try + { + var choices = new List { "None" }; + choices.AddRange(exercises); + + object selected = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Select an exercise (choose 'None' to return to the menu):") + .PageSize(10) + .AddChoices(choices) + .UseConverter(obj => + { + if (obj is string str) + return str; + else if (obj is WeightExercise exercise) + return $"Date Start: {exercise.DateStart}\tName: {exercise.Name}\tDuration: {exercise.Duration}\tSets: {exercise.Sets}\tRepetitions: {exercise.Repetitions}"; + return string.Empty; + }) + ); + + if (selected is string s && s == "None") + { + return null; + } + else if (selected is WeightExercise exercise) + { + return exercise; + } + else + { + return null; + } + } + catch (Exception ex) + { + AnsiConsole.MarkupLine("[red]There was an error when retrieving the exercises! Please try again later.[/]"); + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); + return null; + } + } + public async Task DisplayExercises() { try From fd5f86fb179e073fff776abf3a279cba485302d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicholas=20Pe=C3=A7anha?= Date: Tue, 25 Mar 2025 10:52:20 -0300 Subject: [PATCH 4/4] Changed model duration to not be saved and calculated automatically --- .../ExerciseTracker.selnoom/Menu/Menu.cs | 6 +- ...0325134923_ChangedDurationCalc.Designer.cs | 55 ++++++++++++++++++ .../20250325134923_ChangedDurationCalc.cs | 30 ++++++++++ .../ExerciseDbContextModelSnapshot.cs | 3 - .../Models/WeightExercise.cs | 12 +++- .../ExerciseTracker.selnoom/exercise.db | Bin 28672 -> 28672 bytes 6 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.Designer.cs create mode 100644 ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.cs diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs index af36efd5..22896336 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Menu/Menu.cs @@ -52,9 +52,6 @@ private async Task ViewExercises() AnsiConsole.Clear(); await DisplayExercises(); - - AnsiConsole.MarkupLine("\nPress enter to continue"); - AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); } public async Task CreateExercise() @@ -277,6 +274,9 @@ public async Task DisplayExercises() { AnsiConsole.MarkupLine("[red]There was an error while retrieving the exercises! Please try again later.[/]"); } + + AnsiConsole.MarkupLine("\nPress enter to continue"); + AnsiConsole.Prompt(new TextPrompt("").AllowEmpty()); } internal static (string?, string?) GetStartAndEndTimes() diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.Designer.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.Designer.cs new file mode 100644 index 00000000..77f42537 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.Designer.cs @@ -0,0 +1,55 @@ +// +using System; +using ExerciseTracker.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + [DbContext(typeof(ExerciseDbContext))] + [Migration("20250325134923_ChangedDurationCalc")] + partial class ChangedDurationCalc + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("ExerciseTracker.selnoom.Models.WeightExercise", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateEnd") + .HasColumnType("TEXT"); + + b.Property("DateStart") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Repetitions") + .HasColumnType("INTEGER"); + + b.Property("Sets") + .HasColumnType("INTEGER"); + + b.Property("Weight") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("Exercises"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.cs new file mode 100644 index 00000000..92072a45 --- /dev/null +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/20250325134923_ChangedDurationCalc.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ExerciseTracker.selnoom.Migrations +{ + /// + public partial class ChangedDurationCalc : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Duration", + table: "Exercises"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Duration", + table: "Exercises", + type: "TEXT", + nullable: false, + defaultValue: new TimeSpan(0, 0, 0, 0, 0)); + } + } +} diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs index 38341173..a6802fa1 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Migrations/ExerciseDbContextModelSnapshot.cs @@ -29,9 +29,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DateStart") .HasColumnType("TEXT"); - b.Property("Duration") - .HasColumnType("TEXT"); - b.Property("Name") .IsRequired() .HasColumnType("TEXT"); diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs index d4b04159..b5c963d8 100644 --- a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs +++ b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/Models/WeightExercise.cs @@ -1,11 +1,19 @@ -namespace ExerciseTracker.selnoom.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ExerciseTracker.selnoom.Models; public class WeightExercise { public int Id { get; set; } public DateTime DateStart { get; set; } public DateTime DateEnd { get; set; } - public TimeSpan Duration { get; set; } + + [NotMapped] + public TimeSpan Duration + { + get { return DateEnd - DateStart; } + } + public string Name { get; set; } = string.Empty; public double Weight { get; set; } public int Sets { get; set; } diff --git a/ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db b/ExerciseTracker.selnoom/ExerciseTracker.selnoom/exercise.db index fc98f96894b8bf166e3b17d3883adcd4a1473907..afec039080d112b7853540e46a6ddefcf6f5f684 100644 GIT binary patch delta 363 zcmZp8z}WDBae}lUD+2=qI}n3tMj+;&sADY2%Ai+O$jft@ftA~cf!~IYkMAWan^A)#c;5T>+3VNq)&F>RG-Yw%Rjk{bLC_^u1d8uad~;hR?(8gq@2{I z5U8?nFgLZy1;__Vw}emrz|%Q-8Mhr!OWEd1o<2rKj?El=PX*OY4Xq3f^o-37jSUSg zv<(cb3=DXI4rAkIW#GTXzmdP4Kc3%^pB3nok9;~>Y|Me;Mg~Tv2F6CFhQ=nAM#k~Z z8Hsu6sVOd{MTsSu`FYNXImwoKKoch$>R%RM<5ywef55+oe>#5&zc;@M&=>)Jbv5Qd zM#84BP4>3mx|uQIKmX?c{`dHmnVI!DQq$r~QgaL9T`N+Hk~51_i+PxtZn6)r?&NqbI}Vr9qQsKS{JhDHyseYDxw+Yoip$G0wgpfAz~d^_RLssU zuCLG7Dq515l#`m;lnmqprCUXT(&CfTIr%4x@g`%Kv$>FG8Y3h7W)8lmf~v*_Rz~J} z<|c-Q#>S@F1_o9J2E0Ibvho`;@ZaL!$luN%&u<8HmpuPuL;cHqto+^#{15o|@K5J2 z;r9lLTk=o#w%@uaL10tBU4CUIW_^y-wD^+L+=6)5iqxXy%;MBy9wuga4mfW!qr-oG GMg{