- add generating timesheets from google calendar

- minor fixes
This commit is contained in:
2025-10-10 10:49:20 +02:00
parent 584ffa36cb
commit f9ea678e00
12 changed files with 200 additions and 24 deletions

View File

@@ -86,8 +86,8 @@ public static class TimesheetIO
foreach ((DateTime, DateTime, bool) tuple in timesheet.Value)
{
worksheet.Cells[17 + tuple.Item1.Day, 2].Value = tuple.Item1.Hour + (tuple.Item1.Minute == 0 ? ":00" : tuple.Item1.Minute.ToString());
worksheet.Cells[17 + tuple.Item1.Day, 3].Value = tuple.Item2.Hour + (tuple.Item2.Minute == 0 ? ":00" : tuple.Item1.Minute.ToString());
worksheet.Cells[17 + tuple.Item1.Day, 2].Value = tuple.Item1.Hour + ":" + (tuple.Item1.Minute == 0 ? "00" : tuple.Item1.Minute.ToString());
worksheet.Cells[17 + tuple.Item1.Day, 3].Value = tuple.Item2.Hour + ":" + (tuple.Item2.Minute == 0 ? "00" : tuple.Item1.Minute.ToString());
if (tuple.Item3) worksheet.Cells[17 + tuple.Item1.Day, 5].Value = "1:00";
}

View File

@@ -25,6 +25,7 @@
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="EPPlus" Version="7.5.3" />
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.69.0.3746" />
<PackageReference Include="SkiaSharp" Version="3.118.0-preview.2.3" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.118.0-preview.2.3" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="3.118.0-preview.2.3" />
@@ -43,4 +44,8 @@
<ItemGroup>
<Folder Include="Resources\icon\" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="View\TimeSheetView\TimeSheetView.axaml" />
</ItemGroup>
</Project>

View File

