class WakatimeSummary { final List data; final String start; final String end; final CumulativeTotal cumulativeTotal; final DailyAverage dailyAverage; WakatimeSummary({ required this.data, required this.start, required this.end, required this.cumulativeTotal, required this.dailyAverage, }); factory WakatimeSummary.fromJson(Map json) { return WakatimeSummary( data: (json['data'] as List).map((e) => SummaryData.fromJson(e)).toList(), start: json['start'], end: json['end'], cumulativeTotal: CumulativeTotal.fromJson(json['cumulative_total']), dailyAverage: DailyAverage.fromJson(json['daily_average']), ); } // 汇总语言数据 List getAggregatedLanguages() { final Map aggregated = {}; for (var dayData in data) { for (var lang in dayData.languages) { if (aggregated.containsKey(lang.name)) { final existing = aggregated[lang.name]!; aggregated[lang.name] = Language( name: lang.name, totalSeconds: existing.totalSeconds + lang.totalSeconds, digital: _formatTime(existing.totalSeconds + lang.totalSeconds), decimal: ((existing.totalSeconds + lang.totalSeconds) / 3600) .toStringAsFixed(2), text: _formatTimeText(existing.totalSeconds + lang.totalSeconds), hours: ((existing.totalSeconds + lang.totalSeconds) / 3600).floor(), minutes: ((existing.totalSeconds + lang.totalSeconds) % 3600 / 60) .floor(), seconds: ((existing.totalSeconds + lang.totalSeconds) % 60).floor(), percent: 0, // 将在计算完总和后更新 ); } else { aggregated[lang.name] = lang; } } } final totalSeconds = aggregated.values .fold(0, (sum, lang) => sum + lang.totalSeconds); final result = aggregated.values .map((lang) => Language( name: lang.name, totalSeconds: lang.totalSeconds, digital: lang.digital, decimal: lang.decimal, text: lang.text, hours: lang.hours, minutes: lang.minutes, seconds: lang.seconds, percent: (lang.totalSeconds / totalSeconds * 100), )) .toList(); // 按使用时间排序 result.sort((a, b) => b.totalSeconds.compareTo(a.totalSeconds)); return result; } // 汇总编辑器数据 List getAggregatedEditors() { final Map aggregated = {}; for (var dayData in data) { for (var editor in dayData.editors) { if (aggregated.containsKey(editor.name)) { final existing = aggregated[editor.name]!; aggregated[editor.name] = Editor( name: editor.name, totalSeconds: existing.totalSeconds + editor.totalSeconds, digital: _formatTime(existing.totalSeconds + editor.totalSeconds), decimal: ((existing.totalSeconds + editor.totalSeconds) / 3600) .toStringAsFixed(2), text: _formatTimeText(existing.totalSeconds + editor.totalSeconds), hours: ((existing.totalSeconds + editor.totalSeconds) / 3600).floor(), minutes: ((existing.totalSeconds + editor.totalSeconds) % 3600 / 60) .floor(), seconds: ((existing.totalSeconds + editor.totalSeconds) % 60).floor(), percent: 0, // 将在计算完总和后更新 ); } else { aggregated[editor.name] = editor; } } } final totalSeconds = aggregated.values .fold(0, (sum, editor) => sum + editor.totalSeconds); final result = aggregated.values .map((editor) => Editor( name: editor.name, totalSeconds: editor.totalSeconds, digital: editor.digital, decimal: editor.decimal, text: editor.text, hours: editor.hours, minutes: editor.minutes, seconds: editor.seconds, percent: (editor.totalSeconds / totalSeconds * 100), )) .toList(); // 按使用时间排序 result.sort((a, b) => b.totalSeconds.compareTo(a.totalSeconds)); return result; } // 汇总项目数据 List getAggregatedProjects() { final Map aggregated = {}; for (var dayData in data) { for (var project in dayData.projects) { if (aggregated.containsKey(project.name)) { final existing = aggregated[project.name]!; aggregated[project.name] = Project( name: project.name, totalSeconds: existing.totalSeconds + project.totalSeconds, color: project.color, digital: _formatTime(existing.totalSeconds + project.totalSeconds), decimal: ((existing.totalSeconds + project.totalSeconds) / 3600) .toStringAsFixed(2), text: _formatTimeText(existing.totalSeconds + project.totalSeconds), hours: ((existing.totalSeconds + project.totalSeconds) / 3600).floor(), minutes: ((existing.totalSeconds + project.totalSeconds) % 3600 / 60) .floor(), seconds: ((existing.totalSeconds + project.totalSeconds) % 60).floor(), percent: 0, // 将在计算完总和后更新 ); } else { aggregated[project.name] = project; } } } final totalSeconds = aggregated.values .fold(0, (sum, project) => sum + project.totalSeconds); final result = aggregated.values .map((project) => Project( name: project.name, totalSeconds: project.totalSeconds, color: project.color, digital: project.digital, decimal: project.decimal, text: project.text, hours: project.hours, minutes: project.minutes, seconds: project.seconds, percent: (project.totalSeconds / totalSeconds * 100), )) .toList(); // 按使用时间排序 result.sort((a, b) => b.totalSeconds.compareTo(a.totalSeconds)); return result; } // 格式化时间为 HH:mm:ss String _formatTime(double seconds) { final hours = (seconds / 3600).floor(); final minutes = ((seconds % 3600) / 60).floor(); final secs = (seconds % 60).floor(); return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}'; } // 格式化时间为可读文本 String _formatTimeText(double seconds) { final hours = (seconds / 3600).floor(); final minutes = ((seconds % 3600) / 60).floor(); final secs = (seconds % 60).floor(); if (hours > 0) { return '$hours hr${hours > 1 ? 's' : ''} ${minutes > 0 ? '$minutes min${minutes > 1 ? 's' : ''}' : ''}'; } else if (minutes > 0) { return '$minutes min${minutes > 1 ? 's' : ''}'; } else { return '$secs sec${secs > 1 ? 's' : ''}'; } } } class SummaryData { final GrandTotal grandTotal; final Range range; final List projects; final List languages; final List editors; final List operatingSystems; final List categories; SummaryData({ required this.grandTotal, required this.range, required this.projects, required this.languages, required this.editors, required this.operatingSystems, required this.categories, }); factory SummaryData.fromJson(Map json) { return SummaryData( grandTotal: GrandTotal.fromJson(json['grand_total']), range: Range.fromJson(json['range']), projects: (json['projects'] as List).map((e) => Project.fromJson(e)).toList(), languages: (json['languages'] as List).map((e) => Language.fromJson(e)).toList(), editors: (json['editors'] as List).map((e) => Editor.fromJson(e)).toList(), operatingSystems: (json['operating_systems'] as List) .map((e) => OperatingSystem.fromJson(e)) .toList(), categories: (json['categories'] as List) .map((e) => Category.fromJson(e)) .toList(), ); } } class GrandTotal { final int hours; final int minutes; final double totalSeconds; final String digital; final String decimal; final String text; GrandTotal({ required this.hours, required this.minutes, required this.totalSeconds, required this.digital, required this.decimal, required this.text, }); factory GrandTotal.fromJson(Map json) { return GrandTotal( hours: json['hours'], minutes: json['minutes'], totalSeconds: json['total_seconds'].toDouble(), digital: json['digital'], decimal: json['decimal'], text: json['text'], ); } } class Range { final String start; final String end; final String date; final String text; final String timezone; Range({ required this.start, required this.end, required this.date, required this.text, required this.timezone, }); factory Range.fromJson(Map json) { return Range( start: json['start'], end: json['end'], date: json['date'], text: json['text'], timezone: json['timezone'], ); } } class Project { final String name; final double totalSeconds; final String? color; final String digital; final String decimal; final String text; final int hours; final int minutes; final int seconds; final double percent; Project({ required this.name, required this.totalSeconds, this.color, required this.digital, required this.decimal, required this.text, required this.hours, required this.minutes, required this.seconds, required this.percent, }); factory Project.fromJson(Map json) { return Project( name: json['name'], totalSeconds: json['total_seconds'].toDouble(), color: json['color'], digital: json['digital'], decimal: json['decimal'], text: json['text'], hours: json['hours'], minutes: json['minutes'], seconds: json['seconds'], percent: json['percent'].toDouble(), ); } } class Language { final String name; final double totalSeconds; final String digital; final String decimal; final String text; final int hours; final int minutes; final int seconds; final double percent; Language({ required this.name, required this.totalSeconds, required this.digital, required this.decimal, required this.text, required this.hours, required this.minutes, required this.seconds, required this.percent, }); factory Language.fromJson(Map json) { return Language( name: json['name'], totalSeconds: json['total_seconds'].toDouble(), digital: json['digital'], decimal: json['decimal'], text: json['text'], hours: json['hours'], minutes: json['minutes'], seconds: json['seconds'], percent: json['percent'].toDouble(), ); } } class Editor { final String name; final double totalSeconds; final String digital; final String decimal; final String text; final int hours; final int minutes; final int seconds; final double percent; Editor({ required this.name, required this.totalSeconds, required this.digital, required this.decimal, required this.text, required this.hours, required this.minutes, required this.seconds, required this.percent, }); factory Editor.fromJson(Map json) { return Editor( name: json['name'], totalSeconds: json['total_seconds'].toDouble(), digital: json['digital'], decimal: json['decimal'], text: json['text'], hours: json['hours'], minutes: json['minutes'], seconds: json['seconds'], percent: json['percent'].toDouble(), ); } } class OperatingSystem { final String name; final double totalSeconds; final String digital; final String decimal; final String text; final int hours; final int minutes; final int seconds; final double percent; OperatingSystem({ required this.name, required this.totalSeconds, required this.digital, required this.decimal, required this.text, required this.hours, required this.minutes, required this.seconds, required this.percent, }); factory OperatingSystem.fromJson(Map json) { return OperatingSystem( name: json['name'], totalSeconds: json['total_seconds'].toDouble(), digital: json['digital'], decimal: json['decimal'], text: json['text'], hours: json['hours'], minutes: json['minutes'], seconds: json['seconds'], percent: json['percent'].toDouble(), ); } } class Category { final String name; final double totalSeconds; final String digital; final String decimal; final String text; final int hours; final int minutes; final int seconds; final double percent; Category({ required this.name, required this.totalSeconds, required this.digital, required this.decimal, required this.text, required this.hours, required this.minutes, required this.seconds, required this.percent, }); factory Category.fromJson(Map json) { return Category( name: json['name'], totalSeconds: json['total_seconds'].toDouble(), digital: json['digital'], decimal: json['decimal'], text: json['text'], hours: json['hours'], minutes: json['minutes'], seconds: json['seconds'], percent: json['percent'].toDouble(), ); } } class CumulativeTotal { final double seconds; final String text; final String digital; final String decimal; CumulativeTotal({ required this.seconds, required this.text, required this.digital, required this.decimal, }); factory CumulativeTotal.fromJson(Map json) { return CumulativeTotal( seconds: json['seconds'].toDouble(), text: json['text'], digital: json['digital'], decimal: json['decimal'], ); } } class DailyAverage { final int holidays; final int daysMinusHolidays; final int daysIncludingHolidays; final int seconds; final int secondsIncludingOtherLanguage; final String text; final String textIncludingOtherLanguage; DailyAverage({ required this.holidays, required this.daysMinusHolidays, required this.daysIncludingHolidays, required this.seconds, required this.secondsIncludingOtherLanguage, required this.text, required this.textIncludingOtherLanguage, }); factory DailyAverage.fromJson(Map json) { return DailyAverage( holidays: json['holidays'], daysMinusHolidays: json['days_minus_holidays'], daysIncludingHolidays: json['days_including_holidays'], seconds: json['seconds'], secondsIncludingOtherLanguage: json['seconds_including_other_language'], text: json['text'], textIncludingOtherLanguage: json['text_including_other_language'], ); } }