Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6431bdd
initialize commit
Nov 14, 2025
62b3527
Created Enums, CodingSesion class and config file
NoNameLudovik Nov 17, 2025
8364c5c
Add files to previous commit
NoNameLudovik Nov 17, 2025
d0c8b03
Some changes
NoNameLudovik Nov 24, 2025
d86280e
.
NoNameLudovik Nov 24, 2025
ac1d92a
.
NoNameLudovik Dec 1, 2025
2d36dd8
Config file is changed. Added classes for database and menu, added he…
NoNameLudovik Dec 1, 2025
bb3e9e5
Added format validation to GetDateTime method
NoNameLudovik Dec 8, 2025
a97117b
Added Insert method to DataBaseController
NoNameLudovik Dec 8, 2025
cc22fd4
Added methods SelectFromTable and ShowSessions
NoNameLudovik Dec 9, 2025
8b9d6ad
Added methodes for deleting sessions from table
NoNameLudovik Dec 12, 2025
9ef73a9
Added methodes for editing sessions
NoNameLudovik Dec 12, 2025
264ff66
Add validation for dates and IDs
NoNameLudovik Dec 16, 2025
4f7c431
Add validation for dates and IDs
NoNameLudovik Dec 16, 2025
30eb315
clean
NoNameLudovik Dec 18, 2025
5569d77
ignore database
NoNameLudovik Dec 18, 2025
32e73cb
Merge remote-tracking branch 'origin/main'
NoNameLudovik Dec 18, 2025
c6daf03
.
NoNameLudovik Dec 18, 2025
82e2a2a
add gitignore for database
NoNameLudovik Dec 18, 2025
4dbd83d
Delete global.json
NoNameLudovik Jan 5, 2026
8cf96fd
Changes in DataBase path
NoNameLudovik Jan 5, 2026
edd357d
Changes in DataBase path
NoNameLudovik Jan 5, 2026
3e9f0d8
Merge remote-tracking branch 'origin/main'
NoNameLudovik Jan 5, 2026
ce0fc53
Clean comments
NoNameLudovik Jan 6, 2026
ab2f756
Add some exception handling
NoNameLudovik Jan 6, 2026
7da2db5
Add ReadMe file
NoNameLudovik Jan 6, 2026
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
Empty file modified .codacy.yml
100644 → 100755
Empty file.
Empty file modified .gitignore
100644 → 100755
Empty file.
1 change: 1 addition & 0 deletions NoNameLudovik.CodingTracker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.db
16 changes: 16 additions & 0 deletions NoNameLudovik.CodingTracker/NoNameLudovik.CodingTracker.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoNameLudovik.CodingTracker", "NoNameLudovik.CodingTracker\NoNameLudovik.CodingTracker.csproj", "{761778D9-5806-4FEB-A75E-C95264B3D608}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{761778D9-5806-4FEB-A75E-C95264B3D608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{761778D9-5806-4FEB-A75E-C95264B3D608}.Debug|Any CPU.Build.0 = Debug|Any CPU
{761778D9-5806-4FEB-A75E-C95264B3D608}.Release|Any CPU.ActiveCfg = Release|Any CPU
{761778D9-5806-4FEB-A75E-C95264B3D608}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Globalization;

namespace NoNameLudovik.CodingTracker;

public class CodingSession
{
internal int Id{get;set;}
internal string StartTime{get;set;}
internal string EndTime{get;set;}
internal TimeSpan Duration{get; set;}

internal void CalculateDuration()
{
var endTime = DateTime.ParseExact(EndTime, "dd-MM-yyyy HH:mm", new CultureInfo("en-US"), DateTimeStyles.None);
var startTime = DateTime.ParseExact(StartTime, "dd-MM-yyyy HH:mm", new CultureInfo("en-US"), DateTimeStyles.None);

Duration = endTime - startTime;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using Microsoft.Extensions.Configuration;
using Dapper;
using Microsoft.Data.Sqlite;

namespace NoNameLudovik.CodingTracker;

internal class DataBaseController
{
static IConfigurationRoot _config = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional:false, reloadOnChange:true).Build();
static string _projectRoot = Path.GetFullPath(
Path.Combine(
AppContext.BaseDirectory,
"..", "..", ".."));
static string _dbPath = Path.Combine(_projectRoot, _config["dbPath"]);
static SqliteConnection _connection = new SqliteConnection($"Data source = {_dbPath}");

internal static void CreatTable()
{
string createTableSqlQuery =
@"CREATE TABLE IF NOT EXISTS codingSessions (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
StartTime TEXT,
EndTime TEXT);";
_connection.Execute(createTableSqlQuery);
}

internal static void Insert(DateTime startTime, DateTime endTime)
{
string insertSqlQuery =
@"INSERT INTO codingSessions (StartTime, EndTime) VALUES (@StartTime, @EndTime)";

var newSession = new {StartTime = startTime.ToString("dd-MM-yyyy HH:mm"), EndTime = endTime.ToString("dd-MM-yyyy HH:mm")};

try
{
_connection.Execute(insertSqlQuery, newSession);
}
catch (SqliteException error)
{
throw new Exception($"Error while adding new session to database", error);
}
}

internal static List<CodingSession> SelectFromTable()
{
string selectSqlQuery = @"SELECT * FROM codingSessions";

var codingSessions = _connection.Query<CodingSession>(selectSqlQuery).ToList();
return codingSessions;
}

internal static CodingSession SelectFromTable(int sessionId)
{
string selectSqlQuery = @$"SELECT * FROM codingSessions WHERE Id = {sessionId}";

var codingSessions = _connection.Query<CodingSession>(selectSqlQuery).ToList();
return codingSessions[0];
}

internal static void DeleteFromTable(int sessionId)
{
string deleteSqlQuery = @"DELETE FROM codingSessions WHERE Id = @sessionId";
try
{
_connection.Execute(deleteSqlQuery, new { sessionId });
}
catch (SqliteException error)
{
throw new Exception($"Error while deleting coding session {sessionId} in database", error);
}
}

internal static void UpdateRowInTable(int sessionId, string newTime, EditOptions editOption)
{
string updateSqlQuery = null;

switch (editOption)
{
case EditOptions.EditStartTime:
updateSqlQuery = @"UPDATE codingSessions
SET StartTime = @newTime
WHERE Id = @sessionId";
break;
case EditOptions.EditEndTime:
updateSqlQuery = @"UPDATE codingSessions
SET EndTime = @newTime
WHERE Id = @sessionId";
break;
}

try
{
_connection.Execute(updateSqlQuery, new { newTime, sessionId });
}
catch(SqliteException error)
{
throw new Exception($"Error while updating coding session {sessionId} in database", error);
}
}
}
16 changes: 16 additions & 0 deletions NoNameLudovik.CodingTracker/NoNameLudovik.CodingTracker/Enums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NoNameLudovik.CodingTracker;

internal enum MenuOptions
{
AddSession,
EditSession,
DeleteSession,
ShowSessions,
Exit
}

internal enum EditOptions
{
EditStartTime,
EditEndTime,
}
81 changes: 81 additions & 0 deletions NoNameLudovik.CodingTracker/NoNameLudovik.CodingTracker/Helper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Globalization;
using Spectre.Console;

namespace NoNameLudovik.CodingTracker;

internal static class Helper
{
internal static DateTime GetDateTime()
{
while (true)
{
var input = AnsiConsole.Ask(
"Please, type in date at format \"dd-MM-yyyy HH:mm\" or leave it blank to type in [green]current time[/]",
DateTime.Now.ToString("dd-MM-yyyy HH:mm"));

if (DateTime.TryParseExact(input, "dd-MM-yyyy HH:mm", new CultureInfo("en-US"), DateTimeStyles.None, out var date))
return date;

AnsiConsole.MarkupLine($"Wrong format [red]{input}[/]! Try again!");
}
}

internal static int GetId()
{
while (true)
{
var sessionId = AnsiConsole.Ask<int>("Type in [green]ID[/] of session you want to edit?");
if (!Helper.CheckIdExist(sessionId))
{
AnsiConsole.MarkupLine($"[red]{sessionId}[/] doesn't exist! Try again!");
continue;
}

return sessionId;
}
}

internal static bool TimeValidation(DateTime newTime, int sessionId, EditOptions option)
{
var session = DataBaseController.SelectFromTable(sessionId);

switch (option)
{
case EditOptions.EditStartTime:
if (newTime < DateTime.ParseExact(session.EndTime, "dd-MM-yyyy HH:mm", new CultureInfo("en-US"), DateTimeStyles.None))
{
return true;
}
break;
case EditOptions.EditEndTime:
if (newTime > DateTime.ParseExact(session.StartTime,"dd-MM-yyyy HH:mm", new CultureInfo("en-US"), DateTimeStyles.None))
{
return true;
}
break;
}

return false;
}

internal static bool CompareDates(DateTime startTime, DateTime endTime)
{
if (startTime > endTime)
return false;

return true;
}

private static bool CheckIdExist(int sessionId)
{
var codingSessions = DataBaseController.SelectFromTable();
var ids = new List<int>();

foreach (var session in codingSessions)
{
ids.Add(session.Id);
}

return ids.Contains(sessionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using Spectre.Console;

namespace NoNameLudovik.CodingTracker;

internal class MenuController
{
internal static void MainMenu()
{
while (true)
{
Console.Clear();

var optionChoice = AnsiConsole.Prompt(
new SelectionPrompt<MenuOptions>()
.Title("Choose an option:")
.AddChoices(Enum.GetValues<MenuOptions>()));

switch (optionChoice)
{
case MenuOptions.AddSession:
AddSession();
break;
case MenuOptions.EditSession:
EditSession();
Console.ReadKey();
break;
case MenuOptions.DeleteSession:
DeleteSession();
Console.ReadKey();
break;
case MenuOptions.ShowSessions:
ShowSessions();
Console.ReadKey();
break;
case MenuOptions.Exit:
Environment.Exit(0);
break;
}
}
}

private static void AddSession()
{
while (true)
{
AnsiConsole.Clear();
AnsiConsole.MarkupLine("[bold yellow]Add Session[/]\n");

AnsiConsole.MarkupLine("[blue]Start Time[/]");
var startTime = Helper.GetDateTime();

AnsiConsole.MarkupLine("[blue]End Time[/]");
var endTime = Helper.GetDateTime();

if (startTime > endTime)
{
AnsiConsole.MarkupLine("[red]StartTime[/] can't be later then [red]EndTime[/]!");
Console.ReadKey();
continue;
}

try
{
DataBaseController.Insert(startTime, endTime);
AnsiConsole.MarkupLine("[green]Success![/]Session added!");
}
catch (Exception error)
{
AnsiConsole.MarkupLine($"[red]{error.Message}[/]");
}

break;
}
}

private static void EditSession()
{
ShowSessions();

DateTime newTime;
var sessionId = Helper.GetId();
var editOption = AnsiConsole.Prompt(new SelectionPrompt<EditOptions>().
Title("Choose what you want to edit:").
AddChoices(Enum.GetValues<EditOptions>()));

while (true)
{
AnsiConsole.Clear();
newTime = Helper.GetDateTime();
if (!Helper.TimeValidation(newTime, sessionId, editOption))
{
AnsiConsole.MarkupLine("[red]StartTime[/] can't be later then [red]EndTime[/]!");
Console.ReadKey();
continue;
}

break;
}

try
{
DataBaseController.UpdateRowInTable(sessionId, newTime.ToString("dd-MM-yyyy HH:mm"), editOption);

AnsiConsole.Clear();
AnsiConsole.MarkupLine("[green]Success![/]Date was changed!");
}
catch (Exception error)
{
AnsiConsole.MarkupLine($"[red]{error.Message}[/]");
}
}

private static void DeleteSession()
{
ShowSessions();

var sessionId = Helper.GetId();
try
{
DataBaseController.DeleteFromTable(sessionId);
AnsiConsole.MarkupLine("[green]Success![/]Session deleted!");
}
catch(Exception error)
{
AnsiConsole.MarkupLine($"[red]{error.Message}[/]");
}
}

private static void ShowSessions()
{
var codingSessions = DataBaseController.SelectFromTable();

var table = new Table();
table.AddColumn(new TableColumn("[green]ID[/]").Centered());
table.AddColumn(new TableColumn("[blue]StartTime[/]").Centered());
table.AddColumn(new TableColumn("[blue]EndTime[/]").Centered());
table.AddColumn(new TableColumn("[yellow]Duration[/]").Centered());
table.ShowRowSeparators();
table.Border(TableBorder.Rounded);

foreach (var codingSession in codingSessions)
{
codingSession.CalculateDuration();
table.AddRow(codingSession.Id.ToString(),
codingSession.StartTime,
codingSession.EndTime,
string.Format("{0}:{1:mm}:{1:ss}", (int)codingSession.Duration.TotalHours, codingSession.Duration));
}

AnsiConsole.Write(table);
}
}
Loading