Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ff3cecf3a | |||
| e83d5e9e0f | |||
| c6776503e3 | |||
| 69f8565a61 |
+22
-2
@@ -1,6 +1,6 @@
|
|||||||
# LeanderShiftPlannerV2 – Technische Dokumentation
|
# LeanderShiftPlannerV2 – Technische Dokumentation
|
||||||
|
|
||||||
**Version:** 2.0.1
|
**Version:** 2.0.2
|
||||||
**Sprache:** C# / .NET 9.0
|
**Sprache:** C# / .NET 9.0
|
||||||
**Framework:** Avalonia UI 11.2.1
|
**Framework:** Avalonia UI 11.2.1
|
||||||
**Repository:** https://gitea.leanderserver.de/leander19961/LeanderShiftPlannerV2
|
**Repository:** https://gitea.leanderserver.de/leander19961/LeanderShiftPlannerV2
|
||||||
@@ -343,4 +343,24 @@ Die Oberfläche ist mit **Avalonia UI** und dem **Fluent Theme** umgesetzt. Sie
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Dokumentation erstellt am 22.04.2026*
|
**Google API Zugangsdaten einrichten**
|
||||||
|
Da das Programm über deinen persönlichen Google Account auf den Kalender zugreift, musst du einmalig eigene OAuth-Zugangsdaten in der Google Cloud Console erstellen und in der Datei Resources/templates/googleapi.txt hinterlegen.
|
||||||
|
|
||||||
|
Schritt-für-Schritt-Anleitung:
|
||||||
|
|
||||||
|
- Öffne die [Google Cloud Console](https://console.cloud.google.com/) und melde dich mit deinem Google-Konto an.
|
||||||
|
- Erstelle ein neues Projekt oder wähle ein bestehendes aus.
|
||||||
|
- Navigiere zu „APIs & Dienste" → „Bibliothek" und suche nach „Google Calendar API". Klicke auf „Aktivieren".
|
||||||
|
- Gehe zu „APIs & Dienste" → „Anmeldedaten" und klicke auf „Anmeldedaten erstellen" → „OAuth-Client-ID".
|
||||||
|
- Falls noch nicht geschehen, wirst du aufgefordert, den OAuth-Zustimmungsbildschirm zu konfigurieren. Wähle dort „Extern" und fülle die Pflichtfelder (App-Name, Support-E-Mail) aus. Alle anderen Felder können leer bleiben.
|
||||||
|
- Als Anwendungstyp wähle „Desktop-App" und vergib einen beliebigen Namen.
|
||||||
|
- Nach der Erstellung siehst du deine Client-ID und dein Client-Secret. Trage beide Werte in die Datei Resources/templates/googleapi.txt ein:
|
||||||
|
|
||||||
|
`ClientId=deine-client-id`
|
||||||
|
`ClientSecret=dein-client-secret`
|
||||||
|
|
||||||
|
Beim ersten Programmstart öffnet sich einmalig ein Browser-Fenster, in dem du den Zugriff für dein Google-Konto bestätigst. Danach wird das Zugriffstoken lokal gespeichert und der Dialog erscheint nicht mehr.
|
||||||
|
|
||||||
|
**Wichtig ist:** Es muss am Ende ein OAuth2-Client für die Google Calendar API des eigenen Kontos freigegeben sein, die ClientID und das ClientSecret werden vom Programm für den Kalenderzugriff benötigt.
|
||||||
|
|
||||||
|
*Dokumentation erstellt am 28.04.2026*
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 leander19961
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -7,7 +7,7 @@ namespace LeanderShiftPlannerV2.FileIO;
|
|||||||
|
|
||||||
public static class GoogleApiLoader
|
public static class GoogleApiLoader
|
||||||
{
|
{
|
||||||
public static bool CheckFileSystem()
|
public static bool CheckFileSystem_ReadFile()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(Constants.DataPath))
|
if (!Directory.Exists(Constants.DataPath))
|
||||||
Directory.CreateDirectory(Constants.DataPath);
|
Directory.CreateDirectory(Constants.DataPath);
|
||||||
@@ -20,10 +20,22 @@ public static class GoogleApiLoader
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool CheckFileSystem_ReadToken()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(Constants.DataPath))
|
||||||
|
Directory.CreateDirectory(Constants.DataPath);
|
||||||
|
if (!File.Exists(Constants.DataPath + Constants.GoogleApiTokenPath))
|
||||||
|
{
|
||||||
|
File.Create(Constants.DataPath + Constants.GoogleApiTokenPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static Dictionary<string, string> ReadFile()
|
public static Dictionary<string, string> ReadFile()
|
||||||
{
|
{
|
||||||
if (!CheckFileSystem()) return new Dictionary<string, string>();
|
if (!CheckFileSystem_ReadFile()) return new Dictionary<string, string>();
|
||||||
Dictionary<string, string> apiSecrets = new Dictionary<string, string>();
|
Dictionary<string, string> apiSecrets = new Dictionary<string, string>();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -51,4 +63,30 @@ public static class GoogleApiLoader
|
|||||||
|
|
||||||
return apiSecrets;
|
return apiSecrets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void WriteToken(string token)
|
||||||
|
{
|
||||||
|
if (File.Exists(Constants.DataPath + Constants.GoogleApiTokenPath))
|
||||||
|
File.Delete(Constants.DataPath + Constants.GoogleApiTokenPath);
|
||||||
|
File.Create(Constants.DataPath + Constants.GoogleApiTokenPath).Close();
|
||||||
|
File.WriteAllText(Constants.DataPath + Constants.GoogleApiTokenPath, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? ReadToken()
|
||||||
|
{
|
||||||
|
if (!CheckFileSystem_ReadToken()) return null;
|
||||||
|
string tokenString = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tokenString = File.ReadAllText(Constants.DataPath + Constants.GoogleApiTokenPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
Console.WriteLine($"Fehler beim Lesen der Datei: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tokenString.Equals(String.Empty) ? null : tokenString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
<ApplicationIcon>Resources/icon/ShiftPlannerIcon.ico</ApplicationIcon>
|
<ApplicationIcon>Resources/icon/ShiftPlannerIcon.ico</ApplicationIcon>
|
||||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
<PackageIcon>Resources\icon\ShiftPlannerIcon.png</PackageIcon>
|
<PackageIcon>Resources\icon\ShiftPlannerIcon.png</PackageIcon>
|
||||||
<AssemblyVersion>1.0.5</AssemblyVersion>
|
<AssemblyVersion>2.0.2</AssemblyVersion>
|
||||||
<FileVersion>1.0.5</FileVersion>
|
<FileVersion>2.0.2</FileVersion>
|
||||||
<NeutralLanguage>en</NeutralLanguage>
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
<Version>1.0.5</Version>
|
<Version>2.0.2</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ using System.Threading.Tasks;
|
|||||||
using LeanderShiftPlannerV2.Model;
|
using LeanderShiftPlannerV2.Model;
|
||||||
using LeanderShiftPlannerV2.Util;
|
using LeanderShiftPlannerV2.Util;
|
||||||
using Google.Apis.Auth.OAuth2;
|
using Google.Apis.Auth.OAuth2;
|
||||||
|
using Google.Apis.Auth.OAuth2.Flows;
|
||||||
|
using Google.Apis.Auth.OAuth2.Responses;
|
||||||
using Google.Apis.Calendar.v3;
|
using Google.Apis.Calendar.v3;
|
||||||
using Google.Apis.Services;
|
using Google.Apis.Services;
|
||||||
using LeanderShiftPlannerV2.FileIO;
|
using LeanderShiftPlannerV2.FileIO;
|
||||||
@@ -30,51 +32,81 @@ public class TimesheetService
|
|||||||
|
|
||||||
public async Task GenerateGoogleTimeSheets(List<Person> persons, bool randomize)
|
public async Task GenerateGoogleTimeSheets(List<Person> persons, bool randomize)
|
||||||
{
|
{
|
||||||
string[] scopes = { CalendarService.Scope.CalendarReadonly };
|
|
||||||
const string applicationName = "LeanderShiftPlanner";
|
const string applicationName = "LeanderShiftPlanner";
|
||||||
|
|
||||||
Dictionary<string, string> secrets = GoogleApiLoader.ReadFile();
|
Dictionary<string, string> secrets = GoogleApiLoader.ReadFile();
|
||||||
|
|
||||||
if (!secrets.ContainsKey(Constants.GoogleClientId) || !secrets.ContainsKey(Constants.GoogleClientSecret))
|
if (!secrets.ContainsKey(Constants.GoogleClientId) || !secrets.ContainsKey(Constants.GoogleClientSecret))
|
||||||
return;
|
return;
|
||||||
if (secrets[Constants.GoogleClientId].Equals(string.Empty) || secrets[Constants.GoogleClientSecret].Equals(string.Empty))
|
if (secrets[Constants.GoogleClientId].Equals(string.Empty) ||
|
||||||
|
secrets[Constants.GoogleClientSecret].Equals(string.Empty))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
UserCredential credential;
|
||||||
|
string[] scopes = { CalendarService.Scope.CalendarReadonly };
|
||||||
ClientSecrets clientSecrets = new ClientSecrets();
|
ClientSecrets clientSecrets = new ClientSecrets();
|
||||||
clientSecrets.ClientId = secrets[Constants.GoogleClientId];
|
clientSecrets.ClientId = secrets[Constants.GoogleClientId];
|
||||||
clientSecrets.ClientSecret = secrets[Constants.GoogleClientSecret];
|
clientSecrets.ClientSecret = secrets[Constants.GoogleClientSecret];
|
||||||
//clientSecrets.ClientId = "823920428427-ub5n9a9dkfpuvjlut4eneg2058so49o3.apps.googleusercontent.com";
|
string? tokenString = GoogleApiLoader.ReadToken();
|
||||||
//clientSecrets.ClientSecret = "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";
|
|
||||||
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, scopes, "user", CancellationToken.None);
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TokenResponse token = new TokenResponse { RefreshToken = tokenString };
|
||||||
|
|
||||||
|
credential = new UserCredential(
|
||||||
|
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
|
||||||
|
{
|
||||||
|
ClientSecrets = clientSecrets
|
||||||
|
}),
|
||||||
|
"user",
|
||||||
|
token
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!await credential.RefreshTokenAsync(CancellationToken.None))
|
||||||
|
{
|
||||||
|
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, scopes, "user",
|
||||||
|
CancellationToken.None);
|
||||||
|
GoogleApiLoader.WriteToken(credential.Token.RefreshToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, scopes, "user",
|
||||||
|
CancellationToken.None);
|
||||||
|
GoogleApiLoader.WriteToken(credential.Token.RefreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Erstelle den CalendarService
|
// Erstelle den CalendarService
|
||||||
var service = new CalendarService(new BaseClientService.Initializer()
|
CalendarService service = new CalendarService(new BaseClientService.Initializer()
|
||||||
{
|
{
|
||||||
HttpClientInitializer = credential,
|
HttpClientInitializer = credential,
|
||||||
ApplicationName = applicationName,
|
ApplicationName = applicationName,
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (Person person in persons)
|
foreach (Person person in persons)
|
||||||
{
|
{
|
||||||
foreach (string month in Constants.Months)
|
foreach (string month in Constants.Months)
|
||||||
{
|
{
|
||||||
if (month == Constants.Months[0]) continue;
|
if (month == Constants.Months[0]) continue;
|
||||||
List<(DateTime, DateTime, bool)> timesheetValue = new List<(DateTime, DateTime, bool)>();
|
List<(DateTime, DateTime, bool)> timesheetValue = new List<(DateTime, DateTime, bool)>();
|
||||||
|
|
||||||
// Aktuelles Datum
|
// Aktuelles Datum
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
// Erster Tag des aktuellen Monats (00:00:00 Uhr)
|
// Erster Tag des aktuellen Monats (00:00:00 Uhr)
|
||||||
DateTime firstDayOfMonth = new DateTime(now.Year, Constants.Months.IndexOf(month), 1, 0, 0, 0);
|
DateTime firstDayOfMonth = new DateTime(now.Year, Constants.Months.IndexOf(month), 1, 0, 0, 0);
|
||||||
// Letzter Tag des aktuellen Monats (23:59:59 Uhr)
|
// Letzter Tag des aktuellen Monats (23:59:59 Uhr)
|
||||||
DateTime lastDayOfMonth = new DateTime(now.Year, Constants.Months.IndexOf(month), DateTime.DaysInMonth(now.Year, Constants.Months.IndexOf(month)),
|
DateTime lastDayOfMonth = new DateTime(now.Year, Constants.Months.IndexOf(month),
|
||||||
|
DateTime.DaysInMonth(now.Year, Constants.Months.IndexOf(month)),
|
||||||
23, 59, 59);
|
23, 59, 59);
|
||||||
|
|
||||||
// Beispiel: Liste die nächsten 10 Ereignisse im ITM Kalender auf
|
// Beispiel: Liste die nächsten 10 Ereignisse im ITM Kalender auf
|
||||||
var requestItm = service.Events.List("on089es7kq4ugi3rqvslkn7720@group.calendar.google.com");
|
var requestItm = service.Events.List("on089es7kq4ugi3rqvslkn7720@group.calendar.google.com");
|
||||||
requestItm.TimeMinDateTimeOffset = firstDayOfMonth;
|
requestItm.TimeMinDateTimeOffset = firstDayOfMonth;
|
||||||
requestItm.TimeMaxDateTimeOffset = lastDayOfMonth;
|
requestItm.TimeMaxDateTimeOffset = lastDayOfMonth;
|
||||||
requestItm.SingleEvents = true;
|
requestItm.SingleEvents = true;
|
||||||
|
|
||||||
var requestWebService = service.Events.List("s4rdo3uk2on8ualv1goaktbiec@group.calendar.google.com");
|
var requestWebService = service.Events.List("s4rdo3uk2on8ualv1goaktbiec@group.calendar.google.com");
|
||||||
requestWebService.TimeMinDateTimeOffset = firstDayOfMonth;
|
requestWebService.TimeMinDateTimeOffset = firstDayOfMonth;
|
||||||
requestWebService.TimeMaxDateTimeOffset = lastDayOfMonth;
|
requestWebService.TimeMaxDateTimeOffset = lastDayOfMonth;
|
||||||
@@ -82,14 +114,14 @@ public class TimesheetService
|
|||||||
|
|
||||||
var eventsItm = await requestItm.ExecuteAsync();
|
var eventsItm = await requestItm.ExecuteAsync();
|
||||||
var eventsWebService = await requestWebService.ExecuteAsync();
|
var eventsWebService = await requestWebService.ExecuteAsync();
|
||||||
|
|
||||||
foreach (var eventItem in eventsItm.Items)
|
foreach (var eventItem in eventsItm.Items)
|
||||||
{
|
{
|
||||||
if (eventItem.Start.DateTime == null || eventItem.End.DateTime == null) continue;
|
if (eventItem.Start.DateTime == null || eventItem.End.DateTime == null) continue;
|
||||||
if (eventItem.Summary.Contains(person.FirstName))
|
if (eventItem.Summary.Contains(person.FirstName))
|
||||||
{
|
{
|
||||||
DateTime dayStart = (DateTime) eventItem.Start.DateTime;
|
DateTime dayStart = (DateTime)eventItem.Start.DateTime;
|
||||||
DateTime dayEnd = (DateTime) eventItem.End.DateTime;
|
DateTime dayEnd = (DateTime)eventItem.End.DateTime;
|
||||||
timesheetValue.Add((dayStart, dayEnd, (dayEnd.Hour > 13)));
|
timesheetValue.Add((dayStart, dayEnd, (dayEnd.Hour > 13)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,12 +131,12 @@ public class TimesheetService
|
|||||||
if (eventItem.Start.DateTime == null || eventItem.End.DateTime == null) continue;
|
if (eventItem.Start.DateTime == null || eventItem.End.DateTime == null) continue;
|
||||||
if (eventItem.Summary.Contains(person.FirstName))
|
if (eventItem.Summary.Contains(person.FirstName))
|
||||||
{
|
{
|
||||||
DateTime dayStart = (DateTime) eventItem.Start.DateTime;
|
DateTime dayStart = (DateTime)eventItem.Start.DateTime;
|
||||||
DateTime dayEnd = (DateTime) eventItem.End.DateTime;
|
DateTime dayEnd = (DateTime)eventItem.End.DateTime;
|
||||||
timesheetValue.Add((dayStart, dayEnd, false));
|
timesheetValue.Add((dayStart, dayEnd, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
person.Timesheets.Add(new Timesheet(month, timesheetValue));
|
person.Timesheets.Add(new Timesheet(month, timesheetValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,17 +156,18 @@ public class TimesheetService
|
|||||||
if (pattern.Count == 0) continue;
|
if (pattern.Count == 0) continue;
|
||||||
List<DateTime> holidays = await GetHolidays();
|
List<DateTime> holidays = await GetHolidays();
|
||||||
List<DateTime> weekends = GetWeekends();
|
List<DateTime> weekends = GetWeekends();
|
||||||
|
|
||||||
int monthHours = person.Hours * 4;
|
int monthHours = person.Hours * 4;
|
||||||
foreach (string month in Constants.Months)
|
foreach (string month in Constants.Months)
|
||||||
{
|
{
|
||||||
if (month == Constants.Months[0]) continue;
|
if (month == Constants.Months[0]) continue;
|
||||||
List<(DateTime, DateTime, bool)> timesheetValue = new List<(DateTime, DateTime, bool)>();
|
List<(DateTime, DateTime, bool)> timesheetValue = new List<(DateTime, DateTime, bool)>();
|
||||||
|
|
||||||
int currentPersonHours = 0;
|
int currentPersonHours = 0;
|
||||||
int currentPatternIndex = 0;
|
int currentPatternIndex = 0;
|
||||||
int dayOfMonth = 1;
|
int dayOfMonth = 1;
|
||||||
int lastDayOfMonth = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth).AddMonths(1).AddDays(-1).Day;
|
int lastDayOfMonth = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth)
|
||||||
|
.AddMonths(1).AddDays(-1).Day;
|
||||||
while (currentPersonHours != monthHours)
|
while (currentPersonHours != monthHours)
|
||||||
{
|
{
|
||||||
DateTime currentDay = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth);
|
DateTime currentDay = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth);
|
||||||
@@ -148,7 +181,8 @@ public class TimesheetService
|
|||||||
{
|
{
|
||||||
case 6:
|
case 6:
|
||||||
{
|
{
|
||||||
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 9, 0, 0);
|
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 9, 0,
|
||||||
|
0);
|
||||||
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 16, 0, 0);
|
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 16, 0, 0);
|
||||||
timesheetValue.Add((dayStart, dayEnd, true));
|
timesheetValue.Add((dayStart, dayEnd, true));
|
||||||
currentPersonHours += 6;
|
currentPersonHours += 6;
|
||||||
@@ -156,7 +190,8 @@ public class TimesheetService
|
|||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 9, 0, 0);
|
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 9, 0,
|
||||||
|
0);
|
||||||
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 13, 0, 0);
|
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 13, 0, 0);
|
||||||
timesheetValue.Add((dayStart, dayEnd, false));
|
timesheetValue.Add((dayStart, dayEnd, false));
|
||||||
currentPersonHours += 4;
|
currentPersonHours += 4;
|
||||||
@@ -164,7 +199,8 @@ public class TimesheetService
|
|||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 14, 0, 0);
|
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 14, 0,
|
||||||
|
0);
|
||||||
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 16, 0, 0);
|
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 16, 0, 0);
|
||||||
timesheetValue.Add((dayStart, dayEnd, false));
|
timesheetValue.Add((dayStart, dayEnd, false));
|
||||||
currentPersonHours += 2;
|
currentPersonHours += 2;
|
||||||
@@ -172,14 +208,15 @@ public class TimesheetService
|
|||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 9, 0, 0);
|
DateTime dayStart = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 9, 0,
|
||||||
|
0);
|
||||||
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 10, 0, 0);
|
DateTime dayEnd = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 10, 0, 0);
|
||||||
timesheetValue.Add((dayStart, dayEnd, false));
|
timesheetValue.Add((dayStart, dayEnd, false));
|
||||||
currentPersonHours += 1;
|
currentPersonHours += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dayOfMonth++;
|
dayOfMonth++;
|
||||||
if (dayOfMonth > lastDayOfMonth) throw new Exception("Too many hours!");
|
if (dayOfMonth > lastDayOfMonth) throw new Exception("Too many hours!");
|
||||||
currentPatternIndex = (currentPatternIndex + 1) % pattern.Count;
|
currentPatternIndex = (currentPatternIndex + 1) % pattern.Count;
|
||||||
@@ -202,7 +239,8 @@ public class TimesheetService
|
|||||||
for (int i = 0; i <= daysInYear; i++)
|
for (int i = 0; i <= daysInYear; i++)
|
||||||
{
|
{
|
||||||
DateTime currentDay = firstDayOfYear.AddDays(i);
|
DateTime currentDay = firstDayOfYear.AddDays(i);
|
||||||
if (currentDay.DayOfWeek == DayOfWeek.Saturday || currentDay.DayOfWeek == DayOfWeek.Sunday) weekends.Add(currentDay);
|
if (currentDay.DayOfWeek == DayOfWeek.Saturday || currentDay.DayOfWeek == DayOfWeek.Sunday)
|
||||||
|
weekends.Add(currentDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
return weekends;
|
return weekends;
|
||||||
@@ -295,7 +333,7 @@ public class TimesheetService
|
|||||||
private int CalcHours(Timesheet timesheet)
|
private int CalcHours(Timesheet timesheet)
|
||||||
{
|
{
|
||||||
double hours = 0;
|
double hours = 0;
|
||||||
|
|
||||||
foreach ((DateTime, DateTime, bool) tuple in timesheet.Value)
|
foreach ((DateTime, DateTime, bool) tuple in timesheet.Value)
|
||||||
{
|
{
|
||||||
if (tuple.Item3)
|
if (tuple.Item3)
|
||||||
@@ -307,32 +345,34 @@ public class TimesheetService
|
|||||||
hours += (tuple.Item2 - tuple.Item1).TotalHours;
|
hours += (tuple.Item2 - tuple.Item1).TotalHours;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int) hours;
|
return (int)hours;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckForNotEnoughHours(Person person)
|
private async Task CheckForNotEnoughHours(Person person)
|
||||||
{
|
{
|
||||||
List<DateTime> holidays = await GetHolidays();
|
List<DateTime> holidays = await GetHolidays();
|
||||||
List<DateTime> weekends = GetWeekends();
|
List<DateTime> weekends = GetWeekends();
|
||||||
|
|
||||||
int monthHours = person.Hours * 4;
|
int monthHours = person.Hours * 4;
|
||||||
foreach (string month in Constants.Months)
|
foreach (string month in Constants.Months)
|
||||||
{
|
{
|
||||||
if (month == Constants.Months[0]) continue;
|
if (month == Constants.Months[0]) continue;
|
||||||
Timesheet timesheet = person.GetTimeSheetByMonth(month);
|
Timesheet timesheet = person.GetTimeSheetByMonth(month);
|
||||||
List<(DateTime, DateTime, bool)> timesheetValue = timesheet.Value;
|
List<(DateTime, DateTime, bool)> timesheetValue = timesheet.Value;
|
||||||
|
|
||||||
int currentPersonHours = CalcHours(timesheet);
|
int currentPersonHours = CalcHours(timesheet);
|
||||||
List<int> pattern = GetPattern((person.Hours * 4) - currentPersonHours);
|
List<int> pattern = GetPattern((person.Hours * 4) - currentPersonHours);
|
||||||
|
|
||||||
int currentPatternIndex = 0;
|
int currentPatternIndex = 0;
|
||||||
int dayOfMonth = 1;
|
int dayOfMonth = 1;
|
||||||
int lastDayOfMonth = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth).AddMonths(1).AddDays(-1).Day;
|
int lastDayOfMonth = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth)
|
||||||
|
.AddMonths(1).AddDays(-1).Day;
|
||||||
while (currentPersonHours != monthHours)
|
while (currentPersonHours != monthHours)
|
||||||
{
|
{
|
||||||
DateTime currentDay = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth);
|
DateTime currentDay = new DateTime(DateTime.Now.Year, Constants.Months.IndexOf(month), dayOfMonth);
|
||||||
if (holidays.Contains(currentDay) || weekends.Contains(currentDay) || CheckForDayIsNotEmpty(currentDay, timesheetValue))
|
if (holidays.Contains(currentDay) || weekends.Contains(currentDay) ||
|
||||||
|
CheckForDayIsNotEmpty(currentDay, timesheetValue))
|
||||||
{
|
{
|
||||||
dayOfMonth++;
|
dayOfMonth++;
|
||||||
continue;
|
continue;
|
||||||
@@ -373,7 +413,7 @@ public class TimesheetService
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dayOfMonth++;
|
dayOfMonth++;
|
||||||
if (dayOfMonth > lastDayOfMonth) throw new Exception($"Too many hours for Person {person.FirstName}!");
|
if (dayOfMonth > lastDayOfMonth) throw new Exception($"Too many hours for Person {person.FirstName}!");
|
||||||
currentPatternIndex = (currentPatternIndex + 1) % pattern.Count;
|
currentPatternIndex = (currentPatternIndex + 1) % pattern.Count;
|
||||||
@@ -387,7 +427,7 @@ public class TimesheetService
|
|||||||
{
|
{
|
||||||
if (tuple.Item1.Date.Day == currentDay.Day) return true;
|
if (tuple.Item1.Date.Day == currentDay.Day) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ namespace LeanderShiftPlannerV2.Util
|
|||||||
public const string GoogleClientId = "GoogleClientId";
|
public const string GoogleClientId = "GoogleClientId";
|
||||||
public const string GoogleClientSecret = "GoogleClientSecret";
|
public const string GoogleClientSecret = "GoogleClientSecret";
|
||||||
public const string GoogleApiFileResource = @"Resources/templates/googleapi.txt";
|
public const string GoogleApiFileResource = @"Resources/templates/googleapi.txt";
|
||||||
|
public const string GoogleApiTokenPath = @"/googleapitoken.txt";
|
||||||
|
|
||||||
// TimeSheets
|
// TimeSheets
|
||||||
public static readonly string TimeSheetSourceRandom = "Random";
|
public static readonly string TimeSheetSourceRandom = "Random";
|
||||||
|
|||||||
+2
-1
@@ -47,7 +47,8 @@ public partial class LeanderShiftPlannerMainWindow : Window
|
|||||||
|
|
||||||
private void CreateApiFile()
|
private void CreateApiFile()
|
||||||
{
|
{
|
||||||
GoogleApiLoader.CheckFileSystem();
|
GoogleApiLoader.CheckFileSystem_ReadFile();
|
||||||
|
GoogleApiLoader.CheckFileSystem_ReadToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------UI-Interactions------------------------------------------------------
|
// ------------------------------------------------------UI-Interactions------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user