Skip to content
Draft
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
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 4

[*.cs]
wrap_before_binary_opsign = true
wrap_chained_binary_expressions = chop_if_long
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ riderModule.iml
/_ReSharper.Caches/
.idea/
*~
*.user
36 changes: 36 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/TimeTracker.Server/bin/Debug/net7.0/TimeTracker.Server.dll",
"args": [],
"cwd": "${workspaceFolder}/TimeTracker.Server",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
// "pattern": "\\bNow listening on:\\s+(http?://\\S+)"
"pattern": "\\bgoto\\s+(http://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
41 changes: 41 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/TimeTracker.Server/TimeTracker.Server.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/TimeTracker.Server/TimeTracker.Server.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/TimeTracker.Server/TimeTracker.Server.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
10 changes: 10 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- [ ] create separate TimeTracker.Core with common classes
- [ ] create interface with API methods
- [ ] test it with Server running on localhost
- [ ] authorization with OAuth2

Expected API:
- `/categories` - get list of all categories
- `/categores/update` - set new list of categories - it should not be very big
- `/entries` - get a page of entries since provided DateTime
- `/entries/update` - update provided entry
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace TimeTracker;

public static class Constants
{
public const string DatabasePath = "tracker.db";
public const string DatabaseName = "tracker.db";

public const SQLiteOpenFlags Flags =
// open the database in read/write mode
Expand All @@ -13,4 +13,4 @@ public static class Constants
SQLiteOpenFlags.Create |
// enable multi-threaded database access
SQLiteOpenFlags.SharedCache;
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using SQLite;

namespace TimeTracker.Database.Migrations;

public class M005_AddTimestampAndReworkElapsed : IDbMigration
{
public async Task Do(SQLiteAsyncConnection db)
{
var timestamp = DateTime.Now;

await db.ExecuteAsync("ALTER TABLE TrackedTimeDb ADD Timestamp datetime");
await db.ExecuteAsync("ALTER TABLE TrackedTimeDb ADD EndTime datetime");

var all = await db.Table<TrackedTimeDb_M005>().ToListAsync();
foreach (var trackedTimeDb in all)
{
trackedTimeDb.Timestamp = timestamp;
#pragma warning disable CS0618
trackedTimeDb.EndTime = trackedTimeDb.StartTime + trackedTimeDb.ElapsedTime;
#pragma warning restore CS0618
await db.UpdateAsync(trackedTimeDb);
}

// NOTE: sqlite does not support DROP COLUMN
// await db.ExecuteAsync("ALTER TABLE TrackedTimeDb DROP COLUMN ElapsedTime");

const string TemporaryTable = "t1_backup";
await db.ExecuteAsync(
$"CREATE TABLE {TemporaryTable} AS SELECT Id, Uuid, Name, Status, StartTime, EndTime, Timestamp FROM TrackedTimeDb");
await db.ExecuteAsync("DROP TABLE TrackedTimeDb");
await db.ExecuteAsync($"ALTER TABLE {TemporaryTable} RENAME TO TrackedTimeDb");
}

public Task UnDo(SQLiteAsyncConnection db)
{
throw new NotImplementedException();
}

public string Serialize()
{
throw new NotImplementedException();
}
}

/// <summary>
/// Data model at the time of migration <see cref="M005_AddTimestampAndReworkElapsed"/>
/// </summary>
[Table(nameof(TrackedTimeDb))]
public class TrackedTimeDb_M005 : ITable
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

public Guid Uuid { get; set; }

public string Name { get; set; }

public DateTime StartTime { get; set; }

public TimeSpan ElapsedTime { get; init; }
public DateTime EndTime { get; set; }

public int Status { get; set; }

/// <summary>
/// Last modification time - considering Start and Elapsed may ba changed later
/// </summary>
public DateTime Timestamp { get; set; }

public enum TrackingStatus
{
Completed = 0,

/// <summary>
/// Currently running
/// </summary>
Running = 1,
}
}
58 changes: 58 additions & 0 deletions TimeTracker.Core/Database/Migrations/M4_AddUuid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using SQLite;

