From 02c8e0ea3c7198e08358851d4402aa79549e3bd8 Mon Sep 17 00:00:00 2001 From: Daniel Triendl Date: Mon, 13 Apr 2026 10:42:11 +0200 Subject: [PATCH] Get Availability and extension for all users --- server/3cx_Tapi.sln | 6 +++ .../src/CPATapi.Server/CPATapi.Server.csproj | 4 ++ .../Controllers/AvailabilityController.cs | 15 ++++--- .../Interfaces/IZeitConsensRepository.cs | 4 +- .../src/CPATapi.Server/Models/Availability.cs | 7 +++- server/src/CPATapi.Server/Models/Stamp.cs | 7 ---- .../Repository/ZeitConsensRepository.cs | 41 +++++++++++-------- .../CPATapi.Server.Tests.csproj | 28 +++++++++++++ .../ZeitConsensRepositoryTests.cs | 37 +++++++++++++++++ 9 files changed, 116 insertions(+), 33 deletions(-) delete mode 100644 server/src/CPATapi.Server/Models/Stamp.cs create mode 100644 server/test/CPATapi.Server.Tests/CPATapi.Server.Tests.csproj create mode 100644 server/test/CPATapi.Server.Tests/ZeitConsensRepositoryTests.cs diff --git a/server/3cx_Tapi.sln b/server/3cx_Tapi.sln index 6bd12f8..ea8368f 100644 --- a/server/3cx_Tapi.sln +++ b/server/3cx_Tapi.sln @@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPATapi.Client", "src\CPATa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPATapi.Client.Tests", "test\CPATapi.Client.Tests\CPATapi.Client.Tests.csproj", "{17F37791-4F68-46D5-8CF5-5F1736F6776E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPATapi.Server.Tests", "test\CPATapi.Server.Tests\CPATapi.Server.Tests.csproj", "{72486DC9-2C7D-409B-9E14-6D90F67B92CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,6 +32,10 @@ Global {17F37791-4F68-46D5-8CF5-5F1736F6776E}.Debug|Any CPU.Build.0 = Debug|Any CPU {17F37791-4F68-46D5-8CF5-5F1736F6776E}.Release|Any CPU.ActiveCfg = Release|Any CPU {17F37791-4F68-46D5-8CF5-5F1736F6776E}.Release|Any CPU.Build.0 = Release|Any CPU + {72486DC9-2C7D-409B-9E14-6D90F67B92CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72486DC9-2C7D-409B-9E14-6D90F67B92CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72486DC9-2C7D-409B-9E14-6D90F67B92CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72486DC9-2C7D-409B-9E14-6D90F67B92CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/server/src/CPATapi.Server/CPATapi.Server.csproj b/server/src/CPATapi.Server/CPATapi.Server.csproj index 008d76e..96ceb73 100644 --- a/server/src/CPATapi.Server/CPATapi.Server.csproj +++ b/server/src/CPATapi.Server/CPATapi.Server.csproj @@ -30,4 +30,8 @@ + + + + diff --git a/server/src/CPATapi.Server/Controllers/AvailabilityController.cs b/server/src/CPATapi.Server/Controllers/AvailabilityController.cs index 0c9b2a9..df36957 100644 --- a/server/src/CPATapi.Server/Controllers/AvailabilityController.cs +++ b/server/src/CPATapi.Server/Controllers/AvailabilityController.cs @@ -11,19 +11,24 @@ namespace CPATapi.Server.Controllers; public class AvailabilityController(IZeitConsensRepository zeitConsens) : ControllerBase { [HttpGet] - [Route("users")] - [ProducesResponseType>(StatusCodes.Status200OK)] + [Route("")] + [ProducesResponseType>(StatusCodes.Status200OK)] public async Task GetUsers() { - return Ok(await zeitConsens.GetUsersAsync()); + return Ok(await zeitConsens.GetUsersAvailabilityAsync(DateTime.Now.Date, DateTime.Now.AddDays(1).Date)); } [HttpGet] [Route("{user}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetAvailability(string user) { - var stampCount = (await zeitConsens.GetStampsAsync(user, DateTime.Now.Date, DateTime.Now.AddDays(1).Date)).Count(); - return Ok(new Availability { User = user, LoggedIn = stampCount % 2 != 0 }); + var availability = await zeitConsens.GetUserAvailabilityAsync(user, DateTime.Now.Date, DateTime.Now.AddDays(1).Date); + if (availability == null) + { + return NotFound(); + } + return Ok(availability); } } diff --git a/server/src/CPATapi.Server/Interfaces/IZeitConsensRepository.cs b/server/src/CPATapi.Server/Interfaces/IZeitConsensRepository.cs index 3a0024c..a7ae06b 100644 --- a/server/src/CPATapi.Server/Interfaces/IZeitConsensRepository.cs +++ b/server/src/CPATapi.Server/Interfaces/IZeitConsensRepository.cs @@ -4,6 +4,6 @@ namespace CPATapi.Server.Interfaces; public interface IZeitConsensRepository : IRepository { - Task> GetUsersAsync(); - Task> GetStampsAsync(string user, DateTime from, DateTime to); + Task> GetUsersAvailabilityAsync(DateTime from, DateTime to); + Task GetUserAvailabilityAsync(string user, DateTime from, DateTime to); } diff --git a/server/src/CPATapi.Server/Models/Availability.cs b/server/src/CPATapi.Server/Models/Availability.cs index 24d2e19..73e295c 100644 --- a/server/src/CPATapi.Server/Models/Availability.cs +++ b/server/src/CPATapi.Server/Models/Availability.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; namespace CPATapi.Server.Models; @@ -5,7 +6,9 @@ namespace CPATapi.Server.Models; public class Availability { [JsonPropertyName("user")] - public string User { get; set; } + public required string MA_USER_NAME { get; set; } [JsonPropertyName("loggedIn")] - public bool LoggedIn { get; set; } + public bool LOGGED_IN { get; set; } + [JsonPropertyName("extension")] + public string? US_EXTENSION { get; set; } } diff --git a/server/src/CPATapi.Server/Models/Stamp.cs b/server/src/CPATapi.Server/Models/Stamp.cs deleted file mode 100644 index 5ad9a15..0000000 --- a/server/src/CPATapi.Server/Models/Stamp.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CPATapi.Server.Models; - -public class Stamp -{ - public int MA_NR { get; set; } - public DateTime BU_BU { get; set; } -} diff --git a/server/src/CPATapi.Server/Repository/ZeitConsensRepository.cs b/server/src/CPATapi.Server/Repository/ZeitConsensRepository.cs index 28344a4..4772d1c 100644 --- a/server/src/CPATapi.Server/Repository/ZeitConsensRepository.cs +++ b/server/src/CPATapi.Server/Repository/ZeitConsensRepository.cs @@ -6,29 +6,36 @@ namespace CPATapi.Server.Repository; internal class ZeitConsensRepository(IConfiguration config) : Repository(config), IZeitConsensRepository { - public async Task> GetUsersAsync() + private const string SelectStampsQuery = """ + SELECT + ma.MA_USER_NAME + ,bu.LOGGED_IN + ,us.US_EXTENSION + FROM dbo.MA_DATEN ma + INNER JOIN projectmanagement.dbo.CP_USER us on us.US_LOGINNAME = ma.MA_USER_NAME + OUTER APPLY ( + SELECT count(*) % 2 AS LOGGED_IN + FROM dbo.BU + WHERE bu.BU_MA_NR = ma.MA_NR + AND + BU_BU >= @from AND BU_BU < @to + ) bu + WHERE + ma.MA_USER_AKTIV = 1 + """; + + public async Task> GetUsersAvailabilityAsync(DateTime from, DateTime to) { await using var con = await OpenAsync("ZeitConsens"); - return await con.QueryAsync(""" - SELECT DISTINCT MA_USER_NAME - FROM dbo.MA_DATEN - WHERE MA_USER_NAME IS NOT NULL AND MA_USER_AKTIV = 1 - ORDER BY MA_USER_NAME - """); + return await con.QueryAsync(SelectStampsQuery, new { from, to }); } - public async Task> GetStampsAsync(string user, DateTime from, DateTime to) + public async Task GetUserAvailabilityAsync(string user, DateTime from, DateTime to) { await using var con = await OpenAsync("ZeitConsens"); - return await con.QueryAsync(""" - SELECT - MA_NR - ,BU_BU - FROM dbo.BU - INNER JOIN dbo.MA_DATEN on MA_NR = BU_MA_NR - WHERE - MA_USER_NAME = @user AND - BU_BU >= @from AND BU_BU < @to + return await con.QueryFirstOrDefaultAsync($""" + {SelectStampsQuery} + AND ma.MA_USER_NAME = @user """, new { user, from, to }); } } diff --git a/server/test/CPATapi.Server.Tests/CPATapi.Server.Tests.csproj b/server/test/CPATapi.Server.Tests/CPATapi.Server.Tests.csproj new file mode 100644 index 0000000..7f3e199 --- /dev/null +++ b/server/test/CPATapi.Server.Tests/CPATapi.Server.Tests.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + latest + enable + enable + false + a7b40068-a2f6-4c63-bbd9-0fd346908fb0 + + + + + + + + + + + + + + + + + + + diff --git a/server/test/CPATapi.Server.Tests/ZeitConsensRepositoryTests.cs b/server/test/CPATapi.Server.Tests/ZeitConsensRepositoryTests.cs new file mode 100644 index 0000000..2bb80cc --- /dev/null +++ b/server/test/CPATapi.Server.Tests/ZeitConsensRepositoryTests.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Configuration; +using CPATapi.Server.Repository; + +namespace CPATapi.Server.Tests; + +public class ZeitConsensRepositoryTest +{ + [OneTimeSetUp] + public void Setup() + { + // the type specified here is just so the secrets library can + // find the UserSecretId we added in the csproj file + var builder = new ConfigurationBuilder().AddUserSecrets(); + + _configuration = builder.Build(); + } + + private IConfigurationRoot _configuration { get; set; } + + [Test] + public async Task TestGetUsersAvailabilityAsync() + { + var zcRepo = new ZeitConsensRepository(_configuration); + var availability= (await zcRepo.GetUsersAvailabilityAsync(DateTime.Now.Date, DateTime.Now.AddDays(1).Date)).ToList(); + Assert.That(availability, Is.Not.Empty); + } + + [Test] + public async Task TestGetUserAvailabilityAsync() + { + var zcRepo = new ZeitConsensRepository(_configuration); + var availability = await zcRepo.GetUserAvailabilityAsync("CPATRD", DateTime.Now.Date, DateTime.Now.AddDays(1).Date); + Assert.That(availability, Is.Not.Null); + Assert.That(availability.MA_USER_NAME, Is.EqualTo("CPATRD")); + Assert.That(availability.US_EXTENSION, Is.EqualTo("203")); + } +}