From daeafa91507abc70ff5b1e393e3192caf5534825 Mon Sep 17 00:00:00 2001
From: muellerp <Philipp1.Mueller@Student.Reutlingen-University.de>
Date: Tue, 4 Mar 2025 22:55:12 +0100
Subject: [PATCH] implemented statistics screen
---
.../InnoLabProjektDektopApp.csproj | 29 ++
.../FirstLaunch/03_2ProgramsList.xaml.cs | 13 +-
.../Screens/Regulaer/SessionStatistics.xaml | 26 ++
.../Regulaer/SessionStatistics.xaml.cs | 353 ++++++++++++++++++
.../Services/ProcessMonitor.cs | 73 ++--
.../Utils/NotifyIconManager.cs | 12 +-
6 files changed, 468 insertions(+), 38 deletions(-)
create mode 100644 InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml
create mode 100644 InnoLabProjektDektopApp/InnoLabProjektDektopApp/Screens/Regulaer/SessionStatistics.xaml.cs
diff --git a/InnoLabProjektDektopApp/InnoLabProjektDektopApp/InnoLabProjektDektopApp.csproj b/InnoLabProjektDektopApp/InnoLabProjektDektopApp/InnoLabProjektDektopApp.csproj
index 2e00aeb..365dc8d 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 a79295e..c8eca2c 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 0000000..e34880c
--- /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 0000000..ddf7675
--- /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 6d8eee2..8b38143 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 2956258..de15c3c 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));
}
}
}
--
GitLab