namespace TimeTracker.Database.Migrations;

public class M4_AddUuid : IDbMigration
{
public async Task Do(SQLiteAsyncConnection db)
{
await db.ExecuteAsync("ALTER TABLE TrackedTimeDb ADD Uuid text");

var all = await db.Table<TrackedTimeDb_M004>().ToListAsync();
foreach (var trackedTimeDb in all)
{
trackedTimeDb.Uuid = Guid.NewGuid();
await db.UpdateAsync(trackedTimeDb);
}
}

public Task UnDo(SQLiteAsyncConnection db)
{
throw new NotImplementedException();
}

public string Serialize()
{
throw new NotImplementedException();
}
}

/// <summary>
/// Data model at the time of migration <see cref="M4_AddUuid"/>
/// </summary>
[Table(nameof(TrackedTimeDb))]
public class TrackedTimeDb_M004 : ITable
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

public Guid Uuid { get; set; }

public string Name { get; set; }

public DateTime StartTime { get; set; }

public TimeSpan ElapsedTime { get; init; }

public int Status { get; set; }

public enum TrackingStatus
{
Completed = 0,

/// <summary>
/// Currently running
/// </summary>
Running = 1,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public static class Migrator
new M1_InitializeDb(),
new M2_AddCategories(),
new M3_AddStatuses(),
new M4_AddUuid(),
new M005_AddTimestampAndReworkElapsed(),
};

public static async Task Migrate(SQLiteAsyncConnection db)
Expand All @@ -36,7 +38,15 @@ public static async Task Migrate(SQLiteAsyncConnection db)
{
var migration = Migrations[i];

await migration.Do(db);
try
{
await migration.Do(db);
}
catch (Exception e)
{
Debug.WriteLine($"Migration {migration.GetType().Name} failed: {e}");
throw;
}
await db.UpdateAsync(new ControlDb(ControlDb.ParamId.Version, i + 1));
}
}
Expand Down
65 changes: 65 additions & 0 deletions TimeTracker.Core/Database/Tables/CategoryDb.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Text.Json.Serialization;
using Microsoft.Maui.Graphics;
using SQLite;

namespace TimeTracker;

public class CategoryDb : ITable
{
public enum CategoryState
{
Enabled = 0,
Disabled = 1,
}

[PrimaryKey, AutoIncrement, JsonIgnore] public int Id { get; set; }

public int State { get; set; }

[Ignore, JsonIgnore]
public CategoryState StateEnum
{
get => (CategoryState)State;
set => State = (int)value;
}

public string Name { get; set; }

/// <summary>
/// Group of categories
/// </summary>
public string CategoryGroup { get; set; }

public string ColorString { get; set; }

[Ignore, JsonIgnore]
public Color ColorObject
{
get => Color.Parse(ColorString);
init => ColorString = value.ToArgbHex();
}

public override string ToString()
{
return $"Category: {Name}";
}

public override bool Equals(object? obj)
{
if (obj is not CategoryDb other)
return false;

return Equals(other);
}

protected bool Equals(CategoryDb other)
{
// time trackers reference only category name, so we can compare only by name
return Name == other.Name;
}

public override int GetHashCode()
{
return HashCode.Combine(Name, CategoryGroup, State, ColorString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace TimeTracker;

/// <summary>
/// Table to store control parameters.
/// </summary>
public class ControlDb : ITable
{
public enum ParamId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,31 @@

namespace TimeTracker;

/// <summary>
///
/// </summary>
public class TrackedTimeDb : ITable
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

public Guid Uuid { get; set; }

public string Name { get; set; }

public DateTime StartTime { get; set; }

public TimeSpan ElapsedTime { get; init; }
public DateTime EndTime { get; set; }

[Ignore] public TimeSpan ElapsedTime => EndTime - StartTime;

public int Status { get; set; }

/// <summary>
/// Last modification time - considering Start and Elapsed may ba changed later
/// </summary>
public DateTime Timestamp { get; set; }

[Ignore]
public TrackingStatus StatusEnum
{
Expand Down
Loading