add stats page
All checks were successful
All checks were successful
This commit is contained in:
@@ -168,6 +168,208 @@ class YearlyMileage {
|
||||
);
|
||||
}
|
||||
|
||||
class StatsAbout {
|
||||
final Map<int, StatsYear> years;
|
||||
|
||||
StatsAbout({required this.years});
|
||||
|
||||
factory StatsAbout.fromJson(Map<String, dynamic> json) {
|
||||
final mileageByYear = <int, double>{};
|
||||
final classByYear = <int, List<StatsClassMileage>>{};
|
||||
final networkByYear = <int, List<StatsNetworkMileage>>{};
|
||||
final stationByYear = <int, List<StatsStationVisits>>{};
|
||||
final winnersByYear = <int, int>{};
|
||||
|
||||
void addYearMileage(dynamic entry) {
|
||||
if (entry is Map<String, dynamic>) {
|
||||
final year = entry['year'] is int
|
||||
? entry['year'] as int
|
||||
: int.tryParse('${entry['year']}');
|
||||
if (year != null) {
|
||||
mileageByYear[year] = _asDouble(entry['mileage']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json['year_mileages'] is List) {
|
||||
for (final entry in json['year_mileages']) {
|
||||
if (entry is Map<String, dynamic>) {
|
||||
addYearMileage(entry);
|
||||
} else if (entry is Map) {
|
||||
addYearMileage(entry
|
||||
.map((key, value) => MapEntry(key.toString(), value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<StatsClassMileage> parseClassList(dynamic value) {
|
||||
if (value is List) {
|
||||
return value
|
||||
.whereType<Map>()
|
||||
.map((e) => StatsClassMileage.fromJson(
|
||||
e.map((key, value) => MapEntry(key.toString(), value))))
|
||||
.toList();
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
List<StatsNetworkMileage> parseNetworkList(dynamic value) {
|
||||
if (value is List) {
|
||||
return value
|
||||
.whereType<Map>()
|
||||
.map((e) => StatsNetworkMileage.fromJson(
|
||||
e.map((key, value) => MapEntry(key.toString(), value))))
|
||||
.toList();
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
List<StatsStationVisits> parseStationList(dynamic value) {
|
||||
if (value is List) {
|
||||
return value
|
||||
.whereType<Map>()
|
||||
.map((e) => StatsStationVisits.fromJson(
|
||||
e.map((key, value) => MapEntry(key.toString(), value))))
|
||||
.toList();
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
void parseYearMap<T>(
|
||||
dynamic source,
|
||||
Map<int, T> target,
|
||||
T Function(dynamic value) mapper,
|
||||
) {
|
||||
if (source is Map) {
|
||||
source.forEach((key, value) {
|
||||
final year = int.tryParse(key.toString());
|
||||
if (year == null) return;
|
||||
target[year] = mapper(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
parseYearMap<List<StatsClassMileage>>(
|
||||
json['top_classes'],
|
||||
classByYear,
|
||||
parseClassList,
|
||||
);
|
||||
parseYearMap<List<StatsNetworkMileage>>(
|
||||
json['top_networks'],
|
||||
networkByYear,
|
||||
parseNetworkList,
|
||||
);
|
||||
parseYearMap<List<StatsStationVisits>>(
|
||||
json['top_stations'],
|
||||
stationByYear,
|
||||
parseStationList,
|
||||
);
|
||||
if (json['year_winners'] is Map) {
|
||||
(json['year_winners'] as Map).forEach((key, value) {
|
||||
final year = int.tryParse(key.toString());
|
||||
if (year == null) return;
|
||||
if (value is List) {
|
||||
winnersByYear[year] = value.length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final years = <int>{
|
||||
...mileageByYear.keys,
|
||||
...classByYear.keys,
|
||||
...networkByYear.keys,
|
||||
...stationByYear.keys,
|
||||
...winnersByYear.keys,
|
||||
}..removeWhere((year) => year == 0);
|
||||
|
||||
final yearMap = <int, StatsYear>{};
|
||||
for (final year in years) {
|
||||
yearMap[year] = StatsYear(
|
||||
year: year,
|
||||
mileage: mileageByYear[year] ?? 0,
|
||||
topClasses: classByYear[year] ?? const [],
|
||||
topNetworks: networkByYear[year] ?? const [],
|
||||
topStations: stationByYear[year] ?? const [],
|
||||
winnerCount: winnersByYear[year] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
return StatsAbout(years: yearMap);
|
||||
}
|
||||
|
||||
List<StatsYear> get sortedYears {
|
||||
final list = years.values.toList();
|
||||
list.sort((a, b) => b.year.compareTo(a.year));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
class StatsYear {
|
||||
final int year;
|
||||
final double mileage;
|
||||
final List<StatsClassMileage> topClasses;
|
||||
final List<StatsNetworkMileage> topNetworks;
|
||||
final List<StatsStationVisits> topStations;
|
||||
final int winnerCount;
|
||||
|
||||
StatsYear({
|
||||
required this.year,
|
||||
required this.mileage,
|
||||
required this.topClasses,
|
||||
required this.topNetworks,
|
||||
required this.topStations,
|
||||
required this.winnerCount,
|
||||
});
|
||||
}
|
||||
|
||||
class StatsClassMileage {
|
||||
final String locoClass;
|
||||
final double mileage;
|
||||
|
||||
StatsClassMileage({
|
||||
required this.locoClass,
|
||||
required this.mileage,
|
||||
});
|
||||
|
||||
factory StatsClassMileage.fromJson(Map<String, dynamic> json) =>
|
||||
StatsClassMileage(
|
||||
locoClass: _asString(json['loco_class'], 'Unknown'),
|
||||
mileage: _asDouble(json['mileage']),
|
||||
);
|
||||
}
|
||||
|
||||
class StatsNetworkMileage {
|
||||
final String network;
|
||||
final double mileage;
|
||||
|
||||
StatsNetworkMileage({
|
||||
required this.network,
|
||||
required this.mileage,
|
||||
});
|
||||
|
||||
factory StatsNetworkMileage.fromJson(Map<String, dynamic> json) =>
|
||||
StatsNetworkMileage(
|
||||
network: _asString(json['network'], 'Unknown'),
|
||||
mileage: _asDouble(json['mileage']),
|
||||
);
|
||||
}
|
||||
|
||||
class StatsStationVisits {
|
||||
final String station;
|
||||
final int visits;
|
||||
|
||||
StatsStationVisits({
|
||||
required this.station,
|
||||
required this.visits,
|
||||
});
|
||||
|
||||
factory StatsStationVisits.fromJson(Map<String, dynamic> json) =>
|
||||
StatsStationVisits(
|
||||
station: _asString(json['station'], 'Unknown'),
|
||||
visits: _asInt(json['visits']),
|
||||
);
|
||||
}
|
||||
|
||||
class Loco {
|
||||
final int id;
|
||||
final String type, number, locoClass;
|
||||
|
||||
Reference in New Issue
Block a user