Files
Blueberry/Blueberry/HoursWindow.xaml.cs

252 lines
9.3 KiB
C#

using Blueberry.Redmine;
using Blueberry.Redmine.Dto;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Windows;
using Wpf.Ui.Controls;
namespace Blueberry
{
/// <summary>
/// Interaction logic for HoursWindow.xaml
/// </summary>
public partial class HoursWindow : FluentWindow
{
private readonly List<UserInfo.User> _users = [];
private readonly RedmineManager _manager;
private readonly RedmineConfig _config;
private readonly ConcurrentDictionary<int, string> _issueNames = [];
public ObservableCollection<DisplayHours> Hours { get; set; } = [];
public HoursWindow(RedmineManager manager, RedmineConfig config)
{
InitializeComponent();
DataContext = this;
_manager = manager;
_config = config;
}
private async void FluentWindow_Loaded(object sender, RoutedEventArgs e)
{
userComboBox.IsEnabled =
searchButton.IsEnabled =
dateButton.IsEnabled = false;
var u = await _manager.GetUsersAsync(progress: UpdateProgress());
var current = await _manager.GetCurrentUserAsync();
hoursProgress.Visibility = Visibility.Hidden;
hoursProgress.IsIndeterminate = true;
_users.Clear();
_users.AddRange(u);
userComboBox.Items.Clear();
foreach (var user in u)
userComboBox.Items.Add(user);
userComboBox.SelectedItem = current;
userComboBox.IsEnabled =
searchButton.IsEnabled =
dateButton.IsEnabled = true;
}
private IProgress<(int, int)> UpdateProgress()
{
var p = new Progress<(int current, int total)>((x) =>
{
Dispatcher.Invoke(() =>
{
hoursProgress.IsIndeterminate = false;
var percent = (int)((double)x.current / x.total * 100);
hoursProgress.Progress = percent;
});
});
return p;
}
private void dateButton_Click(object sender, RoutedEventArgs e)
{
calendarFlyout.IsOpen = true;
}
private void UpdateProgressI()
{
Dispatcher.Invoke(() =>
{
var percent = (int)((double)loadI / totalI * 100);
lock (_lock)
{
hoursProgress.Progress = percent;
}
});
}
private int loadInternalI = 0;
private object _lock = new();
private int loadI { get
{
var result = 0;
lock(_lock)
{
result = loadInternalI;
}
return result;
}
set
{
lock (_lock)
{
loadInternalI = value;
}
}
}
private int totalI = 0;
private async void searchButton_Click(object sender, RoutedEventArgs e)
{
var user = userComboBox.SelectedItem as UserInfo.User;
if(user is null)
return;
var selectedDates = userCalendar.SelectedDates;
if (selectedDates.Count == 0)
return;
ConcurrentBag<DisplayHours> hours = [];
Task[] tasks = new Task[selectedDates.Count];
Hours.Clear();
hoursProgress.Visibility= Visibility.Visible;
for (int i = 0; i < selectedDates.Count; i++)
{
hoursProgress.IsIndeterminate = false;
totalI = selectedDates.Count;
var date = selectedDates[i];
tasks[i] = Task.Run(async () =>
{
var h = await _manager.GetTimeForUserAsync(user.Id, date, date);
foreach (var item in h)
{
var dh = new DisplayHours()
{
ProjectName = item.Project.Name,
IssueName = await GetIssueNameAsync(item.Issue.Id),
IssueId = item.Issue.Id.ToString(),
Date = date,
CreatedOn = item.CreatedOn,
Hours = item.Hours.ToString(),
Comments = item.Comments,
IsSeparator = false
};
hours.Add(dh);
}
loadI++;
UpdateProgressI();
});
await Task.Delay(10);
}
await Task.WhenAll(tasks);
hoursProgress.IsIndeterminate = true;
var newTickets = await _manager.GetIssuesAsync(user.Id, createdFrom: selectedDates.First(), createdTo: selectedDates.Last());
var closedTickets = await _manager.GetIssuesAsync(user.Id, isOpen: false, updatedFrom: selectedDates.First(), updatedTo: selectedDates.Last());
var currentTickets = await _manager.GetUserOpenIssuesAsync(user.Id);
var total = hours.Sum(x => double.Parse(x.Hours));
totalHoursTextBlock.Text = total.ToString();
avgDayTextBlock.Text = Math.Round(total / selectedDates.Count, 2).ToString();
int workingDays = 0;
foreach (var day in selectedDates)
{
if(day.DayOfWeek != DayOfWeek.Saturday &&
day.DayOfWeek != DayOfWeek.Sunday)
workingDays++;
}
avgWorkdayTextBlock.Text = Math.Round(total / workingDays, 2).ToString();
newTicketsTextBlock.Text = newTickets.Count.ToString();
closedTicketsTextBlock.Text = closedTickets.Count.ToString();
minTicketAgeTextBlock.Text = Math.Round(currentTickets.Min(x => (DateTime.Now - x.CreatedOn).TotalDays), 2) + " nap";
maxTicketAgeTextBlock.Text = Math.Round(currentTickets.Max(x => (DateTime.Now - x.CreatedOn).TotalDays), 2) + " nap";
avgTicketAgeTextBlock.Text = Math.Round(currentTickets.Average(x => (DateTime.Now - x.CreatedOn).TotalDays), 2) + " nap";
var ages = currentTickets.Select(x => (DateTime.Now - x.CreatedOn).TotalDays).Order().ToList();
medianTicketAgeTextBlock.Text = Math.Round(ages[ages.Count / 2], 2) + " nap";
var orderedHours = hours.OrderByDescending(h => h.CreatedOn).OrderBy(h => h.Date).ToList();
var previousDate = DateTime.MinValue;
for (int i = 0; i < orderedHours.Count; i++)
{
if(orderedHours[i].Date.Date > previousDate)
{
total = orderedHours.Where(x => x.Date.Date == orderedHours[i].Date.Date).Sum(x => double.Parse(x.Hours));
var dh = new DisplayHours()
{
IsSeparator = true,
SeparatorText = orderedHours[i].Date.ToString("yyyy-MM-dd") + " | Összesen: " + total + " óra"
};
orderedHours.Insert(i, dh);
previousDate = orderedHours[i+1].Date.Date;
}
}
hoursProgress.Visibility = Visibility.Hidden;
foreach (var item in orderedHours)
Hours.Add(item);
}
private async Task<string> GetIssueNameAsync(int issueId)
{
try
{
if (_issueNames.ContainsKey(issueId))
return _issueNames[issueId];
var name = (await _manager.GetSimpleIssueAsync(issueId)).Subject;
_issueNames.TryAdd(issueId, name);
return name;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(issueId);
throw;
}
}
public class DisplayHours
{
public string ProjectName { get; set; } = "";
public string IssueName { get; set; } = "";
public string IssueId { get; set; } = "";
public DateTime Date { get; set; }
public DateTime CreatedOn { get; set; }
public string Hours { get; set; } = "";
public string Comments { get; set; } = "";
public string SeparatorText { get; set; } = "";
public bool IsSeparator { get; set; }
public Visibility CardVisibility => IsSeparator ? Visibility.Visible : Visibility.Hidden;
public Visibility ButtonVisibility => IsSeparator ? Visibility.Hidden : Visibility.Visible;
}
private async void openIssueButton_Click(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement button && button.DataContext is DisplayHours item)
{
var issueId = int.Parse(item.IssueId);
try
{
var issue = await _manager.GetIssueAsync(issueId);
var issueWindow = new IssueWindow(issue, _manager, _config);
issueWindow.Show();
}
catch (Exception)
{
}
}
}
}
}