Compare commits
2 Commits
fcf845af4d
...
enhancemen
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e97f6723a | |||
| 9a0386f22d |
@@ -143,7 +143,7 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
|
|||||||
return rowsAffected > 0;
|
return rowsAffected > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a player with the given [id] exists in the database.
|
/// Checks if a player with the given [playerId] exists in the database.
|
||||||
/// Returns `true` if the player exists, `false` otherwise.
|
/// Returns `true` if the player exists, `false` otherwise.
|
||||||
Future<bool> playerExists({required String playerId}) async {
|
Future<bool> playerExists({required String playerId}) async {
|
||||||
final query = select(playerTable)..where((p) => p.id.equals(playerId));
|
final query = select(playerTable)..where((p) => p.id.equals(playerId));
|
||||||
@@ -157,9 +157,11 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
|
|||||||
required String newName,
|
required String newName,
|
||||||
}) async {
|
}) async {
|
||||||
// Get previous name and name count for the player before updating
|
// Get previous name and name count for the player before updating
|
||||||
final previousPlayerName = await (select(
|
final previousPlayerName =
|
||||||
playerTable,
|
await (select(playerTable)..where((p) => p.id.equals(playerId)))
|
||||||
)..where((p) => p.id.equals(playerId))).map((row) => row.name).getSingle();
|
.map((row) => row.name)
|
||||||
|
.getSingleOrNull() ??
|
||||||
|
'';
|
||||||
final previousNameCount = await getNameCount(name: previousPlayerName);
|
final previousNameCount = await getNameCount(name: previousPlayerName);
|
||||||
|
|
||||||
await (update(playerTable)..where((p) => p.id.equals(playerId))).write(
|
await (update(playerTable)..where((p) => p.id.equals(playerId))).write(
|
||||||
@@ -197,7 +199,7 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
|
|||||||
return count ?? 0;
|
return count ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the count of players with the given [name] in the database.
|
/// Retrieves the count of players with the given [name].
|
||||||
Future<int> getNameCount({required String name}) async {
|
Future<int> getNameCount({required String name}) async {
|
||||||
final query = select(playerTable)..where((p) => p.name.equals(name));
|
final query = select(playerTable)..where((p) => p.name.equals(name));
|
||||||
final result = await query.get();
|
final result = await query.get();
|
||||||
|
|||||||
@@ -18,9 +18,18 @@ class StatisticsView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _StatisticsViewState extends State<StatisticsView> {
|
class _StatisticsViewState extends State<StatisticsView> {
|
||||||
List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 1));
|
List<(Player, int)> winCounts = List.filled(6, (
|
||||||
List<(String, int)> matchCounts = List.filled(6, ('Skeleton Player', 1));
|
Player(name: 'Skeleton Player'),
|
||||||
List<(String, double)> winRates = List.filled(6, ('Skeleton Player', 1));
|
1,
|
||||||
|
));
|
||||||
|
List<(Player, int)> matchCounts = List.filled(6, (
|
||||||
|
Player(name: 'Skeleton Player'),
|
||||||
|
1,
|
||||||
|
));
|
||||||
|
List<(Player, double)> winRates = List.filled(6, (
|
||||||
|
Player(name: 'Skeleton Player'),
|
||||||
|
1,
|
||||||
|
));
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -121,7 +130,10 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
players: players,
|
players: players,
|
||||||
context: context,
|
context: context,
|
||||||
);
|
);
|
||||||
winRates = computeWinRatePercent(wins: winCounts, matches: matchCounts);
|
winRates = computeWinRatePercent(
|
||||||
|
winCounts: winCounts,
|
||||||
|
matchCounts: matchCounts,
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
@@ -130,47 +142,47 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
|
|
||||||
/// Calculates the number of wins for each player
|
/// Calculates the number of wins for each player
|
||||||
/// and returns a sorted list of tuples (playerName, winCount)
|
/// and returns a sorted list of tuples (playerName, winCount)
|
||||||
List<(String, int)> _calculateWinsForAllPlayers({
|
List<(Player, int)> _calculateWinsForAllPlayers({
|
||||||
required List<Match> matches,
|
required List<Match> matches,
|
||||||
required List<Player> players,
|
required List<Player> players,
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
}) {
|
}) {
|
||||||
List<(String, int)> winCounts = [];
|
List<(Player, int)> winCounts = [];
|
||||||
final loc = AppLocalizations.of(context);
|
final loc = AppLocalizations.of(context);
|
||||||
|
|
||||||
// Getting the winners
|
// Getting the winners
|
||||||
for (var match in matches) {
|
for (var match in matches) {
|
||||||
final winner = match.winner;
|
final winner = match.winner;
|
||||||
if (winner != null) {
|
if (winner != null) {
|
||||||
final index = winCounts.indexWhere((entry) => entry.$1 == winner.id);
|
final index = winCounts.indexWhere((entry) => entry.$1.id == winner.id);
|
||||||
// -1 means winner not found in winCounts
|
// -1 means winner not found in winCounts
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
final current = winCounts[index].$2;
|
final current = winCounts[index].$2;
|
||||||
winCounts[index] = (winner.id, current + 1);
|
winCounts[index] = (winner, current + 1);
|
||||||
} else {
|
} else {
|
||||||
winCounts.add((winner.id, 1));
|
winCounts.add((winner, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding all players with zero wins
|
// Adding all players with zero wins
|
||||||
for (var player in players) {
|
for (var player in players) {
|
||||||
final index = winCounts.indexWhere((entry) => entry.$1 == player.id);
|
final index = winCounts.indexWhere((entry) => entry.$1.id == player.id);
|
||||||
// -1 means player not found in winCounts
|
// -1 means player not found in winCounts
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
winCounts.add((player.id, 0));
|
winCounts.add((player, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace player IDs with names
|
// Replace player IDs with names
|
||||||
for (int i = 0; i < winCounts.length; i++) {
|
for (int i = 0; i < winCounts.length; i++) {
|
||||||
final playerId = winCounts[i].$1;
|
final playerId = winCounts[i].$1.id;
|
||||||
final player = players.firstWhere(
|
final player = players.firstWhere(
|
||||||
(p) => p.id == playerId,
|
(p) => p.id == playerId,
|
||||||
orElse: () =>
|
orElse: () =>
|
||||||
Player(id: playerId, name: loc.not_available, description: ''),
|
Player(id: playerId, name: loc.not_available, description: ''),
|
||||||
);
|
);
|
||||||
winCounts[i] = (player.name, winCounts[i].$2);
|
winCounts[i] = (player, winCounts[i].$2);
|
||||||
}
|
}
|
||||||
|
|
||||||
winCounts.sort((a, b) => b.$2.compareTo(a.$2));
|
winCounts.sort((a, b) => b.$2.compareTo(a.$2));
|
||||||
@@ -180,60 +192,51 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
|
|
||||||
/// Calculates the number of matches played for each player
|
/// Calculates the number of matches played for each player
|
||||||
/// and returns a sorted list of tuples (playerName, matchCount)
|
/// and returns a sorted list of tuples (playerName, matchCount)
|
||||||
List<(String, int)> _calculateMatchAmountsForAllPlayers({
|
List<(Player, int)> _calculateMatchAmountsForAllPlayers({
|
||||||
required List<Match> matches,
|
required List<Match> matches,
|
||||||
required List<Player> players,
|
required List<Player> players,
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
}) {
|
}) {
|
||||||
List<(String, int)> matchCounts = [];
|
List<(Player, int)> matchCounts = [];
|
||||||
final loc = AppLocalizations.of(context);
|
final loc = AppLocalizations.of(context);
|
||||||
|
|
||||||
// Counting matches for each player
|
// Counting matches for each player
|
||||||
for (var match in matches) {
|
for (var match in matches) {
|
||||||
if (match.group != null) {
|
for (Player player in match.players) {
|
||||||
final members = match.group!.members.map((p) => p.id).toList();
|
// Check if the player is already in matchCounts
|
||||||
for (var playerId in members) {
|
final index = matchCounts.indexWhere(
|
||||||
final index = matchCounts.indexWhere((entry) => entry.$1 == playerId);
|
(entry) => entry.$1.id == player.id,
|
||||||
// -1 means player not found in matchCounts
|
);
|
||||||
if (index != -1) {
|
|
||||||
final current = matchCounts[index].$2;
|
// -1 -> not found
|
||||||
matchCounts[index] = (playerId, current + 1);
|
if (index == -1) {
|
||||||
} else {
|
// Add new entry
|
||||||
matchCounts.add((playerId, 1));
|
matchCounts.add((player, 1));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final members = match.players.map((p) => p.id).toList();
|
|
||||||
for (var playerId in members) {
|
|
||||||
final index = matchCounts.indexWhere((entry) => entry.$1 == playerId);
|
|
||||||
// -1 means player not found in matchCounts
|
|
||||||
if (index != -1) {
|
|
||||||
final current = matchCounts[index].$2;
|
|
||||||
matchCounts[index] = (playerId, current + 1);
|
|
||||||
} else {
|
} else {
|
||||||
matchCounts.add((playerId, 1));
|
// Update existing entry
|
||||||
|
final currentMatchAmount = matchCounts[index].$2;
|
||||||
|
matchCounts[index] = (player, currentMatchAmount + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding all players with zero matches
|
// Adding all players with zero matches
|
||||||
for (var player in players) {
|
for (var player in players) {
|
||||||
final index = matchCounts.indexWhere((entry) => entry.$1 == player.id);
|
final index = matchCounts.indexWhere((entry) => entry.$1.id == player.id);
|
||||||
// -1 means player not found in matchCounts
|
// -1 means player not found in matchCounts
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
matchCounts.add((player.id, 0));
|
matchCounts.add((player, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace player IDs with names
|
// Replace player IDs with names
|
||||||
for (int i = 0; i < matchCounts.length; i++) {
|
for (int i = 0; i < matchCounts.length; i++) {
|
||||||
final playerId = matchCounts[i].$1;
|
final playerId = matchCounts[i].$1.id;
|
||||||
final player = players.firstWhere(
|
final player = players.firstWhere(
|
||||||
(p) => p.id == playerId,
|
(p) => p.id == playerId,
|
||||||
orElse: () =>
|
orElse: () => Player(id: playerId, name: loc.not_available),
|
||||||
Player(id: playerId, name: loc.not_available, description: ''),
|
|
||||||
);
|
);
|
||||||
matchCounts[i] = (player.name, matchCounts[i].$2);
|
matchCounts[i] = (player, matchCounts[i].$2);
|
||||||
}
|
}
|
||||||
|
|
||||||
matchCounts.sort((a, b) => b.$2.compareTo(a.$2));
|
matchCounts.sort((a, b) => b.$2.compareTo(a.$2));
|
||||||
@@ -241,25 +244,24 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
return matchCounts;
|
return matchCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dart
|
List<(Player, double)> computeWinRatePercent({
|
||||||
List<(String, double)> computeWinRatePercent({
|
required List<(Player, int)> winCounts,
|
||||||
required List<(String, int)> wins,
|
required List<(Player, int)> matchCounts,
|
||||||
required List<(String, int)> matches,
|
|
||||||
}) {
|
}) {
|
||||||
final Map<String, int> winsMap = {for (var e in wins) e.$1: e.$2};
|
final Map<Player, int> winsMap = {for (var e in winCounts) e.$1: e.$2};
|
||||||
final Map<String, int> matchesMap = {for (var e in matches) e.$1: e.$2};
|
final Map<Player, int> matchesMap = {for (var e in matchCounts) e.$1: e.$2};
|
||||||
|
|
||||||
// Get all unique player names
|
// Get all unique player names
|
||||||
final names = {...winsMap.keys, ...matchesMap.keys};
|
final player = {...matchesMap.keys};
|
||||||
|
|
||||||
// Calculate win rates
|
// Calculate win rates
|
||||||
final result = names.map((name) {
|
final result = player.map((name) {
|
||||||
final int w = winsMap[name] ?? 0;
|
final int w = winsMap[name] ?? 0;
|
||||||
final int g = matchesMap[name] ?? 0;
|
final int m = matchesMap[name] ?? 0;
|
||||||
// Calculate percentage and round to 2 decimal places
|
// Calculate percentage and round to 2 decimal places
|
||||||
// Avoid division by zero
|
// Avoid division by zero
|
||||||
final double percent = (g > 0)
|
final double percent = (m > 0)
|
||||||
? double.parse(((w / g)).toStringAsFixed(2))
|
? double.parse(((w / m)).toStringAsFixed(2))
|
||||||
: 0;
|
: 0;
|
||||||
return (name, percent);
|
return (name, percent);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tallee/core/common.dart';
|
||||||
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
|
import 'package:tallee/data/models/player.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ class StatisticsTile extends StatelessWidget {
|
|||||||
final double width;
|
final double width;
|
||||||
|
|
||||||
/// A list of tuples containing labels and their corresponding numeric values.
|
/// A list of tuples containing labels and their corresponding numeric values.
|
||||||
final List<(String, num)> values;
|
final List<(Player, num)> values;
|
||||||
|
|
||||||
/// The maximum number of items to display.
|
/// The maximum number of items to display.
|
||||||
final int itemCount;
|
final int itemCount;
|
||||||
@@ -89,11 +92,29 @@ class StatisticsTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 4.0),
|
padding: const EdgeInsets.only(left: 4.0),
|
||||||
child: Text(
|
child: RichText(
|
||||||
values[index].$1,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
text: TextSpan(
|
||||||
fontSize: 16,
|
style: DefaultTextStyle.of(context).style,
|
||||||
fontWeight: FontWeight.bold,
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: values[index].$1.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: getNameCountText(values[index].$1),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: CustomTheme.textColor.withAlpha(
|
||||||
|
150,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user