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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Solution>
<Project Path="CodingTracker.CSharpAcademy-Learner/CodingTracker.CSharpAcademy-Learner.csproj" />
</Solution>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>CodingTracker.CSharpAcademy_Learner</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.79" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.9" />
<PackageReference Include="Spectre.Console" Version="0.57.1" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Text;

namespace CodingTracker.CSharpAcademy_Learner
{
public static class Configuration
{
private static readonly IConfigurationRoot _config = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory).AddJsonFile("appsettings.json", false, true).Build();

public static string GetConnectionString()
{
return _config.GetConnectionString("DefaultConnection") ?? "Data Source=coding-tracker.db";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using CodingTracker.CSharpAcademy_Learner.Models;
using Dapper;
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.Text;

namespace CodingTracker.CSharpAcademy_Learner.Controllers
{
internal class CodingController
{
internal List<CodingSession> GetAllSessions()
{
using (var connection = new SqliteConnection(Configuration.GetConnectionString()))
{
var querySQL = "SELECT * FROM coding_sessions";
var sessions = connection.Query<CodingSession>(querySQL).ToList();

return sessions;
}
}

internal void InsertSession(string startTime, string endTime, int duration)
{
using (var connection = new SqliteConnection(Configuration.GetConnectionString()))
{
var parameters = new { StartTime = startTime, EndTime = endTime, Duration = duration };
var insertSQL = "INSERT INTO coding_sessions(StartTime, EndTime, Duration) VALUES (@StartTime, @EndTime, @Duration)";
connection.Execute(insertSQL, parameters);
}
}

internal void UpdateSession(int sessionId, string startTime, string endTime, int duration)
{
using (var connection = new SqliteConnection(Configuration.GetConnectionString()))
{
var parameters = new { Id = sessionId, StartTime = startTime, EndTime = endTime, Duration = duration };
var updateSQL = "UPDATE coding_sessions SET StartTime = @StartTime, EndTime = @EndTime, Duration = @Duration WHERE Id = @Id";
connection.Execute(updateSQL, parameters);
}
}

internal void DeleteSession(int sessionId)
{
using (var connection = new SqliteConnection(Configuration.GetConnectionString()))
{
var parameters = new { Id = sessionId };
var deleteSQL = "DELETE FROM coding_sessions WHERE Id = @Id";
connection.Execute(deleteSQL, parameters);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Dapper;
using Microsoft.Data.Sqlite;

namespace CodingTracker.CSharpAcademy_Learner
{
public static class DatabaseManager
{
public static void InitializeDatabase()
{
var createTableSQL = @"CREATE TABLE IF NOT EXISTS coding_sessions (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
StartTime TEXT,
EndTime TEXT,
Duration INTEGER
)";

using (var connection = new SqliteConnection(Configuration.GetConnectionString()))
{
connection.Execute(createTableSQL);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Text;

namespace CodingTracker.CSharpAcademy_Learner
{
internal static class EnumExtensions
{
internal static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType().GetMember(enumValue.ToString()).FirstOrDefault()?.GetCustomAttribute<DisplayAttribute>()?.GetName() ?? enumValue.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;

namespace CodingTracker.CSharpAcademy_Learner
{
internal class Enums
{
internal enum MenuAction
{
[Display(Name = "View Records")]
ViewRecords,

[Display(Name = "Add Record")]
AddRecord,

[Display(Name = "Update Record")]
UpdateRecord,

[Display(Name = "Delete Record")]
DeleteRecord,

[Display(Name = "Stopwatch")]
StopwatchService,
Exit
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace CodingTracker.CSharpAcademy_Learner.Models
{
internal class CodingSession
{
public int Id { get; set; }
public string StartTime { get; set; } = string.Empty;
public string EndTime { get; set; } = string.Empty;
public int Duration { get; set; } = 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using CodingTracker.CSharpAcademy_Learner;
using CodingTracker.CSharpAcademy_Learner.Controllers;

DatabaseManager.InitializeDatabase();
var codingController = new CodingController();
var userInterface = new UserInterface(codingController);
userInterface.MainMenu();
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Coding Tracker

A console-based CRUD application to track coding sessions, written in C# and powered by SQLite.

## How It Works

The application connects to a local SQLite database using Dapper. Upon the very first execution, it automatically creates the coding_sessions table.
The user can then add, view, update and delete records via the console.

## Challenges Completed

1. **Stopwatch**: Added the functionality of tracking the coding time via a stopwatch so the user can track the session as it happens.

### What Was Easy?

A lot of the concepts were carried on from the Habit Tracker, although they had to be separated instead of it all being in one big Program.cs file.

### What Was Hard

This was the first project without following a video tutorial, so I had find things out by searching online and reading through documentation. There were also quite a few new concepts.

### What I Learned

I got a better grasp of separation of concerns and how code in different files can interact with each other.

### Extra Notes

I probably got a bit acrried away with some things. For example, I preferred to use Enums for the menu options instead of hardcoded strings, this introduced a new issue of having the menu options look good, as Enums can't have spaces. I managed to find a way to use the Display Attribute instead. Also, I tried to make the application what I think is a bit more user friendly by giving the user the option to Escape to the main menu after selecting a menu option, in case they selected the wrong option.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using CodingTracker.CSharpAcademy_Learner.Controllers;
using Spectre.Console;
using System.Diagnostics;

namespace CodingTracker.CSharpAcademy_Learner
{
internal class StopwatchService
{
private readonly CodingController _codingController;
public StopwatchService(CodingController codingController)
{
_codingController = codingController;
}
internal void TrackSession()
{

if (!UserInterfaceHelpers.WaitForEnterOrEscape_CheckForEnter("Tracking live session"))
{
return;
}

UserInterfaceHelpers.WaitForSpecificKey(ConsoleKey.Enter, "Press [green]ENTER[/] to start tracking.");

var stopwatch = Stopwatch.StartNew();
var startTime = DateTime.Now;

AnsiConsole.Status()
.Start("Tracking session... Press [green]ENTER[/] to stop...", ctx =>
{
while (!Console.KeyAvailable || Console.ReadKey(true).Key != ConsoleKey.Enter)
{
ctx.Status($"Tracking session... Elapsed time: {stopwatch.Elapsed:hh\\:mm\\:ss}... Press [green]ENTER[/] to stop...");
Thread.Sleep(500);
}
});

stopwatch.Stop();
var endTime = DateTime.Now;

var formattedStart = startTime.ToString(Validation.DatabaseDateFormat);
var formattedEnd = endTime.ToString(Validation.DatabaseDateFormat);
var duration = Validation.CalculateDurationInSeconds(startTime, endTime);

AnsiConsole.MarkupLine($"STOPPED. Start time: {formattedStart}. End time: {formattedEnd}. Duration: {duration} seconds");

if (AnsiConsole.Confirm("Do you want to save this session?"))
{
_codingController.InsertSession(formattedStart, formattedEnd, duration);
}
else
{
return;
}

Console.ReadKey();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using CodingTracker.CSharpAcademy_Learner.Models;
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Text;

namespace CodingTracker.CSharpAcademy_Learner
{
internal static class TableVisualisationEngine
{
internal static void DisplaySessions(List<CodingSession> sessions)
{
var table = new Table();
table.Border(TableBorder.Rounded);

table.AddColumn("ID");
table.AddColumn("Start Date");
table.AddColumn("End Date");
table.AddColumn("Duration");

foreach(var session in sessions)
{
table.AddRow(
session.Id.ToString(),
session.StartTime,
session.EndTime,
Validation.ShowDurationInFriendlyFormat(session.Duration)
);
}

AnsiConsole.Write(table);
}
}
}
Loading