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.
+ -
+
+## 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.
+ -
+ - **Add a Record (Type '2')**: Log an entry for a specific habit by entering the date and quantity.
+ -
+ - **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.
+ -
+ - 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.
+ -
+ - 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.
+ -
+
+## 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