diff --git a/HABITTRACKER/HABIT TRACKER.docx b/HABITTRACKER/HABIT TRACKER.docx new file mode 100644 index 00000000..b088e5b9 Binary files /dev/null and b/HABITTRACKER/HABIT TRACKER.docx differ diff --git a/HABITTRACKER/HABITTRACKER.UnitTesting.UnitTests/HABITTRACKER.UnitTesting.UnitTests.csproj b/HABITTRACKER/HABITTRACKER.UnitTesting.UnitTests/HABITTRACKER.UnitTesting.UnitTests.csproj new file mode 100644 index 00000000..c4b3d04c --- /dev/null +++ b/HABITTRACKER/HABITTRACKER.UnitTesting.UnitTests/HABITTRACKER.UnitTesting.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/HABITTRACKER/HABITTRACKER.UnitTesting.UnitTests/ValidatorInputTests.cs b/HABITTRACKER/HABITTRACKER.UnitTesting.UnitTests/ValidatorInputTests.cs new file mode 100644 index 00000000..25eeadca --- /dev/null +++ b/HABITTRACKER/HABITTRACKER.UnitTesting.UnitTests/ValidatorInputTests.cs @@ -0,0 +1,121 @@ +using FluentAssertions; +using Habit_tracker; +using System.Diagnostics.Metrics; +using Xunit; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace HABITTRACKER.UnitTesting.UnitTests; + +public class ValidatorInputTests +{ + [Theory] + [InlineData("11-11-11", "11-11-11")] + [InlineData(" 11-11-11", "11-11-11")] + [InlineData("11-11-11 ", "11-11-11")] + [InlineData("01/01/00\n01-01-00", "01-01-00")] + [InlineData("a\n4\n31-12-99", "31-12-99")] + public void CorrectDateInput_ReturnCorrectDate_AndWhitInvalidInput(string inputSequence, string expected) + { + // Arrange + var input = new StringReader(inputSequence); + + // Act + var result = InputInsert.GetDateInput(input); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void CorrectDateInput_WhenInputIs0_ReturnToMainMenu() + { + // Arrange + var input = new StringReader("0"); + + // Act + var result = InputInsert.GetDateInput(input); + + // Assert + result.Should().Be("0"); + } + + [Fact] + public void CorrectDateInput_ReturnDateExactFormat_WhenUserAddSpace() + { + // Arrange + var input = new StringReader(" 11-11-11"); + + // Act + var result = InputInsert.GetDateInput(input); + + // Assert + result.Should().Be("11-11-11"); + } + + [Fact] + public void CorrectDateInput_ReturnDateExactFormat_WhenUserAddSpaceAfterDate() + { + // Arrange + var input = new StringReader("11-11-11 "); + + // Act + var result = InputInsert.GetDateInput(input); + + // Assert + result.Should().Be("11-11-11"); + } + + [Theory] + [InlineData("-5\n5", 5)] + [InlineData("-5\na\nA\n11-11-11\n8", 8)] + [InlineData("a\nA\n11-11-11\n0", 0)] + [InlineData("5 \n", 5)] + [InlineData(" 5\n", 5)] + public void CorrectNumberInput_ReturnNumber_WhitInvalidInput_AndReturnExactNumber(string inputSequence, int exepcted) + { + // Arrange + var inputNumber = new StringReader(inputSequence); + + // Act + var result = InputInsert.GetNumberInput("ENTER A NUMBER.", inputNumber); + + // Assert + result.Should().Be(exepcted); + } + + [Theory] + [InlineData("TEXT", "TEXT")] + [InlineData(" TEXT", "TEXT")] + [InlineData("TEXT ", "TEXT")] + [InlineData("\n\nHABIT", "HABIT")] + [InlineData("4\nNEW HABIT", "NEW HABIT")] + public void CorrectNewHabitInput_ReturnNewHabit_WhitInvalidInput(string inputSequence, string exepcted) + { + // Arrange + var inputHabit = new StringReader(inputSequence); + + // Act + var result = InputInsert.GetNewHabitInput(inputHabit); + + // Assert + result.Should().Be(exepcted); + } + + [Theory] + [InlineData("LITERS", "LITERS")] + [InlineData(" LITERS", "LITERS")] + [InlineData("LITERS ", "LITERS")] + [InlineData("\n\nKILOGRAMS", "KILOGRAMS")] + [InlineData("4\nGRAMS", "GRAMS")] + public void CorrectNewUnitOfMeasureInput_ReturnNewUnitOfMeasure_WhitInvalidInput(string inputSequence, string exepcted) + { + // Arrange + var inputUnitOfMeasure = new StringReader(inputSequence); + + // Act + var result = InputInsert.GetNewUnitOfMeasureInput(inputUnitOfMeasure); + + // Assert + result.Should().Be(exepcted); + } +} diff --git a/HABITTRACKER/Habit-tracker/1.png b/HABITTRACKER/Habit-tracker/1.png new file mode 100644 index 00000000..a701c1d2 Binary files /dev/null and b/HABITTRACKER/Habit-tracker/1.png differ diff --git a/HABITTRACKER/Habit-tracker/2.png b/HABITTRACKER/Habit-tracker/2.png new file mode 100644 index 00000000..8a59fc1c Binary files /dev/null and b/HABITTRACKER/Habit-tracker/2.png differ diff --git a/HABITTRACKER/Habit-tracker/3.png b/HABITTRACKER/Habit-tracker/3.png new file mode 100644 index 00000000..6244200f Binary files /dev/null and b/HABITTRACKER/Habit-tracker/3.png differ diff --git a/HABITTRACKER/Habit-tracker/4.png b/HABITTRACKER/Habit-tracker/4.png new file mode 100644 index 00000000..606b530f Binary files /dev/null and b/HABITTRACKER/Habit-tracker/4.png differ diff --git a/HABITTRACKER/Habit-tracker/5.png b/HABITTRACKER/Habit-tracker/5.png new file mode 100644 index 00000000..158276eb Binary files /dev/null and b/HABITTRACKER/Habit-tracker/5.png differ diff --git a/HABITTRACKER/Habit-tracker/DatabaseManager.cs b/HABITTRACKER/Habit-tracker/DatabaseManager.cs new file mode 100644 index 00000000..8c1dade4 --- /dev/null +++ b/HABITTRACKER/Habit-tracker/DatabaseManager.cs @@ -0,0 +1,431 @@ +using Microsoft.Data.Sqlite; +using System.Globalization; + +namespace Habit_tracker; + +public class DatabaseManager +{ + static string connectionString = @"Data Source=habit-tracker.db"; + + public static void Register() + { + Console.Clear(); + string nameHabit = InputInsert.GetNewHabitInput(Console.In); + + if (nameHabit == "0") return; + + string unitOfMeasure = InputInsert.GetNewUnitOfMeasureInput(Console.In); + + if (unitOfMeasure == "0") return; + + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + tableCmd.CommandText = + $"INSERT INTO Register_Habit(name_habit, unit_of_measurement) VALUES('{nameHabit}', '{unitOfMeasure}')"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + } + } + + public static void Insert() + { + Console.Clear(); + + GetAllHabit(); + + int chooseHabit = InputInsert.GetHabitInput("CHOOSE A YOUR HABIT THAT YOU WOULD LIKE TO ADD. TYPE 0 TO RETURN TO MAIN MENU."); + if (chooseHabit == 0) return; + + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + tableCmd.CommandText = + $"SELECT EXISTS (SELECT 1 FROM Register_Habit WHERE name_habit = '{chooseHabit}'"; + } + + string date = InputInsert.GetDateInput(Console.In); + + if (int.TryParse(date, out int insertDate)) + { + if (insertDate == 0) return; + } + + int quantity = InputInsert.GetNumberInput("\nPLEAE ENTER HOW MANY TIMES YOU HAVE PERFORMED THE HABIT. TYPE 0 TO RETURN TO MAIN MENU.", Console.In); + + if (quantity == 0) return; + + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + tableCmd.CommandText = + $"INSERT INTO habit(date, quantity, HabitId) VALUES('{date}', {quantity}, {chooseHabit})"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + } + } + + public static void GetAllHabit() + { + Console.Clear(); + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + tableCmd.CommandText = "SELECT * FROM Register_Habit"; + + List tableData = new List(); + + SqliteDataReader reader = tableCmd.ExecuteReader(); // Execute the query and get a reader; ExecuteReader is used for SELECT statements + + if (reader.HasRows) // Check if there are any rows + { + // Read each row + while (reader.Read()) + { + tableData.Add( + new RegisterHabit + { + Id = reader.GetInt32(0), // Get the value of the first column (Id) + NameHabit = reader.GetString(1), // Get the value of the second column (NameHabit) + UnitOfMeasurement = reader.GetString(2) // Get the value of the third column (UnitOfMeasurement) + }); + } + } + else + { + Console.WriteLine("No rows found."); + } + + connection.Close(); + + WriteLine("-------------------------------------------------------"); + foreach (var db in tableData) + { + WriteLine($"ID: {db.Id} - {db.NameHabit} - UNIT OF MEASURE: {db.UnitOfMeasurement}"); + } + WriteLine("-------------------------------------------------------"); + } + } + + public static void GetAllrecords() + { + Console.Clear(); + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + + // SQL query to select all records + // h => habit table alias; r => register_habit table alias + tableCmd.CommandText = @" + SELECT + h.Id, + h.Date, + h.Quantity, + r.Name_Habit + FROM habit h INNER JOIN register_habit r ON h.HabitId = r.Id"; + + List tableData = new List(); + + SqliteDataReader reader = tableCmd.ExecuteReader(); // Execute the query and get a reader; ExecuteReader is used for SELECT statements + + if (reader.HasRows) // Check if there are any rows + { + // Read each row + while (reader.Read()) + { + tableData.Add( + new RegisterHabit + { + Id = reader.GetInt32(0), // Get the value of the first column (Id) + Date = DateTime.ParseExact(reader.GetString(1), "dd-MM-yy", new CultureInfo("en-US"), DateTimeStyles.None), // Get the value of the second column (Date) + Quantity = reader.GetInt32(2), // Get the value of the third column (Quantity) + NameHabit = reader.GetString(3) // Get the value of the fourth column (NameHabit) + }); + } + } + else + { + Console.WriteLine("No rows found."); + } + + connection.Close(); + + WriteLine("-------------------------------------------------------"); + foreach (var db in tableData) + { + WriteLine($"ID: {db.Id} - {db.Date.ToString("dd-MM-yy")} - QUANTITY: {db.Quantity} - NAME HABIT: {db.NameHabit}"); + } + WriteLine("-------------------------------------------------------"); + } + } + + public static void Update() + { + GetAllrecords(); + bool continueUpdate = true; + + while (continueUpdate) + { + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + tableCmd.CommandText = "SELECT * FROM habit"; // SQL query to select all records + SqliteDataReader reader = tableCmd.ExecuteReader(); + + if (!reader.HasRows) // Check if there are any rows + { + WriteLine("\nNO RECORDS TO UPDATE."); + connection.Close(); + continueUpdate = false; + } + + else + { + var recordId = InputInsert.GetNumberInput("\nPLEASE ENTER THE ID OF THE RECORD YOU WANT TO UPDATE. TYPE 0 TO RETURN TO MAIN MENU.", Console.In); + if (recordId == 0) + { + connection.Close(); + return; + } + else + { + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"SELECT EXISTS (SELECT 1 FROM habit WHERE Id = {recordId})"; + + var checkQuery = Convert.ToInt32(tableCmd.ExecuteScalar()); // ExecuteScalar is used to get a single value, it used to check if the record exists + if (checkQuery == 0) + { + WriteLine("\nRECORD NOT FOUND."); + connection.Close(); + Update(); + } + + WriteLine("\nTYPE 1 IF YOU WANT UPDATE A NAME HABIT. TYPE 2 TO CHANGE DATE. TYPE 0 TO RETURN TO MAIN MENU."); + string? updateInput = ReadLine(); + + while (!int.TryParse(updateInput, out _) || Convert.ToInt32(updateInput) < 0) + { + WriteLine("\nINVALID INPUT. PLEASE TRY AGAIN."); + connection.Close(); + updateInput = ReadLine(); + } + + if (updateInput == "0") + { + connection.Close(); + return; + } + + if (updateInput == "1") + { + WriteLine("\nUPDATING A NAME HABIT. TYPE '0' TO RETURN TO MAIN MENU."); + GetAllHabit(); + + int recordHabitId = InputInsert.GetNumberInput("\nPLEASE ENTER THE ID OF THE HABIT YOU WANT TO UPDATE. TYPE 0 TO RETURN TO MAIN MENU.", Console.In); + if (recordHabitId == 0) + { + connection.Close(); + return; + } + + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"SELECT EXISTS (SELECT 1 FROM Register_Habit WHERE Id = {recordHabitId})"; + + var checkHabitQuery = Convert.ToInt32(tableCmd.ExecuteScalar()); + if (checkHabitQuery == 0) + { + WriteLine("\nHABIT NOT FOUND."); + connection.Close(); + Update(); + } + + string newNameHabit = InputInsert.GetNewHabitInput(Console.In); + if (newNameHabit == "0") + { + connection.Close(); + return; + } + + string newUnitOfMeasure = InputInsert.GetNewUnitOfMeasureInput(Console.In); + if (newUnitOfMeasure == "0") + { + connection.Close(); + return; + } + + tableCmd.CommandText = + $"UPDATE Register_Habit SET name_habit = '{newNameHabit}', unit_of_measurement = '{newUnitOfMeasure}' WHERE Id = {recordHabitId}"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + continueUpdate = false; + } + + if (updateInput == "2") + { + string UpDate = InputInsert.GetDateInput(); + + if (int.TryParse(UpDate, out int UpMenu)) + { + if (UpMenu == 0) + { + connection.Close(); + return; + } + } + + int UpQuantity = InputInsert.GetNumberInput("\nPLEAE ENTER HOW MANY TIMES YOU HAVE PERFORMED THE HABIT. TYPE 0 TO RETURN TO MAIN MENU."); + + if (UpQuantity == 0) + { + connection.Close(); + return; + } + + tableCmd.CommandText = $"UPDATE habit SET Date = '{UpDate}', Quantity = {UpQuantity} WHERE Id = {recordId}"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + continueUpdate = false; + } + } + } + } + } + } + + public static void Delete() + { + Console.Clear(); + GetAllrecords(); + + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + + tableCmd.CommandText = "SELECT * FROM habit"; // SQL query to select all records + + SqliteDataReader reader = tableCmd.ExecuteReader(); + + if (!reader.HasRows) // Check if there are any rows + { + WriteLine("\nNO RECORDS TO DELETE."); + connection.Close(); + return; + } + else + { + WriteLine("\nTYPE 'D' TO DELETE ALL YOUR HABITS. PRESS 'R' TO DELETE THE NAME HABIT. PRESS 'C' TO SELECT THE RECORD. PRESS 0 TO RETURN TO MAIN MENU."); + + string? delInput = ReadLine()?.ToUpper(); + bool inputValid = false; + + do + { + if (Int32.TryParse(delInput, out int numberInput)) + { + if (numberInput == 0) + { + connection.Close(); + return; + } + } + + if ((delInput == "D")) + { + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"DELETE FROM habit"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + inputValid = true; + + } + else if (delInput == "R") + { + Console.Clear(); + GetAllHabit(); + + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"SELECT * FROM Register_Habit"; + + SqliteDataReader readerHabit = tableCmd.ExecuteReader(); + + if (!readerHabit.HasRows) + { + WriteLine("\nNO HABITS TO DELETE."); + connection.Close(); + return; + } + + else + { + WriteLine("TYPE 'D' TO DELETE ALL HABIT NAMES. TYPE 'C' TO SELECT THE RECROD. TYPE 0 TO RETURN TO MAIN MENU. "); + string? delHabitInput = ReadLine()?.ToUpper(); + + if (int.TryParse(delHabitInput, out int habitInput)) + { + if (habitInput == 0) + { + connection.Close(); + return; + } + } + + if (delHabitInput == "D") + { + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"DELETE FROM Register_Habit"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + inputValid = true; + } + else if (delHabitInput == "C") + { + int recordHabitId = InputInsert.GetNumberInput("\nPLEASE ENTER THE ID OF THE RECORD THAT YOU WANT TO DELETE. TYOE 0 TO RETURN TO MAIN MENU."); + if (recordHabitId == 0) + { + connection.Close(); + return; + } + + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"DELETE FROM Register_Habit WHERE Id = {recordHabitId}"; + tableCmd.ExecuteNonQuery(); + connection.Close(); + inputValid = true; + } + } + } + else if (delInput == "C") + { + int recordId = InputInsert.GetNumberInput("" + + "\nPLEASE ENTER THE ID OF THE RECORD YOU WANT TO DELETE. TYPE 0 TO RETURN TO MAIN MENU."); + + if (recordId == 0) + { + connection.Close(); + return; + } + + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"DELETE FROM habit WHERE Id = {recordId}"; + tableCmd.ExecuteNonQuery(); // ExecuteNonQuery is used for DELETE statements + connection.Close(); + inputValid = true; + } + else + { + WriteLine("\nINVALID INPUT. PLEASE TRY AGAIN."); + delInput = ReadLine()?.ToUpper(); + } + } while (!inputValid); + + } + } + } +} \ No newline at end of file diff --git a/HABITTRACKER/Habit-tracker/Habit-tracker.csproj b/HABITTRACKER/Habit-tracker/Habit-tracker.csproj new file mode 100644 index 00000000..4340bb85 --- /dev/null +++ b/HABITTRACKER/Habit-tracker/Habit-tracker.csproj @@ -0,0 +1,23 @@ + + + + Exe + net9.0 + Habit_tracker + enable + enable + + + + + + + + + + + + + + + diff --git a/HABITTRACKER/Habit-tracker/Habit-tracker.slnx b/HABITTRACKER/Habit-tracker/Habit-tracker.slnx new file mode 100644 index 00000000..afc0875e --- /dev/null +++ b/HABITTRACKER/Habit-tracker/Habit-tracker.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/HABITTRACKER/Habit-tracker/InputInsert.cs b/HABITTRACKER/Habit-tracker/InputInsert.cs new file mode 100644 index 00000000..64a576b8 --- /dev/null +++ b/HABITTRACKER/Habit-tracker/InputInsert.cs @@ -0,0 +1,109 @@ +using System.Globalization; +using System.Reflection.Metadata.Ecma335; + +namespace Habit_tracker; + +public class InputInsert +{ + public static string GetNewHabitInput(TextReader? reader = null) + { + reader ??= Console.In; + + WriteLine("\nPLEASE ENTER THE NAME OF THE HABIT YOU WANT TO REGISTER. TYPE 0 TO RETURN TO MAIN MENU."); + string? userInputNewHabit = reader?.ReadLine()?.Trim(); + if (userInputNewHabit == "0") return "0"; + + while (int.TryParse(userInputNewHabit, out _) || string.IsNullOrEmpty(userInputNewHabit)) + { + WriteLine("\nINVALID INPUT. PLEASE ENTER A VALID HABIT NAME. TYPE 0 TO RETURN TO MAIN MENU."); + userInputNewHabit = reader?.ReadLine()?.Trim(); + if (userInputNewHabit == "0") return "0"; + } + + return userInputNewHabit!; // the ! operator is used to indicate that userInputDate is not null here + } + + public static string GetNewUnitOfMeasureInput(TextReader? reader = null) + { + reader ??= Console.In; + + WriteLine("\nPLEASE ENTER THE UNIT OF MEASUREMENT FOR THE HABIT YOU WANT TO REGISTER. TYPE 0 TO RETURN TO MAIN MENU."); + string? userInputNewUnitOfMeasure = reader?.ReadLine()?.Trim(); + + if (userInputNewUnitOfMeasure == "0") return "0"; + + while (int.TryParse(userInputNewUnitOfMeasure, out _) || string.IsNullOrEmpty(userInputNewUnitOfMeasure)) + { + WriteLine("\nINVALID INPUT. PLEASE ENTER A VALID UNIT OF MEASUREMENT. TYPE 0 TO RETURN TO MAIN MENU."); + userInputNewUnitOfMeasure = reader?.ReadLine()?.Trim(); + if (userInputNewUnitOfMeasure == "0") return "0"; + } + + return userInputNewUnitOfMeasure!; + } + + public static int GetHabitInput(string message, TextReader? reader = null) + { + reader ??= Console.In; + + WriteLine(message); + string? userInputHabit = reader?.ReadLine()?.Trim(); + + if (userInputHabit == "0") return 0; + + while (!Int32.TryParse(userInputHabit, out _) || Convert.ToInt32(userInputHabit) < 0) + { + WriteLine("\nINVALID INPUT. PLEASE ENTER A VALID HABIT NAME. TYPE 0 TO RETURN TO MAIN MENU."); + userInputHabit = reader?.ReadLine()?.Trim(); + if (userInputHabit == "0") return 0; + } + int finalInputHabit = Convert.ToInt32(userInputHabit); + return finalInputHabit!; + } + + public static string GetDateInput(TextReader? reader = null) + { + reader ??= Console.In; // if reader is null, assign Console.In to it + + WriteLine("\nPLEASE ENTER DATE (dd-MM-yy). TYPE 0 TO RETURN TO MAIN MENU. "); + string? userInputDate = reader?.ReadLine()?.Trim(); + if (userInputDate == "0") return "0"; + + while (!DateTime.TryParseExact(userInputDate, "dd-MM-yy", new CultureInfo("it-IT"), + DateTimeStyles.None, out _)) + { + WriteLine("\nINVALID DATE FORMAT. PLEASE ENTER DATE IN FORMAT (dd-MM-yy). TYPE 0 TO RETURN TO MAIN MENU."); + userInputDate = reader?.ReadLine()?.Trim(); + if (userInputDate == "0") return "0"; + } + return userInputDate!; + } + + public static int GetNumberInput(string message, TextReader? reader = null) + { + reader ??= Console.In; + + WriteLine(message); + string? userInputNumber = reader?.ReadLine()?.Trim(); + if (userInputNumber == "0") return 0; + + while (!Int32.TryParse(userInputNumber, out _) || Convert.ToInt32(userInputNumber) < 0) + { + WriteLine("\nINVALID NUMBER. PLEASE ENTER A VALID NUMBER. TYPE 0 TO RETURN TO MAIN MENU."); + userInputNumber = reader?.ReadLine()?.Trim(); + if (userInputNumber == "0") return 0; + } + + int finalInputNumber = Convert.ToInt32(userInputNumber); + return finalInputNumber!; + } +} + +public class RegisterHabit +{ + public int Id { get; set; } + public DateTime Date { get; set; } + public int Quantity { get; set; } + public string NameHabit { get; set; } + public string UnitOfMeasurement { get; set; } +} \ No newline at end of file diff --git a/HABITTRACKER/Habit-tracker/Interfaccia_utente.png b/HABITTRACKER/Habit-tracker/Interfaccia_utente.png new file mode 100644 index 00000000..1f0446cb Binary files /dev/null and b/HABITTRACKER/Habit-tracker/Interfaccia_utente.png differ diff --git a/HABITTRACKER/Habit-tracker/Menu.cs b/HABITTRACKER/Habit-tracker/Menu.cs new file mode 100644 index 00000000..dfeef88d --- /dev/null +++ b/HABITTRACKER/Habit-tracker/Menu.cs @@ -0,0 +1,51 @@ +namespace Habit_tracker; + +public class Menu +{ + public static void GetUserInput() + { + bool closeApp = false; + + while (!closeApp) + { + WriteLine("\nWELCOME IN YOUR HABIT TRACKER!"); + WriteLine("\nWHAT WOULD YOU LIKE TO DO?"); + WriteLine("\nTYPE 0 TO CLOSE APP"); + WriteLine("TYPE 1 TO A REGISTER A NEW HABIT"); + WriteLine("TYPE 2 TO ADD A YOUR HABIT"); + WriteLine("TYPE 3 TO UPDATE RECORD"); + WriteLine("TYPE 4 TO DELETE RECORD"); + WriteLine("TYPE 5 TO SEE ALL RECORDS"); + + string? userInput = ReadLine(); + + while (!Int32.TryParse(userInput, out _) || Convert.ToInt32(userInput) < 0 || Convert.ToInt32(userInput) > 5) + { + WriteLine("\nINVALID INPUT, PLEASE TRY AGAIN:"); + userInput = ReadLine(); + } + + switch (userInput) + { + case "0": + closeApp = true; + break; + case "1": + DatabaseManager.Register(); + break; + case "2": + DatabaseManager.Insert(); + break; + case"3": + DatabaseManager.Update(); + break; + case "4": + DatabaseManager.Delete(); + break; + case "5": + DatabaseManager.GetAllrecords(); + break; + } + } + } +} \ No newline at end of file diff --git a/HABITTRACKER/Habit-tracker/Program.cs b/HABITTRACKER/Habit-tracker/Program.cs new file mode 100644 index 00000000..4c32259e --- /dev/null +++ b/HABITTRACKER/Habit-tracker/Program.cs @@ -0,0 +1,114 @@ +using Microsoft.Data.Sqlite; +using System.Collections.Specialized; + +namespace Habit_tracker +{ + internal class Program + { + static string connectionString = @"Data Source=Habit-tracker.db"; + static void Main(string[] args) + { + using (var connection = new SqliteConnection(connectionString)) + { + connection.Open(); + var tableCmd = connection.CreateCommand(); + tableCmd.CommandText = @" + CREATE TABLE IF NOT EXISTS Habit ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Date TEXT, + Quantity INTEGER, + HabitId INTEGER); + + CREATE TABLE IF NOT EXISTS Register_Habit ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Name_Habit TEXT, + Unit_Of_Measurement TEXT)"; + + tableCmd.ExecuteNonQuery(); + connection.Close(); + + #region To register some habits and some records for testing purposes + + (string Col1, string Col2)[] arrayDb = new (string, string)[] + { ("DRINKING COFFEE", "NUMBER OF CUPS"), + ("READING BOOKS", "NUMBER OF PAGES"), + ("RUNNING", "KILOMETERS"), + ("MEDITATION", "MINUTES"), + ("WATER INTAKE", "LITERS") }; + + connection.Open(); + + // check if the Register_habit table is empty + string checkQuery = "SELECT COUNT(*) FROM Register_Habit"; + long count = 0; + + using (var checkCommand = new SqliteCommand(checkQuery, connection)) + { + // ExecuteScalar can be null or return DBNull, so we need to handle that + object? result = checkCommand.ExecuteScalar(); + count = (result != null && result != DBNull.Value) ? Convert.ToInt64(result) : 0; + } + + if (count == 0) + { + foreach (var dbArray in arrayDb) + { + string query = + $"INSERT INTO Register_Habit (Name_Habit, Unit_Of_Measurement) VALUES(@val1, @val2)"; + + using (var command = new SqliteCommand(query, connection)) + { + command.Parameters.AddWithValue("@val1", dbArray.Col1); // Name_Habit + command.Parameters.AddWithValue("@val2", dbArray.Col2); // Unit_Of_Measurement + + command.ExecuteNonQuery(); + } + } + } + + string checkQueryHabit = "SELECT COUNT(*) FROM Habit"; + long countHabit = 0; + + using (var checkCommand = new SqliteCommand(checkQueryHabit, connection)) + { + object? result = checkCommand.ExecuteScalar(); + countHabit = (result != null && result != DBNull.Value) ? Convert.ToInt64(result) : 0; + } + + if (countHabit == 0) + { + // I take all the habits id from the Register_Habit table + List habitsId = new List(); + connection.Open(); + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = "SELECT Id FROM Register_Habit"; + + SqliteDataReader reader = tableCmd.ExecuteReader(); + while (reader.Read()) + { + habitsId.Add(reader.GetInt32(0)); // Assuming the Id is in the first column + } + + Random random = new Random(); + + foreach (int id in habitsId) + { + for (int i = 0; i < 20; i++) + { + string randomDate = DateTime.Now.AddDays(-random.Next(0, 31)).ToString("dd-MM-yy"); // Random date within the last year + + int randomQuantity = random.Next(1, 11); // Random quantity between 1 and 100 + + tableCmd = connection.CreateCommand(); + tableCmd.CommandText = $"INSERT INTO Habit (Date, Quantity, HabitId) VALUES('{randomDate}', {randomQuantity}, {id})"; + tableCmd.ExecuteNonQuery(); + } + } + } + #endregion + } + + Menu.GetUserInput(); + } + } +} diff --git a/HABITTRACKER/Habit-tracker/README.md b/HABITTRACKER/Habit-tracker/README.md new file mode 100644 index 00000000..ee33157d --- /dev/null +++ b/HABITTRACKER/Habit-tracker/README.md @@ -0,0 +1,59 @@ +# HABIT-TRACKER + +Habit Tracker is my first C# application using SQLite to monitor daily habits. + +It is a full CRUD (Create, Read, Update, Delete) application designed to help users manage and track their progress efficiently. + +## Key features + +- **Habit Registration**: register new habits with specific names and units of measurement. +- **Quantitative Tracking**: all habits are managed based on quantity (e.g., liters of water, kilometers run). +- **CRUD application**: the application allows to add, view, modify and delete habits +- **Persistent Storage**: Uses SQLite via ADO.NET to record and manage data. +- **Auto-Database Initialization**: On startup, the application automatically creates the SQLite database if it doesn't already exist. +- **Seed Data**: The app generates random default habits upon first launch to facilitate immediate testing. +- **Robust Error Handling**: The application is designed to catch and handle errors gracefully. +- **Console UI**: A simple, intuitive, and menu-driven command-line interface. + - user interface + +## Functionality & Usage + +From the main menu, you can navigate the following features: + +- **Registration and insertion** + - **Register a New Habit (Type '1')**: Define a new habit by entering its name and unit of measure. + - To register new habit + - **Add a Record (Type '2')**: Log an entry for a specific habit by entering the date and quantity. + - habit insertion + - **Date Format**: The application ensures that dates are entered in the correct dd-MM-yy format. + - **Numeric Validation**: It verifies that only numbers are entered for quantities, preventing invalid data entry. + - **Seamless Navigation**: The user can return to the main menu at any time during the process. + +- **Update record** + - **Update Records (Type '3')**: Modify existing data. You can change the habit name, update a specific date, or return to the menu. + - Update habits + - The program offers flexible update options to keep your data accurate: + - **Change Habit Name**: Modify the name of an existing habit. + - **Update Date Only**: Adjust the date of a specific record without changing other details. + - **Menu Navigation**: You can exit the update process and return to the main menu at any time. + +- **Delete record** + - **Delete Records (Type '4')**: Remove data with multiple options: delete all habits, delete a specific record, or delete a single habit category. + - Delete habits + - The application provides several ways to manage and remove your data: + - **Delete All Records**: Wipe all habit data from the database. + - **Delete All Entries for a Specific Habit**: Clear the history of a single habit while keeping the habit itself. + - **Select and Delete a Specific Habit**: Remove a habit category and all its associated data. + - **Menu Navigation**: Cancel the operation and return to the main menu. + +- See all records + - **View Records (Type '5')**: Display a complete list of all registered habits and logs. + - See all habits + +## What I Learned + +- Database Integration: I learned how to create, connect, and communicate with a database using ADO.NET. +- Problem Solving: Implementing logic to generate random test records was a rewarding challenge that helped me ensure the app's stability. +- Code Organization: I improved my use of classes and methods to keep the codebase clean, modular, and maintainable. +- Unit Testing: I learned the importance of writing unit tests to verify that methods behave as expected. + diff --git a/HABITTRACKER/Habit.docx b/HABITTRACKER/Habit.docx new file mode 100644 index 00000000..a9519789 Binary files /dev/null and b/HABITTRACKER/Habit.docx differ