From 8236f93b4abb1da12df34902676e1d8eee01d576 Mon Sep 17 00:00:00 2001 From: SylvesterNdwata Date: Thu, 21 May 2026 08:21:27 +0200 Subject: [PATCH 1/2] feat: first version flashcard project --- .../Controllers/DatabaseController.cs | 48 ++++ .../Controllers/FlashCardController.cs | 157 +++++++++++ .../Controllers/StacksController.cs | 256 ++++++++++++++++++ silvermax.FlashCards/DataSeeder.cs | 65 +++++ silvermax.FlashCards/DatabaseHelper.cs | 31 +++ silvermax.FlashCards/DbConfig.cs | 26 ++ .../DtOs/FlashCardResponseDto.cs | 7 + silvermax.FlashCards/DtOs/StackResponseDto.cs | 6 + silvermax.FlashCards/Enums.cs | 37 +++ silvermax.FlashCards/Models/FlashCard.cs | 9 + silvermax.FlashCards/Models/Stacks.cs | 7 + silvermax.FlashCards/Models/StudySession.cs | 9 + silvermax.FlashCards/Program.cs | 11 + silvermax.FlashCards/UIHelper.cs | 47 ++++ silvermax.FlashCards/UserInput.cs | 14 + silvermax.FlashCards/UserInterface.cs | 45 +++ silvermax.FlashCards/appsettings.json | 6 + .../silvermax.FlashCards.csproj | 24 ++ .../silvermax.FlashCards.slnx | 3 + 19 files changed, 808 insertions(+) create mode 100644 silvermax.FlashCards/Controllers/DatabaseController.cs create mode 100644 silvermax.FlashCards/Controllers/FlashCardController.cs create mode 100644 silvermax.FlashCards/Controllers/StacksController.cs create mode 100644 silvermax.FlashCards/DataSeeder.cs create mode 100644 silvermax.FlashCards/DatabaseHelper.cs create mode 100644 silvermax.FlashCards/DbConfig.cs create mode 100644 silvermax.FlashCards/DtOs/FlashCardResponseDto.cs create mode 100644 silvermax.FlashCards/DtOs/StackResponseDto.cs create mode 100644 silvermax.FlashCards/Enums.cs create mode 100644 silvermax.FlashCards/Models/FlashCard.cs create mode 100644 silvermax.FlashCards/Models/Stacks.cs create mode 100644 silvermax.FlashCards/Models/StudySession.cs create mode 100644 silvermax.FlashCards/Program.cs create mode 100644 silvermax.FlashCards/UIHelper.cs create mode 100644 silvermax.FlashCards/UserInput.cs create mode 100644 silvermax.FlashCards/UserInterface.cs create mode 100644 silvermax.FlashCards/appsettings.json create mode 100644 silvermax.FlashCards/silvermax.FlashCards.csproj create mode 100644 silvermax.FlashCards/silvermax.FlashCards.slnx diff --git a/silvermax.FlashCards/Controllers/DatabaseController.cs b/silvermax.FlashCards/Controllers/DatabaseController.cs new file mode 100644 index 00000000..df85d263 --- /dev/null +++ b/silvermax.FlashCards/Controllers/DatabaseController.cs @@ -0,0 +1,48 @@ +using Microsoft.Data.SqlClient; +using Dapper; + +namespace silvermax.FlashCards.Controllers; + +internal class DatabaseController +{ + private readonly DbConfig dbConfig = new(); + public void InitTables() + { + using (var masterCon = new SqlConnection(dbConfig.MasterConnectionString)) + { + masterCon.Open(); + + masterCon.Execute("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'flashcardDB') CREATE DATABASE flashcardDB"); + } + + using (var connection = new SqlConnection(dbConfig.ConnectionString)) + { + connection.Open(); + + string createStackTableSql = @" + IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Stacks') + BEGIN + CREATE TABLE Stacks ( + Id INT PRIMARY KEY IDENTITY(1,1), + StackName NVARCHAR(50) NOT NULL + ) + END"; + + string createCardTableSql = @" + IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Flashcards') + BEGIN + CREATE TABLE Flashcards ( + Id INT PRIMARY KEY IDENTITY(1,1), + StackId INT NOT NULL, + Word NVARCHAR(50), + Translation NVARCHAR(50) + CONSTRAINT FK_Flashcards_Stacks FOREIGN KEY (StackId) + REFERENCES Stacks(Id) ON DELETE CASCADE + ) + END"; + + connection.Execute(createStackTableSql); + connection.Execute(createCardTableSql); + } + } +} diff --git a/silvermax.FlashCards/Controllers/FlashCardController.cs b/silvermax.FlashCards/Controllers/FlashCardController.cs new file mode 100644 index 00000000..15ea0e8f --- /dev/null +++ b/silvermax.FlashCards/Controllers/FlashCardController.cs @@ -0,0 +1,157 @@ +using Dapper; +using Microsoft.Data.SqlClient; +using silvermax.FlashCards.DtOs; +using silvermax.FlashCards.Models; +using Spectre.Console; +using static silvermax.FlashCards.Enums; + +namespace silvermax.FlashCards.Controllers; + +internal class FlashCardController +{ + private readonly DatabaseHelper db = new(); + StacksController controller = new(); + UserInput input = new(); + public void Start() + { + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Choose an action to do on flashcards") + .UseConverter(e => Enums.ToDisplayName(e)) + .AddChoices(Enum.GetValues())); + + switch (choice) + { + case ManageFlashCard.AddFlashcard: + AddFlashCard(); + break; + + case ManageFlashCard.EditFlashcard: + EditFlashCard(); + break; + + case ManageFlashCard.DeleteFlashcard: + DeleteFlashCard(); + break; + + case ManageFlashCard.ViewAllFlashcards: + ViewAllFlashCards(); + break; + + case ManageFlashCard.Back: + return; + } + } + + private void ViewAllFlashCards() + { + using (var connection = db.GetConnection()) + { + + var cards = connection.Query("SELECT * FROM Flashcards").ToList(); + + AnsiConsole.Write(UIHelper.BuildFlashCardTable(cards)); + } + + AnsiConsole.MarkupLine("Press Any Key to continue..."); + Console.ReadKey(); + } + + private void DeleteFlashCard() + { + var word = AnsiConsole.Ask("Please enter the word of the FlashCard you want to delete: "); + + if (!db.FlashCardExists(word)) + { + AnsiConsole.MarkupLine($"[red]FlashCard with word {word} does not exist.[/]"); + UIHelper.PressAnyKey(); + return; + } + + using (var connection = db.GetConnection()) + { + connection.Execute("DELETE FROM Flashcards WHERE Word = @Word", new { Word = word }); + + AnsiConsole.MarkupLine($"[green]Flashcard with word {word} deleted successfully[/]"); + } + + UIHelper.PressAnyKey(); + } + + private void EditFlashCard() + { + var wordToChange = AnsiConsole.Ask("Please enter the word of the FlashCard you want to delete: "); + var (word, translation) = input.GetUserInput(); + + if (!db.FlashCardExists(wordToChange)) + { + AnsiConsole.MarkupLine($"[red]FlashCard with word {wordToChange} does not exist.[/]"); + UIHelper.PressAnyKey(); + return; + } + + using (var connection = db.GetConnection()) + { + + string updatesql = "UPDATE Flashcards SET Word = @NewWord, Translation = @Translation WHERE Word = @Word"; + + var cardToUpdate = new + { + NewWord = wordToChange, + Word = word, + Translation = translation, + }; + + connection.Execute(updatesql, cardToUpdate); + + AnsiConsole.MarkupLine($"[green]FlashCard with word {word} updated successfully[/]"); + } + + UIHelper.PressAnyKey(); + } + + private void AddFlashCard() + { + var (word, translation) = input.GetUserInput(); + controller.GetAllStacks(); + var stackName = AnsiConsole.Ask("Please enter the Id of the Stack that this flashcard will be added to: "); + + using (var connection = db.GetConnection()) + { + var stackId = db.GetStackId(stackName); + + if (!db.StackExists(stackId)) + { + AnsiConsole.MarkupLine($"[red]Stack {stackName} does not exist.[/]."); + UIHelper.PressAnyKey(); + return; + } + + string insertSql = """ + IF NOT EXISTS (SELECT 1 FROM Flashcards WHERE StackId = @StackId AND Word = @Word) + INSERT INTO Flashcards (StackId, Word, Translation) VALUES (@StackId, @Word, @Translation) + """; + + var entry = new FlashCard + { + StackId = stackId, + Word = word, + Translation = translation + }; + + int rowsAffected = connection.Execute(insertSql, entry); + + if (rowsAffected <= 0) + { + AnsiConsole.MarkupLine($"[red]'{word}' already exists in the database[/]"); + } + else + { + AnsiConsole.MarkupLine($"[green]Flashcard added sucessfully to the database[/]"); + } + } + + AnsiConsole.MarkupLine("Press Any Key to continue..."); + Console.ReadKey(); + } +} diff --git a/silvermax.FlashCards/Controllers/StacksController.cs b/silvermax.FlashCards/Controllers/StacksController.cs new file mode 100644 index 00000000..1c68036e --- /dev/null +++ b/silvermax.FlashCards/Controllers/StacksController.cs @@ -0,0 +1,256 @@ +using Dapper; +using Microsoft.Data.SqlClient; +using silvermax.FlashCards.DtOs; +using silvermax.FlashCards.Models; +using Spectre.Console; +using static silvermax.FlashCards.Enums; + +namespace silvermax.FlashCards.Controllers; + +internal class StacksController +{ + private readonly DatabaseHelper db = new(); + UserInput input = new(); + public void Start() + { + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Choose an action to do on stacks") + .UseConverter(e => Enums.ToDisplayName(e)) + .AddChoices(Enum.GetValues())); + + switch (choice) + { + case ManageStack.AddStack: + AddStack(); + break; + + case ManageStack.EditStack: + EditStack(); + break; + + case ManageStack.DeleteStack: + DeleteStack(); + break; + + case ManageStack.CreateFlashcardInStack: + AddFlashCardToStack(); + break; + + case ManageStack.ShowAllFlashcardsInStack: + ShowAllCardsInStack(); + break; + + case ManageStack.Back: + return; + } + } + + private void ShowAllCardsInStack() + { + var currentStack = ChooseStack(); + + using (var connection = db.GetConnection()) + { + + var table = new Table(); + table.Border = TableBorder.Rounded; + + table.AddColumn("[yellow]Id[/]"); + table.AddColumn("[yellow]Front[/]"); + table.AddColumn("[yellow]Back[/]"); + + int currentStackID = connection.QuerySingle("SELECT Id FROM Stacks WHERE StackName = @StackName", new { StackName = currentStack }); + + var cards = connection.Query("SELECT * FROM Flashcards WHERE StackId = @Id", new { Id = currentStackID }).ToList(); + + if (cards.Count == 0) + { + AnsiConsole.MarkupLine("[red]No rows found in the database[/]"); + } + else + { + int displayId = 1; + foreach (var card in cards) + { + table.AddRow( + (displayId++).ToString(), + $"[purple]{card.Word}[/]", + $"[purple]{card.Translation}[/]"); + } + + AnsiConsole.Write(table); + } + } + + AnsiConsole.MarkupLine("Press Any Key to continue..."); + Console.ReadKey(); + } + + public string ChooseStack() + { + var stack = AnsiConsole.Ask("Please write the name of the stack you want to work on: ").ToLower(); + + AnsiConsole.MarkupLine($"Current working stack: {stack}"); + + return stack; + } + + private void AddFlashCardToStack() + { + string currentStack = ChooseStack(); + + var (word, translation) = input.GetUserInput(); + + using (var connection = db.GetConnection()) + { + int currentStackID = db.GetStackId(currentStack); + + string sql = """ + IF NOT EXISTS (SELECT 1 FROM Flashcards WHERE StackId = @StackId AND Word = @Word) + INSERT INTO Flashcards (StackId, Word, Translation) VALUES (@StackId, @Word, @Translation) + """; + + var card = new FlashCard + { + StackId = currentStackID, + Word = word, + Translation = translation + }; + + int rowsAffected = connection.Execute(sql, card); + + if (rowsAffected <= 0) + { + AnsiConsole.MarkupLine($"[red]'{word}' already exists in the {currentStack} stack.[/]"); + } + else + { + AnsiConsole.MarkupLine($"[green]Flashcard added sucessfully to the {currentStack} stack[/]"); + } + } + + AnsiConsole.MarkupLine("Press Any Key to continue..."); + Console.ReadKey(); + } + private void DeleteStack() + { + int stackId = UIHelper.PromptId("delete", "stack"); + + bool confirmation = AnsiConsole.Confirm("[red]Are you sure you want to delete this stack? All Cards in the stack will be deleted too.[/]"); + + if (!confirmation) + { + return; + } + + using (var connection = db.GetConnection()) + { + if (!db.StackExists(stackId)) + { + AnsiConsole.MarkupLine($"[red]Stack {stackId} does not exist.[/]."); + UIHelper.PressAnyKey(); + return; + } + + connection.Execute("DELETE FROM Stacks WHERE Id = @Id", new { Id = stackId }); + + AnsiConsole.MarkupLine($"[green]Stack with Id {stackId} deleted successfully[/]"); + } + + UIHelper.PressAnyKey(); + + + } + + private void AddStack() + { + var stackName = AnsiConsole.Ask("Please input the name of the stack to add: "); + + using (var connection = db.GetConnection()) + { + string insertSql = """ + IF NOT EXISTS (SELECT 1 FROM Stacks WHERE StackName = @StackName) + INSERT INTO Stacks (StackName) VALUES (@StackName) + """; + + var newStack = new Stacks + { + StackName = stackName.ToLower(), + }; + + int rowsAffected = connection.Execute(insertSql, newStack); + + if (rowsAffected <= 0) + { + AnsiConsole.MarkupLine($"[red]'{stackName}' already exists in the database[/]"); + } + else + { + AnsiConsole.MarkupLine($"[green]Stack {stackName} added sucessfully to the database[/]"); + } + } + + AnsiConsole.MarkupLine("Press Any Key to continue..."); + Console.ReadKey(); + } + + private void EditStack() + { + var stackName = AnsiConsole.Ask("Please input the name of the stack to update: "); + + using (var connection = db.GetConnection()) + { + int stackId = db.GetStackId(stackName); + + if (!db.StackExists(stackId)) + { + AnsiConsole.MarkupLine($"[red]Stack {stackId} does not exist.[/]."); + UIHelper.PressAnyKey(); + return; + } + + string updateSql = "UPDATE Stacks SET StackName = @StackName WHERE stackName = @StackName"; + + var stackToUpdate = new Stacks + { + StackName = stackName, + }; + + connection.Execute(updateSql, stackToUpdate); + + AnsiConsole.MarkupLine($"[green]Stack {stackName} updated successfully[/]"); + } + + UIHelper.PressAnyKey(); + } + + public void GetAllStacks() + { + using (var connection = db.GetConnection()) + { + var table = new Table(); + table.Border(TableBorder.Rounded); + + table.AddColumn("[yellow]Name[/]"); + + var stacks = connection.Query("SELECT * FROM Stacks").ToList(); + + if (stacks.Count < 0) + { + AnsiConsole.MarkupLine("[red]No rows found in the database[/]"); + } + else + { + foreach (var stack in stacks) + { + table.AddRow( + $"[purple]{stack.StackName}[/]".ToUpper() + ); + } + + AnsiConsole.Write(table); + } + } + } +} diff --git a/silvermax.FlashCards/DataSeeder.cs b/silvermax.FlashCards/DataSeeder.cs new file mode 100644 index 00000000..b3743da7 --- /dev/null +++ b/silvermax.FlashCards/DataSeeder.cs @@ -0,0 +1,65 @@ +using Dapper; +using Microsoft.Data.SqlClient; + +namespace silvermax.FlashCards; + +internal class DataSeeder +{ + private readonly DbConfig dbConfig = new(); + public void Seed() + { + var stacks = new Dictionary> + { + ["portuguese"] = new() + { + ("house", "casa"), + ("dog", "cachorro"), + ("cat", "gato"), + ("water", "água"), + ("food", "comida"), + }, + ["spanish"] = new() + { + ("house", "casa"), + ("dog", "perro"), + ("cat", "gato"), + ("water", "agua"), + ("food", "comida"), + }, + ["french"] = new() + { + ("house", "maison"), + ("dog", "chien"), + ("cat", "chat"), + ("water", "eau"), + ("food", "nourriture"), + }, + }; + + using var connection = new SqlConnection(dbConfig.ConnectionString); + + foreach (var stack in stacks) + { + connection.Execute(""" + IF NOT EXISTS (SELECT 1 FROM Stacks WHERE StackName = @StackName) + INSERT INTO Stacks (StackName) VALUES (@StackName) + """, new { StackName = stack.Key }); + + int stackId = connection.QuerySingle( + "SELECT Id FROM Stacks WHERE StackName = @StackName", + new { StackName = stack.Key }); + + foreach (var (word, translation) in stack.Value) + { + // Only insert if not already present + connection.Execute(""" + IF NOT EXISTS (SELECT 1 FROM Flashcards WHERE StackId = @StackId AND Word = @Word) + INSERT INTO Flashcards (StackId, Word, Translation) VALUES (@StackId, @Word, @Translation) + """, new { StackId = stackId, Word = word, Translation = translation }); + } + } + + Console.WriteLine("Database seeded successfully!"); + Thread.Sleep(1000); + } +} diff --git a/silvermax.FlashCards/DatabaseHelper.cs b/silvermax.FlashCards/DatabaseHelper.cs new file mode 100644 index 00000000..306bebcd --- /dev/null +++ b/silvermax.FlashCards/DatabaseHelper.cs @@ -0,0 +1,31 @@ +using Dapper; +using Microsoft.Data.SqlClient; + +namespace silvermax.FlashCards; + +internal class DatabaseHelper +{ + private readonly DbConfig dbConfig = new(); + + public SqlConnection GetConnection() => new SqlConnection(dbConfig.ConnectionString); + + public int GetStackId(string stackName) + { + using var connection = GetConnection(); + + return connection.QuerySingle("SELECT Id FROM Stacks WHERE StackName = @StackName", new { StackName = stackName }); + } + + public bool StackExists(int stackId) + { + using var connection = GetConnection(); + + return connection.ExecuteScalar("SELECT COUNT(1) FROM Stacks WHERE Id = @Id", new { Id = stackId }); + } + + public bool FlashCardExists(string word) + { + using var connection = GetConnection(); + return connection.ExecuteScalar("SELECT COUNT(1) FROM Flashcards WHERE Word = @Word", new { Word = word }); + } +} diff --git a/silvermax.FlashCards/DbConfig.cs b/silvermax.FlashCards/DbConfig.cs new file mode 100644 index 00000000..e117bff0 --- /dev/null +++ b/silvermax.FlashCards/DbConfig.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Configuration; + +namespace silvermax.FlashCards +{ + internal class DbConfig + { + private readonly string _connectionString; + private readonly string _masterConnectionString; + + public DbConfig() + { + var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .Build(); + + _connectionString = config.GetConnectionString("DefaultConnection") + ?? throw new Exception("Connection String not found"); + + _masterConnectionString = config.GetConnectionString("masterConnectionString") + ?? throw new Exception("Connection String not found"); + } + + public string ConnectionString => _connectionString; + public string MasterConnectionString => _masterConnectionString; + } +} \ No newline at end of file diff --git a/silvermax.FlashCards/DtOs/FlashCardResponseDto.cs b/silvermax.FlashCards/DtOs/FlashCardResponseDto.cs new file mode 100644 index 00000000..6b8bb27c --- /dev/null +++ b/silvermax.FlashCards/DtOs/FlashCardResponseDto.cs @@ -0,0 +1,7 @@ +namespace silvermax.FlashCards.DtOs; + +internal class FlashCardResponseDto +{ + public string? Word { get; set; } + public string? Translation { get; set; } +} diff --git a/silvermax.FlashCards/DtOs/StackResponseDto.cs b/silvermax.FlashCards/DtOs/StackResponseDto.cs new file mode 100644 index 00000000..d68926d3 --- /dev/null +++ b/silvermax.FlashCards/DtOs/StackResponseDto.cs @@ -0,0 +1,6 @@ +namespace silvermax.FlashCards.DtOs; + +internal class StackResponseDto +{ + public string? StackName { get; set; } +} diff --git a/silvermax.FlashCards/Enums.cs b/silvermax.FlashCards/Enums.cs new file mode 100644 index 00000000..45d2c852 --- /dev/null +++ b/silvermax.FlashCards/Enums.cs @@ -0,0 +1,37 @@ +using System.Text.RegularExpressions; + +namespace silvermax.FlashCards; + +internal class Enums +{ + internal static string ToDisplayName(Enum value) => + Regex.Replace(value.ToString(), "(?($"Please enter the Id of the {entity} you want to {action}: ") + .Validate(input => + { + if (!int.TryParse(input, out id)) + return ValidationResult.Error($"Please enter a valid {entity} Id"); + return ValidationResult.Success(); + })); + + return id; + } + + public static Table BuildFlashCardTable(IEnumerable cards) + { + var table = new Table { Border = TableBorder.Rounded }; + table.AddColumn("[yellow]Id[/]"); + table.AddColumn("[yellow]Front[/]"); + table.AddColumn("[yellow]Back[/]"); + + int displayId = 1; + foreach (var card in cards) + { + table.AddRow( + (displayId++).ToString(), + $"[purple]{card.Word}[/]", + $"[purple]{card.Translation}[/]"); + } + + return table; + } +} diff --git a/silvermax.FlashCards/UserInput.cs b/silvermax.FlashCards/UserInput.cs new file mode 100644 index 00000000..777801ca --- /dev/null +++ b/silvermax.FlashCards/UserInput.cs @@ -0,0 +1,14 @@ +using Spectre.Console; + +namespace silvermax.FlashCards; + +internal class UserInput +{ + public (string word, string translation) GetUserInput() + { + var word = AnsiConsole.Ask("Please enter the word: "); + var translation = AnsiConsole.Ask("Please enter the translation: "); + + return (word, translation); + } +} diff --git a/silvermax.FlashCards/UserInterface.cs b/silvermax.FlashCards/UserInterface.cs new file mode 100644 index 00000000..7fc53af1 --- /dev/null +++ b/silvermax.FlashCards/UserInterface.cs @@ -0,0 +1,45 @@ +using silvermax.FlashCards.Controllers; +using Spectre.Console; +using static silvermax.FlashCards.Enums; + +namespace silvermax.FlashCards; + +internal class UserInterface +{ + public void Start() + { + DatabaseController controller = new(); + FlashCardController flashCardController = new(); + StacksController stacksController = new(); + + controller.InitTables(); + + bool openApp = true; + while (openApp) + { + Console.Clear(); + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Welcome to the Flash card app.") + .UseConverter(e => Enums.ToDisplayName(e)) + .AddChoices(Enum.GetValues())); + + switch (choice) + { + case MenuOptions.ManageFlashcards: + flashCardController.Start(); + break; + + case MenuOptions.ManageStacks: + stacksController.GetAllStacks(); + stacksController.Start(); + break; + + case MenuOptions.exit: + openApp = false; + break; + } + } + + } +} diff --git a/silvermax.FlashCards/appsettings.json b/silvermax.FlashCards/appsettings.json new file mode 100644 index 00000000..203f933a --- /dev/null +++ b/silvermax.FlashCards/appsettings.json @@ -0,0 +1,6 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=localhost; Initial Catalog=flashcardDB;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=True;Application Name=SQL Server Management Studio;Command Timeout=0", + "masterConnectionString": "Data Source=localhost;Initial Catalog=master;Integrated Security=True;Encrypt=True;TrustServerCertificate=True;" + } +} \ No newline at end of file diff --git a/silvermax.FlashCards/silvermax.FlashCards.csproj b/silvermax.FlashCards/silvermax.FlashCards.csproj new file mode 100644 index 00000000..6297c45d --- /dev/null +++ b/silvermax.FlashCards/silvermax.FlashCards.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/silvermax.FlashCards/silvermax.FlashCards.slnx b/silvermax.FlashCards/silvermax.FlashCards.slnx new file mode 100644 index 00000000..ab24c719 --- /dev/null +++ b/silvermax.FlashCards/silvermax.FlashCards.slnx @@ -0,0 +1,3 @@ + + + From bd9b51f942da8c6bd9291da83ca69c26e27a1582 Mon Sep 17 00:00:00 2001 From: SylvesterNdwata Date: Thu, 21 May 2026 10:02:19 +0200 Subject: [PATCH 2/2] feat: add study session and be able to view all study sessions --- .../Controllers/DatabaseController.cs | 10 ++ silvermax.FlashCards/DtOs/StudySessionDto.cs | 6 + silvermax.FlashCards/Models/Session.cs | 8 ++ silvermax.FlashCards/Program.cs | 3 + silvermax.FlashCards/StudySession.cs | 111 ++++++++++++++++++ silvermax.FlashCards/UserInterface.cs | 9 ++ 6 files changed, 147 insertions(+) create mode 100644 silvermax.FlashCards/DtOs/StudySessionDto.cs create mode 100644 silvermax.FlashCards/Models/Session.cs create mode 100644 silvermax.FlashCards/StudySession.cs diff --git a/silvermax.FlashCards/Controllers/DatabaseController.cs b/silvermax.FlashCards/Controllers/DatabaseController.cs index df85d263..26bbb8e3 100644 --- a/silvermax.FlashCards/Controllers/DatabaseController.cs +++ b/silvermax.FlashCards/Controllers/DatabaseController.cs @@ -41,8 +41,18 @@ REFERENCES Stacks(Id) ON DELETE CASCADE ) END"; + string createSessionTableSql = @" + IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Sessions') + BEGIN + CREATE TABLE Sessions ( + Id INT PRIMARY KEY IDENTITY(1,1), + Date DATETIME2 NOT NULL, + Score INT NOT NULL + ) + END"; connection.Execute(createStackTableSql); connection.Execute(createCardTableSql); + connection.Execute(createSessionTableSql); } } } diff --git a/silvermax.FlashCards/DtOs/StudySessionDto.cs b/silvermax.FlashCards/DtOs/StudySessionDto.cs new file mode 100644 index 00000000..96cc67a3 --- /dev/null +++ b/silvermax.FlashCards/DtOs/StudySessionDto.cs @@ -0,0 +1,6 @@ +namespace silvermax.FlashCards.DtOs; + +internal class StudySessionDto +{ + public string? Translation { get; set; } +} diff --git a/silvermax.FlashCards/Models/Session.cs b/silvermax.FlashCards/Models/Session.cs new file mode 100644 index 00000000..242b2b0d --- /dev/null +++ b/silvermax.FlashCards/Models/Session.cs @@ -0,0 +1,8 @@ +namespace silvermax.FlashCards.Models; + +internal class Session +{ + public int Id { get; set; } + public DateTime Date { get; set; } + public int Score { get; set; } +} diff --git a/silvermax.FlashCards/Program.cs b/silvermax.FlashCards/Program.cs index a3209b4b..eddc4b52 100644 --- a/silvermax.FlashCards/Program.cs +++ b/silvermax.FlashCards/Program.cs @@ -5,7 +5,10 @@ internal class Program private static void Main(string[] args) { UserInterface ui = new(); + DataSeeder seeder = new(); + seeder.Seed(); ui.Start(); + } } \ No newline at end of file diff --git a/silvermax.FlashCards/StudySession.cs b/silvermax.FlashCards/StudySession.cs new file mode 100644 index 00000000..9c4ddfe6 --- /dev/null +++ b/silvermax.FlashCards/StudySession.cs @@ -0,0 +1,111 @@ +using Dapper; +using silvermax.FlashCards.Controllers; +using silvermax.FlashCards.DtOs; +using silvermax.FlashCards.Models; +using Spectre.Console; +using System.Transactions; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace silvermax.FlashCards; + +internal class StudySession +{ + private readonly DatabaseHelper db = new(); + StacksController controller = new(); + public void Study() + { + AnsiConsole.MarkupLine("[blue]Welcome to the Study Session[/]"); + + controller.GetAllStacks(); + + var choosenStack = AnsiConsole.Ask("Please write the name of the stack to want to stdy on: "); + var numberOfQuestions = AnsiConsole.Ask("What is the number of questions you want to do?: "); + + using (var connection = db.GetConnection()) + { + int stackId = db.GetStackId(choosenStack); + + var allCards = connection.Query("SELECT * FROM Flashcards WHERE StackId = @StackId", new { StackId = stackId }).ToList(); + + if (allCards.Count == 0) + { + AnsiConsole.MarkupLine("[red]No flashcards found in this stack.[/]"); + UIHelper.PressAnyKey(); + return; + } + + var questions = allCards + .OrderBy(_ => Random.Shared.Next()) + .Take(numberOfQuestions) + .ToList(); + + int score = 0; + + foreach (var card in questions) + { + var answer = AnsiConsole.Ask($"[yellow]Translate:[/] [purple]{card.Word}[/] : "); + + if (answer.Trim().Equals(card.Translation, StringComparison.OrdinalIgnoreCase)) + { + AnsiConsole.MarkupLine("[green]Correct![/]"); + score++; + } + else + { + AnsiConsole.MarkupLine($"[red]Wrong! The correct answer was: {card.Translation}[/]"); + } + } + + AnsiConsole.MarkupLine($"\n[blue]Session complete! Score: {score}/{questions.Count}[/]"); + InsertToDb(score); + AnsiConsole.MarkupLine("[green]Session successfully saved in the database[/]"); + UIHelper.PressAnyKey(); + } + } + + public void ViewStudySessionData() + { + using (var connection = db.GetConnection()) + { + var table = new Table(); + table.Border = TableBorder.Rounded; + + table.AddColumn("[yellow]Id[/]"); + table.AddColumn("[yellow]Date[/]"); + table.AddColumn("[yellow]Score[/]"); + + var sessions = connection.Query("SELECT * FROM Sessions").ToList(); + + if (sessions.Count <= 0) + { + AnsiConsole.MarkupLine("[red]No rows found in the database[/]"); + UIHelper.PressAnyKey(); + return; + } + + foreach (var session in sessions) + { + table.AddRow( + session.Id.ToString(), + $"[purple]{session.Date}[/]", + $"[purple]{session.Score}[/]" + ); + } + + AnsiConsole.Write(table); + } + + UIHelper.PressAnyKey(); + } + + private void InsertToDb(int score) + { + var date = DateTime.UtcNow.Date; + using (var connection = db.GetConnection()) + { + string insertSql = "INSERT INTO Sessions (Date, Score) VALUES (@Date, @Score)"; + + connection.Execute(insertSql, new { Date = date, Score = score }); + } + } +} diff --git a/silvermax.FlashCards/UserInterface.cs b/silvermax.FlashCards/UserInterface.cs index 7fc53af1..c06822d2 100644 --- a/silvermax.FlashCards/UserInterface.cs +++ b/silvermax.FlashCards/UserInterface.cs @@ -11,6 +11,7 @@ public void Start() DatabaseController controller = new(); FlashCardController flashCardController = new(); StacksController stacksController = new(); + StudySession session = new(); controller.InitTables(); @@ -35,6 +36,14 @@ public void Start() stacksController.Start(); break; + case MenuOptions.Study: + session.Study(); + break; + + case MenuOptions.ViewStudySessionData: + session.ViewStudySessionData(); + break; + case MenuOptions.exit: openApp = false; break;