Skip to content

Commit

Permalink
feat(workspace): implement daily achievement board
Browse files Browse the repository at this point in the history
Signed-off-by: hoyho <[email protected]>
  • Loading branch information
hoyho committed Jun 17, 2024
1 parent c301210 commit 937a183
Show file tree
Hide file tree
Showing 10 changed files with 487 additions and 183 deletions.
154 changes: 154 additions & 0 deletions Foundation/Statistics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using DynamicData;
using DynamicData.Kernel;
using iTimeSlot.Models;

public interface IStatistics
{
DailyStat ReadTodayData();
void ReadWeekData();

void CompleteTask(int minute);
void CompleteBreak(int minute);
}


//DiskStatistics is a class that implements IStatistics interface using disk as storage
public class DiskStatistics : IStatistics
{
private string iTimeslotDateFormat ="yyyy-MM-dd"; //date format: 2024-06-01
private string _dataPath = "";

public DiskStatistics(string dataPath)
{
_dataPath = dataPath;
}

private void EnsureExist()
{
if (string.IsNullOrEmpty(_dataPath))
{
throw new Exception("Data path is not set");
}
var parentDir = Path.GetDirectoryName(_dataPath);
if (string.IsNullOrEmpty(parentDir))
throw new Exception("Invalid Parent directory");

if (!Directory.Exists(parentDir))
{
Directory.CreateDirectory(parentDir);
}

if (!File.Exists(_dataPath))
{
File.WriteAllText(_dataPath, "{}");
}
}

public void CompleteBreak(int minute)
{
EnsureExist();
LogFinishedInterval(IntervalType.Break, minute);
}

public void CompleteTask(int minute)
{
EnsureExist();
LogFinishedInterval(IntervalType.Work, minute);
}



private void LogFinishedInterval(IntervalType type, int minute)
{
//read from disk and update
try
{
string jsonString = File.ReadAllText(_dataPath);
var stats = JsonSerializer.Deserialize(jsonString, new JsonContext().Stats);
if (stats == null)
{
stats = new Stats()
{
DailyStats = new List<DailyStat>() { }
};
}
else if (stats.DailyStats == null)
{
stats.DailyStats = new List<DailyStat>() { };
}

var entry = new DailyStat()
{
Date = DateTime.Today.ToString(iTimeslotDateFormat),
};

var existed = stats.DailyStats.FirstOrDefault(s => s.Date == DateTime.Today.ToString(iTimeslotDateFormat));
if (existed != null)
{
entry = existed;
}

//update entry
if (type == IntervalType.Work)
{
entry.WorkCount += 1;
entry.TotalWorkMinutes += minute;
}
else
{
entry.BreakCount += 1;
entry.TotalBreakMinutes += minute;
}

//add to list if not existed
if (existed == null)
{
stats.DailyStats.Add(entry);
}

var json = JsonSerializer.Serialize(stats, new JsonContext().Stats);
File.WriteAllText(_dataPath, json);
}
catch (Exception ex)
{
Console.WriteLine("Failed to update db:" + ex.Message);
}

}

public DailyStat ReadTodayData()
{
EnsureExist();
string jsonString = File.ReadAllText(_dataPath);
var stats = JsonSerializer.Deserialize(jsonString, new JsonContext().Stats);

var ds = new DailyStat()
{
Date = DateTime.Today.ToString(iTimeslotDateFormat),
};

if (stats == null || stats.DailyStats == null)
{
return ds;
}

var existed = stats.DailyStats.FirstOrDefault(s => s.Date == DateTime.Today.ToString(iTimeslotDateFormat));
if (existed != null)
{
ds = existed;
}

return ds;
}

public void ReadWeekData()
{
EnsureExist();
throw new NotImplementedException();
}
}
21 changes: 11 additions & 10 deletions Models/JsonContext.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Text.Json.Serialization;

namespace iTimeSlot.Models;

[JsonSerializable(typeof(Settings))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(TimeSpan))]
[JsonSerializable(typeof(TimeSlot))]
using System;
using System.Text.Json.Serialization;

namespace iTimeSlot.Models;

[JsonSerializable(typeof(Stats))]
[JsonSerializable(typeof(Settings))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(TimeSpan))]
[JsonSerializable(typeof(TimeSlot))]
internal partial class JsonContext : JsonSerializerContext {}
27 changes: 27 additions & 0 deletions Models/Stat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@


using System;
using System.Collections.Generic;

public class DailyStat
{
public string Date { get; set; }

Check warning on line 8 in Models/Stat.cs

View workflow job for this annotation

GitHub Actions / Pre Release (ubuntu-latest)

Non-nullable property 'Date' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 8 in Models/Stat.cs

View workflow job for this annotation

GitHub Actions / Pre Release (windows-latest)

Non-nullable property 'Date' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 8 in Models/Stat.cs

View workflow job for this annotation

GitHub Actions / Pre Release (macOS-latest)

Non-nullable property 'Date' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

//WorkCount is the number of work intervals completed
public int WorkCount { get; set; }

//TotalWorkMinutes is the total minutes spent on work
public int TotalWorkMinutes { get; set; }

//BreakCount is the number of break intervals completed
public int BreakCount { get; set; }

//TotalBreakMinutes is the total minutes spent on break
public int TotalBreakMinutes { get; set; }

}

public class Stats
{
public List<DailyStat> DailyStats { get; set; }

Check warning on line 26 in Models/Stat.cs

View workflow job for this annotation

GitHub Actions / Pre Release (ubuntu-latest)

Non-nullable property 'DailyStats' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 26 in Models/Stat.cs

View workflow job for this annotation

GitHub Actions / Pre Release (windows-latest)

Non-nullable property 'DailyStats' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 26 in Models/Stat.cs

View workflow job for this annotation

GitHub Actions / Pre Release (macOS-latest)

Non-nullable property 'DailyStats' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
122 changes: 65 additions & 57 deletions Models/TimeSlot.cs
Original file line number Diff line number Diff line change
@@ -1,57 +1,65 @@
using System;
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;

namespace iTimeSlot.Models
{
public class TimeSlot : ObservableObject
{
public bool IsSystemPreserved { get; set; }

private TimeSpan _ts;

[JsonPropertyName("TimeSpan")]
public TimeSpan Ts
{
get { return _ts; }
set { _ts = value; }
}


//keep this public parameterless constructor for json serialization and deserialization
[JsonConstructor]
public TimeSlot()
{

}

public TimeSlot(TimeSpan srcTs, bool isSystemPreserved = false)
{
this._ts = srcTs;
IsSystemPreserved = isSystemPreserved;
}

public TimeSlot(int minute, bool isSystemPreserved = false) : this(TimeSpan.FromMinutes(minute), isSystemPreserved)
{

}

public override string ToString()
{
int m = (int)_ts.TotalMinutes;
string unit = m >1? "mins" : "min";
return $"{(int)_ts.TotalMinutes} {unit}".TrimEnd();
}

public TimeSpan ToTimeSpan()
{
return _ts;
}

public int TotalSeconds()
{
return (int)_ts.TotalSeconds;
}

}
}
using System;
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;

namespace iTimeSlot.Models
{
public class TimeSlot : ObservableObject
{
public bool IsSystemPreserved { get; set; }
public IntervalType IntervalType { get; set; }

private TimeSpan _ts;

[JsonPropertyName("TimeSpan")]
public TimeSpan Ts
{
get { return _ts; }
set { _ts = value; }
}


//keep this public parameterless constructor for json serialization and deserialization
[JsonConstructor]
public TimeSlot()
{

}

public TimeSlot(TimeSpan srcTs, IntervalType iType, bool isSystemPreserved = false)
{
this._ts = srcTs;
IsSystemPreserved = isSystemPreserved;
IntervalType = iType;
}

public TimeSlot(int minute, IntervalType iType, bool isSystemPreserved = false) :
this(TimeSpan.FromMinutes(minute),iType, isSystemPreserved)
{

}

public override string ToString()
{
int m = (int)_ts.TotalMinutes;
string unit = m > 1 ? "mins" : "min";
return $"{(int)_ts.TotalMinutes} {unit}".TrimEnd();
}

public TimeSpan ToTimeSpan()
{
return _ts;
}

public int TotalSeconds()
{
return (int)_ts.TotalSeconds;
}

}
public enum IntervalType
{
Work,
Break
}
}
58 changes: 30 additions & 28 deletions Shared/DefaultConfig.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
using System;
using System.Collections.Generic;

namespace iTimeSlot.Shared
{

public static class DefaultConfig
{
public static string[] RemindBefores = new string[]
{
"1 minute before",
"2 minute before",
"5 minute before",
"10 minute before"
};


public static readonly List<TimeSpan> SysTimeSlots = new List<TimeSpan>{
TimeSpan.FromMinutes(5),
TimeSpan.FromMinutes(25),
TimeSpan.FromMinutes(60)
};

public static readonly bool SysCloseWithoutExit = true;
public static readonly bool SysPlaySound = false;
public static readonly bool SysShowProgInTray = true;
}

using System;
using System.Collections.Generic;

namespace iTimeSlot.Shared
{

public static class DefaultConfig
{
public static string[] RemindBefores = new string[]
{
"1 minute before",
"2 minute before",
"5 minute before",
"10 minute before"
};


public static readonly List<TimeSpan> SysWorkTimeSlots = new List<TimeSpan>{
TimeSpan.FromMinutes(25),
TimeSpan.FromMinutes(60)
};
public static readonly List<TimeSpan> SysBreakTimeSlots = new List<TimeSpan>{
TimeSpan.FromMinutes(5),
};

public static readonly bool SysCloseWithoutExit = true;
public static readonly bool SysPlaySound = false;
public static readonly bool SysShowProgInTray = true;
}

}
Loading

0 comments on commit 937a183

Please sign in to comment.