@@ -12,6 +12,7 @@ namespace LeanderShiftPlannerV2.Model
private string _firstFirstName = null!;
private string _surname = null!;
private int _hours;
private string _timeSheetSource;
private ObservableCollection<Shift> _shifts;
[XmlIgnore]
@@ -30,11 +31,12 @@ namespace LeanderShiftPlannerV2.Model
_timesheets = new List<Timesheet>();
}
public Person(string firstFirstName, string surname, int hours)
public Person(string firstFirstName, string surname, int hours, string timeSheetSource)
{
this._firstFirstName = firstFirstName;
this._surname = surname;
this._hours = hours;
this._timeSheetSource = timeSheetSource;
_shifts = new ObservableCollection<Shift>();
_timesheets = new List<Timesheet>();
}
@@ -69,6 +71,16 @@ namespace LeanderShiftPlannerV2.Model
}
}
public string TimeSheetSource
{
get => _timeSheetSource;
set
{
_timeSheetSource = value;
OnPropertyChanged(nameof(TimeSheetSource));
}
}
[XmlIgnore]
public string ShiftsString
{

View File

@@ -1,7 +1,9 @@
using LeanderShiftPlannerV2.FileIO;
using System;
using LeanderShiftPlannerV2.FileIO;
using LeanderShiftPlannerV2.Model;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using LeanderShiftPlannerV2.Util;
namespace LeanderShiftPlannerV2.Service
{
@@ -9,11 +11,17 @@ namespace LeanderShiftPlannerV2.Service
{
private readonly AppService _appService;
private readonly ObservableCollection<Person> _persons;
private readonly List<Person> _randomTimeSheets;
private readonly List<Person> _googleTimeSheets;
private readonly List<Person> _randomGoogleTimeSheets;
public PersonService(AppService appService)
{
this._appService = appService;
_persons = new ObservableCollection<Person>();
_randomTimeSheets = new List<Person>();
_googleTimeSheets = new List<Person>();
_randomGoogleTimeSheets = new List<Person>();
LoadPersonsFromFile();
}
@@ -22,6 +30,7 @@ namespace LeanderShiftPlannerV2.Service
foreach (Person person in LoadDataFromFile.LoadPersons())
{
_persons.Add(person);
AddToTimeSheetList(person);
}
}
@@ -30,14 +39,30 @@ namespace LeanderShiftPlannerV2.Service
return _persons;
}
public Person AddNewPerson(string firstName, string surname, string hours)
public List<Person> GetRandomTimeSheetList()
{
Person newPerson = new Person(firstName, surname, int.Parse(hours));
return _randomTimeSheets;
}
public List<Person> GetGoogleTimeSheetList()
{
return _googleTimeSheets;
}
public List<Person> GetRandomGoogleTimeSheetList()
{
return _randomGoogleTimeSheets;
}
public Person AddNewPerson(string firstName, string surname, string hours, string timeSheetSource)
{
Person newPerson = new Person(firstName, surname, int.Parse(hours), timeSheetSource);
_persons.Add(newPerson);
AddToTimeSheetList(newPerson);
return newPerson;
}
public void EditPerson(Person person, string? firstName, string? surname, int? hours)
public void EditPerson(Person person, string? firstName, string? surname, int? hours, string? timeSheetSource)
{
if (firstName != null)
{
@@ -53,6 +78,35 @@ namespace LeanderShiftPlannerV2.Service
{
person.Hours = (int)hours;
}
if (timeSheetSource != null)
{
person.TimeSheetSource = timeSheetSource;
}
AddToTimeSheetList(person);
}
private void AddToTimeSheetList(Person person)
{
if (person.TimeSheetSource == Constants.TimeSheetSourceRandom)
{
_randomTimeSheets.Add(person);
_googleTimeSheets.Remove(person);
_randomGoogleTimeSheets.Remove(person);
}
else if (person.TimeSheetSource == Constants.TimeSheetSourceGoogle)
{
_randomTimeSheets.Remove(person);
_googleTimeSheets.Add(person);
_randomGoogleTimeSheets.Remove(person);
}
else if (person.TimeSheetSource == Constants.TimeSheetSourceRandomGoogle)
{
_randomGoogleTimeSheets.Remove(person);
_googleTimeSheets.Remove(person);
_randomGoogleTimeSheets.Add(person);
}
}
public void DeletePerson(Person person)

View File

@@ -1,12 +1,17 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using LeanderShiftPlannerV2.Model;
using LeanderShiftPlannerV2.Util;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Calendar.v3;
using Google.Apis.Services;
namespace LeanderShiftPlannerV2.Service;
@@ -15,15 +20,90 @@ public class TimesheetService
private readonly AppService _appService;
private const string HolidayApiUriPart1 = "https://feiertage-api.de/api/?jahr=";
private const string HolidayApiUriPart2 = "&nur_land=";
private const string GoogleClientId = "823920428427-ub5n9a9dkfpuvjlut4eneg2058so49o3.apps.googleusercontent.com";
private const string GoogleClientSecret = "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";
public TimesheetService(AppService appService)
{
_appService = appService;
}
public async Task GenerateTimeSheets()
public async Task GenerateGoogleTimeSheets(List<Person> persons)
{
List<Person> persons = _appService.PersonService.GetPersonList().ToList();
string[] scopes = { CalendarService.Scope.CalendarReadonly };
const string applicationName = "LeanderShiftPlanner";
ClientSecrets clientSecrets = new ClientSecrets();
clientSecrets.ClientId = GoogleClientId;
clientSecrets.ClientSecret = GoogleClientSecret;
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, scopes, "user", CancellationToken.None);
// Erstelle den CalendarService
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = applicationName,
});
foreach (string month in Constants.Months)
{
if (month == Constants.Months[0]) continue;
List<(DateTime, DateTime, bool)> timesheetValue = new List<(DateTime, DateTime, bool)>();
// Aktuelles Datum
DateTime now = DateTime.Now;
// Erster Tag des aktuellen Monats (00:00:00 Uhr)
DateTime firstDayOfMonth = new DateTime(now.Year, Constants.Months.IndexOf(month), 1, 0, 0, 0);
// 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)),
23, 59, 59);
foreach (Person person in persons)
{
// Beispiel: Liste die nächsten 10 Ereignisse im ITM Kalender auf
var requestItm = service.Events.List("on089es7kq4ugi3rqvslkn7720@group.calendar.google.com");
requestItm.TimeMinDateTimeOffset = firstDayOfMonth;
requestItm.TimeMaxDateTimeOffset = lastDayOfMonth;
requestItm.SingleEvents = true;
var requestWebService = service.Events.List("s4rdo3uk2on8ualv1goaktbiec@group.calendar.google.com");
requestWebService.TimeMinDateTimeOffset = firstDayOfMonth;
requestWebService.TimeMaxDateTimeOffset = lastDayOfMonth;
requestWebService.SingleEvents = true;
var eventsItm = await requestItm.ExecuteAsync();
var eventsWebService = await requestWebService.ExecuteAsync();
foreach (var eventItem in eventsItm.Items)
{
if (eventItem.Start.DateTime == null || eventItem.End.DateTime == null) continue;
if (eventItem.Summary.Contains(person.FirstName))
{
DateTime dayStart = (DateTime) eventItem.Start.DateTime;
DateTime dayEnd = (DateTime) eventItem.End.DateTime;
timesheetValue.Add((dayStart, dayEnd, false));
}
}
foreach (var eventItem in eventsWebService.Items)
{
if (eventItem.Start.DateTime == null || eventItem.End.DateTime == null) continue;
if (eventItem.Summary.Contains(person.FirstName))
{
DateTime dayStart = (DateTime) eventItem.Start.DateTime;
DateTime dayEnd = (DateTime) eventItem.End.DateTime;
timesheetValue.Add((dayStart, dayEnd, false));
}
}
person.Timesheets.Add(new Timesheet(month, timesheetValue));
}
}
}
public async Task GenerateRandomTimeSheets(List<Person> persons)
{
//List<Person> persons = _appService.PersonService.GetPersonList().ToList();
foreach (Person person in persons)
{

View File

@@ -17,6 +17,10 @@ namespace LeanderShiftPlannerV2.Util
public const string FontPath = @"Resources/font/Emblem.ttf";
// TimeSheets
public static readonly string TimeSheetSourceRandom = "Random";
public static readonly string TimeSheetSourceGoogle = "Google";
public static readonly string TimeSheetSourceRandomGoogle = "RandomGoogle";
public static readonly List<string> Months = new List<string>
{
"ERROR!",

View File

@@ -15,10 +15,11 @@
<ListBox.Template>
<ControlTemplate>
<DockPanel>
<Grid DockPanel.Dock="Top" Height="30" ColumnDefinitions="100,70,1030" Margin="10,0, 0, 0">
<Grid DockPanel.Dock="Top" Height="30" ColumnDefinitions="100,70,130,900" Margin="10,0, 0, 0">
<TextBlock Grid.Column="0" Text="Name" TextAlignment="Center" Width="100"/>
<TextBlock Grid.Column="1" Text="Hours" TextAlignment="Center" Width="70"/>
<TextBlock Grid.Column="2" Text="Shifts" TextAlignment="Center" Width="1030"/>
<TextBlock Grid.Column="2" Text="TimeSheetSource" TextAlignment="Center" Width="130"/>
<TextBlock Grid.Column="3" Text="Shifts" TextAlignment="Center" Width="900"/>
</Grid>
<ItemsPresenter/>
</DockPanel>
@@ -26,10 +27,11 @@
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="100,70,1030">
<Grid ColumnDefinitions="100,70,130,900">
<TextBlock Grid.Column="0" Text="{Binding FirstName, Mode=OneWay}" TextAlignment="Center" Width="100" x:DataType="model:Person"/>
<TextBlock Grid.Column="1" Text="{Binding Hours, Mode=OneWay}" TextAlignment="Center" Width="70" x:DataType="model:Person"/>
<TextBlock Grid.Column="2" Text="{Binding ShiftsString, Mode=OneWay}" TextAlignment="Center" Width="1030" x:DataType="model:Person"/>
<TextBlock Grid.Column="2" Text="{Binding TimeSheetSource, Mode=OneWay}" TextAlignment="Center" Width="130" x:DataType="model:Person"/>
<TextBlock Grid.Column="3" Text="{Binding ShiftsString, Mode=OneWay}" TextAlignment="Center" Width="900" x:DataType="model:Person"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>

View File

@@ -129,14 +129,18 @@ public partial class LeanderShiftPlannerMainWindow : Window
{
try
{
await _appService.TimesheetService.GenerateTimeSheets();
ProgressbarCalculaion.IsIndeterminate = true;
await _appService.TimesheetService.GenerateGoogleTimeSheets(_appService.PersonService.GetGoogleTimeSheetList());
ProgressbarCalculaion.IsIndeterminate = false;
int error = TimesheetIO.ExportTimesheets(_appService.PersonService.GetPersonList().ToList());
if (error == 1) ViewService.ShowGeneralError(this).ErrorText = "There was an error exporting the timesheets.";
if (error == 2) ViewService.ShowGeneralError(this).ErrorText = "There was an error creating the timesheets.";
if (error == 1) ;
}
catch (Exception exception)
{
ViewService.ShowGeneralError(this).ErrorText = exception.Message;
Console.WriteLine(exception);
}
}

View File

@@ -8,7 +8,7 @@
<Grid>
<Label Content="Create new person" Height="25" VerticalAlignment="Top" Margin="0,15,0,0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Grid ColumnDefinitions="Auto, Auto" RowDefinitions="Auto, Auto, Auto" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid ColumnDefinitions="Auto, Auto" RowDefinitions="Auto, Auto, Auto, Auto" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Grid.Column="0" Grid.Row="0" Content="Enter person name: " Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="0" x:Name="TextBoxName" Height="32" Width="100" HorizontalContentAlignment="Center"/>
@@ -17,9 +17,12 @@
<Label Grid.Column="0" Grid.Row="2" Content="Enter person hours: " Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="2" x:Name="TextBoxHours" Height="32" Width="100" HorizontalContentAlignment="Center" TextInput="TextBoxHours_PreviewTextInput" />
<Label Grid.Column="0" Grid.Row="3" Content="Select TimeSheetSource: " Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
<ComboBox Grid.Column="1" Grid.Row="3" x:Name="ComboBoxTimeSheetSource" Height="32" Width="100" HorizontalContentAlignment="Center" />
</Grid>
<Button x:Name="ButtonCreatePerson" Content="Create new person" HorizontalContentAlignment="Center" Height="32" Width="125" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,15" Click="ButtonCreatePerson_Click"/>
<Button x:Name="ButtonCreatePerson" Content="Create new person" HorizontalContentAlignment="Center" Height="32" Width="150" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,15" Click="ButtonCreatePerson_Click"/>
</Grid>
</Window>

View File

@@ -15,14 +15,18 @@ public partial class PersonCreator : Window
InitializeComponent();
this._appService = appService;
ComboBoxTimeSheetSource.Items.Add(Constants.TimeSheetSourceRandom);
ComboBoxTimeSheetSource.Items.Add(Constants.TimeSheetSourceGoogle);
ComboBoxTimeSheetSource.Items.Add(Constants.TimeSheetSourceRandomGoogle);
}
private void ButtonCreatePerson_Click(object sender, RoutedEventArgs e)
{
if (TextBoxName.Text != "" && TextBoxHours.Text != "" && TextBoxSurname.Text != ""
&& TextBoxName.Text != null && TextBoxHours.Text != null && TextBoxSurname.Text != null)
&& TextBoxName.Text != null && TextBoxHours.Text != null && TextBoxSurname.Text != null
&& ComboBoxTimeSheetSource.SelectedItem != null)
{
_appService.PersonService.AddNewPerson(TextBoxName.Text, TextBoxSurname.Text, TextBoxHours.Text);
_appService.PersonService.AddNewPerson(TextBoxName.Text, TextBoxSurname.Text, TextBoxHours.Text, ComboBoxTimeSheetSource.SelectedItem.ToString()!);
this.Close();
}
else

View File

@@ -8,7 +8,7 @@
<Grid>
<Label Content="Edit person" Height="25" VerticalAlignment="Top" Margin="0,15,0,0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Grid ColumnDefinitions="Auto, Auto" RowDefinitions="Auto, Auto, Auto" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid ColumnDefinitions="Auto, Auto" RowDefinitions="Auto, Auto, Auto, Auto" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Grid.Column="0" Grid.Row="0" Content="Enter person name: " Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="0" x:Name="TextBoxName" Height="32" Width="100" HorizontalContentAlignment="Center"/>
@@ -17,6 +17,9 @@
<Label Grid.Column="0" Grid.Row="2" Content="Enter person hours: " Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="2" x:Name="TextBoxHours" Height="32" Width="100" HorizontalContentAlignment="Center" TextInput="TextBoxHours_PreviewTextInput" />
<Label Grid.Column="0" Grid.Row="3" Content="Select TimeSheetSource: " Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
<ComboBox Grid.Column="1" Grid.Row="3" x:Name="ComboBoxTimeSheetSource" Height="32" Width="100" HorizontalContentAlignment="Center" />
</Grid>
<Button x:Name="ButtonEditPerson" Content="Edit person" HorizontalContentAlignment="Center" Height="32" Width="125" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,15" Click="ButtonEditPerson_Click" />

View File

@@ -22,19 +22,24 @@ public partial class PersonEditor : Window
TextBoxName.Text = _model.FirstName;
TextBoxSurname.Text = _model.Surname;
TextBoxHours.Text = _model.Hours.ToString();
ComboBoxTimeSheetSource.Items.Add(Constants.TimeSheetSourceRandom);
ComboBoxTimeSheetSource.Items.Add(Constants.TimeSheetSourceGoogle);
ComboBoxTimeSheetSource.Items.Add(Constants.TimeSheetSourceRandomGoogle);
}
private void ButtonEditPerson_Click(object sender, RoutedEventArgs e)
{
if (TextBoxName.Text != "" && TextBoxHours.Text != "" && TextBoxSurname.Text != ""
&& TextBoxName.Text != null && TextBoxHours.Text != null && TextBoxSurname.Text != null)
&& TextBoxName.Text != null && TextBoxHours.Text != null && TextBoxSurname.Text != null
&& ComboBoxTimeSheetSource.SelectedItem != null)
{
_appService.PersonService.EditPerson(_model, TextBoxName.Text, TextBoxSurname.Text, int.Parse(TextBoxHours.Text));
_appService.PersonService.EditPerson(_model, TextBoxName.Text, TextBoxSurname.Text, int.Parse(TextBoxHours.Text), ComboBoxTimeSheetSource.SelectedItem.ToString()!);
this.Close();
}
else
{
//TODO: Error, no input!
ViewService.ShowNoInputError(this);
}
}