From 636334ddd0e9a37654583a5b8ce6b58946e58442 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Mon, 12 Jun 2023 01:11:47 +0200 Subject: [PATCH 01/11] Introduce TimeTracker.Server --- .vscode/launch.json | 36 ++++++++++++++++ .vscode/tasks.json | 41 +++++++++++++++++++ .../Controllers/WeatherForecastController.cs | 32 +++++++++++++++ TimeTracker.Server/Program.cs | 27 ++++++++++++ .../Properties/launchSettings.json | 41 +++++++++++++++++++ TimeTracker.Server/TimeTracker.Server.csproj | 14 +++++++ TimeTracker.Server/WeatherForecast.cs | 12 ++++++ .../appsettings.Development.json | 8 ++++ TimeTracker.Server/appsettings.json | 9 ++++ TimeTracker.sln | 7 ++++ 10 files changed, 227 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 TimeTracker.Server/Controllers/WeatherForecastController.cs create mode 100644 TimeTracker.Server/Program.cs create mode 100644 TimeTracker.Server/Properties/launchSettings.json create mode 100644 TimeTracker.Server/TimeTracker.Server.csproj create mode 100644 TimeTracker.Server/WeatherForecast.cs create mode 100644 TimeTracker.Server/appsettings.Development.json create mode 100644 TimeTracker.Server/appsettings.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2f5b08c --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..fcb23ca --- /dev/null +++ b/.vscode/tasks.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/TimeTracker.Server/Controllers/WeatherForecastController.cs b/TimeTracker.Server/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..8f89b17 --- /dev/null +++ b/TimeTracker.Server/Controllers/WeatherForecastController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace TimeTracker.Server.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} diff --git a/TimeTracker.Server/Program.cs b/TimeTracker.Server/Program.cs new file mode 100644 index 0000000..5f12a98 --- /dev/null +++ b/TimeTracker.Server/Program.cs @@ -0,0 +1,27 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +Console.WriteLine("goto http://localhost:5007/WeatherForecast"); + +app.Run(); diff --git a/TimeTracker.Server/Properties/launchSettings.json b/TimeTracker.Server/Properties/launchSettings.json new file mode 100644 index 0000000..5d16db3 --- /dev/null +++ b/TimeTracker.Server/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:50925", + "sslPort": 44394 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5007", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7191;http://localhost:5007", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/TimeTracker.Server/TimeTracker.Server.csproj b/TimeTracker.Server/TimeTracker.Server.csproj new file mode 100644 index 0000000..499d259 --- /dev/null +++ b/TimeTracker.Server/TimeTracker.Server.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/TimeTracker.Server/WeatherForecast.cs b/TimeTracker.Server/WeatherForecast.cs new file mode 100644 index 0000000..c52be2d --- /dev/null +++ b/TimeTracker.Server/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace TimeTracker.Server; + +public class WeatherForecast +{ + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} diff --git a/TimeTracker.Server/appsettings.Development.json b/TimeTracker.Server/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/TimeTracker.Server/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/TimeTracker.Server/appsettings.json b/TimeTracker.Server/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/TimeTracker.Server/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/TimeTracker.sln b/TimeTracker.sln index 27dc4af..53b2bd3 100644 --- a/TimeTracker.sln +++ b/TimeTracker.sln @@ -1,7 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 +# Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker", "TimeTracker\TimeTracker.csproj", "{9B5B958D-5ABE-42CA-AE57-1EDF19DDE07D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker.Server", "TimeTracker.Server\TimeTracker.Server.csproj", "{9D532BBD-5895-461F-AC46-C78A22801F11}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +15,9 @@ Global {9B5B958D-5ABE-42CA-AE57-1EDF19DDE07D}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B5B958D-5ABE-42CA-AE57-1EDF19DDE07D}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B5B958D-5ABE-42CA-AE57-1EDF19DDE07D}.Release|Any CPU.Build.0 = Release|Any CPU + {9D532BBD-5895-461F-AC46-C78A22801F11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D532BBD-5895-461F-AC46-C78A22801F11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D532BBD-5895-461F-AC46-C78A22801F11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D532BBD-5895-461F-AC46-C78A22801F11}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 2de129777602365ed94fa054161881789da8ee40 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Wed, 14 Jun 2023 23:43:57 +0200 Subject: [PATCH 02/11] Add TODO.md --- TODO.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..f6bd683 --- /dev/null +++ b/TODO.md @@ -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 From 906e4fe78bf7e70c4dd3cbe369d8f352fff38a65 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 19:57:33 +0200 Subject: [PATCH 03/11] Introduce TimeTracker.Core --- TimeTracker.Core/Class1.cs | 5 +++++ TimeTracker.Core/TimeTracker.Core.csproj | 9 +++++++++ TimeTracker.sln | 10 ++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 TimeTracker.Core/Class1.cs create mode 100644 TimeTracker.Core/TimeTracker.Core.csproj diff --git a/TimeTracker.Core/Class1.cs b/TimeTracker.Core/Class1.cs new file mode 100644 index 0000000..6462a53 --- /dev/null +++ b/TimeTracker.Core/Class1.cs @@ -0,0 +1,5 @@ +namespace TimeTracker.Core; + +public class Class1 +{ +} \ No newline at end of file diff --git a/TimeTracker.Core/TimeTracker.Core.csproj b/TimeTracker.Core/TimeTracker.Core.csproj new file mode 100644 index 0000000..eb2460e --- /dev/null +++ b/TimeTracker.Core/TimeTracker.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/TimeTracker.sln b/TimeTracker.sln index 53b2bd3..d729405 100644 --- a/TimeTracker.sln +++ b/TimeTracker.sln @@ -1,11 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# +# Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker", "TimeTracker\TimeTracker.csproj", "{9B5B958D-5ABE-42CA-AE57-1EDF19DDE07D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker.Server", "TimeTracker.Server\TimeTracker.Server.csproj", "{9D532BBD-5895-461F-AC46-C78A22801F11}" EndProject -Global +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker.Core", "TimeTracker.Core\TimeTracker.Core.csproj", "{E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}" +EndProject +Global` GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -19,5 +21,9 @@ Global {9D532BBD-5895-461F-AC46-C78A22801F11}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D532BBD-5895-461F-AC46-C78A22801F11}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D532BBD-5895-461F-AC46-C78A22801F11}.Release|Any CPU.Build.0 = Release|Any CPU + {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 0ede2ef0c9fc029506a1749a9a11b5089f676e44 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 20:18:57 +0200 Subject: [PATCH 04/11] Move some classes to Core library --- .../Database/Constants.cs | 0 .../Database/ITable.cs | 0 .../Database/Migrations/IDbMigration.cs | 0 .../Database/Migrations/M1_InitializeDb.cs | 0 .../Database/Migrations/M2_AddCategories.cs | 0 .../Database/Migrations/M3_AddStatuses.cs | 0 .../Database/Migrator.cs | 0 .../Database/Tables/CategoryDb.cs | 2 +- .../Database/Tables/ControlDb.cs | 0 .../Database/Tables/TrackedTimeDb.cs | 0 .../Database/TrackerDatabase.cs | 3 ++- .../Helpers/AsyncLazy.cs | 0 .../Helpers/AsyncUtil.cs | 0 .../Models/TimeTracker.cs | 0 TimeTracker.Core/TimeTracker.Core.csproj | 8 ++++++-- TimeTracker.Server/TimeTracker.Server.csproj | 4 ++++ TimeTracker/TimeTracker.csproj | 5 ++++- TimeTracker/WebRequests.cs | 13 +++++++++++++ 18 files changed, 30 insertions(+), 5 deletions(-) rename {TimeTracker => TimeTracker.Core}/Database/Constants.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/ITable.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Migrations/IDbMigration.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Migrations/M1_InitializeDb.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Migrations/M2_AddCategories.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Migrations/M3_AddStatuses.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Migrator.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Tables/CategoryDb.cs (95%) rename {TimeTracker => TimeTracker.Core}/Database/Tables/ControlDb.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/Tables/TrackedTimeDb.cs (100%) rename {TimeTracker => TimeTracker.Core}/Database/TrackerDatabase.cs (99%) rename {TimeTracker => TimeTracker.Core}/Helpers/AsyncLazy.cs (100%) rename {TimeTracker => TimeTracker.Core}/Helpers/AsyncUtil.cs (100%) rename {TimeTracker => TimeTracker.Core}/Models/TimeTracker.cs (100%) create mode 100644 TimeTracker/WebRequests.cs diff --git a/TimeTracker/Database/Constants.cs b/TimeTracker.Core/Database/Constants.cs similarity index 100% rename from TimeTracker/Database/Constants.cs rename to TimeTracker.Core/Database/Constants.cs diff --git a/TimeTracker/Database/ITable.cs b/TimeTracker.Core/Database/ITable.cs similarity index 100% rename from TimeTracker/Database/ITable.cs rename to TimeTracker.Core/Database/ITable.cs diff --git a/TimeTracker/Database/Migrations/IDbMigration.cs b/TimeTracker.Core/Database/Migrations/IDbMigration.cs similarity index 100% rename from TimeTracker/Database/Migrations/IDbMigration.cs rename to TimeTracker.Core/Database/Migrations/IDbMigration.cs diff --git a/TimeTracker/Database/Migrations/M1_InitializeDb.cs b/TimeTracker.Core/Database/Migrations/M1_InitializeDb.cs similarity index 100% rename from TimeTracker/Database/Migrations/M1_InitializeDb.cs rename to TimeTracker.Core/Database/Migrations/M1_InitializeDb.cs diff --git a/TimeTracker/Database/Migrations/M2_AddCategories.cs b/TimeTracker.Core/Database/Migrations/M2_AddCategories.cs similarity index 100% rename from TimeTracker/Database/Migrations/M2_AddCategories.cs rename to TimeTracker.Core/Database/Migrations/M2_AddCategories.cs diff --git a/TimeTracker/Database/Migrations/M3_AddStatuses.cs b/TimeTracker.Core/Database/Migrations/M3_AddStatuses.cs similarity index 100% rename from TimeTracker/Database/Migrations/M3_AddStatuses.cs rename to TimeTracker.Core/Database/Migrations/M3_AddStatuses.cs diff --git a/TimeTracker/Database/Migrator.cs b/TimeTracker.Core/Database/Migrator.cs similarity index 100% rename from TimeTracker/Database/Migrator.cs rename to TimeTracker.Core/Database/Migrator.cs diff --git a/TimeTracker/Database/Tables/CategoryDb.cs b/TimeTracker.Core/Database/Tables/CategoryDb.cs similarity index 95% rename from TimeTracker/Database/Tables/CategoryDb.cs rename to TimeTracker.Core/Database/Tables/CategoryDb.cs index 34a6e52..9179aa4 100644 --- a/TimeTracker/Database/Tables/CategoryDb.cs +++ b/TimeTracker.Core/Database/Tables/CategoryDb.cs @@ -1,4 +1,4 @@ -using Microsoft.Maui.Graphics.Text; +using Microsoft.Maui.Graphics; using SQLite; namespace TimeTracker; diff --git a/TimeTracker/Database/Tables/ControlDb.cs b/TimeTracker.Core/Database/Tables/ControlDb.cs similarity index 100% rename from TimeTracker/Database/Tables/ControlDb.cs rename to TimeTracker.Core/Database/Tables/ControlDb.cs diff --git a/TimeTracker/Database/Tables/TrackedTimeDb.cs b/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs similarity index 100% rename from TimeTracker/Database/Tables/TrackedTimeDb.cs rename to TimeTracker.Core/Database/Tables/TrackedTimeDb.cs diff --git a/TimeTracker/Database/TrackerDatabase.cs b/TimeTracker.Core/Database/TrackerDatabase.cs similarity index 99% rename from TimeTracker/Database/TrackerDatabase.cs rename to TimeTracker.Core/Database/TrackerDatabase.cs index 1bab219..b1f041e 100644 --- a/TimeTracker/Database/TrackerDatabase.cs +++ b/TimeTracker.Core/Database/TrackerDatabase.cs @@ -1,4 +1,5 @@ -using SQLite; +using Microsoft.Maui.Graphics; +using SQLite; namespace TimeTracker; diff --git a/TimeTracker/Helpers/AsyncLazy.cs b/TimeTracker.Core/Helpers/AsyncLazy.cs similarity index 100% rename from TimeTracker/Helpers/AsyncLazy.cs rename to TimeTracker.Core/Helpers/AsyncLazy.cs diff --git a/TimeTracker/Helpers/AsyncUtil.cs b/TimeTracker.Core/Helpers/AsyncUtil.cs similarity index 100% rename from TimeTracker/Helpers/AsyncUtil.cs rename to TimeTracker.Core/Helpers/AsyncUtil.cs diff --git a/TimeTracker/Models/TimeTracker.cs b/TimeTracker.Core/Models/TimeTracker.cs similarity index 100% rename from TimeTracker/Models/TimeTracker.cs rename to TimeTracker.Core/Models/TimeTracker.cs diff --git a/TimeTracker.Core/TimeTracker.Core.csproj b/TimeTracker.Core/TimeTracker.Core.csproj index eb2460e..5e02f2f 100644 --- a/TimeTracker.Core/TimeTracker.Core.csproj +++ b/TimeTracker.Core/TimeTracker.Core.csproj @@ -1,9 +1,13 @@ - net6.0 + net7.0 enable enable - + + + + + diff --git a/TimeTracker.Server/TimeTracker.Server.csproj b/TimeTracker.Server/TimeTracker.Server.csproj index 499d259..8ef5b38 100644 --- a/TimeTracker.Server/TimeTracker.Server.csproj +++ b/TimeTracker.Server/TimeTracker.Server.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/TimeTracker/TimeTracker.csproj b/TimeTracker/TimeTracker.csproj index a6c0f8d..b972c94 100644 --- a/TimeTracker/TimeTracker.csproj +++ b/TimeTracker/TimeTracker.csproj @@ -52,7 +52,6 @@ - @@ -89,4 +88,8 @@ + + + + diff --git a/TimeTracker/WebRequests.cs b/TimeTracker/WebRequests.cs new file mode 100644 index 0000000..610d4da --- /dev/null +++ b/TimeTracker/WebRequests.cs @@ -0,0 +1,13 @@ +namespace TimeTracker; + +/// +/// Performs web requests to the TimeTracker API. +/// +public class WebRequests +{ + public void Synchronize() + { + // 1 download all data from server + // 2 push all uncynced data to server + } +} From 4fc1d94b27e07ff236f1ec0a949ba01df55902da Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 20:21:52 +0200 Subject: [PATCH 05/11] Drop default class --- TimeTracker.Core/Class1.cs | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 TimeTracker.Core/Class1.cs diff --git a/TimeTracker.Core/Class1.cs b/TimeTracker.Core/Class1.cs deleted file mode 100644 index 6462a53..0000000 --- a/TimeTracker.Core/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace TimeTracker.Core; - -public class Class1 -{ -} \ No newline at end of file From 5ca69f93dfde2e4c876d5a163b4f46a099999e82 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 20:33:29 +0200 Subject: [PATCH 06/11] Implement Categories request --- TimeTracker.Core/Database/Constants.cs | 4 +-- .../Database/Tables/CategoryDb.cs | 4 ++- TimeTracker.Core/Database/TrackerDatabase.cs | 12 +++++-- .../Controllers/CategoriesController.cs | 24 ++++++++++++++ .../Controllers/WeatherForecastController.cs | 32 ------------------- TimeTracker.Server/Program.cs | 4 +++ TimeTracker.Server/WebConstants.cs | 6 ++++ 7 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 TimeTracker.Server/Controllers/CategoriesController.cs delete mode 100644 TimeTracker.Server/Controllers/WeatherForecastController.cs create mode 100644 TimeTracker.Server/WebConstants.cs diff --git a/TimeTracker.Core/Database/Constants.cs b/TimeTracker.Core/Database/Constants.cs index bfadf80..3d92011 100644 --- a/TimeTracker.Core/Database/Constants.cs +++ b/TimeTracker.Core/Database/Constants.cs @@ -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 @@ -13,4 +13,4 @@ public static class Constants SQLiteOpenFlags.Create | // enable multi-threaded database access SQLiteOpenFlags.SharedCache; -} \ No newline at end of file +} diff --git a/TimeTracker.Core/Database/Tables/CategoryDb.cs b/TimeTracker.Core/Database/Tables/CategoryDb.cs index 9179aa4..7c122fa 100644 --- a/TimeTracker.Core/Database/Tables/CategoryDb.cs +++ b/TimeTracker.Core/Database/Tables/CategoryDb.cs @@ -1,4 +1,5 @@ -using Microsoft.Maui.Graphics; +using System.Text.Json.Serialization; +using Microsoft.Maui.Graphics; using SQLite; namespace TimeTracker; @@ -33,6 +34,7 @@ public CategoryState StateEnum public string ColorString { get; set; } [Ignore] + [JsonIgnore] public Color ColorObject { get => Color.Parse(ColorString); diff --git a/TimeTracker.Core/Database/TrackerDatabase.cs b/TimeTracker.Core/Database/TrackerDatabase.cs index b1f041e..957e968 100644 --- a/TimeTracker.Core/Database/TrackerDatabase.cs +++ b/TimeTracker.Core/Database/TrackerDatabase.cs @@ -6,16 +6,22 @@ namespace TimeTracker; public class TrackerDatabase { private readonly SQLiteAsyncConnection _database; + private static string _databaseName = Constants.DatabaseName; - private TrackerDatabase() + private TrackerDatabase(string databaseName) { var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - var path = Path.Combine(home, Constants.DatabasePath); + var path = Path.Combine(home, databaseName); _database = new SQLiteAsyncConnection(path, Constants.Flags); } - public static readonly AsyncLazy Instance = new(async () => await new TrackerDatabase().InitializeDb()); + public static readonly AsyncLazy Instance = new(async () => await new TrackerDatabase(_databaseName).InitializeDb()); + + public static void InitializePath(string databaseName) + { + _databaseName = databaseName; + } public Task InsertAsync(T result) where T : ITable, new() { diff --git a/TimeTracker.Server/Controllers/CategoriesController.cs b/TimeTracker.Server/Controllers/CategoriesController.cs new file mode 100644 index 0000000..d9b4127 --- /dev/null +++ b/TimeTracker.Server/Controllers/CategoriesController.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Mvc; + +namespace TimeTracker.Server.Controllers; + +[ApiController] +[Route("[controller]")] +public class CategoriesController : ControllerBase +{ + private readonly ILogger _logger; + + public CategoriesController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public async Task> Get() + { + var db = await TrackerDatabase.Instance; + var categories = await db.GetCategories(); + + return categories.ToArray(); + } +} diff --git a/TimeTracker.Server/Controllers/WeatherForecastController.cs b/TimeTracker.Server/Controllers/WeatherForecastController.cs deleted file mode 100644 index 8f89b17..0000000 --- a/TimeTracker.Server/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace TimeTracker.Server.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} diff --git a/TimeTracker.Server/Program.cs b/TimeTracker.Server/Program.cs index 5f12a98..6bfd5b6 100644 --- a/TimeTracker.Server/Program.cs +++ b/TimeTracker.Server/Program.cs @@ -1,5 +1,9 @@ +using TimeTracker.Server; + var builder = WebApplication.CreateBuilder(args); +TimeTracker.TrackerDatabase.InitializePath(WebConstants.WebDatabaseName); + // Add services to the container. builder.Services.AddControllers(); diff --git a/TimeTracker.Server/WebConstants.cs b/TimeTracker.Server/WebConstants.cs new file mode 100644 index 0000000..607e0d5 --- /dev/null +++ b/TimeTracker.Server/WebConstants.cs @@ -0,0 +1,6 @@ +namespace TimeTracker.Server; + +public static class WebConstants +{ + public const string WebDatabaseName = "web.tracker.db"; +} From 4077d0cbc2050ebb9adac9382dc0e94f359df470 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 20:44:01 +0200 Subject: [PATCH 07/11] Introduce TimeTracker.Tests --- TimeTracker.Tests/TimeTracker.Tests.csproj | 24 ++++++++++++++++++++++ TimeTracker.Tests/UnitTest1.cs | 15 ++++++++++++++ TimeTracker.Tests/Usings.cs | 1 + TimeTracker.sln | 8 +++++++- 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 TimeTracker.Tests/TimeTracker.Tests.csproj create mode 100644 TimeTracker.Tests/UnitTest1.cs create mode 100644 TimeTracker.Tests/Usings.cs diff --git a/TimeTracker.Tests/TimeTracker.Tests.csproj b/TimeTracker.Tests/TimeTracker.Tests.csproj new file mode 100644 index 0000000..62d720c --- /dev/null +++ b/TimeTracker.Tests/TimeTracker.Tests.csproj @@ -0,0 +1,24 @@ + + + + net7.0 + enable + enable + + false + + + + + + + + + + + + + + + + diff --git a/TimeTracker.Tests/UnitTest1.cs b/TimeTracker.Tests/UnitTest1.cs new file mode 100644 index 0000000..418f7d6 --- /dev/null +++ b/TimeTracker.Tests/UnitTest1.cs @@ -0,0 +1,15 @@ +namespace TimeTracker.Tests; + +public class Tests +{ + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + Assert.Pass(); + } +} \ No newline at end of file diff --git a/TimeTracker.Tests/Usings.cs b/TimeTracker.Tests/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/TimeTracker.Tests/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/TimeTracker.sln b/TimeTracker.sln index d729405..39ee23d 100644 --- a/TimeTracker.sln +++ b/TimeTracker.sln @@ -7,7 +7,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker.Server", "TimeT EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker.Core", "TimeTracker.Core\TimeTracker.Core.csproj", "{E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}" EndProject -Global` +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeTracker.Tests", "TimeTracker.Tests\TimeTracker.Tests.csproj", "{C8D7C701-1AEE-4CAE-AAAB-EFCE377D9271}" +EndProject +Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -25,5 +27,9 @@ Global` {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Debug|Any CPU.Build.0 = Debug|Any CPU {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Release|Any CPU.ActiveCfg = Release|Any CPU {E46CD9A9-210A-4F7A-9F53-E8AAD80BA919}.Release|Any CPU.Build.0 = Release|Any CPU + {C8D7C701-1AEE-4CAE-AAAB-EFCE377D9271}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8D7C701-1AEE-4CAE-AAAB-EFCE377D9271}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8D7C701-1AEE-4CAE-AAAB-EFCE377D9271}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8D7C701-1AEE-4CAE-AAAB-EFCE377D9271}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From def63cd16be536f5e05a57a76459ef27596e1bb6 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 21:16:03 +0200 Subject: [PATCH 08/11] Add Uuid to TrackedTime --- .../Database/Migrations/M4_AddUuid.cs | 31 +++++++++++++++++++ TimeTracker.Core/Database/Migrator.cs | 1 + TimeTracker.Core/Database/Tables/ControlDb.cs | 3 ++ .../Database/Tables/TrackedTimeDb.cs | 5 +++ TimeTracker.Core/Models/TimeTracker.cs | 1 + 5 files changed, 41 insertions(+) create mode 100644 TimeTracker.Core/Database/Migrations/M4_AddUuid.cs diff --git a/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs b/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs new file mode 100644 index 0000000..fc73bda --- /dev/null +++ b/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs @@ -0,0 +1,31 @@ +using SQLite; + +namespace TimeTracker.Database.Migrations; + +public class M4_AddUuid : IDbMigration +{ + public Task Do(SQLiteAsyncConnection db) + { + // iterate over TrackedTimeDb and add Guid.NewGuid() as parameter + return db.ExecuteAsync("ALTER TABLE TrackedTimeDb ADD Uuid text") + .ContinueWith(async _ => + { + var all = await db.Table().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(); + } +} diff --git a/TimeTracker.Core/Database/Migrator.cs b/TimeTracker.Core/Database/Migrator.cs index e2e632a..74b5e1e 100644 --- a/TimeTracker.Core/Database/Migrator.cs +++ b/TimeTracker.Core/Database/Migrator.cs @@ -17,6 +17,7 @@ public static class Migrator new M1_InitializeDb(), new M2_AddCategories(), new M3_AddStatuses(), + new M4_AddUuid(), }; public static async Task Migrate(SQLiteAsyncConnection db) diff --git a/TimeTracker.Core/Database/Tables/ControlDb.cs b/TimeTracker.Core/Database/Tables/ControlDb.cs index ba33258..9a507c7 100644 --- a/TimeTracker.Core/Database/Tables/ControlDb.cs +++ b/TimeTracker.Core/Database/Tables/ControlDb.cs @@ -2,6 +2,9 @@ namespace TimeTracker; +/// +/// Table to store control parameters. +/// public class ControlDb : ITable { public enum ParamId diff --git a/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs b/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs index 6ab5bfe..7087219 100644 --- a/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs +++ b/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs @@ -2,11 +2,16 @@ namespace TimeTracker; +/// +/// +/// 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; } diff --git a/TimeTracker.Core/Models/TimeTracker.cs b/TimeTracker.Core/Models/TimeTracker.cs index c226be4..e8d9882 100644 --- a/TimeTracker.Core/Models/TimeTracker.cs +++ b/TimeTracker.Core/Models/TimeTracker.cs @@ -53,6 +53,7 @@ public TrackedTimeDb ToDb() return new TrackedTimeDb { Id = Id, + Uuid = new Guid(), Name = Name, StartTime = StartTime, ElapsedTime = ElapsedTime, From 9398a1a06ce23e4f6cd2c7d8b8df38163a41c8d8 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 21:42:48 +0200 Subject: [PATCH 09/11] Introduce .editorconfig --- .editorconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3bf394b --- /dev/null +++ b/.editorconfig @@ -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 From cd1e4947ea698fa2b6300fd70f727c5c88d82d83 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sat, 8 Jul 2023 22:43:45 +0200 Subject: [PATCH 10/11] Add Categories/update method --- .gitignore | 1 + .../Database/Tables/CategoryDb.cs | 33 ++++++-- TimeTracker.Core/Database/TrackerDatabase.cs | 62 +++++++++++---- .../Controllers/CategoriesController.cs | 34 ++++++++- TimeTracker.Tests/CategoriesTest.cs | 75 +++++++++++++++++++ TimeTracker.Tests/UnitTest1.cs | 15 ---- 6 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 TimeTracker.Tests/CategoriesTest.cs delete mode 100644 TimeTracker.Tests/UnitTest1.cs diff --git a/.gitignore b/.gitignore index 0759950..4c50e11 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ riderModule.iml /_ReSharper.Caches/ .idea/ *~ +*.user diff --git a/TimeTracker.Core/Database/Tables/CategoryDb.cs b/TimeTracker.Core/Database/Tables/CategoryDb.cs index 7c122fa..9956525 100644 --- a/TimeTracker.Core/Database/Tables/CategoryDb.cs +++ b/TimeTracker.Core/Database/Tables/CategoryDb.cs @@ -12,16 +12,15 @@ public enum CategoryState Disabled = 1, } - [PrimaryKey, AutoIncrement] - public int Id { get; set; } + [PrimaryKey, AutoIncrement, JsonIgnore] public int Id { get; set; } public int State { get; set; } - [Ignore] + [Ignore, JsonIgnore] public CategoryState StateEnum { - get => (CategoryState) State; - set => State = (int) value; + get => (CategoryState)State; + set => State = (int)value; } public string Name { get; set; } @@ -33,16 +32,34 @@ public CategoryState StateEnum public string ColorString { get; set; } - [Ignore] - [JsonIgnore] + [Ignore, JsonIgnore] public Color ColorObject { get => Color.Parse(ColorString); - set => ColorString = value.ToArgbHex(); + 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); + } } diff --git a/TimeTracker.Core/Database/TrackerDatabase.cs b/TimeTracker.Core/Database/TrackerDatabase.cs index 957e968..5bededd 100644 --- a/TimeTracker.Core/Database/TrackerDatabase.cs +++ b/TimeTracker.Core/Database/TrackerDatabase.cs @@ -58,27 +58,27 @@ public Task> GetCategories() internal static Task> DefaultCategories() { - return Task.FromResult(new List() + return Task.FromResult(new List { - new CategoryDb() + new CategoryDb { Name = "Code", ColorObject = Colors.DarkSlateBlue, CategoryGroup = Work, }, - new CategoryDb() + new CategoryDb { Name = "Tasks", ColorObject = Colors.DarkCyan, CategoryGroup = Work, }, - new CategoryDb() + new CategoryDb { Name = "Call", ColorObject = Colors.DarkGreen, CategoryGroup = Work, }, - new CategoryDb() + new CategoryDb { Name = "Review", ColorObject = Colors.DarkSeaGreen, @@ -90,43 +90,43 @@ internal static Task> DefaultCategories() ColorObject = Colors.DarkSeaGreen, CategoryGroup = Work, }, - new CategoryDb() + new CategoryDb { Name = "Pet", ColorObject = Colors.DarkKhaki, CategoryGroup = Work, }, - new CategoryDb() + new CategoryDb { Name = "Sport", ColorObject = Colors.LightGray, CategoryGroup = Personal, }, - new CategoryDb() + new CategoryDb { Name = "Game", ColorObject = Colors.DarkOrchid, CategoryGroup = Personal, }, - new CategoryDb() + new CategoryDb { Name = "Eat", ColorObject = Colors.DarkViolet, CategoryGroup = Personal, }, - new CategoryDb() + new CategoryDb { Name = "Leisure", ColorObject = Colors.DarkGoldenrod, CategoryGroup = Personal, }, - new CategoryDb() + new CategoryDb { Name = "Art", ColorObject = Colors.DarkRed, CategoryGroup = Personal, }, - new CategoryDb() + new CategoryDb { Name = "Family", ColorObject = Colors.DarkOliveGreen, @@ -168,6 +168,28 @@ public async Task Update(TrackedTimeDb tracker) } } + public async Task Update(CategoryDb category) + { + await _dbSemaphore.WaitAsync(); + + try + { + if (category.Id == 0) + { + await _database.InsertAsync(category); + category.Id = category.Id; + } + else + { + await _database.UpdateAsync(category); + } + } + finally + { + _dbSemaphore.Release(); + } + } + public async Task> ListRunningTrackers() { await _dbSemaphore.WaitAsync(); @@ -188,7 +210,7 @@ public async Task> ListRunningTrackers() } } - public async Task Remove(TrackedTimeDb tracker) + public async Task RemoveAsync(TrackedTimeDb tracker) { await _dbSemaphore.WaitAsync(); @@ -201,4 +223,18 @@ public async Task Remove(TrackedTimeDb tracker) _dbSemaphore.Release(); } } + + public async Task RemoveAsync(CategoryDb category) + { + await _dbSemaphore.WaitAsync(); + + try + { + await _database.DeleteAsync(category); + } + finally + { + _dbSemaphore.Release(); + } + } } diff --git a/TimeTracker.Server/Controllers/CategoriesController.cs b/TimeTracker.Server/Controllers/CategoriesController.cs index d9b4127..6783f3e 100644 --- a/TimeTracker.Server/Controllers/CategoriesController.cs +++ b/TimeTracker.Server/Controllers/CategoriesController.cs @@ -1,8 +1,8 @@ +using System.Text.Json; using Microsoft.AspNetCore.Mvc; namespace TimeTracker.Server.Controllers; -[ApiController] [Route("[controller]")] public class CategoriesController : ControllerBase { @@ -13,12 +13,40 @@ public CategoriesController(ILogger logger) _logger = logger; } - [HttpGet(Name = "GetWeatherForecast")] + [HttpGet(template: "get", Name = "GetAllCategories")] public async Task> Get() { var db = await TrackerDatabase.Instance; var categories = await db.GetCategories(); - return categories.ToArray(); + return categories; + } + + [HttpPost(template: "update", Name = "UpdateCategories")] + public async Task UpdateCategories([FromBody] List newCategories) + { + var db = await TrackerDatabase.Instance; + var oldCategories = await db.GetCategories(); + + var added = newCategories.Except(oldCategories); + var removed = oldCategories.Except(newCategories); + var modified = newCategories.Intersect(oldCategories); + + foreach (var category in added) + { + await db.InsertAsync(category); + } + + foreach (var category in removed) + { + await db.RemoveAsync(category); + } + + foreach (var category in modified) + { + await db.Update(category); + } + + return Ok(); } } diff --git a/TimeTracker.Tests/CategoriesTest.cs b/TimeTracker.Tests/CategoriesTest.cs new file mode 100644 index 0000000..a7ebbff --- /dev/null +++ b/TimeTracker.Tests/CategoriesTest.cs @@ -0,0 +1,75 @@ +using System.Diagnostics; +using Microsoft.Maui.Graphics; + +namespace TimeTracker.Tests; + +public class CategoriesTest +{ + [SetUp] + public void Setup() + { + } + + [Test] + public void TestIfCategoriesEquals() + { + var cat1 = new CategoryDb + { + Id = 1, + Name = "Review", + CategoryGroup = "Work", + StateEnum = CategoryDb.CategoryState.Enabled, + ColorObject = Colors.Aqua, + }; + + var cat2 = new CategoryDb + { + Id = 2, + Name = "Review", + CategoryGroup = "Work", + StateEnum = CategoryDb.CategoryState.Enabled, + ColorObject = Colors.Aqua, + }; + + Assert.That(cat1, Is.EqualTo(cat2)); + } + + [Test] + public void TestIfCategoriesList() + { + var cat1 = new CategoryDb + { + Id = 1, + Name = "Task1", + CategoryGroup = "Work", + StateEnum = CategoryDb.CategoryState.Enabled, + ColorObject = Colors.Aqua, + }; + + var cat2 = new CategoryDb + { + Id = 2, + Name = "Task2", + CategoryGroup = "Work", + StateEnum = CategoryDb.CategoryState.Enabled, + ColorObject = Colors.Beige, + }; + + var cat3 = new CategoryDb + { + Id = 3, + Name = "Task3", + CategoryGroup = "Personal", + StateEnum = CategoryDb.CategoryState.Enabled, + ColorObject = Colors.Chocolate, + }; + + var list1 = new List { cat1, cat2 }; + var list2 = new List { cat2, cat3 }; + + var intersection = list1.Intersect(list2).ToList(); + + Assert.That(intersection, Has.Count.EqualTo(1)); + CollectionAssert.Contains(intersection, cat2); + } +} diff --git a/TimeTracker.Tests/UnitTest1.cs b/TimeTracker.Tests/UnitTest1.cs deleted file mode 100644 index 418f7d6..0000000 --- a/TimeTracker.Tests/UnitTest1.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace TimeTracker.Tests; - -public class Tests -{ - [SetUp] - public void Setup() - { - } - - [Test] - public void Test1() - { - Assert.Pass(); - } -} \ No newline at end of file From 8eb9696898a20eb83087417c23a5d7ac0be6d0b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Vornychev Date: Sun, 9 Jul 2023 00:14:44 +0200 Subject: [PATCH 11/11] Replace ElapsedTime column with EndTime + Timestamp --- .../M005_AddTimestampAndReworkElapsed.cs | 79 +++++++++++++++++++ .../Database/Migrations/M4_AddUuid.cs | 51 +++++++++--- TimeTracker.Core/Database/Migrator.cs | 11 ++- .../Database/Tables/TrackedTimeDb.cs | 9 ++- TimeTracker.Core/Models/TimeTracker.cs | 4 +- TimeTracker.Core/TimeTracker.Core.csproj | 1 + 6 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 TimeTracker.Core/Database/Migrations/M005_AddTimestampAndReworkElapsed.cs diff --git a/TimeTracker.Core/Database/Migrations/M005_AddTimestampAndReworkElapsed.cs b/TimeTracker.Core/Database/Migrations/M005_AddTimestampAndReworkElapsed.cs new file mode 100644 index 0000000..bf3b305 --- /dev/null +++ b/TimeTracker.Core/Database/Migrations/M005_AddTimestampAndReworkElapsed.cs @@ -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().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(); + } +} + +/// +/// Data model at the time of migration +/// +[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; } + + /// + /// Last modification time - considering Start and Elapsed may ba changed later + /// + public DateTime Timestamp { get; set; } + + public enum TrackingStatus + { + Completed = 0, + + /// + /// Currently running + /// + Running = 1, + } +} diff --git a/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs b/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs index fc73bda..334909d 100644 --- a/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs +++ b/TimeTracker.Core/Database/Migrations/M4_AddUuid.cs @@ -4,19 +4,16 @@ namespace TimeTracker.Database.Migrations; public class M4_AddUuid : IDbMigration { - public Task Do(SQLiteAsyncConnection db) + public async Task Do(SQLiteAsyncConnection db) { - // iterate over TrackedTimeDb and add Guid.NewGuid() as parameter - return db.ExecuteAsync("ALTER TABLE TrackedTimeDb ADD Uuid text") - .ContinueWith(async _ => - { - var all = await db.Table().ToListAsync(); - foreach (var trackedTimeDb in all) - { - trackedTimeDb.Uuid = Guid.NewGuid(); - await db.UpdateAsync(trackedTimeDb); - } - }); + await db.ExecuteAsync("ALTER TABLE TrackedTimeDb ADD Uuid text"); + + var all = await db.Table().ToListAsync(); + foreach (var trackedTimeDb in all) + { + trackedTimeDb.Uuid = Guid.NewGuid(); + await db.UpdateAsync(trackedTimeDb); + } } public Task UnDo(SQLiteAsyncConnection db) @@ -29,3 +26,33 @@ public string Serialize() throw new NotImplementedException(); } } + +/// +/// Data model at the time of migration +/// +[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, + + /// + /// Currently running + /// + Running = 1, + } +} diff --git a/TimeTracker.Core/Database/Migrator.cs b/TimeTracker.Core/Database/Migrator.cs index 74b5e1e..b2501d6 100644 --- a/TimeTracker.Core/Database/Migrator.cs +++ b/TimeTracker.Core/Database/Migrator.cs @@ -18,6 +18,7 @@ public static class Migrator new M2_AddCategories(), new M3_AddStatuses(), new M4_AddUuid(), + new M005_AddTimestampAndReworkElapsed(), }; public static async Task Migrate(SQLiteAsyncConnection db) @@ -37,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)); } } diff --git a/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs b/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs index 7087219..d12cd29 100644 --- a/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs +++ b/TimeTracker.Core/Database/Tables/TrackedTimeDb.cs @@ -16,10 +16,17 @@ public class TrackedTimeDb : ITable 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; } + /// + /// Last modification time - considering Start and Elapsed may ba changed later + /// + public DateTime Timestamp { get; set; } + [Ignore] public TrackingStatus StatusEnum { diff --git a/TimeTracker.Core/Models/TimeTracker.cs b/TimeTracker.Core/Models/TimeTracker.cs index e8d9882..f8d4bea 100644 --- a/TimeTracker.Core/Models/TimeTracker.cs +++ b/TimeTracker.Core/Models/TimeTracker.cs @@ -30,7 +30,7 @@ public TimeTracker(TrackedTimeDb tracker) StartTime = tracker.StartTime; IsRunning = tracker.StatusEnum == TrackedTimeDb.TrackingStatus.Running; if (IsRunning == false) - EndTime = tracker.StartTime + tracker.ElapsedTime; + EndTime = tracker.EndTime; } public TimeTracker Start() @@ -56,7 +56,7 @@ public TrackedTimeDb ToDb() Uuid = new Guid(), Name = Name, StartTime = StartTime, - ElapsedTime = ElapsedTime, + EndTime = EndTime, StatusEnum = IsRunning ? TrackedTimeDb.TrackingStatus.Running : TrackedTimeDb.TrackingStatus.Completed, diff --git a/TimeTracker.Core/TimeTracker.Core.csproj b/TimeTracker.Core/TimeTracker.Core.csproj index 5e02f2f..af093fc 100644 --- a/TimeTracker.Core/TimeTracker.Core.csproj +++ b/TimeTracker.Core/TimeTracker.Core.csproj @@ -4,6 +4,7 @@ net7.0 enable enable + TimeTracker