diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/InnoLabProjektDektopApp.csproj b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/InnoLabProjektDektopApp.csproj index 2e00aeb74cfa45529a5e4de3caa1c5f0f5b0961e..365dc8dc1922f9f5511d092e07a8978c73dc8fb3 100644 --- a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/InnoLabProjektDektopApp.csproj +++ b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/InnoLabProjektDektopApp.csproj @@ -296,6 +296,7 @@ <None Remove="Assets\MascottAnimation\Down\7\frame7.png" /> <None Remove="Assets\MascottAnimation\Down\7\frame8.png" /> <None Remove="Assets\MascottAnimation\Down\7\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\1\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame11.png" /> @@ -373,6 +374,7 @@ <None Remove="Assets\MascottAnimation\Up\1\frame77.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\2\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame11.png" /> @@ -401,6 +403,7 @@ <None Remove="Assets\MascottAnimation\Up\2\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\3\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame11.png" /> @@ -422,6 +425,7 @@ <None Remove="Assets\MascottAnimation\Up\3\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\4\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame11.png" /> @@ -476,6 +480,7 @@ <None Remove="Assets\MascottAnimation\Up\4\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\5\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame11.png" /> @@ -504,6 +509,7 @@ <None Remove="Assets\MascottAnimation\Up\5\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\6\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame11.png" /> @@ -531,6 +537,7 @@ <None Remove="Assets\MascottAnimation\Up\6\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame9.png" /> + <None Remove="Assets\MascottAnimation\Up\7\frame0.png" /> <None Remove="Assets\MascottAnimation\Up\7\frame1.png" /> <None Remove="Assets\MascottAnimation\Up\7\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\7\frame11.png" /> @@ -622,6 +629,7 @@ <ItemGroup> <PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="2.0.1" /> + <PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc5.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.3" /> <PackageReference Include="System.Management" Version="9.0.1" /> @@ -643,6 +651,27 @@ <Content Include="Assets\gamesicon.png"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> + <Content Include="Assets\MascottAnimation\Up\1\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="Assets\MascottAnimation\Up\2\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="Assets\MascottAnimation\Up\3\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="Assets\MascottAnimation\Up\4\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="Assets\MascottAnimation\Up\5\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="Assets\MascottAnimation\Up\6\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="Assets\MascottAnimation\Up\7\frame0.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> <Content Include="Assets\pause.png"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/FirstLaunch/03_2ProgramsList.xaml.cs b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/FirstLaunch/03_2ProgramsList.xaml.cs index a79295e21a3c3f05f93298521e75881d61bdbd39..c8eca2c92350881c8ffeef2b5b99234e51ac3561 100644 --- a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/FirstLaunch/03_2ProgramsList.xaml.cs +++ b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/FirstLaunch/03_2ProgramsList.xaml.cs @@ -58,7 +58,9 @@ namespace InnoLabProjektDektopApp { var filteredItems = value.Where( item => string.IsNullOrEmpty(searchText) || - item.ProcessName.Contains(searchText, StringComparison.CurrentCultureIgnoreCase)) + item.ProcessName.Contains(searchText, StringComparison.CurrentCultureIgnoreCase) || + item.MainWindowTitle.Contains(searchText, StringComparison.CurrentCultureIgnoreCase) + ) .ToList(); foreach (var item in filteredItems) @@ -67,6 +69,7 @@ namespace InnoLabProjektDektopApp ShowNewItemInList(item.ProcessName, item.MainWindowTitle, item.IsDistracting); } } + RefreshRunningProcessList(searchText); } @@ -134,9 +137,15 @@ namespace InnoLabProjektDektopApp RefreshRunningProcessList(); } - private void RefreshRunningProcessList() + private void RefreshRunningProcessList(string filter = null) { var windowedProcesses = ProcessMonitor.GetWindowedProcesses(); + windowedProcesses = windowedProcesses.Where( + item => string.IsNullOrEmpty(filter) || + item.Name.Contains(filter, StringComparison.CurrentCultureIgnoreCase) || + item.MainWindowTitle.Contains(filter, StringComparison.CurrentCultureIgnoreCase) + ) + .ToList(); ItemsPanel2.Children.Clear(); foreach (var process in windowedProcesses) { diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml new file mode 100644 index 0000000000000000000000000000000000000000..e34880c5acd074153d7621e36d3588050b276abb --- /dev/null +++ b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml @@ -0,0 +1,26 @@ +<Page x:Class="InnoLabProjektDektopApp.Screens.Regulaer.SessionStatistics" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:InnoLabProjektDektopApp.Screens.Regulaer" + xmlns:header="clr-namespace:InnoLabProjektDektopApp.Screens.Templates" + xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF" + mc:Ignorable="d" + Height="550" Width="900" + Title="SessionStatistics"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + + <Canvas Name="chartCanvas" Margin="50" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top"/> + + <Grid Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top" Width="350" Height="350"> + <lvc:PieChart Series="{Binding Series}" /> + <Image Source="{Binding CenterImageSource}" Width="200" Height="200" HorizontalAlignment="Center" VerticalAlignment="Center"/> + </Grid> + </Grid> +</Page> diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml.cs b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml.cs new file mode 100644 index 0000000000000000000000000000000000000000..ddf76753a6b810c614bf33b0df81f1aa800c1ce6 --- /dev/null +++ b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using LiveChartsCore; +using System.Collections.Generic; +using LiveChartsCore.SkiaSharpView.Extensions; +using LiveChartsCore.SkiaSharpView; +using LiveChartsCore.SkiaSharpView.Painting; +using SkiaSharp; +using Newtonsoft.Json.Linq; +using InnoLabProjektDektopApp.Services; +using System.IO; +using System.ComponentModel; + +namespace InnoLabProjektDektopApp.Screens.Regulaer +{ + /// <summary> + /// Interaction logic for SessionStatistics.xaml + /// </summary> + public partial class SessionStatistics : Page + { + TimeSpan distractedTotal; + TimeSpan breakTotal = new(); + TimeSpan pausedTotal = new(); + TimeSpan productiveTotal; + int final_stage = 0; + int offset = 130; + + public IEnumerable<ISeries> Series { get; set; } + + public event PropertyChangedEventHandler PropertyChanged; + + private BitmapImage _centerImageSource; + public BitmapImage CenterImageSource + { + get => _centerImageSource; + set + { + _centerImageSource = value; + OnPropertyChanged(nameof(CenterImageSource)); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public SessionStatistics(string sessionDataJsonPath = "C:\\Users\\Phil\\Source\\Repos\\CoFlow\\InnoLabProjektDektopApp\\InnoLabProjektDektopApp\\bin\\Debug\\net8.0-windows\\Assets\\sessionData\\2025\\3\\4\\4.json") + { + DataContext = this; + InitializeComponent(); + DrawChart(sessionDataJsonPath); + UpdateSeriesValues(); + } + + private void DrawChart(string json_file_path) + { + // Example JSON data + var jsonData = File.ReadAllText(json_file_path); + + // Deserialize JSON data + var sessionData = System.Text.Json.JsonSerializer.Deserialize<SessionData>(jsonData); + + // Create a canvas to draw the chart + int bar_width = 400; + int bar_height = 20; + + if (sessionData == null) + { + return; + } + final_stage = sessionData.FinalDistractionStage; + + // Draw bars for each cycle + foreach (var cycle in sessionData.CycleStartTimes) + { + DateTime cycleStartTime = DateTime.Parse(cycle.Value); + DateTime cycleEndTime = // start of next cycle or end of session + cycle.Key < sessionData.CycleStartTimes.Count + ? DateTime.Parse(sessionData.CycleStartTimes[cycle.Key + 1]) + : DateTime.Parse(sessionData.SessionEndTime); + + // Calculate total cycle duration + TimeSpan cycleDuration = cycleEndTime - cycleStartTime; + + // Draw the entire cycle in green + Rectangle cycleSegment = new Rectangle + { + Width = bar_width, + Height = bar_height, + Fill = Brushes.Green + }; + + Canvas.SetLeft(cycleSegment, 0); + Canvas.SetTop(cycleSegment, (cycle.Key - 1) * 1.5 * bar_height + offset); + chartCanvas.Children.Add(cycleSegment); + + // Add cycle number text + TextBlock cycleText = new TextBlock + { + Text = $"Cycle {cycle.Key}", + Foreground = Brushes.Black, + FontSize = 15, + FontWeight = FontWeight.FromOpenTypeWeight(500) + }; + + Canvas.SetLeft(cycleText, bar_width + 5); // Adjust the position as needed + Canvas.SetTop(cycleText, (cycle.Key - 1) * 1.5 * bar_height + offset); + chartCanvas.Children.Add(cycleText); + + // Draw distracted segments in red + foreach (var process in sessionData.ProcessInfoList.Where(p => p.Cycle == cycle.Key)) + { + DateTime processStartTime = DateTime.Parse(process.StartTime); + DateTime processEndTime = DateTime.Parse(process.EndTime); + + // Calculate segment duration + TimeSpan segmentDuration = processEndTime - processStartTime; + + // Determine if the process is a distraction + + // Draw segment + Rectangle segment = new() + { + Width = bar_width * (segmentDuration / cycleDuration), + Height = bar_height, + Fill = Brushes.Red + }; + + Canvas.SetLeft(segment, bar_width * (processStartTime - cycleStartTime).TotalSeconds / cycleDuration.TotalSeconds); + Canvas.SetTop(segment, (cycle.Key - 1) * 1.5 * bar_height + offset); + chartCanvas.Children.Add(segment); + } + + // Draw break segments in blue + foreach (var breakInfo in sessionData.BreakInfoList.Where(b => b.Cycle == cycle.Key)) + { + DateTime breakStartTime = DateTime.Parse(breakInfo.StartTime); + DateTime breakEndTime = DateTime.Parse(breakInfo.EndTime); + + // Calculate break duration + TimeSpan breakDuration = breakEndTime - breakStartTime; + + // Draw break segment + Rectangle breakSegment; + if (breakInfo.IsBreak) + { + breakTotal += breakEndTime - breakStartTime; + breakSegment = new() + { + Width = bar_width * (breakDuration.TotalSeconds / cycleDuration.TotalSeconds), + Height = bar_height, + Fill = Brushes.Blue + }; + } + else + { + pausedTotal += breakEndTime - breakStartTime; + breakSegment = new() + { + Width = bar_width * (breakDuration.TotalSeconds / cycleDuration.TotalSeconds), + Height = bar_height, + Fill = Brushes.LightBlue + }; + } + + Canvas.SetLeft(breakSegment, bar_width * (breakStartTime - cycleStartTime).TotalSeconds / cycleDuration.TotalSeconds); + Canvas.SetTop(breakSegment, (cycle.Key - 1) * 1.5 * bar_height + offset); + chartCanvas.Children.Add(breakSegment); + } + + } + productiveTotal = DateTime.Parse(sessionData.SessionEndTime) - (DateTime.Parse(sessionData.CycleStartTimes[1]) + breakTotal + pausedTotal + distractedTotal); + // Add legend + AddLegend(); + + // Add axis + Line yAxis = new() + { + X1 = 0, + Y1 = offset - 5, + X2 = 0, + Y2 = 1.5 * 20 * (sessionData.CycleStartTimes.Count) + offset - 5, + Stroke = Brushes.Black, + StrokeThickness = 2 + }; + Canvas.SetTop(yAxis, 0); + Canvas.SetLeft(yAxis, 0); + chartCanvas.Children.Add(yAxis); + + // Convert ProcessInfoList to List<(DateTime Start, DateTime End)> + var processIntervals = sessionData.ProcessInfoList + .Select(p => (Start: DateTime.Parse(p.StartTime), End: DateTime.Parse(p.EndTime))) + .ToList(); + var allBreaksIntervals = sessionData.BreakInfoList + .Select(p => (Start: DateTime.Parse(p.StartTime), End: DateTime.Parse(p.EndTime))) + .ToList(); + var pauseIntervals = sessionData.BreakInfoList + .Where(p => !p.IsBreak) + .Select(p => (Start: DateTime.Parse(p.StartTime), End: DateTime.Parse(p.EndTime))) + .ToList(); + var breakIntervals = sessionData.BreakInfoList + .Where(p => p.IsBreak) + .Select(p => (Start: DateTime.Parse(p.StartTime), End: DateTime.Parse(p.EndTime))) + .ToList(); + + // Merge intervals + var mergedDistractionIntervals = ProcessMonitor.MergeIntervals(processIntervals); + distractedTotal = ProcessMonitor.SubtractBreaksFromDistractionTime(mergedDistractionIntervals, allBreaksIntervals); + breakTotal = breakIntervals.Aggregate(TimeSpan.Zero, (total, interval) => total + (interval.End - interval.Start)); + } + + private void AddLegend() + { + // Create legend items + var legendItems = new List<(string Text, Brush Color)> + { + ("Productive Work", Brushes.Green), + ("Distraction", Brushes.Red), + ("Break", Brushes.Blue), + ("Pause", Brushes.LightBlue) + }; + + // Draw legend + for (int i = 0; i < legendItems.Count; i++) + { + var legendItem = legendItems[i]; + + // Draw legend color box + Rectangle colorBox = new Rectangle + { + Width = 20, + Height = 20, + Fill = legendItem.Color + }; + Canvas.SetLeft(colorBox, 0); + Canvas.SetTop(colorBox, -110 + i * 25 + offset); + chartCanvas.Children.Add(colorBox); + + // Draw legend text + TextBlock legendText = new TextBlock + { + Text = legendItem.Text, + Foreground = Brushes.Black, + FontSize = 20 + }; + legendText.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + Canvas.SetLeft(legendText, 25); + Canvas.SetTop(legendText, -115 + i * 25 + offset); + chartCanvas.Children.Add(legendText); + } + } + + private void UpdateSeriesValues() + { + // Calculate the new values for each series + TimeSpan totalTime = breakTotal + pausedTotal + productiveTotal; + TimeSpan distractedTime = distractedTotal; + TimeSpan breakTime = breakTotal; + TimeSpan pausedTime = pausedTotal; + TimeSpan productiveTime = totalTime - distractedTime - breakTime - pausedTime; + + // Convert TimeSpan to formatted strings + string distractedTimeFormatted = FormatTimeSpan(distractedTime); + string breakTimeFormatted = FormatTimeSpan(breakTime); + string pausedTimeFormatted = FormatTimeSpan(pausedTime); + string productiveTimeFormatted = FormatTimeSpan(productiveTime); + + // Update the series values + Series = new List<ISeries> + { + new PieSeries<double> { + Name = $"Distractions:", + Values = distractedTime.TotalMinutes > 0 ? [distractedTime.TotalMinutes] : new List<double>(), + MaxRadialColumnWidth = 50, + Fill = new SolidColorPaint(SKColors.Red), + ToolTipLabelFormatter = value => $"{distractedTimeFormatted} minutes" + }, + new PieSeries<double> { + Name = $"Breaks:", + Values = breakTime.TotalMinutes > 0 ? [breakTime.TotalMinutes] : new List<double>(), + MaxRadialColumnWidth = 50, + Fill = new SolidColorPaint(SKColors.Blue), + ToolTipLabelFormatter = value => $"{breakTimeFormatted} minutes" + }, + new PieSeries<double> { + Name = $"Manually paused:", + Values = pausedTime.TotalMinutes > 0 ? [pausedTime.TotalMinutes] : new List<double>(), + MaxRadialColumnWidth = 50, + Fill = new SolidColorPaint(SKColors.LightBlue), + ToolTipLabelFormatter = value => $"{pausedTimeFormatted} minutes" + }, + new PieSeries<double> { + Name = $"Productive:", + Values = productiveTime.TotalMinutes > 0 ? [productiveTime.TotalMinutes] : new List<double>(), + MaxRadialColumnWidth = 50, + Fill = new SolidColorPaint(SKColors.Green), + ToolTipLabelFormatter = value => $"{productiveTimeFormatted} minutes" + } + }; + + CenterImageSource = new BitmapImage(new Uri($"pack://application:,,,/Assets/MascottAnimation/Up/{final_stage}/frame0.png")); + + // Notify the UI to update the chart + DataContext = null; + DataContext = this; + } + + // Helper method to format TimeSpan as "mm:ss" + private string FormatTimeSpan(TimeSpan timeSpan) + { + return $"{(int)timeSpan.TotalMinutes}:{timeSpan.Seconds:D2}"; + } + + public class SessionData + { + public required Dictionary<int, string> CycleStartTimes { get; set; } + public required string SessionEndTime { get; set; } + public required List<ProcessInfo> ProcessInfoList { get; set; } + public required List<BreakInfo> BreakInfoList { get; set; } + public required int FinalDistractionStage { get; set; } + } + + public class ProcessInfo + { + public required string ProcessName { get; set; } + public required string StartTime { get; set; } + public required string EndTime { get; set; } + public required int Cycle { get; set; } + } + + public class BreakInfo + { + public required string StartTime { get; set; } + public required string EndTime { get; set; } + public required bool IsBreak { get; set; } + public required int Cycle { get; set; } + } + } +} diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Services/ProcessMonitor.cs b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Services/ProcessMonitor.cs index 6d8eee2bb6957d0d824b11605fbc91ff14239120..8b381439055a0f3a1cfbbdfda3241ccc33eaae79 100644 --- a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Services/ProcessMonitor.cs +++ b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Services/ProcessMonitor.cs @@ -20,7 +20,7 @@ namespace InnoLabProjektDektopApp.Services { public DateTime StartTime { get; set; } = startTime; public DateTime EndTime { get; set; } = endTime; - public bool isBreak { get; set; } = isBreak; + public bool IsBreak { get; set; } = isBreak; public int Cycle { get; set; } = cycle; } @@ -151,7 +151,7 @@ namespace InnoLabProjektDektopApp.Services StartMonitoring(); } - public void FinishSession() + public string FinishSession() { StopMonitoring(); EndBreak(); @@ -159,11 +159,11 @@ namespace InnoLabProjektDektopApp.Services // Create a session info object var sessionInfo = new { - cycleStartTimes = cycleStartTimeList, + CycleStartTimes = cycleStartTimeList, SessionEndTime = DateTime.Now, ProcessInfoList = processInfoList, BreakInfoList = breakInfoList, - finalDistractionStage = CalculateCurrentDistractionStage(DateTime.Now) + FinalDistractionStage = CalculateCurrentDistractionStage(DateTime.Now) }; // the file will be saved in the following folder: @@ -184,16 +184,12 @@ namespace InnoLabProjektDektopApp.Services processStartTimes.Clear(); cycleStartTimeList.Clear(); currentCycle = 1; - - Window mainWindow = Application.Current.MainWindow; - NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService; - mainNavigation.Navigate( - new End(sessionInfoFilePath)); + return sessionInfoFilePath; } public int CalculateCurrentDistractionStage(DateTime endTime) { - + if (cycleStartTimeList.Count == 0) return 4; TimeSpan totalDistractionTime = CalculateTotalDistractionTime(); TimeSpan totalBreakTime = breakInfoList.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.EndTime - interval.StartTime)); Debug.WriteLine("Total distraction time: {0}", totalDistractionTime); @@ -372,16 +368,20 @@ namespace InnoLabProjektDektopApp.Services if (intervals.Count == 0) return TimeSpan.Zero; - var mergedIntervals = new List<(DateTime Start, DateTime End)> { intervals[0] }; + var mergedIntervals = MergeIntervals(intervals); + return SubtractBreaksFromDistractionTime(mergedIntervals, [.. breakInfoList.Select(b => (b.StartTime, b.EndTime))]); + } - // Iterate through the intervals, starting from the second one + public static List<(DateTime Start, DateTime End)> MergeIntervals(List<(DateTime Start, DateTime End)> intervals) + { + var mergedIntervals = new List<(DateTime Start, DateTime End)> { intervals[0] }; foreach (var interval in intervals.Skip(1)) { var last = mergedIntervals.Last(); // If the current interval overlaps with the last merged interval, merge them - if (interval.StartTime <= last.End) + if (interval.Start <= last.End) { - mergedIntervals[^1] = (last.Start, new DateTime(Math.Max(last.End.Ticks, interval.EndTime.Ticks))); + mergedIntervals[^1] = (last.Start, new DateTime(Math.Max(last.End.Ticks, interval.End.Ticks))); } else { @@ -389,45 +389,51 @@ namespace InnoLabProjektDektopApp.Services mergedIntervals.Add(interval); } } + return mergedIntervals; + } - // Remove overlaps with breaks - foreach (var breakInfo in breakInfoList) + public static TimeSpan SubtractBreaksFromDistractionTime( + List<(DateTime Start, DateTime End)> mergedDistractionIntervals, + List<(DateTime Start, DateTime End)> breakIntervals) + { + Debug.WriteLine(mergedDistractionIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start)).ToString()); + foreach (var breakInfo in breakIntervals) { - mergedIntervals = mergedIntervals.SelectMany(interval => + mergedDistractionIntervals = [.. mergedDistractionIntervals.SelectMany(interval => { // If the interval ends before the break starts or starts after the break ends, keep the interval as is - if (interval.End <= breakInfo.StartTime || interval.Start >= breakInfo.EndTime) + if (interval.End <= breakInfo.Start || interval.Start >= breakInfo.End) { return [interval]; } // If the interval starts before the break and ends after the break, split the interval into two - else if (interval.Start < breakInfo.StartTime && interval.End > breakInfo.EndTime) + else if (interval.Start < breakInfo.Start && interval.End > breakInfo.End) { return [ - (interval.Start, breakInfo.StartTime), - (breakInfo.EndTime, interval.End) + (interval.Start, breakInfo.Start), + (breakInfo.End, interval.End) ]; } // If the interval starts before the break and ends during the break, adjust the end time to the start of the break - else if (interval.Start < breakInfo.StartTime) + else if (interval.Start < breakInfo.Start) { - return new List<(DateTime Start, DateTime End)> { (interval.Start, breakInfo.StartTime) }; + return new List<(DateTime Start, DateTime End)> { (interval.Start, breakInfo.Start) }; } // If the interval starts during the break and ends after the break, adjust the start time to the end of the break - else if (interval.End > breakInfo.EndTime) + else if (interval.End > breakInfo.End) { - return [(breakInfo.EndTime, interval.End)]; + return [(breakInfo.End, interval.End)]; } // If the interval is completely within the break, remove the interval else { return []; } - }).ToList(); + })]; } - - return mergedIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start)); + Debug.WriteLine(mergedDistractionIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start)).ToString()); + return mergedDistractionIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start)); } @@ -438,12 +444,15 @@ namespace InnoLabProjektDektopApp.Services var programs = JsonConvert.DeserializeObject<dynamic>(fileContent); var blockedProcesses = new List<string>(); - foreach (var program in programs.Programs) + if (programs?.Programs != null) { - if (program.distracting == true) + foreach (var program in programs.Programs) { - string processName = program.processName; - blockedProcesses.Add(processName.ToLower()); + if (program.distracting == true) + { + string processName = program.processName; + blockedProcesses.Add(processName.ToLower()); + } } } diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Utils/NotifyIconManager.cs b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Utils/NotifyIconManager.cs index 29562588dccaf74d4e35931c9167de127dc2d23e..de15c3ce805fafade8dce62c4fe2762d19210644 100644 --- a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Utils/NotifyIconManager.cs +++ b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/Utils/NotifyIconManager.cs @@ -9,6 +9,7 @@ using System.Windows.Media; using System.Windows.Navigation; using System.IO; using System.Text.Json; +using InnoLabProjektDektopApp.Screens.Regulaer; namespace InnoLabProjektDektopApp.Utils { @@ -98,12 +99,12 @@ namespace InnoLabProjektDektopApp.Utils _notifyIcon.ContextMenuStrip.Items.Add("End Session", Drawing.Image.FromFile("Assets/end.png"), (sender, args) => { - _processMonitor.FinishSession(); + var jsonpath = _processMonitor.FinishSession(); StopCountdown(); + RerenderContextMenu(); Window mainWindow = Application.Current.MainWindow; NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService; - mainNavigation.Navigate(new Overview()); - RerenderContextMenu(true); + mainNavigation.Navigate(new SessionStatistics(jsonpath)); }); } else @@ -194,9 +195,12 @@ namespace InnoLabProjektDektopApp.Utils } else { - _processMonitor.FinishSession(); + var jsonpath = _processMonitor.FinishSession(); StopCountdown(); RerenderContextMenu(); + Window mainWindow = Application.Current.MainWindow; + NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService; + mainNavigation.Navigate(new SessionStatistics(jsonpath)); } } }