Skip to content
Snippets Groups Projects
Commit f3a27713 authored by Philipp Müller's avatar Philipp Müller
Browse files

Merge branch 'statistics-screen' into 'develop'

implemented statistics screen

See merge request !48
parents 6a87607b daeafa91
Branches
No related tags found
1 merge request!48implemented statistics screen
...@@ -296,6 +296,7 @@ ...@@ -296,6 +296,7 @@
<None Remove="Assets\MascottAnimation\Down\7\frame7.png" /> <None Remove="Assets\MascottAnimation\Down\7\frame7.png" />
<None Remove="Assets\MascottAnimation\Down\7\frame8.png" /> <None Remove="Assets\MascottAnimation\Down\7\frame8.png" />
<None Remove="Assets\MascottAnimation\Down\7\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\1\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\1\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame11.png" />
...@@ -373,6 +374,7 @@ ...@@ -373,6 +374,7 @@
<None Remove="Assets\MascottAnimation\Up\1\frame77.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame77.png" />
<None Remove="Assets\MascottAnimation\Up\1\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\1\frame8.png" />
<None Remove="Assets\MascottAnimation\Up\1\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\2\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\2\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame11.png" />
...@@ -401,6 +403,7 @@ ...@@ -401,6 +403,7 @@
<None Remove="Assets\MascottAnimation\Up\2\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame7.png" />
<None Remove="Assets\MascottAnimation\Up\2\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\2\frame8.png" />
<None Remove="Assets\MascottAnimation\Up\2\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\3\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\3\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame11.png" />
...@@ -422,6 +425,7 @@ ...@@ -422,6 +425,7 @@
<None Remove="Assets\MascottAnimation\Up\3\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame7.png" />
<None Remove="Assets\MascottAnimation\Up\3\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\3\frame8.png" />
<None Remove="Assets\MascottAnimation\Up\3\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\4\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\4\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame11.png" />
...@@ -476,6 +480,7 @@ ...@@ -476,6 +480,7 @@
<None Remove="Assets\MascottAnimation\Up\4\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame7.png" />
<None Remove="Assets\MascottAnimation\Up\4\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\4\frame8.png" />
<None Remove="Assets\MascottAnimation\Up\4\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\5\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\5\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame11.png" />
...@@ -504,6 +509,7 @@ ...@@ -504,6 +509,7 @@
<None Remove="Assets\MascottAnimation\Up\5\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame7.png" />
<None Remove="Assets\MascottAnimation\Up\5\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\5\frame8.png" />
<None Remove="Assets\MascottAnimation\Up\5\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\6\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\6\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame11.png" />
...@@ -531,6 +537,7 @@ ...@@ -531,6 +537,7 @@
<None Remove="Assets\MascottAnimation\Up\6\frame7.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame7.png" />
<None Remove="Assets\MascottAnimation\Up\6\frame8.png" /> <None Remove="Assets\MascottAnimation\Up\6\frame8.png" />
<None Remove="Assets\MascottAnimation\Up\6\frame9.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\frame1.png" />
<None Remove="Assets\MascottAnimation\Up\7\frame10.png" /> <None Remove="Assets\MascottAnimation\Up\7\frame10.png" />
<None Remove="Assets\MascottAnimation\Up\7\frame11.png" /> <None Remove="Assets\MascottAnimation\Up\7\frame11.png" />
...@@ -622,6 +629,7 @@ ...@@ -622,6 +629,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="2.0.1" /> <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" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.3" /> <PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.3" />
<PackageReference Include="System.Management" Version="9.0.1" /> <PackageReference Include="System.Management" Version="9.0.1" />
...@@ -643,6 +651,27 @@ ...@@ -643,6 +651,27 @@
<Content Include="Assets\gamesicon.png"> <Content Include="Assets\gamesicon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </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"> <Content Include="Assets\pause.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
......
...@@ -58,7 +58,9 @@ namespace InnoLabProjektDektopApp ...@@ -58,7 +58,9 @@ namespace InnoLabProjektDektopApp
{ {
var filteredItems = value.Where( var filteredItems = value.Where(
item => string.IsNullOrEmpty(searchText) || item => string.IsNullOrEmpty(searchText) ||
item.ProcessName.Contains(searchText, StringComparison.CurrentCultureIgnoreCase)) item.ProcessName.Contains(searchText, StringComparison.CurrentCultureIgnoreCase) ||
item.MainWindowTitle.Contains(searchText, StringComparison.CurrentCultureIgnoreCase)
)
.ToList(); .ToList();
foreach (var item in filteredItems) foreach (var item in filteredItems)
...@@ -67,6 +69,7 @@ namespace InnoLabProjektDektopApp ...@@ -67,6 +69,7 @@ namespace InnoLabProjektDektopApp
ShowNewItemInList(item.ProcessName, item.MainWindowTitle, item.IsDistracting); ShowNewItemInList(item.ProcessName, item.MainWindowTitle, item.IsDistracting);
} }
} }
RefreshRunningProcessList(searchText);
} }
...@@ -134,9 +137,15 @@ namespace InnoLabProjektDektopApp ...@@ -134,9 +137,15 @@ namespace InnoLabProjektDektopApp
RefreshRunningProcessList(); RefreshRunningProcessList();
} }
private void RefreshRunningProcessList() private void RefreshRunningProcessList(string filter = null)
{ {
var windowedProcesses = ProcessMonitor.GetWindowedProcesses(); 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(); ItemsPanel2.Children.Clear();
foreach (var process in windowedProcesses) foreach (var process in windowedProcesses)
{ {
......
<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>
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; }
}
}
}
...@@ -20,7 +20,7 @@ namespace InnoLabProjektDektopApp.Services ...@@ -20,7 +20,7 @@ namespace InnoLabProjektDektopApp.Services
{ {
public DateTime StartTime { get; set; } = startTime; public DateTime StartTime { get; set; } = startTime;
public DateTime EndTime { get; set; } = endTime; public DateTime EndTime { get; set; } = endTime;
public bool isBreak { get; set; } = isBreak; public bool IsBreak { get; set; } = isBreak;
public int Cycle { get; set; } = cycle; public int Cycle { get; set; } = cycle;
} }
...@@ -153,7 +153,7 @@ namespace InnoLabProjektDektopApp.Services ...@@ -153,7 +153,7 @@ namespace InnoLabProjektDektopApp.Services
StartMonitoring(); StartMonitoring();
} }
public void FinishSession() public string FinishSession()
{ {
StopMonitoring(); StopMonitoring();
EndBreak(); EndBreak();
...@@ -161,11 +161,11 @@ namespace InnoLabProjektDektopApp.Services ...@@ -161,11 +161,11 @@ namespace InnoLabProjektDektopApp.Services
// Create a session info object // Create a session info object
var sessionInfo = new var sessionInfo = new
{ {
cycleStartTimes = cycleStartTimeList, CycleStartTimes = cycleStartTimeList,
SessionEndTime = DateTime.Now, SessionEndTime = DateTime.Now,
ProcessInfoList = processInfoList, ProcessInfoList = processInfoList,
BreakInfoList = breakInfoList, BreakInfoList = breakInfoList,
finalDistractionStage = CalculateCurrentDistractionStage(DateTime.Now) FinalDistractionStage = CalculateCurrentDistractionStage(DateTime.Now)
}; };
// the file will be saved in the following folder: // the file will be saved in the following folder:
...@@ -186,11 +186,7 @@ namespace InnoLabProjektDektopApp.Services ...@@ -186,11 +186,7 @@ namespace InnoLabProjektDektopApp.Services
processStartTimes.Clear(); processStartTimes.Clear();
cycleStartTimeList.Clear(); cycleStartTimeList.Clear();
currentCycle = 1; currentCycle = 1;
return sessionInfoFilePath;
Window mainWindow = Application.Current.MainWindow;
NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService;
mainNavigation.Navigate(
new End(sessionInfoFilePath));
} }
public static int tmpStage = 4; public static int tmpStage = 4;
...@@ -380,16 +376,20 @@ namespace InnoLabProjektDektopApp.Services ...@@ -380,16 +376,20 @@ namespace InnoLabProjektDektopApp.Services
if (intervals.Count == 0) if (intervals.Count == 0)
return TimeSpan.Zero; 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)) foreach (var interval in intervals.Skip(1))
{ {
var last = mergedIntervals.Last(); var last = mergedIntervals.Last();
// If the current interval overlaps with the last merged interval, merge them // 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 else
{ {
...@@ -397,45 +397,51 @@ namespace InnoLabProjektDektopApp.Services ...@@ -397,45 +397,51 @@ namespace InnoLabProjektDektopApp.Services
mergedIntervals.Add(interval); mergedIntervals.Add(interval);
} }
} }
return mergedIntervals;
}
// Remove overlaps with breaks public static TimeSpan SubtractBreaksFromDistractionTime(
foreach (var breakInfo in breakInfoList) 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 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]; return [interval];
} }
// If the interval starts before the break and ends after the break, split the interval into two // 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 return
[ [
(interval.Start, breakInfo.StartTime), (interval.Start, breakInfo.Start),
(breakInfo.EndTime, interval.End) (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 // 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 // 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 // If the interval is completely within the break, remove the interval
else else
{ {
return []; return [];
} }
}).ToList(); })];
} }
Debug.WriteLine(mergedDistractionIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start)).ToString());
return mergedIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start)); return mergedDistractionIntervals.Aggregate(TimeSpan.Zero, (sum, interval) => sum + (interval.End - interval.Start));
} }
...@@ -446,6 +452,8 @@ namespace InnoLabProjektDektopApp.Services ...@@ -446,6 +452,8 @@ namespace InnoLabProjektDektopApp.Services
var programs = JsonConvert.DeserializeObject<dynamic>(fileContent); var programs = JsonConvert.DeserializeObject<dynamic>(fileContent);
var blockedProcesses = new List<string>(); var blockedProcesses = new List<string>();
if (programs?.Programs != null)
{
foreach (var program in programs.Programs) foreach (var program in programs.Programs)
{ {
if (program.distracting == true) if (program.distracting == true)
...@@ -454,6 +462,7 @@ namespace InnoLabProjektDektopApp.Services ...@@ -454,6 +462,7 @@ namespace InnoLabProjektDektopApp.Services
blockedProcesses.Add(processName.ToLower()); blockedProcesses.Add(processName.ToLower());
} }
} }
}
return blockedProcesses; return blockedProcesses;
} }
......
...@@ -9,6 +9,7 @@ using System.Windows.Media; ...@@ -9,6 +9,7 @@ using System.Windows.Media;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using InnoLabProjektDektopApp.Screens.Regulaer;
namespace InnoLabProjektDektopApp.Utils namespace InnoLabProjektDektopApp.Utils
{ {
...@@ -98,12 +99,12 @@ namespace InnoLabProjektDektopApp.Utils ...@@ -98,12 +99,12 @@ namespace InnoLabProjektDektopApp.Utils
_notifyIcon.ContextMenuStrip.Items.Add("End Session", Drawing.Image.FromFile("Assets/end.png"), (sender, args) => _notifyIcon.ContextMenuStrip.Items.Add("End Session", Drawing.Image.FromFile("Assets/end.png"), (sender, args) =>
{ {
_processMonitor.FinishSession(); var jsonpath = _processMonitor.FinishSession();
StopCountdown(); StopCountdown();
RerenderContextMenu();
Window mainWindow = Application.Current.MainWindow; Window mainWindow = Application.Current.MainWindow;
NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService; NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService;
mainNavigation.Navigate(new Overview()); mainNavigation.Navigate(new SessionStatistics(jsonpath));
RerenderContextMenu(true);
}); });
} }
else else
...@@ -194,9 +195,12 @@ namespace InnoLabProjektDektopApp.Utils ...@@ -194,9 +195,12 @@ namespace InnoLabProjektDektopApp.Utils
} }
else else
{ {
_processMonitor.FinishSession(); var jsonpath = _processMonitor.FinishSession();
StopCountdown(); StopCountdown();
RerenderContextMenu(); RerenderContextMenu();
Window mainWindow = Application.Current.MainWindow;
NavigationService mainNavigation = ((System.Windows.Navigation.NavigationWindow)mainWindow).NavigationService;
mainNavigation.Navigate(new SessionStatistics(jsonpath));
} }
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment