More tests, better functionality, the server serves the dashboard now on configurable ports. websocket stuff fixed, though I dont think data is really being sent / recieved...

This commit is contained in:
Nate Anderson
2025-06-13 17:09:08 -06:00
parent b373a93f0e
commit d34cccc253
65 changed files with 166413 additions and 1801 deletions
+16 -2
View File
@@ -9,8 +9,8 @@ class Achievement {
final String description;
@JsonKey(name: 'xp_reward')
final int xpReward;
@JsonKey(name: 'achieved_at')
final String? achievedAt;
@JsonKey(name: 'achieved_at', fromJson: _achievedAtFromJson, toJson: _achievedAtToJson)
final DateTime? achievedAt;
@JsonKey(name: 'level_at_achievement')
final int? levelAtAchievement;
@@ -29,4 +29,18 @@ class Achievement {
Map<String, dynamic> toJson() => _$AchievementToJson(this);
bool get isAchieved => achievedAt != null;
static DateTime? _achievedAtFromJson(dynamic achievedAt) {
if (achievedAt == null) return null;
if (achievedAt is int) {
return DateTime.fromMillisecondsSinceEpoch(achievedAt);
} else if (achievedAt is String) {
return DateTime.parse(achievedAt);
}
throw ArgumentError('Invalid achievedAt format: $achievedAt');
}
static int? _achievedAtToJson(DateTime? achievedAt) {
return achievedAt?.millisecondsSinceEpoch;
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ Achievement _$AchievementFromJson(Map<String, dynamic> json) => Achievement(
name: json['name'] as String,
description: json['description'] as String,
xpReward: (json['xp_reward'] as num).toInt(),
achievedAt: json['achieved_at'] as String?,
achievedAt: Achievement._achievedAtFromJson(json['achieved_at']),
levelAtAchievement: (json['level_at_achievement'] as num?)?.toInt(),
);
@@ -21,6 +21,6 @@ Map<String, dynamic> _$AchievementToJson(Achievement instance) =>
'name': instance.name,
'description': instance.description,
'xp_reward': instance.xpReward,
'achieved_at': instance.achievedAt,
'achieved_at': Achievement._achievedAtToJson(instance.achievedAt),
'level_at_achievement': instance.levelAtAchievement,
};
+15 -1
View File
@@ -8,7 +8,8 @@ class Activity {
final String type;
final String application;
final Map<String, dynamic>? metadata;
final String timestamp;
@JsonKey(fromJson: _timestampFromJson, toJson: _timestampToJson)
final DateTime timestamp;
@JsonKey(name: 'duration_seconds')
final int durationSeconds;
@@ -25,4 +26,17 @@ class Activity {
_$ActivityFromJson(json);
Map<String, dynamic> toJson() => _$ActivityToJson(this);
static DateTime _timestampFromJson(dynamic timestamp) {
if (timestamp is int) {
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} else if (timestamp is String) {
return DateTime.parse(timestamp);
}
throw ArgumentError('Invalid timestamp format: $timestamp');
}
static int _timestampToJson(DateTime timestamp) {
return timestamp.millisecondsSinceEpoch;
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ Activity _$ActivityFromJson(Map<String, dynamic> json) => Activity(
type: json['type'] as String,
application: json['application'] as String,
metadata: json['metadata'] as Map<String, dynamic>?,
timestamp: json['timestamp'] as String,
timestamp: Activity._timestampFromJson(json['timestamp']),
durationSeconds: (json['duration_seconds'] as num).toInt(),
);
@@ -20,6 +20,6 @@ Map<String, dynamic> _$ActivityToJson(Activity instance) => <String, dynamic>{
'type': instance.type,
'application': instance.application,
'metadata': instance.metadata,
'timestamp': instance.timestamp,
'timestamp': Activity._timestampToJson(instance.timestamp),
'duration_seconds': instance.durationSeconds,
};
+34 -8
View File
@@ -9,10 +9,10 @@ class ApplicationClassification {
final String applicationName;
@JsonKey(name: 'category_id')
final String categoryId;
@JsonKey(name: 'created_at')
final String createdAt;
@JsonKey(name: 'updated_at')
final String updatedAt;
@JsonKey(name: 'created_at', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime createdAt;
@JsonKey(name: 'updated_at', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime updatedAt;
ApplicationClassification({
required this.id,
@@ -26,6 +26,19 @@ class ApplicationClassification {
_$ApplicationClassificationFromJson(json);
Map<String, dynamic> toJson() => _$ApplicationClassificationToJson(this);
static DateTime _dateTimeFromJson(dynamic timestamp) {
if (timestamp is int) {
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} else if (timestamp is String) {
return DateTime.parse(timestamp);
}
throw ArgumentError('Invalid timestamp format: $timestamp');
}
static int _dateTimeToJson(DateTime timestamp) {
return timestamp.millisecondsSinceEpoch;
}
}
@JsonSerializable()
@@ -33,10 +46,10 @@ class UnclassifiedApplication {
final int id;
@JsonKey(name: 'application_name')
final String applicationName;
@JsonKey(name: 'first_seen')
final String firstSeen;
@JsonKey(name: 'last_seen')
final String lastSeen;
@JsonKey(name: 'first_seen', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime firstSeen;
@JsonKey(name: 'last_seen', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime lastSeen;
@JsonKey(name: 'occurrence_count')
final int occurrenceCount;
@@ -52,6 +65,19 @@ class UnclassifiedApplication {
_$UnclassifiedApplicationFromJson(json);
Map<String, dynamic> toJson() => _$UnclassifiedApplicationToJson(this);
static DateTime _dateTimeFromJson(dynamic timestamp) {
if (timestamp is int) {
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} else if (timestamp is String) {
return DateTime.parse(timestamp);
}
throw ArgumentError('Invalid timestamp format: $timestamp');
}
static int _dateTimeToJson(DateTime timestamp) {
return timestamp.millisecondsSinceEpoch;
}
}
@JsonSerializable()
@@ -12,8 +12,8 @@ ApplicationClassification _$ApplicationClassificationFromJson(
id: (json['id'] as num).toInt(),
applicationName: json['application_name'] as String,
categoryId: json['category_id'] as String,
createdAt: json['created_at'] as String,
updatedAt: json['updated_at'] as String,
createdAt: ApplicationClassification._dateTimeFromJson(json['created_at']),
updatedAt: ApplicationClassification._dateTimeFromJson(json['updated_at']),
);
Map<String, dynamic> _$ApplicationClassificationToJson(
@@ -22,8 +22,8 @@ Map<String, dynamic> _$ApplicationClassificationToJson(
'id': instance.id,
'application_name': instance.applicationName,
'category_id': instance.categoryId,
'created_at': instance.createdAt,
'updated_at': instance.updatedAt,
'created_at': ApplicationClassification._dateTimeToJson(instance.createdAt),
'updated_at': ApplicationClassification._dateTimeToJson(instance.updatedAt),
};
UnclassifiedApplication _$UnclassifiedApplicationFromJson(
@@ -31,8 +31,8 @@ UnclassifiedApplication _$UnclassifiedApplicationFromJson(
) => UnclassifiedApplication(
id: (json['id'] as num).toInt(),
applicationName: json['application_name'] as String,
firstSeen: json['first_seen'] as String,
lastSeen: json['last_seen'] as String,
firstSeen: UnclassifiedApplication._dateTimeFromJson(json['first_seen']),
lastSeen: UnclassifiedApplication._dateTimeFromJson(json['last_seen']),
occurrenceCount: (json['occurrence_count'] as num).toInt(),
);
@@ -41,8 +41,8 @@ Map<String, dynamic> _$UnclassifiedApplicationToJson(
) => <String, dynamic>{
'id': instance.id,
'application_name': instance.applicationName,
'first_seen': instance.firstSeen,
'last_seen': instance.lastSeen,
'first_seen': UnclassifiedApplication._dateTimeToJson(instance.firstSeen),
'last_seen': UnclassifiedApplication._dateTimeToJson(instance.lastSeen),
'occurrence_count': instance.occurrenceCount,
};
+34 -6
View File
@@ -8,7 +8,8 @@ class DashboardStats {
final StreakStats streaks;
@JsonKey(name: 'recent_activity')
final List<RecentActivity> recentActivity;
final int timestamp;
@JsonKey(fromJson: _timestampFromJson, toJson: _timestampToJson)
final DateTime timestamp;
DashboardStats({
required this.today,
@@ -21,6 +22,19 @@ class DashboardStats {
_$DashboardStatsFromJson(json);
Map<String, dynamic> toJson() => _$DashboardStatsToJson(this);
static DateTime _timestampFromJson(dynamic timestamp) {
if (timestamp is int) {
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} else if (timestamp is String) {
return DateTime.parse(timestamp);
}
throw ArgumentError('Invalid timestamp format: $timestamp');
}
static int _timestampToJson(DateTime timestamp) {
return timestamp.millisecondsSinceEpoch;
}
}
@JsonSerializable()
@@ -68,15 +82,16 @@ class StreakStats {
@JsonSerializable()
class RecentActivity {
final String type;
final String application;
final String timestamp;
final String? type;
final String? application;
@JsonKey(fromJson: _timestampFromJson, toJson: _timestampToJson)
final DateTime timestamp;
@JsonKey(name: 'duration_seconds')
final int durationSeconds;
RecentActivity({
required this.type,
required this.application,
this.type,
this.application,
required this.timestamp,
required this.durationSeconds,
});
@@ -85,6 +100,19 @@ class RecentActivity {
_$RecentActivityFromJson(json);
Map<String, dynamic> toJson() => _$RecentActivityToJson(this);
static DateTime _timestampFromJson(dynamic timestamp) {
if (timestamp is int) {
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} else if (timestamp is String) {
return DateTime.parse(timestamp);
}
throw ArgumentError('Invalid timestamp format: $timestamp');
}
static int _timestampToJson(DateTime timestamp) {
return timestamp.millisecondsSinceEpoch;
}
}
@JsonSerializable()
@@ -13,7 +13,7 @@ DashboardStats _$DashboardStatsFromJson(Map<String, dynamic> json) =>
recentActivity: (json['recent_activity'] as List<dynamic>)
.map((e) => RecentActivity.fromJson(e as Map<String, dynamic>))
.toList(),
timestamp: (json['timestamp'] as num).toInt(),
timestamp: DashboardStats._timestampFromJson(json['timestamp']),
);
Map<String, dynamic> _$DashboardStatsToJson(DashboardStats instance) =>
@@ -21,7 +21,7 @@ Map<String, dynamic> _$DashboardStatsToJson(DashboardStats instance) =>
'today': instance.today,
'streaks': instance.streaks,
'recent_activity': instance.recentActivity,
'timestamp': instance.timestamp,
'timestamp': DashboardStats._timestampToJson(instance.timestamp),
};
TodayStats _$TodayStatsFromJson(Map<String, dynamic> json) => TodayStats(
@@ -54,9 +54,9 @@ Map<String, dynamic> _$StreakStatsToJson(StreakStats instance) =>
RecentActivity _$RecentActivityFromJson(Map<String, dynamic> json) =>
RecentActivity(
type: json['type'] as String,
application: json['application'] as String,
timestamp: json['timestamp'] as String,
type: json['type'] as String?,
application: json['application'] as String?,
timestamp: RecentActivity._timestampFromJson(json['timestamp']),
durationSeconds: (json['duration_seconds'] as num).toInt(),
);
@@ -64,7 +64,7 @@ Map<String, dynamic> _$RecentActivityToJson(RecentActivity instance) =>
<String, dynamic>{
'type': instance.type,
'application': instance.application,
'timestamp': instance.timestamp,
'timestamp': RecentActivity._timestampToJson(instance.timestamp),
'duration_seconds': instance.durationSeconds,
};
+30 -8
View File
@@ -5,19 +5,19 @@ part 'focus_session.g.dart';
@JsonSerializable()
class FocusSession {
final int id;
@JsonKey(name: 'start_time')
final String startTime;
@JsonKey(name: 'end_time')
final String? endTime;
@JsonKey(name: 'start_time', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime startTime;
@JsonKey(name: 'end_time', fromJson: _nullableDateTimeFromJson, toJson: _nullableDateTimeToJson)
final DateTime? endTime;
@JsonKey(name: 'duration_seconds')
final int? durationSeconds;
@JsonKey(name: 'xp_earned')
final int xpEarned;
final String status;
@JsonKey(name: 'created_at')
final String createdAt;
@JsonKey(name: 'updated_at')
final String updatedAt;
@JsonKey(name: 'created_at', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime createdAt;
@JsonKey(name: 'updated_at', fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime updatedAt;
FocusSession({
required this.id,
@@ -34,4 +34,26 @@ class FocusSession {
_$FocusSessionFromJson(json);
Map<String, dynamic> toJson() => _$FocusSessionToJson(this);
static DateTime _dateTimeFromJson(dynamic timestamp) {
if (timestamp is int) {
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} else if (timestamp is String) {
return DateTime.parse(timestamp);
}
throw ArgumentError('Invalid timestamp format: $timestamp');
}
static int _dateTimeToJson(DateTime timestamp) {
return timestamp.millisecondsSinceEpoch;
}
static DateTime? _nullableDateTimeFromJson(dynamic timestamp) {
if (timestamp == null) return null;
return _dateTimeFromJson(timestamp);
}
static int? _nullableDateTimeToJson(DateTime? timestamp) {
return timestamp?.millisecondsSinceEpoch;
}
}
@@ -8,23 +8,23 @@ part of 'focus_session.dart';
FocusSession _$FocusSessionFromJson(Map<String, dynamic> json) => FocusSession(
id: (json['id'] as num).toInt(),
startTime: json['start_time'] as String,
endTime: json['end_time'] as String?,
startTime: FocusSession._dateTimeFromJson(json['start_time']),
endTime: FocusSession._nullableDateTimeFromJson(json['end_time']),
durationSeconds: (json['duration_seconds'] as num?)?.toInt(),
xpEarned: (json['xp_earned'] as num).toInt(),
status: json['status'] as String,
createdAt: json['created_at'] as String,
updatedAt: json['updated_at'] as String,
createdAt: FocusSession._dateTimeFromJson(json['created_at']),
updatedAt: FocusSession._dateTimeFromJson(json['updated_at']),
);
Map<String, dynamic> _$FocusSessionToJson(FocusSession instance) =>
<String, dynamic>{
'id': instance.id,
'start_time': instance.startTime,
'end_time': instance.endTime,
'start_time': FocusSession._dateTimeToJson(instance.startTime),
'end_time': FocusSession._nullableDateTimeToJson(instance.endTime),
'duration_seconds': instance.durationSeconds,
'xp_earned': instance.xpEarned,
'status': instance.status,
'created_at': instance.createdAt,
'updated_at': instance.updatedAt,
'created_at': FocusSession._dateTimeToJson(instance.createdAt),
'updated_at': FocusSession._dateTimeToJson(instance.updatedAt),
};