-
Notifications
You must be signed in to change notification settings - Fork 223
Finished #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finished #207
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <?xml version="1.0" encoding="utf-8" ?> | ||
| <configuration> | ||
| <appSettings> | ||
| <add key="ConnectionString" value="Data Source=database/CodingTracker.db;Version=3;" /> | ||
| </appSettings> | ||
| </configuration> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> | ||
| <AnalysisLevel>latest-all</AnalysisLevel> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Folder Include="Database\" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Dapper" Version="2.1.66" /> | ||
| <PackageReference Include="Spectre.Console" Version="0.50.0" /> | ||
| <PackageReference Include="Spectre.Console.Cli" Version="0.50.0" /> | ||
| <PackageReference Include="System.Data.SQLite" Version="1.0.119" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Update="Resources\Resources.Designer.cs"> | ||
| <DesignTime>True</DesignTime> | ||
| <AutoGen>True</AutoGen> | ||
| <DependentUpon>Resources.resx</DependentUpon> | ||
| </Compile> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <EmbeddedResource Update="Resources\Resources.resx"> | ||
| <Generator>ResXFileCodeGenerator</Generator> | ||
| <LastGenOutput>Resources.Designer.cs</LastGenOutput> | ||
| </EmbeddedResource> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <None Update="Database\CodingTracker.db"> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| </None> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.13.35931.197 d17.13 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker", "CodingTracker.csproj", "{C1A062C0-57FF-46E9-8754-C26A7B3EC960}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {C1A062C0-57FF-46E9-8754-C26A7B3EC960}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {C1A062C0-57FF-46E9-8754-C26A7B3EC960}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {C1A062C0-57FF-46E9-8754-C26A7B3EC960}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {C1A062C0-57FF-46E9-8754-C26A7B3EC960}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
| SolutionGuid = {364B4820-5265-46BE-AB66-A9F1D4809E28} | ||
| EndGlobalSection | ||
| EndGlobal |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,204 @@ | ||
| using CodingTracker.Models; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Data.SQLite; | ||
| using System.Linq; | ||
| using Dapper; | ||
|
|
||
| namespace CodingTracker | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 Namespaces ❌ ✔️ |
||
| { | ||
| /// <summary> | ||
| /// This class is responsible for interacting with the SQLite database. | ||
| /// </summary> | ||
| class DatabaseInteractor | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 Explicit Accessibility 💡 Declare 'public', |
||
| { | ||
| #region properties | ||
| /// <summary> | ||
| /// SQLite connection object. | ||
| /// </summary> | ||
| public static SQLiteConnection? Connection { get; set; } | ||
| #endregion | ||
| #region constructors | ||
| /// <summary> | ||
| /// Initializes a new instance of the DatabaseInteractor class and creates an SQLite connection. | ||
| /// </summary> | ||
| public DatabaseInteractor() | ||
| { | ||
| if (Connection == null) | ||
| { | ||
| Connection = CreateSqliteConnection(); | ||
| CreateDatabaseFile(); | ||
| EnsureCodingTrackerTable(); | ||
| } | ||
| } | ||
| #endregion | ||
| #region methods | ||
| /// <summary> | ||
| /// Closes the database connection and disposes of the object. | ||
| /// </summary> | ||
| public static void Dispose() | ||
| { | ||
| if (Connection != null) | ||
| { | ||
| Connection.Close(); | ||
| Connection.Dispose(); | ||
| Connection = null; | ||
| } | ||
| } | ||
| /// <summary> | ||
| /// Retrieves all coding sessions from the database. | ||
| /// </summary> | ||
| /// <returns>List<CodingSession></returns> | ||
| public static List<CodingSession> GetAllSessions() | ||
| { | ||
| string selectQuery = "SELECT * FROM CodingSession;"; | ||
| try | ||
| { | ||
| if (Connection == null) | ||
| { | ||
| throw new InvalidOperationException("Database connection is not initialized."); | ||
| } | ||
|
|
||
| // Simplified collection initialization | ||
| return Connection.Query<CodingSession>(selectQuery).ToList() ?? []; | ||
| } | ||
| catch (SQLiteException ex) | ||
| { | ||
| UI.WriteError("Database error retrieving sessions: " + ex.Message); | ||
| return []; | ||
| } | ||
| catch (InvalidOperationException ex) | ||
| { | ||
| UI.WriteError("Invalid operation: " + ex.Message); | ||
| return []; | ||
| } | ||
| } | ||
| /// <summary> | ||
| /// Inserts a new coding session record into the database. | ||
| /// </summary> | ||
| /// <param name="session"></param> | ||
| /// <returns></returns> | ||
| public static bool CreateSessionRecord(CodingSession session) | ||
| { | ||
| string insertQuery = @"INSERT INTO CodingSession(SessionStart, SessionEnd, Duration) | ||
| VALUES(@SessionStart, @SessionEnd, @Duration);"; | ||
|
|
||
| try | ||
| { | ||
| if (Connection == null) | ||
| { | ||
| throw new InvalidOperationException("Database connection is not initialized."); | ||
| } | ||
|
|
||
| int rowsAffected = Connection.Execute(insertQuery, new | ||
| { | ||
| session.SessionStart, | ||
| session.SessionEnd, | ||
| session.Duration | ||
| }); | ||
|
|
||
| return rowsAffected > 0; | ||
| } | ||
| catch (SQLiteException ex) | ||
| { | ||
| UI.WriteError("Database error inserting session: " + ex.Message); | ||
| return false; | ||
| } | ||
| catch (InvalidOperationException ex) | ||
| { | ||
| UI.WriteError("Invalid operation: " + ex.Message); | ||
| return false; | ||
| } | ||
| } | ||
| /// <summary> | ||
| /// Creates the database file if it does not exist. | ||
| /// </summary> | ||
| /// <returns>True if successful false if not</returns> | ||
| public static bool CreateDatabaseFile() | ||
| { | ||
| try | ||
| { | ||
| if (!System.IO.File.Exists("Database/CodingTracker.db")) | ||
| { | ||
| // Create the database file | ||
| using var stream = System.IO.File.Create("Database/CodingTracker.db"); | ||
| // Close the stream | ||
| stream.Close(); | ||
| } | ||
| return true; | ||
| } | ||
| catch (UnauthorizedAccessException ex) | ||
| { | ||
| UI.WriteError("Access denied while creating database file: " + ex.Message); | ||
| return false; | ||
| } | ||
| catch (IOException ex) | ||
| { | ||
| UI.WriteError("I/O error while creating database file: " + ex.Message); | ||
| return false; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| // Rethrow unexpected exceptions to avoid swallowing critical errors | ||
| throw new InvalidOperationException("Unexpected error while creating database file.", ex); | ||
| } | ||
| } | ||
| /// <summary> | ||
| /// Creates a SQLite connection using the connection string from the app.config file. | ||
| /// </summary> | ||
| /// <returns>SQLiteConnection</returns> | ||
| public static SQLiteConnection CreateSqliteConnection() | ||
| { | ||
| string? connectionString = System.Configuration.ConfigurationManager.AppSettings.Get("ConnectionString"); | ||
| SQLiteConnection connection = new(connectionString); | ||
| try | ||
| { | ||
| connection.Open(); | ||
| } | ||
| catch (SQLiteException ex) | ||
| { | ||
| UI.WriteError("Error opening database connection: " + ex.Message); | ||
| throw; // Rethrow the exception to avoid swallowing it | ||
| } | ||
| return connection; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Ensures that the CodingSession table exists in the database. | ||
| /// </summary> | ||
| /// <returns>True if successful, false if not</returns> | ||
| public static bool EnsureCodingTrackerTable() | ||
| { | ||
| try | ||
| { | ||
| if (Connection == null) | ||
| { | ||
| throw new InvalidOperationException("Database connection is not initialized."); | ||
| } | ||
|
|
||
| string checkTableQuery = "SELECT name FROM sqlite_master WHERE type='table' AND name='CodingSession';"; | ||
| var result = Connection.ExecuteScalar<string>(checkTableQuery); | ||
|
|
||
| if (result == null) | ||
| { | ||
| // Table does not exist, create it using Dapper | ||
| string createTableQuery = Resources.Resources.EnsureCodingSessionSQL; | ||
| Connection.Execute(createTableQuery); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
| catch (SQLiteException ex) | ||
| { | ||
| UI.WriteError("SQLite error ensuring CodingSession table: " + ex.Message); | ||
| return false; | ||
| } | ||
| catch (InvalidOperationException ex) | ||
| { | ||
| UI.WriteError("Invalid operation: " + ex.Message); | ||
| return false; | ||
| } | ||
| } | ||
| #endregion | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Compact Code 💡 Let your code breathe. Whitespace makes your code easier to read. |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using System.Linq; | ||
|
|
||
| namespace CodingTracker.Interfaces | ||
| { | ||
| interface ICodingSession | ||
| { | ||
| #region properties | ||
| int Id { get; set; } | ||
| string SessionStart { get; set; } | ||
| string SessionEnd { get; set; } | ||
| string Duration { get; set; } | ||
| #endregion | ||
| #region methods | ||
| public bool SaveSession(); | ||
| public string CalculateDurationOfSession(); | ||
|
|
||
| #endregion | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| using CodingTracker.Interfaces; | ||
| using System; | ||
| using System.Data.SQLite; | ||
| using System.Globalization; | ||
| using System.Linq; | ||
|
|
||
| namespace CodingTracker.Models | ||
| { | ||
| class CodingSession : ICodingSession | ||
| { | ||
| /// <summary> | ||
| /// Unique identifier for the coding session. | ||
| /// </summary> | ||
| public int Id { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Start time of the coding session. | ||
| /// </summary> | ||
| public string SessionStart { get; set; } = string.Empty; | ||
|
|
||
| /// <summary> | ||
| /// End time of the coding session. | ||
| /// </summary> | ||
| public string SessionEnd { get; set; } = string.Empty; | ||
|
|
||
| /// <summary> | ||
| /// Duration of the coding session. | ||
| /// </summary> | ||
| public string Duration { get; set; } = string.Empty; | ||
|
|
||
| /// <summary> | ||
| /// Constructor for the CodingSession class. | ||
| /// </summary> | ||
| /// <param name="sessionStart"></param> | ||
| /// <param name="sessionEnd"></param> | ||
| public CodingSession(string sessionStart, string sessionEnd) | ||
| { | ||
| SessionStart = sessionStart; | ||
| SessionEnd = sessionEnd; | ||
| Duration = CalculateDurationOfSession(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Default constructor for the CodingSession class. Required by Dapper. | ||
| /// </summary> | ||
| public CodingSession() { } | ||
|
|
||
| /// <summary> | ||
| /// Calls a method to DatabaseInteractor to save the session record to the database. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| public bool SaveSession() | ||
| { | ||
| try | ||
| { | ||
| DatabaseInteractor.CreateSessionRecord(this); | ||
| return true; | ||
| } | ||
| catch (SQLiteException ex) // Catching a more specific exception type | ||
| { | ||
| UI.WriteError($"Database error: {ex.Message}"); | ||
| return false; | ||
| } | ||
| catch (FormatException ex) // Catching another specific exception type | ||
| { | ||
| UI.WriteError($"Formatting error: {ex.Message}"); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Calculates the duration of the session based on the start and end times. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| public string CalculateDurationOfSession() | ||
| { | ||
| // Calculate the duration of the session | ||
| if (SessionStart != null && SessionEnd != null) | ||
| { | ||
| DateTime start = DateTime.Parse(SessionStart, CultureInfo.InvariantCulture); | ||
| DateTime end = DateTime.Parse(SessionEnd, CultureInfo.InvariantCulture); | ||
| TimeSpan duration = end - start; | ||
| return duration.ToString(); | ||
| } | ||
| else | ||
| { | ||
| return "Session not started or ended."; | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟠 Database In Repository
❗Database's should not be included with source code