Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*.user
*.userosscache
*.sln.docstates
*.db

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
Expand Down
24 changes: 24 additions & 0 deletions CodeReviews.Console.HabitTracker.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HabitTracker.natholder", "HabitTracker.natholder\HabitTracker.natholder.csproj", "{AA4088A3-C32F-4B29-48E0-339A2F9967C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA4088A3-C32F-4B29-48E0-339A2F9967C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA4088A3-C32F-4B29-48E0-339A2F9967C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA4088A3-C32F-4B29-48E0-339A2F9967C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA4088A3-C32F-4B29-48E0-339A2F9967C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D67F1646-947A-4847-A048-583B3565ABC1}
EndGlobalSection
EndGlobal
14 changes: 14 additions & 0 deletions HabitTracker.natholder/HabitTracker.natholder.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.10" />
</ItemGroup>

</Project>
269 changes: 269 additions & 0 deletions HabitTracker.natholder/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
using Microsoft.Data.Sqlite;

namespace HabitTracker
{

public class Runs
{
public int Id { get; set; }
public DateTime Date { get; set; }
public double Miles { get; set; }
}
class Program
{
static string dbFileName = "HabitTracker.db";
static string connectionString = $"Data Source ={dbFileName}";
static void Main(string[] args)
{
InitDB();
MenuLoop();
}

static void InitDB()
{
try
{
using var connection = new SqliteConnection(connectionString);
connection.Open();
Console.WriteLine($"Database {dbFileName} created or opened successfully.");
CreateTable(connection);
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
static void MenuLoop()
{
string menuText = @"
__ ____ __ ______ __
/ |/ (_) /__ _____ /_ __/________ ______/ /_____ _____
/ /|_/ / / / _ \/ ___/ / / / ___/ __ `/ ___/ //_/ _ \/ ___/
/ / / / / / __(__ ) / / / / / /_/ / /__/ ,< / __/ /
/_/ /_/_/_/\___/____/ /_/ /_/ \__,_/\___/_/|_|\___/_/";
bool running = true;
string? userInput;
int number;
Console.WriteLine(menuText);
while (running)
{
ShowMenu();
userInput = Console.ReadLine();
if (int.TryParse(userInput, out number) && number > 0 && number <= 6)
{
switch (userInput)
{
case "1":
SelectRecords();
break;
case "2":
Insert();
break;
case "3":
Update();
break;
case "4":
Delete();
break;
default:
running = false;
break;
}
}
else
{
Console.WriteLine("Invalid input. Please try again.");
}
}
}
static void CreateTable(SqliteConnection connection)
{
string CreateTableQuery = @"
CREATE TABLE IF NOT EXISTS Habits (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Date DATETIME,
Miles DOUBLE
)";

using var command = new SqliteCommand(CreateTableQuery, connection);
command.ExecuteNonQuery();
Console.WriteLine("Table 'Habits' created or already exists.");
}

static void Insert()
{
DateTime date = GetDate();
double miles = GetMiles();
using var connection = new SqliteConnection(connectionString);
connection.Open();
var insert = connection.CreateCommand();
insert.CommandText = $"INSERT INTO Habits (Date, Miles) VALUES ('{date}', {miles})";
insert.ExecuteNonQuery();
connection.Close();
}

static void SelectRecords()
{
Console.Clear();
using var connection = new SqliteConnection(connectionString);
connection.Open();
var select = connection.CreateCommand();
select.CommandText = $"SELECT * FROM Habits";
List<Runs> tableData = [];
SqliteDataReader reader = select.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
tableData.Add(
new Runs
{
Id = reader.GetInt32(0),
Date = reader.GetDateTime(1),
Miles = reader.GetDouble(2)
}
);
}
}
else
{
Console.WriteLine("No rows found.");
}
connection.Close();

Console.WriteLine("---------------------------------------");
Console.WriteLine(" Id | Date | Miles");
Console.WriteLine("---------------------------------------");
foreach (var row in tableData)
{
Console.WriteLine($"| {row.Id} | {row.Date.ToString("MM-dd-yyyy")} | {row.Miles} |");
}
Console.WriteLine("---------------------------------------");

}

static void Delete()
{
int id = GetId();
using var connection = new SqliteConnection(connectionString);
connection.Open();
var insert = connection.CreateCommand();
insert.CommandText = $"DELETE FROM Habits WHERE Id = {id}";
int rowsAffected = insert.ExecuteNonQuery();

if (rowsAffected > 0)
{
Console.WriteLine($"Successfully deleted record with ID: {id}");
}
else
{
Console.WriteLine($"No record found with ID: {id}");
}

connection.Close();
}

static void Update()
{
Console.Clear();
SelectRecords();

Console.WriteLine("What is the id of the record you want to update?");
int id = GetId();

Console.WriteLine("Enter new values for this record:");
DateTime newDate = GetDate();
double newMiles = GetMiles();

using var connection = new SqliteConnection(connectionString);
connection.Open();
var update = connection.CreateCommand();
update.CommandText = $"UPDATE Habits SET Date = @date, Miles = @miles WHERE Id = {id}";
update.Parameters.AddWithValue("@date", newDate);
update.Parameters.AddWithValue("@miles", newMiles);
int rowsAffected = update.ExecuteNonQuery();

if (rowsAffected > 0)
{
Console.WriteLine($"Successfully updated record with ID: {id}");
}
else
{
Console.WriteLine($"No record found with ID: {id}");
}

connection.Close();
}


static void ShowMenu()
{
Console.WriteLine("1. View");
Console.WriteLine("2. Insert");
Console.WriteLine("3. Update");
Console.WriteLine("4. Delete");
Console.WriteLine("5. Quit");
}

static double GetMiles()
{
double miles;
string? input;
Console.WriteLine("How many miles did you run?");
input = Console.ReadLine();
if (double.TryParse(input, out miles))
{
return miles;
}
else
{
Console.WriteLine("Please enter a valid number");
return GetMiles();
}
}

static int GetId()
{
int id;
string? input;
Console.WriteLine("Enter the id of the record you would like to delete.");
input = Console.ReadLine();
if (int.TryParse(input, out id))
{
return id;
}
else
{
Console.WriteLine("Please enter a valid number");
return GetId();
}
}

static DateTime GetDate()
{
string? input;
DateTime date;
Console.WriteLine("What day was your run? (type 'td' for today's date, or enter date as MM/DD/YYYY):");
input = Console.ReadLine();

if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("Please enter a valid date or 'td' for today.");
return GetDate(); // Recursive call for empty input
}

if (input?.ToLower() == "td")
{
return DateTime.Today;
}

if (DateTime.TryParse(input, out date))
{
return date;
}

Console.WriteLine("Invalid date format. Please try again (e.g., 02/16/2026) or type 'td' for today.");
return GetDate(); // Recursive call for invalid date
}
}
}
50 changes: 50 additions & 0 deletions HabitTracker.natholder/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# HabitTracker Console App

A console application to log occurrences of miles (runs). The app stores entries in a SQLite database using ADO.NET and supports inserting, viewing, updating and deleting records.

---

## Quick overview

- Record format: `Date` (when the habit occurred) and `Miles` (quantity).
- Database: `HabitTracker.db` is created automatically when the app runs.
- Storage access: ADO.NET only (no EF / Dapper). Parameterized SQL is used to prevent injection.

---

## How to run

1. Open a terminal in the project folder `HabitTracker.natholder`.
2. Build & run:

```bash
dotnet run
```

3. The app will create `HabitTracker.db` (if missing) and the `Habits` table.
4. Use the menu to Insert, View, Update, or Delete records.

---

## Features

- Creates a sqlite db and habit table on first run or if they have been deleted.
- Insert records with validated values..
- View (select) all records
- Update an existing record
- Delete a record
- input validation and error handling

---

## Thought process / retrospective

I think that once i got started and made a little bit of progress, that the rest of the project came a lot easier. I had some trouble implementing the view command and had to use the video tutorial. I feel like I am learning though, and am continuting to improve.

---

## Potential improvements (next steps)

- Add unit tests for parsing/validation logic.

---