Skip to content
Closed
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
3 changes: 3 additions & 0 deletions CodingTracker/CodingTracker.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Solution>
<Project Path="CodingTracker/CodingTracker.csproj" />
</Solution>
24 changes: 24 additions & 0 deletions CodingTracker/CodingTracker/CodingTracker.csproj
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>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

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

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

</Project>
128 changes: 128 additions & 0 deletions CodingTracker/CodingTracker/Controllers/CodingController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using CodingTracker.Model;
using Dapper;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Text;


namespace CodingTracker.Controllers
{
internal class CodingController
{
private readonly string _connectionString;

public CodingController()
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();

_connectionString = config.GetConnectionString("DefaultConnection") ?? "Data Source=coding-tracker.db";

InitDatabase();
}

private void InitDatabase()
{
using var connection = new SqliteConnection(_connectionString);
string initSql = @"
CREATE TABLE IF NOT EXISTS CodingSessions(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
StartTime TEXT NOT NULL,
EndTime TEXT NOT NULL
);";
connection.Execute(initSql);
}

public bool InsertSql(CodingSession session)
{
using var connection = new SqliteConnection(_connectionString);
try
{
string sql = @"
INSERT INTO CodingSessions (StartTime, EndTime)
VALUES (@StartTime, @EndTime);";

int rowsAffected = connection.Execute(sql, new
{
StartTime = session.StartTime.ToString("dd-MM-yyyy HH:mm:ss"),
EndTime = session.EndTime.ToString("dd-MM-yyyy HH:mm:ss")
});

return rowsAffected > 0;
}
catch (Exception)
{
return false;
}
}

public List<CodingSession> GetSessionHistory()
{
using var connection = new SqliteConnection(_connectionString);
try
{
string sql = @"
SELECT
Id,
StartTime AS StartTimeString,
EndTime AS EndTimeString
FROM CodingSessions;";

// Dapper makes List<CodingSession>
List<CodingSession> sessionList = connection.Query<CodingSession>(sql).AsList();

return sessionList;
}
catch (Exception)
{
return new List<CodingSession>();
}
}

public bool UpdateSessionHistory(CodingSession session)
{
using var connection = new SqliteConnection(_connectionString);
try
{
string sql = @"
UPDATE CodingSessions
SET StartTime = @StartTimeString,
EndTime = @EndTimeString
WHERE Id = @Id;";

int rowsAffected = connection.Execute(sql, session);

// true if row was affected
return (rowsAffected > 0);
}
catch (Exception)
{
return false;
}
}

public bool DeleteSession(int id)
{
using var connection = new SqliteConnection(_connectionString);
try
{
string sql = @"
DELETE FROM CodingSessions
WHERE Id = @Id;";

int rowsAffected = connection.Execute(sql,new { Id = id });

// true if row was affected
return (rowsAffected > 0);
}
catch (Exception)
{
return false;
}
}

}
}
49 changes: 49 additions & 0 deletions CodingTracker/CodingTracker/Models/CodingSession.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace CodingTracker.Model
{
internal class CodingSession
{
public int? Id { get; set; } // NULL before Sql autoincrement
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public TimeSpan Duration => EndTime - StartTime;

// String Properties
public string StartTimeString { get; set; } = string.Empty;
public string EndTimeString { get; set; } = string.Empty;
public string DurationString => Duration.TotalHours >= 24
? $"{(int)Duration.TotalHours}:{Duration:mm\\:ss}"
: Duration.ToString(@"hh\:mm\:ss");

public CodingSession() { } //without parameter

public CodingSession(DateTime startDate, DateTime endDate, int? id = null)
{
Id = id;
StartTime = startDate;
EndTime = endDate;
StartTimeString = startDate.ToString("dd-MM-yyyy HH:mm:ss");
EndTimeString = endDate.ToString("dd-MM-yyyy HH:mm:ss");
}
public CodingSession(string startDate, string endDate, int? id = null)
{
Id = id;
StartTimeString = startDate;
EndTimeString = endDate;
StartTime = DateTime.ParseExact(Regex.Replace(StartTimeString?.Trim() ?? "", (@"\s+"), (" ")).Replace(".", "-"),
"dd-MM-yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.None);
EndTime = DateTime.ParseExact(Regex.Replace(EndTimeString?.Trim() ?? "", (@"\s+"), (" ")).Replace(".", "-"),
"dd-MM-yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.None);
}

}


}
12 changes: 12 additions & 0 deletions CodingTracker/CodingTracker/Models/TimeCalculator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace CodingTracker.Model
{
internal static class TimeCalculator
{
internal static TimeSpan GetTimeSinceStart(DateTime startDateTime, DateTime currentDateTime)
{
return currentDateTime - startDateTime;
}
}
}
16 changes: 16 additions & 0 deletions CodingTracker/CodingTracker/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CodingTracker.Controllers;
using CodingTracker.View;

namespace CodingTracker
{
internal class Program
{
static void Main()
{
var codingController = new CodingController();
var userInterface = new UserInterface(codingController);

userInterface.Menu();
}
}
}
74 changes: 74 additions & 0 deletions CodingTracker/CodingTracker/UserInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Text;

namespace CodingTracker
{
internal static class UserInput
{

public static void WaitForUser(string message = "Press any key to continue...")
{
AnsiConsole.MarkupLine($"\n[grey]{message}[/]");
Console.ReadKey(true);
Console.Clear();
}

public static string AnySelection(string msg, string[] choices)
{
string choice = " ";
choice = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title($"[green]{msg}[/]")
.AddChoices(choices));

return choice;
}

public static int GetIntFromUser(List<int> validIDs)
{
int value;
bool incorrectInt = true;
do
{
AnsiConsole.Markup("Enter valid positive Integer here: ");
incorrectInt = !Validation.CheckStringToInt(Console.ReadLine(), out value);
if (incorrectInt || !(validIDs.Contains(value)))
{
AnsiConsole.MarkupLine($"[bold red]INVALID[/] Input. Try again.");
incorrectInt = true;
}
} while (incorrectInt);
return value;
}

public static string GetString(string promptMessage = "Enter here: ")
{
while (true)
{
AnsiConsole.Markup(promptMessage);
string? input = Console.ReadLine()?.Trim();

if (!string.IsNullOrWhiteSpace(input))
{
return input;
}

AnsiConsole.MarkupLine("[bold red]Input cannot be empty. Please try again.[/]");
}
}
public static void StopSessionOnKeyPress()
{
Console.ReadKey(true);
}

public static void ClearInputBuffer()
{
while (Console.KeyAvailable)
{
Console.ReadKey(true);
}
}
}
}
31 changes: 31 additions & 0 deletions CodingTracker/CodingTracker/Validation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Globalization;

using System.Text.RegularExpressions;

namespace CodingTracker
{
internal static class Validation
{
public static bool ValidateStringToDateTime(string? dateTime, out DateTime parsedDateTime)
{
return DateTime.TryParseExact(
Regex.Replace(dateTime?.Trim() ?? "", (@"\s+"), (" ")).Replace(".", "-"), // ?? "" crash safe - Regex.Replace(input, pattern, replacement)
"dd-MM-yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out parsedDateTime);
}

public static bool CheckStringToInt(string? input, out int value)
{
bool check = int.TryParse(input, out value);
return check;
}

public static bool IsEndTimeValid(DateTime start, DateTime end)
{
return end > start;
}
}
}
Loading
Loading